github.com/m3db/m3@v1.5.0/src/integration/resources/resources.go (about) 1 // Copyright (c) 2021 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 resources 22 23 import ( 24 "errors" 25 "fmt" 26 "net" 27 "strconv" 28 "time" 29 30 protobuftypes "github.com/gogo/protobuf/types" 31 "go.uber.org/zap" 32 33 "github.com/m3db/m3/src/cluster/generated/proto/placementpb" 34 "github.com/m3db/m3/src/cluster/placementhandler/handleroptions" 35 "github.com/m3db/m3/src/dbnode/generated/proto/namespace" 36 "github.com/m3db/m3/src/msg/generated/proto/topicpb" 37 "github.com/m3db/m3/src/query/generated/proto/admin" 38 "github.com/m3db/m3/src/x/instrument" 39 ) 40 41 const ( 42 retention = "6h" 43 44 // AggName is the name of the aggregated namespace. 45 AggName = "aggregated" 46 // UnaggName is the name of the unaggregated namespace. 47 UnaggName = "default" 48 // ColdWriteNsName is the name for cold write namespace. 49 ColdWriteNsName = "coldWritesRepairAndNoIndex" 50 // AggregatorInputTopic is the m3msg topic name for coordinator->aggregator traffic. 51 AggregatorInputTopic = "aggregator_ingest" 52 // AggregatorOutputTopic is the m3msg topic name for aggregator->coordinator traffic. 53 AggregatorOutputTopic = "aggregated_metrics" 54 ) 55 56 // SetupCluster setups m3 cluster on provided docker containers. 57 func SetupCluster( 58 cluster M3Resources, 59 opts ClusterOptions, 60 ) error { // nolint: gocyclo 61 coordinator := cluster.Coordinator() 62 iOpts := instrument.NewOptions() 63 logger := iOpts.Logger().With(zap.String("source", "harness")) 64 hosts := make([]*admin.Host, 0, len(cluster.Nodes())) 65 ids := make([]string, 0, len(cluster.Nodes())) 66 for i, n := range cluster.Nodes() { 67 h, err := n.HostDetails(9000) 68 if err != nil { 69 logger.Error("could not get host details", zap.Error(err)) 70 return err 71 } 72 if opts.DBNode != nil && opts.DBNode.NumIsolationGroups > 0 { 73 h.IsolationGroup = fmt.Sprintf("isogroup-%d", int32(i)%opts.DBNode.NumIsolationGroups) 74 } 75 76 hosts = append(hosts, h) 77 ids = append(ids, h.GetId()) 78 } 79 80 replicationFactor := int32(1) 81 numShards := int32(4) 82 if opts.DBNode != nil { 83 if opts.DBNode.RF > 0 { 84 replicationFactor = opts.DBNode.RF 85 } 86 if opts.DBNode.NumShards > 0 { 87 numShards = opts.DBNode.NumShards 88 } 89 } 90 91 var ( 92 unaggDatabase = admin.DatabaseCreateRequest{ 93 Type: "cluster", 94 NamespaceName: UnaggName, 95 RetentionTime: retention, 96 NumShards: numShards, 97 ReplicationFactor: replicationFactor, 98 Hosts: hosts, 99 } 100 101 aggNamespace = admin.NamespaceAddRequest{ 102 Name: AggName, 103 Options: &namespace.NamespaceOptions{ 104 BootstrapEnabled: true, 105 FlushEnabled: true, 106 WritesToCommitLog: true, 107 CleanupEnabled: true, 108 SnapshotEnabled: true, 109 IndexOptions: &namespace.IndexOptions{ 110 Enabled: true, 111 BlockSizeNanos: int64(30 * time.Minute), 112 }, 113 RetentionOptions: &namespace.RetentionOptions{ 114 RetentionPeriodNanos: int64(6 * time.Hour), 115 BlockSizeNanos: int64(30 * time.Minute), 116 BufferFutureNanos: int64(2 * time.Minute), 117 BufferPastNanos: int64(10 * time.Minute), 118 BlockDataExpiry: true, 119 BlockDataExpiryAfterNotAccessPeriodNanos: int64(time.Minute * 5), 120 }, 121 AggregationOptions: &namespace.AggregationOptions{ 122 Aggregations: []*namespace.Aggregation{ 123 { 124 Aggregated: true, 125 Attributes: &namespace.AggregatedAttributes{ 126 ResolutionNanos: int64(5 * time.Second), 127 }, 128 }, 129 }, 130 }, 131 }, 132 } 133 134 coldWriteNamespace = admin.NamespaceAddRequest{ 135 Name: ColdWriteNsName, 136 Options: &namespace.NamespaceOptions{ 137 BootstrapEnabled: true, 138 FlushEnabled: true, 139 WritesToCommitLog: true, 140 CleanupEnabled: true, 141 SnapshotEnabled: true, 142 RepairEnabled: true, 143 ColdWritesEnabled: true, 144 CacheBlocksOnRetrieve: &protobuftypes.BoolValue{Value: true}, 145 RetentionOptions: &namespace.RetentionOptions{ 146 RetentionPeriodNanos: int64(4 * time.Hour), 147 BlockSizeNanos: int64(time.Hour), 148 BufferFutureNanos: int64(time.Minute * 10), 149 BufferPastNanos: int64(time.Minute * 10), 150 BlockDataExpiry: true, 151 BlockDataExpiryAfterNotAccessPeriodNanos: int64(time.Minute * 5), 152 }, 153 }, 154 } 155 ) 156 157 logger.Info("waiting for coordinator") 158 if err := coordinator.WaitForNamespace(""); err != nil { 159 return err 160 } 161 162 logger.Info("creating database", zap.Any("request", unaggDatabase)) 163 if _, err := coordinator.CreateDatabase(unaggDatabase); err != nil { 164 return err 165 } 166 167 logger.Info("waiting for placements", zap.Strings("placement ids", ids)) 168 if err := coordinator.WaitForInstances(ids); err != nil { 169 return err 170 } 171 172 logger.Info("waiting for namespace", zap.String("name", UnaggName)) 173 if err := coordinator.WaitForNamespace(UnaggName); err != nil { 174 return err 175 } 176 177 logger.Info("creating namespace", zap.Any("request", aggNamespace)) 178 if _, err := coordinator.AddNamespace(aggNamespace); err != nil { 179 return err 180 } 181 182 logger.Info("waiting for namespace", zap.String("name", AggName)) 183 if err := coordinator.WaitForNamespace(AggName); err != nil { 184 return err 185 } 186 187 logger.Info("creating namespace", zap.Any("request", coldWriteNamespace)) 188 if _, err := coordinator.AddNamespace(coldWriteNamespace); err != nil { 189 return err 190 } 191 192 logger.Info("waiting for namespace", zap.String("name", ColdWriteNsName)) 193 if err := coordinator.WaitForNamespace(ColdWriteNsName); err != nil { 194 return err 195 } 196 197 logger.Info("waiting for healthy") 198 if err := cluster.Nodes().WaitForHealthy(); err != nil { 199 return err 200 } 201 202 logger.Info("waiting for shards ready") 203 if err := coordinator.WaitForShardsReady(); err != nil { 204 return err 205 } 206 207 logger.Info("waiting for cluster to be ready") 208 if err := coordinator.WaitForClusterReady(); err != nil { 209 return err 210 } 211 212 if opts.Aggregator != nil { 213 aggregators := cluster.Aggregators() 214 if len(aggregators) == 0 { 215 return errors.New("no aggregators have been initiazted") 216 } 217 218 if err := aggregators.WaitForHealthy(); err != nil { 219 return err 220 } 221 } 222 223 logger.Info("all healthy") 224 225 return nil 226 } 227 228 // SetupPlacement configures the placement for the provided coordinators 229 // and aggregators. 230 func SetupPlacement( 231 coordAPI Coordinator, 232 coordHost InstanceInfo, 233 aggs Aggregators, 234 opts AggregatorClusterOptions, 235 ) error { 236 // Setup aggregator placement. 237 var env, zone string 238 instances := make([]*placementpb.Instance, 0, len(aggs)) 239 for i, agg := range aggs { 240 info, err := agg.HostDetails() 241 if err != nil { 242 return err 243 } 244 245 if i == 0 { 246 env = info.Env 247 zone = info.Zone 248 } 249 250 instance := &placementpb.Instance{ 251 Id: info.ID, 252 IsolationGroup: fmt.Sprintf("isogroup-%02d", i%int(opts.NumIsolationGroups)), 253 Zone: info.Zone, 254 Weight: 1, 255 Endpoint: net.JoinHostPort(info.M3msgAddress, strconv.Itoa(int(info.M3msgPort))), 256 Hostname: info.ID, 257 Port: info.M3msgPort, 258 } 259 260 instances = append(instances, instance) 261 } 262 263 aggPlacementRequestOptions := PlacementRequestOptions{ 264 Service: ServiceTypeM3Aggregator, 265 Zone: zone, 266 Env: env, 267 } 268 269 _, err := coordAPI.InitPlacement( 270 aggPlacementRequestOptions, 271 admin.PlacementInitRequest{ 272 NumShards: opts.NumShards, 273 ReplicationFactor: opts.RF, 274 Instances: instances, 275 OptionOverride: &placementpb.Options{ 276 SkipPortMirroring: &protobuftypes.BoolValue{Value: true}, 277 }, 278 }, 279 ) 280 if err != nil { 281 return fmt.Errorf("failed to init aggregator placement: %w", err) 282 } 283 284 // Setup coordinator placement. 285 coordPlacementRequestOptions := PlacementRequestOptions{ 286 Service: ServiceTypeM3Coordinator, 287 Zone: coordHost.Zone, 288 Env: coordHost.Env, 289 } 290 _, err = coordAPI.InitPlacement( 291 coordPlacementRequestOptions, 292 admin.PlacementInitRequest{ 293 Instances: []*placementpb.Instance{ 294 { 295 Id: coordHost.ID, 296 Zone: coordHost.Zone, 297 Endpoint: net.JoinHostPort(coordHost.M3msgAddress, strconv.Itoa(int(coordHost.M3msgPort))), 298 Hostname: coordHost.ID, 299 Port: coordHost.M3msgPort, 300 }, 301 }, 302 }, 303 ) 304 if err != nil { 305 return fmt.Errorf("failed to init coordinator placement: %w", err) 306 } 307 308 return nil 309 } 310 311 // SetupM3MsgTopics sets up the m3msg topics for the provided coordinator 312 // and aggregator. 313 func SetupM3MsgTopics( 314 coord Coordinator, 315 aggInstanceInfo InstanceInfo, 316 opts ClusterOptions, 317 ) error { 318 aggInputTopicOpts := M3msgTopicOptions{ 319 Zone: aggInstanceInfo.Zone, 320 Env: aggInstanceInfo.Env, 321 TopicName: AggregatorInputTopic, 322 } 323 324 aggOutputTopicOpts := M3msgTopicOptions{ 325 Zone: aggInstanceInfo.Zone, 326 Env: aggInstanceInfo.Env, 327 TopicName: AggregatorOutputTopic, 328 } 329 330 _, err := coord.InitM3msgTopic( 331 aggInputTopicOpts, 332 admin.TopicInitRequest{NumberOfShards: uint32(opts.Aggregator.NumShards)}, 333 ) 334 if err != nil { 335 return fmt.Errorf("failed to init aggregator input m3msg topic: %w", err) 336 } 337 338 _, err = coord.AddM3msgTopicConsumer(aggInputTopicOpts, admin.TopicAddRequest{ 339 ConsumerService: &topicpb.ConsumerService{ 340 ServiceId: &topicpb.ServiceID{ 341 Name: handleroptions.M3AggregatorServiceName, 342 Environment: aggInputTopicOpts.Env, 343 Zone: aggInputTopicOpts.Zone, 344 }, 345 ConsumptionType: topicpb.ConsumptionType_REPLICATED, 346 MessageTtlNanos: 600000000000, // 10 mins 347 }, 348 }) 349 if err != nil { 350 return fmt.Errorf("failed to add aggregator input m3msg topic consumer: %w", err) 351 } 352 353 _, err = coord.InitM3msgTopic( 354 aggOutputTopicOpts, 355 admin.TopicInitRequest{NumberOfShards: uint32(opts.DBNode.NumShards)}, 356 ) 357 if err != nil { 358 return fmt.Errorf("failed to init aggregator output m3msg topic: %w", err) 359 } 360 361 _, err = coord.AddM3msgTopicConsumer(aggOutputTopicOpts, admin.TopicAddRequest{ 362 ConsumerService: &topicpb.ConsumerService{ 363 ServiceId: &topicpb.ServiceID{ 364 Name: handleroptions.M3CoordinatorServiceName, 365 Environment: aggInputTopicOpts.Env, 366 Zone: aggInputTopicOpts.Zone, 367 }, 368 ConsumptionType: topicpb.ConsumptionType_SHARED, 369 MessageTtlNanos: 600000000000, // 10 mins 370 }, 371 }) 372 if err != nil { 373 return fmt.Errorf("failed to add aggregator output m3msg topic consumer: %w", err) 374 } 375 376 return nil 377 }