func (client *client) ListInstances(filter InstanceFilter) ([]*ServiceInstance, error) { path := amalgam8.InstancesURL() queryParams := filter.asQueryParams() if len(queryParams) > 0 { path = fmt.Sprintf("%s?%s", path, queryParams.Encode()) } body, err := client.doRequest("GET", path, nil, http.StatusOK) if err != nil { return nil, err } s := struct { Instances []*ServiceInstance `json:"instances"` }{} err = json.Unmarshal(body, &s) if err != nil { return nil, newError(ErrorCodeInternalClientError, "error unmarshaling HTTP response body", err, "") } return s.Instances, nil }
func TestServiceInstancesFiltering(t *testing.T) { tc := struct { sname string // input service name expected int // expected result }{"http-1", http.StatusOK} c := defaultServerConfig() c.Registry.(*mockCatalog).prepopulateServices(services) serviceInstance := store.ServiceInstance{ID: "http-1", ServiceName: "http-1", Protocol: protocol.Amalgam8, Endpoint: &store.Endpoint{Value: "192.168.0.1:80", Type: "tcp"}, Status: "UP", TTL: 30 * time.Second, Metadata: metadata} c.Registry.(*mockCatalog).prepopulateInstances([]mockInstance{{serviceInstance}}) handler, err := setupServer(c) assert.Nil(t, err) filterParams := []string{"?fields=status,ttl,status", // duplicate field name in request "?fields=status,ttl,status,endpoint", "?fields=", // an empty fields query "?fields=endpoint,ttl,metadata,status"} // a "sunny day" test assert1 := func(inst *amalgam8.ServiceInstance) { assert.Equal(t, uint32(30), inst.TTL) assert.EqualValues(t, "UP", inst.Status) // make sure we don't get back Last Heartbeat assert.Nil(t, inst.LastHeartbeat) // make sure we don't get back the metadata assert.NotEqual(t, metadata, inst.Metadata) // make sure endpoint and id are sent in spite of not being supplied assert.Equal(t, "192.168.0.1:80", inst.Endpoint.Value, "Endpoint wasn't sent back") } assert2 := func(inst *amalgam8.ServiceInstance) { assert.Nil(t, inst.LastHeartbeat) assert.NotEqual(t, metadata, inst.Metadata) assert.Equal(t, "192.168.0.1:80", inst.Endpoint.Value, "Endpoint wasn't sent back") assert.Equal(t, "", inst.Status) } sunnyDayAssert := func(inst *amalgam8.ServiceInstance) { assert.Equal(t, serviceInstance.Endpoint.Value, inst.Endpoint.Value) assert.Equal(t, int(serviceInstance.TTL), int(inst.TTL)*int(time.Second)) assert.EqualValues(t, metadata, inst.Metadata) assert.Equal(t, serviceInstance.Status, inst.Status) } asserts := []func(*amalgam8.ServiceInstance){ assert1, assert1, assert2, sunnyDayAssert, } for i := range asserts { recorder := httptest.NewRecorder() req, err := http.NewRequest("GET", serverURL+amalgam8.InstancesURL()+filterParams[i]+"&service_name="+tc.sname, nil) assert.Nil(t, err) req.Header.Set("Content-Type", "application/json") handler.ServeHTTP(recorder, req) assert.Equal(t, tc.expected, recorder.Code, string(tc.sname)) if tc.expected == http.StatusOK { insts := amalgam8.InstancesList{} err = json.Unmarshal(recorder.Body.Bytes(), &insts) assert.Equal(t, 1, len(insts.Instances)) assert.NoError(t, err) inst := insts.Instances[0] asserts[i](inst) } } }
func TestServiceInstancesFilteringByFieldValues(t *testing.T) { c := defaultServerConfig() c.Registry.(*mockCatalog).prepopulateServices(services) serviceInstance1 := store.ServiceInstance{ID: "http-1", ServiceName: "http-1", Protocol: protocol.Amalgam8, Endpoint: &store.Endpoint{Value: "192.168.0.1", Type: "tcp"}, Status: "UP", TTL: 30 * time.Second, Metadata: metadata, Tags: []string{"DB", "NoSQL"}} serviceInstance2 := store.ServiceInstance{ID: "http-2", ServiceName: "http-1", Protocol: protocol.Amalgam8, Endpoint: &store.Endpoint{Value: "192.168.0.1", Type: "tcp"}, Status: "UP", TTL: 30 * time.Second, Metadata: metadata, Tags: []string{"DB"}} serviceInstance3 := store.ServiceInstance{ID: "http-3", ServiceName: "http-1", Protocol: protocol.Amalgam8, Endpoint: &store.Endpoint{Value: "192.168.0.1", Type: "tcp"}, Status: "STARTING", TTL: 30 * time.Second, Metadata: metadata, Tags: []string{"DB"}} serviceInstance4 := store.ServiceInstance{ID: "http-4", ServiceName: "http-1", Protocol: protocol.Amalgam8, Endpoint: &store.Endpoint{Value: "192.168.0.1", Type: "tcp"}, Status: "OUT_OF_SERVICE", TTL: 30 * time.Second, Metadata: metadata, Tags: []string{"DB", "NoSQL"}} serviceInstance5 := store.ServiceInstance{ID: "http-5", ServiceName: "http-1", Protocol: protocol.Amalgam8, Endpoint: &store.Endpoint{Value: "192.168.0.1", Type: "tcp"}, Status: "user_defined", TTL: 30 * time.Second, Metadata: metadata, Tags: []string{"DB", "NoSQL"}} c.Registry.(*mockCatalog).prepopulateInstances([]mockInstance{ mockInstance{serviceInstance1}, mockInstance{serviceInstance2}, mockInstance{serviceInstance3}, mockInstance{serviceInstance4}, mockInstance{serviceInstance5}, }) handler, err := setupServer(c) assert.Nil(t, err) filterParams := []string{ "?tags=DB,NoSQL", // 2 tags // 0 "?tags=DB", // just 1 tag // 1 "?tags=DB,MySQL", // filter out, because we do an AND // 2 "?status=UP", // status is up, should be included // 3 "?status=DOWN", // status is down, should not be included // 4 "?tags=DB,NoSQL&status=UP", // check multiple field values true // 5 "?tags=DB,NoSQL&status=DOWN", // check multiple field values false // 6 "?ttl=5", // check fields of non string/[]string types are bad request // 7 "?BLABLA=5", // check invalid field is bad request // 8 "?status=up", // check status ignore case // 9 "?status=ALL", // all the instances with any status // 10 "?status=STARTING", // all the instances with STARTING status // 11 "?status=OUT_OF_SERVICE", // all the instances with OUT_OF_SERVICE status // 12 "?status=ALL&tags=DB", // all the instances with any status and DB tag // 13 "?status=STARTING&tags=DB", // all the instances with STARTING status and DB tag // 14 "?tags=DB&status=STARTING", // all the instances with STARTING status and DB tag // 15 "?status=user_defined", // all the instances with user_defined status // 16 } assert0 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 3, len(instances)) } assert1 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 5, len(instances)) } assert2 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 0, len(instances)) } assert3 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 2, len(instances)) } assert4 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 0, len(instances)) } assert5 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 1, len(instances)) } assert6 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 0, len(instances)) } assert7 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusBadRequest, responseStatus) } assert8 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusBadRequest, responseStatus) } assert9 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 2, len(instances)) } assert10 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 5, len(instances)) } assert11 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 1, len(instances)) } assert12 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 1, len(instances)) } assert13 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 5, len(instances)) } assert14 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 1, len(instances)) } assert15 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 1, len(instances)) } assert16 := func(instances []*amalgam8.ServiceInstance, responseStatus int) { assert.Equal(t, http.StatusOK, responseStatus) assert.Equal(t, 1, len(instances)) } asserts := []func([]*amalgam8.ServiceInstance, int){ assert0, assert1, assert2, assert3, assert4, assert5, assert6, assert7, assert8, assert9, assert10, assert11, assert12, assert13, assert14, assert15, assert16, } for i := range asserts { recorder := httptest.NewRecorder() req, err := http.NewRequest("GET", serverURL+amalgam8.InstancesURL()+filterParams[i]+"&service_name=http-1", nil) assert.Nil(t, err) req.Header.Set("Content-Type", "application/json") handler.ServeHTTP(recorder, req) insts := amalgam8.InstancesList{} err = json.Unmarshal(recorder.Body.Bytes(), &insts) asserts[i](insts.Instances, recorder.Code) } }