github.com/m3db/m3@v1.5.0/src/cluster/placementhandler/get_test.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package placementhandler 22 23 import ( 24 "fmt" 25 "io/ioutil" 26 "net/http" 27 "net/http/httptest" 28 "testing" 29 30 "github.com/m3db/m3/src/cluster/client" 31 "github.com/m3db/m3/src/cluster/generated/proto/placementpb" 32 "github.com/m3db/m3/src/cluster/kv" 33 "github.com/m3db/m3/src/cluster/kv/mem" 34 "github.com/m3db/m3/src/cluster/placement" 35 "github.com/m3db/m3/src/cluster/placement/service" 36 "github.com/m3db/m3/src/cluster/placement/storage" 37 "github.com/m3db/m3/src/cluster/placementhandler/handleroptions" 38 "github.com/m3db/m3/src/cluster/services" 39 "github.com/m3db/m3/src/x/instrument" 40 41 "github.com/golang/mock/gomock" 42 "github.com/stretchr/testify/assert" 43 "github.com/stretchr/testify/require" 44 ) 45 46 func SetupPlacementTest(t *testing.T, ctrl *gomock.Controller) (*client.MockClient, *placement.MockService) { 47 mockClient := client.NewMockClient(ctrl) 48 require.NotNil(t, mockClient) 49 50 mockServices := services.NewMockServices(ctrl) 51 require.NotNil(t, mockServices) 52 53 mockPlacementService := placement.NewMockService(ctrl) 54 require.NotNil(t, mockPlacementService) 55 56 mockClient.EXPECT().Services(gomock.Any()).Return(mockServices, nil).AnyTimes() 57 mockServices.EXPECT().PlacementService(gomock.Any(), gomock.Any()).Return(mockPlacementService, nil).AnyTimes() 58 59 return mockClient, mockPlacementService 60 } 61 62 func setupPlacementTest(t *testing.T, ctrl *gomock.Controller, initPlacement placement.Placement) *client.MockClient { 63 mockClient := client.NewMockClient(ctrl) 64 require.NotNil(t, mockClient) 65 66 mockServices := services.NewMockServices(ctrl) 67 require.NotNil(t, mockServices) 68 69 mockClient.EXPECT().Services(gomock.Any()).Return(mockServices, nil).AnyTimes() 70 mockServices.EXPECT().PlacementService(gomock.Any(), gomock.Any()).DoAndReturn( 71 func(_ interface{}, opts placement.Options) (placement.Service, error) { 72 ps := service.NewPlacementService( 73 storage.NewPlacementStorage(mem.NewStore(), "", opts), 74 service.WithPlacementOptions(opts)) 75 if initPlacement != nil { 76 _, err := ps.Set(initPlacement) 77 require.NoError(t, err) 78 } 79 return ps, nil 80 }, 81 ).AnyTimes() 82 83 return mockClient 84 } 85 86 func TestPlacementGetHandler(t *testing.T) { 87 runForAllAllowedServices(func(serviceName string) { 88 ctrl := gomock.NewController(t) 89 defer ctrl.Finish() 90 91 mockClient, mockPlacementService := SetupPlacementTest(t, ctrl) 92 handlerOpts, err := NewHandlerOptions( 93 mockClient, placement.Configuration{}, nil, instrument.NewOptions()) 94 require.NoError(t, err) 95 handler := NewGetHandler(handlerOpts) 96 97 // Test successful get 98 w := httptest.NewRecorder() 99 req := httptest.NewRequest(GetHTTPMethod, M3DBGetURL, nil) 100 require.NotNil(t, req) 101 102 placementProto := &placementpb.Placement{ 103 Instances: map[string]*placementpb.Instance{ 104 "host1": &placementpb.Instance{ 105 Id: "host1", 106 IsolationGroup: "rack1", 107 Zone: "test", 108 Weight: 1, 109 Endpoint: "http://host1:1234", 110 Hostname: "host1", 111 Port: 1234, 112 Metadata: &placementpb.InstanceMetadata{ 113 DebugPort: 1, 114 }, 115 }, 116 "host2": &placementpb.Instance{ 117 Id: "host2", 118 IsolationGroup: "rack1", 119 Zone: "test", 120 Weight: 1, 121 Endpoint: "http://host2:1234", 122 Hostname: "host2", 123 Port: 1234, 124 Metadata: &placementpb.InstanceMetadata{ 125 DebugPort: 2, 126 }, 127 }, 128 }, 129 } 130 131 const placementJSON = `{"placement":{"instances":{"host1":{"id":"host1","isolationGroup":"rack1","zone":"test","weight":1,"endpoint":"http://host1:1234","shards":[],"shardSetId":0,"hostname":"host1","port":1234,"metadata":{"debugPort":1}},"host2":{"id":"host2","isolationGroup":"rack1","zone":"test","weight":1,"endpoint":"http://host2:1234","shards":[],"shardSetId":0,"hostname":"host2","port":1234,"metadata":{"debugPort":2}}},"replicaFactor":0,"numShards":0,"isSharded":false,"cutoverTime":"0","isMirrored":false,"maxShardSetId":0},"version":%d}` 132 133 placementObj, err := placement.NewPlacementFromProto(placementProto) 134 require.NoError(t, err) 135 136 svcDefaults := handleroptions.ServiceNameAndDefaults{ 137 ServiceName: serviceName, 138 } 139 140 mockPlacementService.EXPECT().Placement().Return(placementObj, nil) 141 handler.ServeHTTP(svcDefaults, w, req) 142 143 resp := w.Result() 144 body, _ := ioutil.ReadAll(resp.Body) 145 assert.Equal(t, http.StatusOK, resp.StatusCode) 146 assert.Equal(t, fmt.Sprintf(placementJSON, 0), string(body)) 147 148 // Test error case 149 w = httptest.NewRecorder() 150 req = httptest.NewRequest(GetHTTPMethod, M3DBGetURL, nil) 151 require.NotNil(t, req) 152 153 mockPlacementService.EXPECT().Placement().Return(nil, kv.ErrNotFound) 154 handler.ServeHTTP(svcDefaults, w, req) 155 156 resp = w.Result() 157 assert.Equal(t, http.StatusNotFound, resp.StatusCode) 158 159 // With bad version request 160 w = httptest.NewRecorder() 161 req = httptest.NewRequest(GetHTTPMethod, "/placement/get?version=foo", nil) 162 require.NotNil(t, req) 163 164 handler.ServeHTTP(svcDefaults, w, req) 165 resp = w.Result() 166 assert.Equal(t, http.StatusBadRequest, resp.StatusCode) 167 168 // With valid version request 169 w = httptest.NewRecorder() 170 req = httptest.NewRequest(GetHTTPMethod, "/placement/get?version=12", nil) 171 require.NotNil(t, req) 172 173 mockPlacementService.EXPECT().PlacementForVersion(12).Return(placementObj.Clone().SetVersion(12), nil) 174 175 handler.ServeHTTP(svcDefaults, w, req) 176 resp = w.Result() 177 body, _ = ioutil.ReadAll(resp.Body) 178 assert.Equal(t, http.StatusOK, resp.StatusCode) 179 assert.Equal(t, fmt.Sprintf(placementJSON, 12), string(body)) 180 }) 181 }