github.com/m3db/m3@v1.5.0/src/cluster/placementhandler/init_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 "errors" 25 "io/ioutil" 26 "net/http" 27 "net/http/httptest" 28 "strings" 29 "testing" 30 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/placement" 34 "github.com/m3db/m3/src/cluster/placementhandler/handleroptions" 35 "github.com/m3db/m3/src/x/instrument" 36 37 "github.com/golang/mock/gomock" 38 "github.com/stretchr/testify/assert" 39 "github.com/stretchr/testify/require" 40 ) 41 42 var ( 43 initTestPlacementProto = &placementpb.Placement{ 44 Instances: map[string]*placementpb.Instance{ 45 "host1": &placementpb.Instance{ 46 Id: "host1", 47 IsolationGroup: "rack1", 48 Zone: "test", 49 Weight: 1, 50 Endpoint: "http://host1:1234", 51 Hostname: "host1", 52 Port: 1234, 53 Metadata: &placementpb.InstanceMetadata{ 54 DebugPort: 0, 55 }, 56 }, 57 "host2": &placementpb.Instance{ 58 Id: "host2", 59 IsolationGroup: "rack1", 60 Zone: "test", 61 Weight: 1, 62 Endpoint: "http://host2:1234", 63 Hostname: "host2", 64 Port: 1234, 65 Metadata: &placementpb.InstanceMetadata{ 66 DebugPort: 0, 67 }, 68 }, 69 }, 70 } 71 ) 72 73 func TestPlacementInitHandler(t *testing.T) { 74 runForAllAllowedServices(func(serviceName string) { 75 ctrl := gomock.NewController(t) 76 defer ctrl.Finish() 77 78 mockClient, mockPlacementService := SetupPlacementTest(t, ctrl) 79 handlerOpts, err := NewHandlerOptions( 80 mockClient, placement.Configuration{}, nil, instrument.NewOptions()) 81 require.NoError(t, err) 82 handler := NewInitHandler(handlerOpts) 83 84 // Test placement init success 85 var ( 86 w = httptest.NewRecorder() 87 req *http.Request 88 ) 89 if serviceName == handleroptions.M3AggregatorServiceName { 90 req = httptest.NewRequest(InitHTTPMethod, M3DBInitURL, strings.NewReader(`{"instances": [{"id": "host1","isolation_group": "rack1","zone": "test","weight": 1,"endpoint": "http://host1:1234","hostname": "host1","port": 1234, "metadata": {"debugPort":0}},{"id": "host2","isolation_group": "rack1","zone": "test","weight": 1,"endpoint": "http://host2:1234","hostname": "host2","port": 1234, "metadata": {"debugPort":0}}],"num_shards": 16,"replication_factor": 1}`)) 91 } else { 92 req = httptest.NewRequest(InitHTTPMethod, M3DBInitURL, strings.NewReader(`{"instances": [{"id": "host1","isolation_group": "rack1","zone": "test","weight": 1,"endpoint": "http://host1:1234","hostname": "host1","port": 1234, "metadata": {"debugPort":0}},{"id": "host2","isolation_group": "rack1","zone": "test","weight": 1,"endpoint": "http://host2:1234","hostname": "host2","port": 1234, "metadata": {"debugPort":0}}],"num_shards": 16,"replication_factor": 1}`)) 93 } 94 95 require.NotNil(t, req) 96 97 newPlacement, err := placement.NewPlacementFromProto(initTestPlacementProto) 98 require.NoError(t, err) 99 100 svcDefaults := handleroptions.ServiceNameAndDefaults{ 101 ServiceName: serviceName, 102 } 103 104 mockPlacementService.EXPECT().BuildInitialPlacement(gomock.Not(nil), 16, 1).Return(newPlacement, nil) 105 handler.ServeHTTP(svcDefaults, w, req) 106 resp := w.Result() 107 body, err := ioutil.ReadAll(resp.Body) 108 require.NoError(t, err) 109 assert.Equal(t, http.StatusOK, resp.StatusCode) 110 assert.Equal(t, `{"placement":{"instances":{"host1":{"id":"host1","isolationGroup":"rack1","zone":"test","weight":1,"endpoint":"http://host1:1234","shards":[],"shardSetId":0,"hostname":"host1","port":1234,"metadata":{"debugPort":0}},"host2":{"id":"host2","isolationGroup":"rack1","zone":"test","weight":1,"endpoint":"http://host2:1234","shards":[],"shardSetId":0,"hostname":"host2","port":1234,"metadata":{"debugPort":0}}},"replicaFactor":0,"numShards":0,"isSharded":false,"cutoverTime":"0","isMirrored":false,"maxShardSetId":0},"version":0}`, string(body)) 111 112 // Test error response 113 w = httptest.NewRecorder() 114 if serviceName == handleroptions.M3AggregatorServiceName { 115 req = httptest.NewRequest(InitHTTPMethod, M3DBInitURL, strings.NewReader(`{"instances": [{"id": "host1","isolation_group": "rack1","zone": "test","weight": 1,"endpoint": "host1:1234","hostname": "host1","port": 1234, "metadata": {"debugPort":0}},{"id": "host2","isolation_group": "rack1","zone": "test","weight": 1,"endpoint": "http://host2:1234","hostname": "host2","port": 1234, "metadata": {"debugPort":0}}],"num_shards": 64,"replication_factor": 2}`)) 116 } else { 117 req = httptest.NewRequest(InitHTTPMethod, M3DBInitURL, strings.NewReader(`{"instances": [{"id": "host1","isolation_group": "rack1","zone": "test","weight": 1,"endpoint": "host1:1234","hostname": "host1","port": 1234, "metadata": {"debugPort":0}},{"id": "host2","isolation_group": "rack1","zone": "test","weight": 1,"endpoint": "http://host2:1234","hostname": "host2","port": 1234, "metadata": {"debugPort":0}}],"num_shards": 64,"replication_factor": 2}`)) 118 } 119 require.NotNil(t, req) 120 121 switch serviceName { 122 case handleroptions.M3CoordinatorServiceName: 123 mockPlacementService.EXPECT(). 124 BuildInitialPlacement(gomock.Not(nil), 64, 1). 125 Return(nil, errors.New("unable to build initial placement")) 126 default: 127 mockPlacementService.EXPECT(). 128 BuildInitialPlacement(gomock.Not(nil), 64, 2). 129 Return(nil, errors.New("unable to build initial placement")) 130 } 131 132 handler.ServeHTTP(svcDefaults, w, req) 133 resp = w.Result() 134 body, err = ioutil.ReadAll(resp.Body) 135 require.NoError(t, err) 136 assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) 137 assert.JSONEq(t, 138 `{"status":"error","error":"unable to build initial placement"}`, 139 string(body)) 140 141 // Test error response 142 w = httptest.NewRecorder() 143 if serviceName == handleroptions.M3AggregatorServiceName { 144 req = httptest.NewRequest(InitHTTPMethod, M3DBInitURL, strings.NewReader(`{"instances": [{"id": "host1","isolation_group": "rack1","zone": "test","weight": 1,"endpoint": "host1:1234","hostname": "host1","port": 1234, "metadata": {"debugPort":0}},{"id": "host2","isolation_group": "rack1","zone": "test","weight": 1,"endpoint": "http://host2:1234","hostname": "host2","port": 1234, "metadata": {"debugPort":0}}],"num_shards": 64,"replication_factor": 2}`)) 145 } else { 146 req = httptest.NewRequest(InitHTTPMethod, M3DBInitURL, strings.NewReader(`{"instances": [{"id": "host1","isolation_group": "rack1","zone": "test","weight": 1,"endpoint": "host1:1234","hostname": "host1","port": 1234, "metadata": {"debugPort":0}},{"id": "host2","isolation_group": "rack1","zone": "test","weight": 1,"endpoint": "http://host2:1234","hostname": "host2","port": 1234, "metadata": {"debugPort":0}}],"num_shards": 64,"replication_factor": 2}`)) 147 } 148 149 require.NotNil(t, req) 150 switch serviceName { 151 case handleroptions.M3CoordinatorServiceName: 152 mockPlacementService.EXPECT(). 153 BuildInitialPlacement(gomock.Not(nil), 64, 1). 154 Return(nil, kv.ErrAlreadyExists) 155 default: 156 mockPlacementService.EXPECT(). 157 BuildInitialPlacement(gomock.Not(nil), 64, 2). 158 Return(nil, kv.ErrAlreadyExists) 159 } 160 161 handler.ServeHTTP(svcDefaults, w, req) 162 resp = w.Result() 163 _, err = ioutil.ReadAll(resp.Body) 164 require.NoError(t, err) 165 assert.Equal(t, http.StatusConflict, resp.StatusCode) 166 }) 167 }