github.com/m3db/m3@v1.5.0/src/integration/resources/inprocess/coordinator_test.go (about) 1 // +build test_harness 2 // Copyright (c) 2021 Uber Technologies, Inc. 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 package inprocess 23 24 import ( 25 "testing" 26 27 "github.com/m3db/m3/src/cluster/generated/proto/placementpb" 28 "github.com/m3db/m3/src/cluster/placement" 29 "github.com/m3db/m3/src/integration/resources" 30 "github.com/m3db/m3/src/msg/generated/proto/topicpb" 31 "github.com/m3db/m3/src/msg/topic" 32 "github.com/m3db/m3/src/query/generated/proto/admin" 33 "github.com/m3db/m3/src/query/generated/proto/prompb" 34 "github.com/m3db/m3/src/query/storage" 35 xtime "github.com/m3db/m3/src/x/time" 36 37 "github.com/prometheus/common/model" 38 "github.com/stretchr/testify/assert" 39 "github.com/stretchr/testify/require" 40 ) 41 42 func TestNewCoordinator(t *testing.T) { 43 dbnode, err := NewDBNodeFromYAML(defaultDBNodeConfig, DBNodeOptions{Start: true}) 44 require.NoError(t, err) 45 defer func() { 46 assert.NoError(t, dbnode.Close()) 47 }() 48 49 coord, err := NewCoordinatorFromYAML(defaultCoordConfig, CoordinatorOptions{Start: true}) 50 require.NoError(t, err) 51 require.NoError(t, coord.Close()) 52 53 // Restart and shut down again to test restarting. 54 coord, err = NewCoordinatorFromYAML(defaultCoordConfig, CoordinatorOptions{Start: true}) 55 require.NoError(t, err) 56 require.NoError(t, coord.Close()) 57 } 58 59 func TestNewEmbeddedCoordinator(t *testing.T) { 60 dbnode, err := NewDBNodeFromYAML(embeddedCoordConfig, DBNodeOptions{Start: true}) 61 require.NoError(t, err) 62 defer func() { 63 assert.NoError(t, dbnode.Close()) 64 }() 65 66 d, ok := dbnode.(*DBNode) 67 require.True(t, ok) 68 require.True(t, d.started) 69 70 _, err = NewEmbeddedCoordinator(d) 71 require.NoError(t, err) 72 } 73 74 func TestNewEmbeddedCoordinatorNotStarted(t *testing.T) { 75 var dbnode DBNode 76 _, err := NewEmbeddedCoordinator(&dbnode) 77 require.Error(t, err) 78 } 79 80 func TestCoordinatorAPIs(t *testing.T) { 81 _, coord, closer := setupNodeAndCoordinator(t) 82 defer closer() 83 84 testM3msgTopicFunctions(t, coord) 85 testAggPlacementFunctions(t, coord) 86 testMetadataAPIs(t, coord) 87 } 88 89 func testMetadataAPIs(t *testing.T, coordinator resources.Coordinator) { 90 err := coordinator.WriteProm("cpu", map[string]string{"pod": "foo-1234"}, []prompb.Sample{ 91 {Value: 1, Timestamp: storage.TimeToPromTimestamp(xtime.Now())}, 92 }, nil) 93 require.NoError(t, err) 94 95 names, err := coordinator.LabelNames(resources.LabelNamesRequest{}, nil) 96 require.NoError(t, err) 97 require.Equal(t, model.LabelNames{ 98 "__name__", 99 "pod", 100 }, names) 101 102 values, err := coordinator.LabelValues(resources.LabelValuesRequest{ 103 LabelName: "__name__", 104 }, nil) 105 require.NoError(t, err) 106 require.Equal(t, model.LabelValues{"cpu"}, values) 107 108 series, err := coordinator.Series(resources.SeriesRequest{ 109 MetadataRequest: resources.MetadataRequest{ 110 Match: "cpu", 111 }, 112 }, nil) 113 require.NoError(t, err) 114 require.Equal(t, []model.Metric{ 115 { 116 "__name__": "cpu", 117 "pod": "foo-1234", 118 }, 119 }, series) 120 } 121 122 func testM3msgTopicFunctions(t *testing.T, coord resources.Coordinator) { 123 // init an m3msg topic 124 m3msgTopicOpts := resources.M3msgTopicOptions{ 125 Zone: "embedded", 126 Env: "default_env", 127 TopicName: "testtopic", 128 } 129 initResp, err := coord.InitM3msgTopic( 130 m3msgTopicOpts, 131 admin.TopicInitRequest{NumberOfShards: 16}, 132 ) 133 expectedInitResp := admin.TopicGetResponse{ 134 Topic: &topicpb.Topic{ 135 Name: "testtopic", 136 NumberOfShards: 16, 137 ConsumerServices: nil, 138 }, 139 Version: 1, 140 } 141 require.NoError(t, err) 142 validateEqualTopicResp(t, expectedInitResp, initResp) 143 144 // add a consumer service 145 consumer := topicpb.ConsumerService{ 146 ServiceId: &topicpb.ServiceID{ 147 Name: "testservice", 148 Environment: m3msgTopicOpts.Env, 149 Zone: m3msgTopicOpts.Zone, 150 }, 151 ConsumptionType: topicpb.ConsumptionType_SHARED, 152 MessageTtlNanos: 1, 153 } 154 addResp, err := coord.AddM3msgTopicConsumer( 155 m3msgTopicOpts, 156 admin.TopicAddRequest{ConsumerService: &consumer}, 157 ) 158 expectedAddResp := admin.TopicGetResponse{ 159 Topic: &topicpb.Topic{ 160 Name: "testtopic", 161 NumberOfShards: 16, 162 ConsumerServices: []*topicpb.ConsumerService{ 163 &consumer, 164 }, 165 }, 166 Version: 2, 167 } 168 require.NoError(t, err) 169 validateEqualTopicResp(t, expectedAddResp, addResp) 170 171 // get an m3msg topic 172 getResp, err := coord.GetM3msgTopic(m3msgTopicOpts) 173 require.NoError(t, err) 174 validateEqualTopicResp(t, expectedAddResp, getResp) 175 } 176 177 func validateEqualTopicResp(t *testing.T, expected, actual admin.TopicGetResponse) { 178 require.Equal(t, expected.Version, actual.Version) 179 180 t1, err := topic.NewTopicFromProto(expected.Topic) 181 require.NoError(t, err) 182 t2, err := topic.NewTopicFromProto(actual.Topic) 183 require.NoError(t, err) 184 require.Equal(t, t1, t2) 185 } 186 187 func testAggPlacementFunctions(t *testing.T, coord resources.Coordinator) { 188 placementOpts := resources.PlacementRequestOptions{ 189 Service: resources.ServiceTypeM3Aggregator, 190 Env: "default_env", 191 Zone: "embedded", 192 } 193 initRequest := admin.PlacementInitRequest{ 194 Instances: []*placementpb.Instance{ 195 { 196 Id: "host1", 197 IsolationGroup: "rack1", 198 Zone: "embedded", 199 Weight: 1, 200 Endpoint: "http://host1:1234", 201 Hostname: "host1", 202 Port: 1234, 203 Metadata: &placementpb.InstanceMetadata{ 204 DebugPort: 0, 205 }, 206 }, 207 { 208 Id: "host2", 209 IsolationGroup: "rack2", 210 Zone: "embedded", 211 Weight: 1, 212 Endpoint: "http://host2:1234", 213 Hostname: "host2", 214 Port: 1234, 215 Metadata: &placementpb.InstanceMetadata{ 216 DebugPort: 0, 217 }, 218 }, 219 }, 220 NumShards: 1, 221 ReplicationFactor: 2, 222 } 223 instanceMap := make(map[string]*placementpb.Instance, len(initRequest.Instances)) 224 for _, ins := range initRequest.Instances { 225 newIns := *ins 226 newIns.ShardSetId = 1 // initialized 227 newIns.Shards = []*placementpb.Shard{ 228 { 229 Id: 0, 230 State: placementpb.ShardState_INITIALIZING, 231 SourceId: "", 232 CutoverNanos: 0, 233 CutoffNanos: 0, 234 RedirectToShardId: nil, 235 }, 236 } 237 instanceMap[ins.Hostname] = &newIns 238 } 239 initResp, err := coord.InitPlacement(placementOpts, initRequest) 240 require.NoError(t, err) 241 expectedPlacement := &placementpb.Placement{ 242 Instances: instanceMap, 243 ReplicaFactor: uint32(initRequest.ReplicationFactor), 244 NumShards: uint32(initRequest.NumShards), 245 MaxShardSetId: uint32(initRequest.NumShards), 246 IsSharded: true, 247 IsMirrored: true, 248 } 249 require.Equal(t, int32(0), initResp.Version) 250 validateEqualAggPlacement(t, expectedPlacement, initResp.Placement) 251 252 getResp, err := coord.GetPlacement(placementOpts) 253 254 require.NoError(t, err) 255 require.Equal(t, int32(1), getResp.Version) 256 validateEqualAggPlacement(t, expectedPlacement, getResp.Placement) 257 258 wrongPlacementOpts := resources.PlacementRequestOptions{ 259 Service: resources.ServiceTypeM3Aggregator, 260 Env: "default_env_wrong", 261 Zone: "embedded", 262 } 263 _, err = coord.GetPlacement(wrongPlacementOpts) 264 require.NotNil(t, err) 265 } 266 267 func validateEqualAggPlacement(t *testing.T, expected, actual *placementpb.Placement) { 268 p1, err := placement.NewPlacementFromProto(expected) 269 require.NoError(t, err) 270 p2, err := placement.NewPlacementFromProto(actual) 271 require.NoError(t, err) 272 require.Equal(t, p1.String(), p2.String()) 273 } 274 275 const defaultCoordConfig = ` 276 clusters: 277 - client: 278 config: 279 service: 280 env: default_env 281 zone: embedded 282 service: m3db 283 etcdClusters: 284 - zone: embedded 285 endpoints: 286 - 127.0.0.1:2379 287 ` 288 289 const embeddedCoordConfig = ` 290 coordinator: 291 clusters: 292 - client: 293 config: 294 service: 295 env: default_env 296 zone: embedded 297 service: m3db 298 etcdClusters: 299 - zone: embedded 300 endpoints: 301 - 127.0.0.1:2379 302 303 db: {} 304 `