github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/cmd/services/m3dbnode/config/config.go (about) 1 // Copyright (c) 2017 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 config 22 23 import ( 24 "bytes" 25 "errors" 26 "fmt" 27 "net/url" 28 "path" 29 "strings" 30 "time" 31 32 coordinatorcfg "github.com/m3db/m3/src/cmd/services/m3query/config" 33 "github.com/m3db/m3/src/dbnode/client" 34 "github.com/m3db/m3/src/dbnode/discovery" 35 "github.com/m3db/m3/src/dbnode/environment" 36 "github.com/m3db/m3/src/dbnode/storage/repair" 37 "github.com/m3db/m3/src/dbnode/storage/series" 38 "github.com/m3db/m3/src/x/config/hostid" 39 "github.com/m3db/m3/src/x/debug/config" 40 "github.com/m3db/m3/src/x/instrument" 41 xlog "github.com/m3db/m3/src/x/log" 42 "github.com/m3db/m3/src/x/opentracing" 43 44 "github.com/m3dbx/vellum/regexp" 45 "go.etcd.io/etcd/client/pkg/v3/transport" 46 "go.etcd.io/etcd/client/pkg/v3/types" 47 "go.etcd.io/etcd/server/v3/embed" 48 "gopkg.in/yaml.v2" 49 ) 50 51 const ( 52 defaultEtcdDirSuffix = "etcd" 53 defaultEtcdListenHost = "http://0.0.0.0" 54 // DefaultEtcdClientPort is the default port for etcd client. 55 DefaultEtcdClientPort = 2379 56 // DefaultEtcdServerPort is the default port for etcd server. 57 DefaultEtcdServerPort = 2380 58 ) 59 60 var ( 61 defaultLogging = xlog.Configuration{ 62 Level: "info", 63 } 64 defaultMetricsSanitization = instrument.PrometheusMetricSanitization 65 defaultMetricsExtendedMetricsType = instrument.DetailedExtendedMetrics 66 defaultMetrics = instrument.MetricsConfiguration{ 67 PrometheusReporter: &instrument.PrometheusConfiguration{ 68 HandlerPath: "/metrics", 69 }, 70 Sanitization: &defaultMetricsSanitization, 71 SamplingRate: 1.0, 72 ExtendedMetrics: &defaultMetricsExtendedMetricsType, 73 } 74 defaultListenAddress = "0.0.0.0:9000" 75 defaultClusterListenAddress = "0.0.0.0:9001" 76 defaultHTTPNodeListenAddress = "0.0.0.0:9002" 77 defaultHTTPClusterListenAddress = "0.0.0.0:9003" 78 defaultDebugListenAddress = "0.0.0.0:9004" 79 defaultHostIDValue = "m3db_local" 80 defaultHostID = hostid.Configuration{ 81 Resolver: hostid.ConfigResolver, 82 Value: &defaultHostIDValue, 83 } 84 defaultGCPercentage = 100 85 defaultWriteNewSeriesAsync = true 86 defaultWriteNewSeriesBackoffDuration = 2 * time.Millisecond 87 defaultCommitLogPolicy = CommitLogPolicy{ 88 FlushMaxBytes: 524288, 89 FlushEvery: time.Second * 1, 90 Queue: CommitLogQueuePolicy{ 91 Size: 2097152, 92 CalculationType: CalculationTypeFixed, 93 }, 94 } 95 defaultDiscoveryType = discovery.M3DBSingleNodeType 96 defaultDiscovery = discovery.Configuration{ 97 Type: &defaultDiscoveryType, 98 } 99 ) 100 101 // Configuration is the top level configuration that includes both a DB 102 // node and a coordinator. 103 type Configuration struct { 104 // DB is the configuration for a DB node (required). 105 DB *DBConfiguration `yaml:"db"` 106 107 // Coordinator is the configuration for the coordinator to run (optional). 108 Coordinator *coordinatorcfg.Configuration `yaml:"coordinator"` 109 } 110 111 // Validate validates the Configuration. We use this method to validate fields 112 // where the validator package falls short. 113 func (c *Configuration) Validate() error { 114 return c.DB.Validate() 115 } 116 117 // Components returns the number of components configured within the Configuration 118 // object. 119 func (c *Configuration) Components() int { 120 numComponents := 0 121 if c.DB != nil { 122 numComponents++ 123 } 124 if c.Coordinator != nil { 125 numComponents++ 126 } 127 128 return numComponents 129 } 130 131 // DeepCopy returns a deep copy of the current configuration object. 132 func (c *Configuration) DeepCopy() (Configuration, error) { 133 rawCfg, err := yaml.Marshal(c) 134 if err != nil { 135 return Configuration{}, err 136 } 137 var dupe Configuration 138 if err := yaml.Unmarshal(rawCfg, &dupe); err != nil { 139 return Configuration{}, err 140 } 141 return dupe, nil 142 } 143 144 // DBConfiguration is the configuration for a DB node. 145 type DBConfiguration struct { 146 // Index configuration. 147 Index IndexConfiguration `yaml:"index"` 148 149 // Transforms configuration. 150 Transforms TransformConfiguration `yaml:"transforms"` 151 152 // Logging configuration. 153 Logging *xlog.Configuration `yaml:"logging"` 154 155 // Metrics configuration. 156 Metrics *instrument.MetricsConfiguration `yaml:"metrics"` 157 158 // The host and port on which to listen for the node service. 159 ListenAddress *string `yaml:"listenAddress"` 160 161 // The host and port on which to listen for the cluster service. 162 ClusterListenAddress *string `yaml:"clusterListenAddress"` 163 164 // The HTTP host and port on which to listen for the node service. 165 HTTPNodeListenAddress *string `yaml:"httpNodeListenAddress"` 166 167 // The HTTP host and port on which to listen for the cluster service. 168 HTTPClusterListenAddress *string `yaml:"httpClusterListenAddress"` 169 170 // The host and port on which to listen for debug endpoints. 171 DebugListenAddress *string `yaml:"debugListenAddress"` 172 173 // HostID is the local host ID configuration. 174 HostID *hostid.Configuration `yaml:"hostID"` 175 176 // Client configuration, used for inter-node communication and when used as a coordinator. 177 Client client.Configuration `yaml:"client"` 178 179 // The initial garbage collection target percentage. 180 GCPercentage int `yaml:"gcPercentage" validate:"max=100"` 181 182 // The tick configuration, omit this to use default settings. 183 Tick *TickConfiguration `yaml:"tick"` 184 185 // Bootstrap configuration. 186 Bootstrap BootstrapConfiguration `yaml:"bootstrap"` 187 188 // The block retriever policy. 189 BlockRetrieve *BlockRetrievePolicy `yaml:"blockRetrieve"` 190 191 // Cache configurations. 192 Cache CacheConfigurations `yaml:"cache"` 193 194 // The filesystem configuration for the node. 195 Filesystem FilesystemConfiguration `yaml:"filesystem"` 196 197 // The commit log policy for the node. 198 CommitLog *CommitLogPolicy `yaml:"commitlog"` 199 200 // The repair policy for repairing data within a cluster. 201 Repair *RepairPolicy `yaml:"repair"` 202 203 // The replication policy for replicating data between clusters. 204 Replication *ReplicationPolicy `yaml:"replication"` 205 206 // The pooling policy. 207 PoolingPolicy *PoolingPolicy `yaml:"pooling"` 208 209 // The discovery configuration. 210 Discovery *discovery.Configuration `yaml:"discovery"` 211 212 // The configuration for hashing 213 Hashing HashingConfiguration `yaml:"hashing"` 214 215 // Write new series asynchronously for fast ingestion of new ID bursts. 216 WriteNewSeriesAsync *bool `yaml:"writeNewSeriesAsync"` 217 218 // Write new series backoff between batches of new series insertions. 219 WriteNewSeriesBackoffDuration *time.Duration `yaml:"writeNewSeriesBackoffDuration"` 220 221 // Proto contains the configuration specific to running in the ProtoDataMode. 222 Proto *ProtoConfiguration `yaml:"proto"` 223 224 // Tracing configures opentracing. If not provided, tracing is disabled. 225 Tracing *opentracing.TracingConfiguration `yaml:"tracing"` 226 227 // Limits contains configuration for limits that can be applied to M3DB for the purposes 228 // of applying back-pressure or protecting the db nodes. 229 Limits LimitsConfiguration `yaml:"limits"` 230 231 // TChannel exposes TChannel config options. 232 TChannel *TChannelConfiguration `yaml:"tchannel"` 233 234 // Debug configuration. 235 Debug config.DebugConfiguration `yaml:"debug"` 236 237 // ForceColdWritesEnabled will force enable cold writes for all namespaces 238 // if set. 239 ForceColdWritesEnabled *bool `yaml:"forceColdWritesEnabled"` 240 } 241 242 // LoggingOrDefault returns the logging configuration or defaults. 243 func (c *DBConfiguration) LoggingOrDefault() xlog.Configuration { 244 if c.Logging == nil { 245 return defaultLogging 246 } 247 248 return *c.Logging 249 } 250 251 // MetricsOrDefault returns metrics configuration or defaults. 252 func (c *DBConfiguration) MetricsOrDefault() *instrument.MetricsConfiguration { 253 if c.Metrics == nil { 254 return &defaultMetrics 255 } 256 257 return c.Metrics 258 } 259 260 // ListenAddressOrDefault returns the listen address or default. 261 func (c *DBConfiguration) ListenAddressOrDefault() string { 262 if c.ListenAddress == nil { 263 return defaultListenAddress 264 } 265 266 return *c.ListenAddress 267 } 268 269 // ClusterListenAddressOrDefault returns the listen address or default. 270 func (c *DBConfiguration) ClusterListenAddressOrDefault() string { 271 if c.ClusterListenAddress == nil { 272 return defaultClusterListenAddress 273 } 274 275 return *c.ClusterListenAddress 276 } 277 278 // HTTPNodeListenAddressOrDefault returns the listen address or default. 279 func (c *DBConfiguration) HTTPNodeListenAddressOrDefault() string { 280 if c.HTTPNodeListenAddress == nil { 281 return defaultHTTPNodeListenAddress 282 } 283 284 return *c.HTTPNodeListenAddress 285 } 286 287 // HTTPClusterListenAddressOrDefault returns the listen address or default. 288 func (c *DBConfiguration) HTTPClusterListenAddressOrDefault() string { 289 if c.HTTPClusterListenAddress == nil { 290 return defaultHTTPClusterListenAddress 291 } 292 293 return *c.HTTPClusterListenAddress 294 } 295 296 // DebugListenAddressOrDefault returns the listen address or default. 297 func (c *DBConfiguration) DebugListenAddressOrDefault() string { 298 if c.DebugListenAddress == nil { 299 return defaultDebugListenAddress 300 } 301 302 return *c.DebugListenAddress 303 } 304 305 // HostIDOrDefault returns the host ID or default. 306 func (c *DBConfiguration) HostIDOrDefault() hostid.Configuration { 307 if c.HostID == nil { 308 return defaultHostID 309 } 310 311 return *c.HostID 312 } 313 314 // CommitLogOrDefault returns the commit log policy or default. 315 func (c *DBConfiguration) CommitLogOrDefault() CommitLogPolicy { 316 if c.CommitLog == nil { 317 return defaultCommitLogPolicy 318 } 319 320 return *c.CommitLog 321 } 322 323 // GCPercentageOrDefault returns the GC percentage or default. 324 func (c *DBConfiguration) GCPercentageOrDefault() int { 325 if c.GCPercentage == 0 { 326 return defaultGCPercentage 327 } 328 329 return c.GCPercentage 330 } 331 332 // WriteNewSeriesAsyncOrDefault returns whether to write new series async or not. 333 func (c *DBConfiguration) WriteNewSeriesAsyncOrDefault() bool { 334 if c.WriteNewSeriesAsync == nil { 335 return defaultWriteNewSeriesAsync 336 } 337 338 return *c.WriteNewSeriesAsync 339 } 340 341 // WriteNewSeriesBackoffDurationOrDefault returns the backoff duration for new series inserts. 342 func (c *DBConfiguration) WriteNewSeriesBackoffDurationOrDefault() time.Duration { 343 if c.WriteNewSeriesBackoffDuration == nil { 344 return defaultWriteNewSeriesBackoffDuration 345 } 346 347 return *c.WriteNewSeriesBackoffDuration 348 } 349 350 // PoolingPolicyOrDefault returns the pooling policy or default. 351 func (c *DBConfiguration) PoolingPolicyOrDefault() (PoolingPolicy, error) { 352 var policy PoolingPolicy 353 if c.PoolingPolicy != nil { 354 policy = *c.PoolingPolicy 355 } 356 357 if err := policy.InitDefaultsAndValidate(); err != nil { 358 return PoolingPolicy{}, err 359 } 360 361 return policy, nil 362 } 363 364 // DiscoveryOrDefault returns the discovery configuration or defaults. 365 func (c *DBConfiguration) DiscoveryOrDefault() discovery.Configuration { 366 if c.Discovery == nil { 367 return defaultDiscovery 368 } 369 370 return *c.Discovery 371 } 372 373 // Validate validates the Configuration. We use this method to validate fields 374 // where the validator package falls short. 375 func (c *DBConfiguration) Validate() error { 376 if err := c.Filesystem.Validate(); err != nil { 377 return err 378 } 379 380 if _, err := c.PoolingPolicyOrDefault(); err != nil { 381 return err 382 } 383 384 if err := c.Client.Validate(); err != nil { 385 return err 386 } 387 388 if err := c.Proto.Validate(); err != nil { 389 return err 390 } 391 392 if err := c.Transforms.Validate(); err != nil { 393 return err 394 } 395 396 if c.Replication != nil { 397 if err := c.Replication.Validate(); err != nil { 398 return err 399 } 400 } 401 402 return nil 403 } 404 405 // IndexConfiguration contains index-specific configuration. 406 type IndexConfiguration struct { 407 // MaxQueryIDsConcurrency controls the maximum number of outstanding QueryID 408 // requests that can be serviced concurrently. Limiting the concurrency is 409 // important to prevent index queries from overloading the database entirely 410 // as they are very CPU-intensive (regex and FST matching). 411 MaxQueryIDsConcurrency int `yaml:"maxQueryIDsConcurrency" validate:"min=0"` 412 413 // MaxWorkerTime is the maximum time a query can hold an index worker at once. If a query does not finish in this 414 // time it yields the worker and must wait again for another worker to resume. The number of workers available to 415 // all queries is defined by MaxQueryIDsConcurrency. 416 // Capping the maximum time per worker ensures a few large queries don't hold all the concurrent workers and lock 417 // out many small queries from running. 418 MaxWorkerTime time.Duration `yaml:"maxWorkerTime"` 419 420 // RegexpDFALimit is the limit on the max number of states used by a 421 // regexp deterministic finite automaton. Default is 10,000 states. 422 RegexpDFALimit *int `yaml:"regexpDFALimit"` 423 424 // RegexpFSALimit is the limit on the max number of bytes used by the 425 // finite state automaton. Default is 10mb (10 million as int). 426 RegexpFSALimit *uint `yaml:"regexpFSALimit"` 427 428 // ForwardIndexProbability determines the likelihood that an incoming write is 429 // written to the next block, when arriving close to the block boundary. 430 // 431 // NB: this is an optimization which lessens pressure on the index around 432 // block boundaries by eagerly writing the series to the next block 433 // preemptively. 434 ForwardIndexProbability float64 `yaml:"forwardIndexProbability" validate:"min=0.0,max=1.0"` 435 436 // ForwardIndexThreshold determines the threshold for forward writes, as a 437 // fraction of the given namespace's bufferFuture. 438 // 439 // NB: this is an optimization which lessens pressure on the index around 440 // block boundaries by eagerly writing the series to the next block 441 // preemptively. 442 ForwardIndexThreshold float64 `yaml:"forwardIndexThreshold" validate:"min=0.0,max=1.0"` 443 } 444 445 // RegexpDFALimitOrDefault returns the deterministic finite automaton states 446 // limit or default. 447 func (c IndexConfiguration) RegexpDFALimitOrDefault() int { 448 if c.RegexpDFALimit == nil { 449 return regexp.StateLimit() 450 } 451 return *c.RegexpDFALimit 452 } 453 454 // RegexpFSALimitOrDefault returns the finite state automaton size 455 // limit or default. 456 func (c IndexConfiguration) RegexpFSALimitOrDefault() uint { 457 if c.RegexpFSALimit == nil { 458 return regexp.DefaultLimit() 459 } 460 return *c.RegexpFSALimit 461 } 462 463 // TransformConfiguration contains configuration options that can transform 464 // incoming writes. 465 type TransformConfiguration struct { 466 // TruncateBy determines what type of truncatation is applied to incoming 467 // writes. 468 TruncateBy series.TruncateType `yaml:"truncateBy"` 469 // ForcedValue determines what to set all incoming write values to. 470 ForcedValue *float64 `yaml:"forceValue"` 471 } 472 473 // Validate validates the transform configuration. 474 func (c *TransformConfiguration) Validate() error { 475 if c == nil { 476 return nil 477 } 478 479 return c.TruncateBy.Validate() 480 } 481 482 // TickConfiguration is the tick configuration for background processing of 483 // series as blocks are rotated from mutable to immutable and out of order 484 // writes are merged. 485 type TickConfiguration struct { 486 // Tick series batch size is the batch size to process series together 487 // during a tick before yielding and sleeping the per series duration 488 // multiplied by the batch size. 489 // The higher this value is the more variable CPU utilization will be 490 // but the shorter ticks will ultimately be. 491 SeriesBatchSize int `yaml:"seriesBatchSize"` 492 493 // Tick per series sleep at the completion of a tick batch. 494 PerSeriesSleepDuration time.Duration `yaml:"perSeriesSleepDuration"` 495 496 // Tick minimum interval controls the minimum tick interval for the node. 497 MinimumInterval time.Duration `yaml:"minimumInterval"` 498 } 499 500 // BlockRetrievePolicy is the block retrieve policy. 501 type BlockRetrievePolicy struct { 502 // FetchConcurrency is the concurrency to fetch blocks from disk. For 503 // spinning disks it is highly recommended to set this value to 1. 504 FetchConcurrency *int `yaml:"fetchConcurrency" validate:"min=1"` 505 506 // CacheBlocksOnRetrieve globally enables/disables callbacks used to cache blocks fetched 507 // from disk. 508 CacheBlocksOnRetrieve *bool `yaml:"cacheBlocksOnRetrieve"` 509 } 510 511 // CommitLogPolicy is the commit log policy. 512 type CommitLogPolicy struct { 513 // The max size the commit log will flush a segment to disk after buffering. 514 FlushMaxBytes int `yaml:"flushMaxBytes" validate:"nonzero"` 515 516 // The maximum amount of time the commit log will wait to flush to disk. 517 FlushEvery time.Duration `yaml:"flushEvery" validate:"nonzero"` 518 519 // The queue the commit log will keep in front of the current commit log segment. 520 // Modifying values in this policy will control how many pending writes can be 521 // in the commitlog queue before M3DB will begin rejecting writes. 522 Queue CommitLogQueuePolicy `yaml:"queue" validate:"nonzero"` 523 524 // The actual Golang channel that implements the commit log queue. We separate this 525 // from the Queue field for historical / legacy reasons. Generally speaking, the 526 // values in this config should not need to be modified, but we leave it in for 527 // tuning purposes. Unlike the Queue field, values in this policy control the size 528 // of the channel that backs the queue. Since writes to the commitlog are batched, 529 // setting the size of this policy will control how many batches can be queued, and 530 // indrectly how many writes can be queued, but that is dependent on the batch size 531 // of the client. As a result, we recommend that users avoid tuning this field and 532 // modify the Queue size instead which maps directly to the number of writes. This 533 // works in most cases because the default size of the QueueChannel should be large 534 // enough for almost all workloads assuming a reasonable batch size is used. 535 QueueChannel *CommitLogQueuePolicy `yaml:"queueChannel"` 536 } 537 538 // CalculationType is a type of configuration parameter. 539 type CalculationType string 540 541 const ( 542 // CalculationTypeFixed is a fixed parameter not to be scaled of any parameter. 543 CalculationTypeFixed CalculationType = "fixed" 544 // CalculationTypePerCPU is a parameter that needs to be scaled by number of CPUs. 545 CalculationTypePerCPU CalculationType = "percpu" 546 ) 547 548 // CommitLogQueuePolicy is the commit log queue policy. 549 type CommitLogQueuePolicy struct { 550 // The type of calculation for the size. 551 CalculationType CalculationType `yaml:"calculationType"` 552 553 // The size of the commit log, calculated according to the calculation type. 554 Size int `yaml:"size" validate:"nonzero"` 555 } 556 557 // RepairPolicyMode is the repair policy mode. 558 type RepairPolicyMode uint 559 560 // RepairPolicy is the repair policy. 561 type RepairPolicy struct { 562 // Enabled or disabled. 563 Enabled bool `yaml:"enabled"` 564 565 // Type is the type of repair to run. 566 Type repair.Type `yaml:"type"` 567 568 // Strategy is the type of repair strategy to use. 569 Strategy repair.Strategy `yaml:"strategy"` 570 571 // Force the repair to run regardless of whether namespaces have repair enabled or not. 572 Force bool `yaml:"force"` 573 574 // The repair throttle. 575 Throttle time.Duration `yaml:"throttle"` 576 577 // The repair check interval. 578 CheckInterval time.Duration `yaml:"checkInterval"` 579 580 // Concurrency sets the repair shard concurrency if set. 581 Concurrency int `yaml:"concurrency"` 582 583 // Whether debug shadow comparisons are enabled. 584 DebugShadowComparisonsEnabled bool `yaml:"debugShadowComparisonsEnabled"` 585 586 // If enabled, what percentage of metadata should perform a detailed debug 587 // shadow comparison. 588 DebugShadowComparisonsPercentage float64 `yaml:"debugShadowComparisonsPercentage"` 589 } 590 591 // ReplicationPolicy is the replication policy. 592 type ReplicationPolicy struct { 593 Clusters []ReplicatedCluster `yaml:"clusters"` 594 } 595 596 // Validate validates the replication policy. 597 func (r *ReplicationPolicy) Validate() error { 598 names := map[string]bool{} 599 for _, c := range r.Clusters { 600 if err := c.Validate(); err != nil { 601 return err 602 } 603 604 if _, ok := names[c.Name]; ok { 605 return fmt.Errorf( 606 "replicated cluster names must be unique, but %s was repeated", 607 c.Name) 608 } 609 names[c.Name] = true 610 } 611 612 return nil 613 } 614 615 // ReplicatedCluster defines a cluster to replicate data from. 616 type ReplicatedCluster struct { 617 Name string `yaml:"name"` 618 RepairEnabled bool `yaml:"repairEnabled"` 619 Client *client.Configuration `yaml:"client"` 620 } 621 622 // Validate validates the configuration for a replicated cluster. 623 func (r *ReplicatedCluster) Validate() error { 624 if r.Name == "" { 625 return errors.New("replicated cluster must be assigned a name") 626 } 627 628 if r.RepairEnabled && r.Client == nil { 629 return fmt.Errorf( 630 "replicated cluster: %s has repair enabled but not client configuration", r.Name) 631 } 632 633 return nil 634 } 635 636 // HashingConfiguration is the configuration for hashing. 637 type HashingConfiguration struct { 638 // Murmur32 seed value. 639 Seed uint32 `yaml:"seed"` 640 } 641 642 // ProtoConfiguration is the configuration for running with ProtoDataMode enabled. 643 type ProtoConfiguration struct { 644 // Enabled specifies whether proto is enabled. 645 Enabled bool `yaml:"enabled"` 646 SchemaRegistry map[string]NamespaceProtoSchema `yaml:"schema_registry"` 647 } 648 649 // NamespaceProtoSchema is the namespace protobuf schema. 650 type NamespaceProtoSchema struct { 651 // For application m3db client integration test convenience (where a local dbnode is started as a docker container), 652 // we allow loading user schema from local file into schema registry. 653 SchemaFilePath string `yaml:"schemaFilePath"` 654 MessageName string `yaml:"messageName"` 655 } 656 657 // Validate validates the NamespaceProtoSchema. 658 func (c NamespaceProtoSchema) Validate() error { 659 if c.SchemaFilePath == "" { 660 return errors.New("schemaFilePath is required for Proto data mode") 661 } 662 663 if c.MessageName == "" { 664 return errors.New("messageName is required for Proto data mode") 665 } 666 667 return nil 668 } 669 670 // Validate validates the ProtoConfiguration. 671 func (c *ProtoConfiguration) Validate() error { 672 if c == nil || !c.Enabled { 673 return nil 674 } 675 676 for _, schema := range c.SchemaRegistry { 677 if err := schema.Validate(); err != nil { 678 return err 679 } 680 } 681 return nil 682 } 683 684 // NewEtcdEmbedConfig creates a new embedded etcd config from kv config. 685 func NewEtcdEmbedConfig(cfg DBConfiguration) (*embed.Config, error) { 686 newKVCfg := embed.NewConfig() 687 688 hostID, err := cfg.HostIDOrDefault().Resolve() 689 if err != nil { 690 return nil, fmt.Errorf("failed resolving hostID %w", err) 691 } 692 693 discoveryCfg := cfg.DiscoveryOrDefault() 694 envCfg, err := discoveryCfg.EnvironmentConfig(hostID) 695 if err != nil { 696 return nil, fmt.Errorf("failed getting env config from discovery config %w", err) 697 } 698 699 kvCfg := envCfg.SeedNodes 700 newKVCfg.Name = hostID 701 702 dir := kvCfg.RootDir 703 if dir == "" { 704 dir = path.Join(cfg.Filesystem.FilePathPrefixOrDefault(), defaultEtcdDirSuffix) 705 } 706 newKVCfg.Dir = dir 707 708 LPUrls, err := convertToURLsWithDefault(kvCfg.ListenPeerUrls, newURL(defaultEtcdListenHost, DefaultEtcdServerPort)) 709 if err != nil { 710 return nil, err 711 } 712 newKVCfg.LPUrls = LPUrls 713 714 LCUrls, err := convertToURLsWithDefault(kvCfg.ListenClientUrls, newURL(defaultEtcdListenHost, DefaultEtcdClientPort)) 715 if err != nil { 716 return nil, err 717 } 718 newKVCfg.LCUrls = LCUrls 719 720 host, endpoint, err := getHostAndEndpointFromID(kvCfg.InitialCluster, hostID) 721 if err != nil { 722 return nil, err 723 } 724 725 if host.ClusterState != "" { 726 newKVCfg.ClusterState = host.ClusterState 727 } 728 729 APUrls, err := convertToURLsWithDefault(kvCfg.InitialAdvertisePeerUrls, newURL(endpoint, DefaultEtcdServerPort)) 730 if err != nil { 731 return nil, err 732 } 733 newKVCfg.APUrls = APUrls 734 735 ACUrls, err := convertToURLsWithDefault(kvCfg.AdvertiseClientUrls, newURL(endpoint, DefaultEtcdClientPort)) 736 if err != nil { 737 return nil, err 738 } 739 newKVCfg.ACUrls = ACUrls 740 741 newKVCfg.InitialCluster = initialClusterString(kvCfg.InitialCluster) 742 743 copySecurityDetails := func(tls *transport.TLSInfo, ysc *environment.SeedNodeSecurityConfig) { 744 tls.TrustedCAFile = ysc.CAFile 745 tls.CertFile = ysc.CertFile 746 tls.KeyFile = ysc.KeyFile 747 tls.ClientCertAuth = ysc.CertAuth 748 tls.TrustedCAFile = ysc.TrustedCAFile 749 } 750 copySecurityDetails(&newKVCfg.ClientTLSInfo, &kvCfg.ClientTransportSecurity) 751 copySecurityDetails(&newKVCfg.PeerTLSInfo, &kvCfg.PeerTransportSecurity) 752 newKVCfg.ClientAutoTLS = kvCfg.ClientTransportSecurity.AutoTLS 753 newKVCfg.PeerAutoTLS = kvCfg.PeerTransportSecurity.AutoTLS 754 755 return newKVCfg, nil 756 } 757 758 func newURL(host string, port int) string { 759 return fmt.Sprintf("%s:%d", host, port) 760 } 761 762 func convertToURLsWithDefault(urlStrs []string, def ...string) ([]url.URL, error) { 763 if len(urlStrs) == 0 { 764 urlStrs = def 765 } 766 767 urls, err := types.NewURLs(urlStrs) 768 if err != nil { 769 return nil, err 770 } 771 772 return urls, nil 773 } 774 775 func initialClusterString(initialCluster []environment.SeedNode) string { 776 var buffer bytes.Buffer 777 778 for i, seedNode := range initialCluster { 779 buffer.WriteString(seedNode.HostID) 780 buffer.WriteString("=") 781 buffer.WriteString(seedNode.Endpoint) 782 783 if i < len(initialCluster)-1 { 784 buffer.WriteString(",") 785 } 786 } 787 788 return buffer.String() 789 } 790 791 func getHostAndEndpointFromID(initialCluster []environment.SeedNode, hostID string) (environment.SeedNode, string, error) { 792 emptySeedNode := environment.SeedNode{} 793 794 if len(initialCluster) == 0 { 795 return emptySeedNode, "", errors.New("zero seed nodes in initialCluster") 796 } 797 798 for _, seedNode := range initialCluster { 799 if hostID == seedNode.HostID { 800 endpoint := seedNode.Endpoint 801 802 colonIdx := strings.LastIndex(endpoint, ":") 803 if colonIdx == -1 { 804 return emptySeedNode, "", errors.New("invalid initialCluster format") 805 } 806 807 return seedNode, endpoint[:colonIdx], nil 808 } 809 } 810 811 return emptySeedNode, "", errors.New("host not in initialCluster list") 812 } 813 814 // InitialClusterEndpoints returns the endpoints of the initial cluster 815 func InitialClusterEndpoints(initialCluster []environment.SeedNode) ([]string, error) { 816 endpoints := make([]string, 0, len(initialCluster)) 817 818 for _, seedNode := range initialCluster { 819 endpoint := seedNode.Endpoint 820 821 colonIdx := strings.LastIndex(endpoint, ":") 822 if colonIdx == -1 { 823 return nil, errors.New("invalid initialCluster format") 824 } 825 826 endpoints = append(endpoints, newURL(endpoint[:colonIdx], DefaultEtcdClientPort)) 827 } 828 829 return endpoints, nil 830 } 831 832 // IsSeedNode returns whether the given hostID is an etcd node. 833 func IsSeedNode(initialCluster []environment.SeedNode, hostID string) bool { 834 for _, seedNode := range initialCluster { 835 if seedNode.HostID == hostID { 836 return true 837 } 838 } 839 840 return false 841 } 842 843 // TChannelConfiguration holds TChannel config options. 844 type TChannelConfiguration struct { 845 // MaxIdleTime is the maximum idle time. 846 MaxIdleTime time.Duration `yaml:"maxIdleTime"` 847 // IdleCheckInterval is the idle check interval. 848 IdleCheckInterval time.Duration `yaml:"idleCheckInterval"` 849 }