github.com/diptanu/nomad@v0.5.7-0.20170516172507-d72e86cbe3d9/command/agent/config.go (about) 1 package agent 2 3 import ( 4 "encoding/base64" 5 "errors" 6 "fmt" 7 "io" 8 "net" 9 "os" 10 "path/filepath" 11 "runtime" 12 "sort" 13 "strconv" 14 "strings" 15 "time" 16 17 "github.com/hashicorp/go-sockaddr/template" 18 19 client "github.com/hashicorp/nomad/client/config" 20 "github.com/hashicorp/nomad/helper" 21 "github.com/hashicorp/nomad/nomad" 22 "github.com/hashicorp/nomad/nomad/structs/config" 23 ) 24 25 // Config is the configuration for the Nomad agent. 26 type Config struct { 27 // Region is the region this agent is in. Defaults to global. 28 Region string `mapstructure:"region"` 29 30 // Datacenter is the datacenter this agent is in. Defaults to dc1 31 Datacenter string `mapstructure:"datacenter"` 32 33 // NodeName is the name we register as. Defaults to hostname. 34 NodeName string `mapstructure:"name"` 35 36 // DataDir is the directory to store our state in 37 DataDir string `mapstructure:"data_dir"` 38 39 // LogLevel is the level of the logs to putout 40 LogLevel string `mapstructure:"log_level"` 41 42 // BindAddr is the address on which all of nomad's services will 43 // be bound. If not specified, this defaults to 127.0.0.1. 44 BindAddr string `mapstructure:"bind_addr"` 45 46 // EnableDebug is used to enable debugging HTTP endpoints 47 EnableDebug bool `mapstructure:"enable_debug"` 48 49 // Ports is used to control the network ports we bind to. 50 Ports *Ports `mapstructure:"ports"` 51 52 // Addresses is used to override the network addresses we bind to. 53 // 54 // Use normalizedAddrs if you need the host+port to bind to. 55 Addresses *Addresses `mapstructure:"addresses"` 56 57 // normalizedAddr is set to the Address+Port by normalizeAddrs() 58 normalizedAddrs *Addresses 59 60 // AdvertiseAddrs is used to control the addresses we advertise. 61 AdvertiseAddrs *AdvertiseAddrs `mapstructure:"advertise"` 62 63 // Client has our client related settings 64 Client *ClientConfig `mapstructure:"client"` 65 66 // Server has our server related settings 67 Server *ServerConfig `mapstructure:"server"` 68 69 // Telemetry is used to configure sending telemetry 70 Telemetry *Telemetry `mapstructure:"telemetry"` 71 72 // LeaveOnInt is used to gracefully leave on the interrupt signal 73 LeaveOnInt bool `mapstructure:"leave_on_interrupt"` 74 75 // LeaveOnTerm is used to gracefully leave on the terminate signal 76 LeaveOnTerm bool `mapstructure:"leave_on_terminate"` 77 78 // EnableSyslog is used to enable sending logs to syslog 79 EnableSyslog bool `mapstructure:"enable_syslog"` 80 81 // SyslogFacility is used to control the syslog facility used. 82 SyslogFacility string `mapstructure:"syslog_facility"` 83 84 // DisableUpdateCheck is used to disable the periodic update 85 // and security bulletin checking. 86 DisableUpdateCheck bool `mapstructure:"disable_update_check"` 87 88 // DisableAnonymousSignature is used to disable setting the 89 // anonymous signature when doing the update check and looking 90 // for security bulletins 91 DisableAnonymousSignature bool `mapstructure:"disable_anonymous_signature"` 92 93 // AtlasConfig is used to configure Atlas 94 Atlas *AtlasConfig `mapstructure:"atlas"` 95 96 // Consul contains the configuration for the Consul Agent and 97 // parameters necessary to register services, their checks, and 98 // discover the current Nomad servers. 99 Consul *config.ConsulConfig `mapstructure:"consul"` 100 101 // Vault contains the configuration for the Vault Agent and 102 // parameters necessary to derive tokens. 103 Vault *config.VaultConfig `mapstructure:"vault"` 104 105 // NomadConfig is used to override the default config. 106 // This is largly used for testing purposes. 107 NomadConfig *nomad.Config `mapstructure:"-" json:"-"` 108 109 // ClientConfig is used to override the default config. 110 // This is largly used for testing purposes. 111 ClientConfig *client.Config `mapstructure:"-" json:"-"` 112 113 // DevMode is set by the -dev CLI flag. 114 DevMode bool `mapstructure:"-"` 115 116 // Version information is set at compilation time 117 Revision string 118 Version string 119 VersionPrerelease string 120 121 // List of config files that have been loaded (in order) 122 Files []string `mapstructure:"-"` 123 124 // TLSConfig provides TLS related configuration for the Nomad server and 125 // client 126 TLSConfig *config.TLSConfig `mapstructure:"tls"` 127 128 // HTTPAPIResponseHeaders allows users to configure the Nomad http agent to 129 // set arbritrary headers on API responses 130 HTTPAPIResponseHeaders map[string]string `mapstructure:"http_api_response_headers"` 131 } 132 133 // AtlasConfig is used to enable an parameterize the Atlas integration 134 type AtlasConfig struct { 135 // Infrastructure is the name of the infrastructure 136 // we belong to. e.g. hashicorp/stage 137 Infrastructure string `mapstructure:"infrastructure"` 138 139 // Token is our authentication token from Atlas 140 Token string `mapstructure:"token" json:"-"` 141 142 // Join controls if Atlas will attempt to auto-join the node 143 // to it's cluster. Requires Atlas integration. 144 Join bool `mapstructure:"join"` 145 146 // Endpoint is the SCADA endpoint used for Atlas integration. If 147 // empty, the defaults from the provider are used. 148 Endpoint string `mapstructure:"endpoint"` 149 } 150 151 // ClientConfig is configuration specific to the client mode 152 type ClientConfig struct { 153 // Enabled controls if we are a client 154 Enabled bool `mapstructure:"enabled"` 155 156 // StateDir is the state directory 157 StateDir string `mapstructure:"state_dir"` 158 159 // AllocDir is the directory for storing allocation data 160 AllocDir string `mapstructure:"alloc_dir"` 161 162 // Servers is a list of known server addresses. These are as "host:port" 163 Servers []string `mapstructure:"servers"` 164 165 // NodeClass is used to group the node by class 166 NodeClass string `mapstructure:"node_class"` 167 168 // Options is used for configuration of nomad internals, 169 // like fingerprinters and drivers. The format is: 170 // 171 // namespace.option = value 172 Options map[string]string `mapstructure:"options"` 173 174 // Metadata associated with the node 175 Meta map[string]string `mapstructure:"meta"` 176 177 // A mapping of directories on the host OS to attempt to embed inside each 178 // task's chroot. 179 ChrootEnv map[string]string `mapstructure:"chroot_env"` 180 181 // Interface to use for network fingerprinting 182 NetworkInterface string `mapstructure:"network_interface"` 183 184 // NetworkSpeed is used to override any detected or default network link 185 // speed. 186 NetworkSpeed int `mapstructure:"network_speed"` 187 188 // CpuCompute is used to override any detected or default total CPU compute. 189 CpuCompute int `mapstructure:"cpu_total_compute"` 190 191 // MaxKillTimeout allows capping the user-specifiable KillTimeout. 192 MaxKillTimeout string `mapstructure:"max_kill_timeout"` 193 194 // ClientMaxPort is the upper range of the ports that the client uses for 195 // communicating with plugin subsystems 196 ClientMaxPort int `mapstructure:"client_max_port"` 197 198 // ClientMinPort is the lower range of the ports that the client uses for 199 // communicating with plugin subsystems 200 ClientMinPort int `mapstructure:"client_min_port"` 201 202 // Reserved is used to reserve resources from being used by Nomad. This can 203 // be used to target a certain utilization or to prevent Nomad from using a 204 // particular set of ports. 205 Reserved *Resources `mapstructure:"reserved"` 206 207 // GCInterval is the time interval at which the client triggers garbage 208 // collection 209 GCInterval time.Duration `mapstructure:"gc_interval"` 210 211 // GCParallelDestroys is the number of parallel destroys the garbage 212 // collector will allow. 213 GCParallelDestroys int `mapstructure:"gc_parallel_destroys"` 214 215 // GCInodeUsageThreshold is the inode usage threshold beyond which the Nomad 216 // client triggers GC of the terminal allocations 217 GCDiskUsageThreshold float64 `mapstructure:"gc_disk_usage_threshold"` 218 219 // GCInodeUsageThreshold is the inode usage threshold beyond which the Nomad 220 // client triggers GC of the terminal allocations 221 GCInodeUsageThreshold float64 `mapstructure:"gc_inode_usage_threshold"` 222 223 // NoHostUUID disables using the host's UUID and will force generation of a 224 // random UUID. 225 NoHostUUID bool `mapstructure:"no_host_uuid"` 226 } 227 228 // ServerConfig is configuration specific to the server mode 229 type ServerConfig struct { 230 // Enabled controls if we are a server 231 Enabled bool `mapstructure:"enabled"` 232 233 // BootstrapExpect tries to automatically bootstrap the Consul cluster, 234 // by withholding peers until enough servers join. 235 BootstrapExpect int `mapstructure:"bootstrap_expect"` 236 237 // DataDir is the directory to store our state in 238 DataDir string `mapstructure:"data_dir"` 239 240 // ProtocolVersion is the protocol version to speak. This must be between 241 // ProtocolVersionMin and ProtocolVersionMax. 242 ProtocolVersion int `mapstructure:"protocol_version"` 243 244 // NumSchedulers is the number of scheduler thread that are run. 245 // This can be as many as one per core, or zero to disable this server 246 // from doing any scheduling work. 247 NumSchedulers int `mapstructure:"num_schedulers"` 248 249 // EnabledSchedulers controls the set of sub-schedulers that are 250 // enabled for this server to handle. This will restrict the evaluations 251 // that the workers dequeue for processing. 252 EnabledSchedulers []string `mapstructure:"enabled_schedulers"` 253 254 // NodeGCThreshold controls how "old" a node must be to be collected by GC. 255 // Age is not the only requirement for a node to be GCed but the threshold 256 // can be used to filter by age. 257 NodeGCThreshold string `mapstructure:"node_gc_threshold"` 258 259 // JobGCThreshold controls how "old" a job must be to be collected by GC. 260 // Age is not the only requirement for a Job to be GCed but the threshold 261 // can be used to filter by age. 262 JobGCThreshold string `mapstructure:"job_gc_threshold"` 263 264 // EvalGCThreshold controls how "old" an eval must be to be collected by GC. 265 // Age is not the only requirement for a eval to be GCed but the threshold 266 // can be used to filter by age. 267 EvalGCThreshold string `mapstructure:"eval_gc_threshold"` 268 269 // HeartbeatGrace is the grace period beyond the TTL to account for network, 270 // processing delays and clock skew before marking a node as "down". 271 HeartbeatGrace string `mapstructure:"heartbeat_grace"` 272 273 // StartJoin is a list of addresses to attempt to join when the 274 // agent starts. If Serf is unable to communicate with any of these 275 // addresses, then the agent will error and exit. 276 StartJoin []string `mapstructure:"start_join"` 277 278 // RetryJoin is a list of addresses to join with retry enabled. 279 RetryJoin []string `mapstructure:"retry_join"` 280 281 // RetryMaxAttempts specifies the maximum number of times to retry joining a 282 // host on startup. This is useful for cases where we know the node will be 283 // online eventually. 284 RetryMaxAttempts int `mapstructure:"retry_max"` 285 286 // RetryInterval specifies the amount of time to wait in between join 287 // attempts on agent start. The minimum allowed value is 1 second and 288 // the default is 30s. 289 RetryInterval string `mapstructure:"retry_interval"` 290 retryInterval time.Duration `mapstructure:"-"` 291 292 // RejoinAfterLeave controls our interaction with the cluster after leave. 293 // When set to false (default), a leave causes Consul to not rejoin 294 // the cluster until an explicit join is received. If this is set to 295 // true, we ignore the leave, and rejoin the cluster on start. 296 RejoinAfterLeave bool `mapstructure:"rejoin_after_leave"` 297 298 // Encryption key to use for the Serf communication 299 EncryptKey string `mapstructure:"encrypt" json:"-"` 300 } 301 302 // EncryptBytes returns the encryption key configured. 303 func (s *ServerConfig) EncryptBytes() ([]byte, error) { 304 return base64.StdEncoding.DecodeString(s.EncryptKey) 305 } 306 307 // Telemetry is the telemetry configuration for the server 308 type Telemetry struct { 309 StatsiteAddr string `mapstructure:"statsite_address"` 310 StatsdAddr string `mapstructure:"statsd_address"` 311 DataDogAddr string `mapstructure:"datadog_address"` 312 DisableHostname bool `mapstructure:"disable_hostname"` 313 UseNodeName bool `mapstructure:"use_node_name"` 314 CollectionInterval string `mapstructure:"collection_interval"` 315 collectionInterval time.Duration `mapstructure:"-"` 316 PublishAllocationMetrics bool `mapstructure:"publish_allocation_metrics"` 317 PublishNodeMetrics bool `mapstructure:"publish_node_metrics"` 318 319 // Circonus: see https://github.com/circonus-labs/circonus-gometrics 320 // for more details on the various configuration options. 321 // Valid configuration combinations: 322 // - CirconusAPIToken 323 // metric management enabled (search for existing check or create a new one) 324 // - CirconusSubmissionUrl 325 // metric management disabled (use check with specified submission_url, 326 // broker must be using a public SSL certificate) 327 // - CirconusAPIToken + CirconusCheckSubmissionURL 328 // metric management enabled (use check with specified submission_url) 329 // - CirconusAPIToken + CirconusCheckID 330 // metric management enabled (use check with specified id) 331 332 // CirconusAPIToken is a valid API Token used to create/manage check. If provided, 333 // metric management is enabled. 334 // Default: none 335 CirconusAPIToken string `mapstructure:"circonus_api_token"` 336 // CirconusAPIApp is an app name associated with API token. 337 // Default: "nomad" 338 CirconusAPIApp string `mapstructure:"circonus_api_app"` 339 // CirconusAPIURL is the base URL to use for contacting the Circonus API. 340 // Default: "https://api.circonus.com/v2" 341 CirconusAPIURL string `mapstructure:"circonus_api_url"` 342 // CirconusSubmissionInterval is the interval at which metrics are submitted to Circonus. 343 // Default: 10s 344 CirconusSubmissionInterval string `mapstructure:"circonus_submission_interval"` 345 // CirconusCheckSubmissionURL is the check.config.submission_url field from a 346 // previously created HTTPTRAP check. 347 // Default: none 348 CirconusCheckSubmissionURL string `mapstructure:"circonus_submission_url"` 349 // CirconusCheckID is the check id (not check bundle id) from a previously created 350 // HTTPTRAP check. The numeric portion of the check._cid field. 351 // Default: none 352 CirconusCheckID string `mapstructure:"circonus_check_id"` 353 // CirconusCheckForceMetricActivation will force enabling metrics, as they are encountered, 354 // if the metric already exists and is NOT active. If check management is enabled, the default 355 // behavior is to add new metrics as they are encoutered. If the metric already exists in the 356 // check, it will *NOT* be activated. This setting overrides that behavior. 357 // Default: "false" 358 CirconusCheckForceMetricActivation string `mapstructure:"circonus_check_force_metric_activation"` 359 // CirconusCheckInstanceID serves to uniquely identify the metrics comming from this "instance". 360 // It can be used to maintain metric continuity with transient or ephemeral instances as 361 // they move around within an infrastructure. 362 // Default: hostname:app 363 CirconusCheckInstanceID string `mapstructure:"circonus_check_instance_id"` 364 // CirconusCheckSearchTag is a special tag which, when coupled with the instance id, helps to 365 // narrow down the search results when neither a Submission URL or Check ID is provided. 366 // Default: service:app (e.g. service:nomad) 367 CirconusCheckSearchTag string `mapstructure:"circonus_check_search_tag"` 368 // CirconusCheckTags is a comma separated list of tags to apply to the check. Note that 369 // the value of CirconusCheckSearchTag will always be added to the check. 370 // Default: none 371 CirconusCheckTags string `mapstructure:"circonus_check_tags"` 372 // CirconusCheckDisplayName is the name for the check which will be displayed in the Circonus UI. 373 // Default: value of CirconusCheckInstanceID 374 CirconusCheckDisplayName string `mapstructure:"circonus_check_display_name"` 375 // CirconusBrokerID is an explicit broker to use when creating a new check. The numeric portion 376 // of broker._cid. If metric management is enabled and neither a Submission URL nor Check ID 377 // is provided, an attempt will be made to search for an existing check using Instance ID and 378 // Search Tag. If one is not found, a new HTTPTRAP check will be created. 379 // Default: use Select Tag if provided, otherwise, a random Enterprise Broker associated 380 // with the specified API token or the default Circonus Broker. 381 // Default: none 382 CirconusBrokerID string `mapstructure:"circonus_broker_id"` 383 // CirconusBrokerSelectTag is a special tag which will be used to select a broker when 384 // a Broker ID is not provided. The best use of this is to as a hint for which broker 385 // should be used based on *where* this particular instance is running. 386 // (e.g. a specific geo location or datacenter, dc:sfo) 387 // Default: none 388 CirconusBrokerSelectTag string `mapstructure:"circonus_broker_select_tag"` 389 } 390 391 // Ports encapsulates the various ports we bind to for network services. If any 392 // are not specified then the defaults are used instead. 393 type Ports struct { 394 HTTP int `mapstructure:"http"` 395 RPC int `mapstructure:"rpc"` 396 Serf int `mapstructure:"serf"` 397 } 398 399 // Addresses encapsulates all of the addresses we bind to for various 400 // network services. Everything is optional and defaults to BindAddr. 401 type Addresses struct { 402 HTTP string `mapstructure:"http"` 403 RPC string `mapstructure:"rpc"` 404 Serf string `mapstructure:"serf"` 405 } 406 407 // AdvertiseAddrs is used to control the addresses we advertise out for 408 // different network services. All are optional and default to BindAddr and 409 // their default Port. 410 type AdvertiseAddrs struct { 411 HTTP string `mapstructure:"http"` 412 RPC string `mapstructure:"rpc"` 413 Serf string `mapstructure:"serf"` 414 } 415 416 type Resources struct { 417 CPU int `mapstructure:"cpu"` 418 MemoryMB int `mapstructure:"memory"` 419 DiskMB int `mapstructure:"disk"` 420 IOPS int `mapstructure:"iops"` 421 ReservedPorts string `mapstructure:"reserved_ports"` 422 ParsedReservedPorts []int `mapstructure:"-"` 423 } 424 425 // ParseReserved expands the ReservedPorts string into a slice of port numbers. 426 // The supported syntax is comma seperated integers or ranges seperated by 427 // hyphens. For example, "80,120-150,160" 428 func (r *Resources) ParseReserved() error { 429 parts := strings.Split(r.ReservedPorts, ",") 430 431 // Hot path the empty case 432 if len(parts) == 1 && parts[0] == "" { 433 return nil 434 } 435 436 ports := make(map[int]struct{}) 437 for _, part := range parts { 438 part = strings.TrimSpace(part) 439 rangeParts := strings.Split(part, "-") 440 l := len(rangeParts) 441 switch l { 442 case 1: 443 if val := rangeParts[0]; val == "" { 444 return fmt.Errorf("can't specify empty port") 445 } else { 446 port, err := strconv.Atoi(val) 447 if err != nil { 448 return err 449 } 450 ports[port] = struct{}{} 451 } 452 case 2: 453 // We are parsing a range 454 start, err := strconv.Atoi(rangeParts[0]) 455 if err != nil { 456 return err 457 } 458 459 end, err := strconv.Atoi(rangeParts[1]) 460 if err != nil { 461 return err 462 } 463 464 if end < start { 465 return fmt.Errorf("invalid range: starting value (%v) less than ending (%v) value", end, start) 466 } 467 468 for i := start; i <= end; i++ { 469 ports[i] = struct{}{} 470 } 471 default: 472 return fmt.Errorf("can only parse single port numbers or port ranges (ex. 80,100-120,150)") 473 } 474 } 475 476 for port := range ports { 477 r.ParsedReservedPorts = append(r.ParsedReservedPorts, port) 478 } 479 480 sort.Ints(r.ParsedReservedPorts) 481 return nil 482 } 483 484 // DevConfig is a Config that is used for dev mode of Nomad. 485 func DevConfig() *Config { 486 conf := DefaultConfig() 487 conf.BindAddr = "127.0.0.1" 488 conf.LogLevel = "DEBUG" 489 conf.Client.Enabled = true 490 conf.Server.Enabled = true 491 conf.DevMode = true 492 conf.EnableDebug = true 493 conf.DisableAnonymousSignature = true 494 conf.Consul.AutoAdvertise = helper.BoolToPtr(true) 495 if runtime.GOOS == "darwin" { 496 conf.Client.NetworkInterface = "lo0" 497 } else if runtime.GOOS == "linux" { 498 conf.Client.NetworkInterface = "lo" 499 } 500 conf.Client.Options = map[string]string{ 501 "driver.raw_exec.enable": "true", 502 } 503 conf.Client.Options = map[string]string{ 504 "driver.docker.volumes": "true", 505 } 506 conf.Client.GCInterval = 10 * time.Minute 507 conf.Client.GCDiskUsageThreshold = 99 508 conf.Client.GCInodeUsageThreshold = 99 509 510 return conf 511 } 512 513 // DefaultConfig is a the baseline configuration for Nomad 514 func DefaultConfig() *Config { 515 return &Config{ 516 LogLevel: "INFO", 517 Region: "global", 518 Datacenter: "dc1", 519 BindAddr: "0.0.0.0", 520 Ports: &Ports{ 521 HTTP: 4646, 522 RPC: 4647, 523 Serf: 4648, 524 }, 525 Addresses: &Addresses{}, 526 AdvertiseAddrs: &AdvertiseAddrs{}, 527 Atlas: &AtlasConfig{}, 528 Consul: config.DefaultConsulConfig(), 529 Vault: config.DefaultVaultConfig(), 530 Client: &ClientConfig{ 531 Enabled: false, 532 MaxKillTimeout: "30s", 533 ClientMinPort: 14000, 534 ClientMaxPort: 14512, 535 Reserved: &Resources{}, 536 GCInterval: 1 * time.Minute, 537 GCParallelDestroys: 2, 538 GCInodeUsageThreshold: 70, 539 GCDiskUsageThreshold: 80, 540 }, 541 Server: &ServerConfig{ 542 Enabled: false, 543 StartJoin: []string{}, 544 RetryJoin: []string{}, 545 RetryInterval: "30s", 546 RetryMaxAttempts: 0, 547 }, 548 SyslogFacility: "LOCAL0", 549 Telemetry: &Telemetry{ 550 CollectionInterval: "1s", 551 collectionInterval: 1 * time.Second, 552 }, 553 TLSConfig: &config.TLSConfig{}, 554 } 555 } 556 557 // Listener can be used to get a new listener using a custom bind address. 558 // If the bind provided address is empty, the BindAddr is used instead. 559 func (c *Config) Listener(proto, addr string, port int) (net.Listener, error) { 560 if addr == "" { 561 addr = c.BindAddr 562 } 563 564 // Do our own range check to avoid bugs in package net. 565 // 566 // golang.org/issue/11715 567 // golang.org/issue/13447 568 // 569 // Both of the above bugs were fixed by golang.org/cl/12447 which will be 570 // included in Go 1.6. The error returned below is the same as what Go 1.6 571 // will return. 572 if 0 > port || port > 65535 { 573 return nil, &net.OpError{ 574 Op: "listen", 575 Net: proto, 576 Err: &net.AddrError{Err: "invalid port", Addr: fmt.Sprint(port)}, 577 } 578 } 579 return net.Listen(proto, net.JoinHostPort(addr, strconv.Itoa(port))) 580 } 581 582 // Merge merges two configurations. 583 func (c *Config) Merge(b *Config) *Config { 584 result := *c 585 586 if b.Region != "" { 587 result.Region = b.Region 588 } 589 if b.Datacenter != "" { 590 result.Datacenter = b.Datacenter 591 } 592 if b.NodeName != "" { 593 result.NodeName = b.NodeName 594 } 595 if b.DataDir != "" { 596 result.DataDir = b.DataDir 597 } 598 if b.LogLevel != "" { 599 result.LogLevel = b.LogLevel 600 } 601 if b.BindAddr != "" { 602 result.BindAddr = b.BindAddr 603 } 604 if b.EnableDebug { 605 result.EnableDebug = true 606 } 607 if b.LeaveOnInt { 608 result.LeaveOnInt = true 609 } 610 if b.LeaveOnTerm { 611 result.LeaveOnTerm = true 612 } 613 if b.EnableSyslog { 614 result.EnableSyslog = true 615 } 616 if b.SyslogFacility != "" { 617 result.SyslogFacility = b.SyslogFacility 618 } 619 if b.DisableUpdateCheck { 620 result.DisableUpdateCheck = true 621 } 622 if b.DisableAnonymousSignature { 623 result.DisableAnonymousSignature = true 624 } 625 626 // Apply the telemetry config 627 if result.Telemetry == nil && b.Telemetry != nil { 628 telemetry := *b.Telemetry 629 result.Telemetry = &telemetry 630 } else if b.Telemetry != nil { 631 result.Telemetry = result.Telemetry.Merge(b.Telemetry) 632 } 633 634 // Apply the TLS Config 635 if result.TLSConfig == nil && b.TLSConfig != nil { 636 tlsConfig := *b.TLSConfig 637 result.TLSConfig = &tlsConfig 638 } else if b.TLSConfig != nil { 639 result.TLSConfig = result.TLSConfig.Merge(b.TLSConfig) 640 } 641 642 // Apply the client config 643 if result.Client == nil && b.Client != nil { 644 client := *b.Client 645 result.Client = &client 646 } else if b.Client != nil { 647 result.Client = result.Client.Merge(b.Client) 648 } 649 650 // Apply the server config 651 if result.Server == nil && b.Server != nil { 652 server := *b.Server 653 result.Server = &server 654 } else if b.Server != nil { 655 result.Server = result.Server.Merge(b.Server) 656 } 657 658 // Apply the ports config 659 if result.Ports == nil && b.Ports != nil { 660 ports := *b.Ports 661 result.Ports = &ports 662 } else if b.Ports != nil { 663 result.Ports = result.Ports.Merge(b.Ports) 664 } 665 666 // Apply the address config 667 if result.Addresses == nil && b.Addresses != nil { 668 addrs := *b.Addresses 669 result.Addresses = &addrs 670 } else if b.Addresses != nil { 671 result.Addresses = result.Addresses.Merge(b.Addresses) 672 } 673 674 // Apply the advertise addrs config 675 if result.AdvertiseAddrs == nil && b.AdvertiseAddrs != nil { 676 advertise := *b.AdvertiseAddrs 677 result.AdvertiseAddrs = &advertise 678 } else if b.AdvertiseAddrs != nil { 679 result.AdvertiseAddrs = result.AdvertiseAddrs.Merge(b.AdvertiseAddrs) 680 } 681 682 // Apply the Atlas configuration 683 if result.Atlas == nil && b.Atlas != nil { 684 atlasConfig := *b.Atlas 685 result.Atlas = &atlasConfig 686 } else if b.Atlas != nil { 687 result.Atlas = result.Atlas.Merge(b.Atlas) 688 } 689 690 // Apply the Consul Configuration 691 if result.Consul == nil && b.Consul != nil { 692 result.Consul = b.Consul.Copy() 693 } else if b.Consul != nil { 694 result.Consul = result.Consul.Merge(b.Consul) 695 } 696 697 // Apply the Vault Configuration 698 if result.Vault == nil && b.Vault != nil { 699 vaultConfig := *b.Vault 700 result.Vault = &vaultConfig 701 } else if b.Vault != nil { 702 result.Vault = result.Vault.Merge(b.Vault) 703 } 704 705 // Merge config files lists 706 result.Files = append(result.Files, b.Files...) 707 708 // Add the http API response header map values 709 if result.HTTPAPIResponseHeaders == nil { 710 result.HTTPAPIResponseHeaders = make(map[string]string) 711 } 712 for k, v := range b.HTTPAPIResponseHeaders { 713 result.HTTPAPIResponseHeaders[k] = v 714 } 715 716 return &result 717 } 718 719 // normalizeAddrs normalizes Addresses and AdvertiseAddrs to always be 720 // initialized and have sane defaults. 721 func (c *Config) normalizeAddrs() error { 722 if c.BindAddr != "" { 723 ipStr, err := parseSingleIPTemplate(c.BindAddr) 724 if err != nil { 725 return fmt.Errorf("Bind address resolution failed: %v", err) 726 } 727 c.BindAddr = ipStr 728 } 729 730 addr, err := normalizeBind(c.Addresses.HTTP, c.BindAddr) 731 if err != nil { 732 return fmt.Errorf("Failed to parse HTTP address: %v", err) 733 } 734 c.Addresses.HTTP = addr 735 736 addr, err = normalizeBind(c.Addresses.RPC, c.BindAddr) 737 if err != nil { 738 return fmt.Errorf("Failed to parse RPC address: %v", err) 739 } 740 c.Addresses.RPC = addr 741 742 addr, err = normalizeBind(c.Addresses.Serf, c.BindAddr) 743 if err != nil { 744 return fmt.Errorf("Failed to parse Serf address: %v", err) 745 } 746 c.Addresses.Serf = addr 747 748 c.normalizedAddrs = &Addresses{ 749 HTTP: net.JoinHostPort(c.Addresses.HTTP, strconv.Itoa(c.Ports.HTTP)), 750 RPC: net.JoinHostPort(c.Addresses.RPC, strconv.Itoa(c.Ports.RPC)), 751 Serf: net.JoinHostPort(c.Addresses.Serf, strconv.Itoa(c.Ports.Serf)), 752 } 753 754 addr, err = normalizeAdvertise(c.AdvertiseAddrs.HTTP, c.Addresses.HTTP, c.Ports.HTTP, c.DevMode) 755 if err != nil { 756 return fmt.Errorf("Failed to parse HTTP advertise address: %v", err) 757 } 758 c.AdvertiseAddrs.HTTP = addr 759 760 addr, err = normalizeAdvertise(c.AdvertiseAddrs.RPC, c.Addresses.RPC, c.Ports.RPC, c.DevMode) 761 if err != nil { 762 return fmt.Errorf("Failed to parse RPC advertise address: %v", err) 763 } 764 c.AdvertiseAddrs.RPC = addr 765 766 // Skip serf if server is disabled 767 if c.Server != nil && c.Server.Enabled { 768 addr, err = normalizeAdvertise(c.AdvertiseAddrs.Serf, c.Addresses.Serf, c.Ports.Serf, c.DevMode) 769 if err != nil { 770 return fmt.Errorf("Failed to parse Serf advertise address: %v", err) 771 } 772 c.AdvertiseAddrs.Serf = addr 773 } 774 775 return nil 776 } 777 778 // parseSingleIPTemplate is used as a helper function to parse out a single IP 779 // address from a config parameter. 780 func parseSingleIPTemplate(ipTmpl string) (string, error) { 781 out, err := template.Parse(ipTmpl) 782 if err != nil { 783 return "", fmt.Errorf("Unable to parse address template %q: %v", ipTmpl, err) 784 } 785 786 ips := strings.Split(out, " ") 787 switch len(ips) { 788 case 0: 789 return "", errors.New("No addresses found, please configure one.") 790 case 1: 791 return ips[0], nil 792 default: 793 return "", fmt.Errorf("Multiple addresses found (%q), please configure one.", out) 794 } 795 } 796 797 // normalizeBind returns a normalized bind address. 798 // 799 // If addr is set it is used, if not the default bind address is used. 800 func normalizeBind(addr, bind string) (string, error) { 801 if addr == "" { 802 return bind, nil 803 } else { 804 return parseSingleIPTemplate(addr) 805 } 806 } 807 808 // normalizeAdvertise returns a normalized advertise address. 809 // 810 // If addr is set, it is used and the default port is appended if no port is 811 // set. 812 // 813 // If addr is not set and bind is a valid address, the returned string is the 814 // bind+port. 815 // 816 // If addr is not set and bind is not a valid advertise address, the hostname 817 // is resolved and returned with the port. 818 // 819 // Loopback is only considered a valid advertise address in dev mode. 820 func normalizeAdvertise(addr string, bind string, defport int, dev bool) (string, error) { 821 addr, err := parseSingleIPTemplate(addr) 822 if err != nil { 823 return "", fmt.Errorf("Error parsing advertise address template: %v", err) 824 } 825 826 if addr != "" { 827 // Default to using manually configured address 828 host, port, err := net.SplitHostPort(addr) 829 if err != nil { 830 if !isMissingPort(err) { 831 return "", fmt.Errorf("Error parsing advertise address %q: %v", addr, err) 832 } 833 host = addr 834 port = strconv.Itoa(defport) 835 } 836 837 return net.JoinHostPort(host, port), nil 838 } 839 840 // Fallback to bind address first, and then try resolving the local hostname 841 ips, err := net.LookupIP(bind) 842 if err != nil { 843 return "", fmt.Errorf("Error resolving bind address %q: %v", bind, err) 844 } 845 846 // Return the first unicast address 847 for _, ip := range ips { 848 if ip.IsLinkLocalUnicast() || ip.IsGlobalUnicast() { 849 return net.JoinHostPort(ip.String(), strconv.Itoa(defport)), nil 850 } 851 if !ip.IsLoopback() || (ip.IsLoopback() && dev) { 852 // loopback is fine for dev mode 853 return net.JoinHostPort(ip.String(), strconv.Itoa(defport)), nil 854 } 855 } 856 857 // Otherwise, default to the private IP address 858 addr, err = parseSingleIPTemplate("{{ GetPrivateIP }}") 859 if err != nil { 860 return "", fmt.Errorf("Unable to parse default advertise address: %v", err) 861 } 862 return net.JoinHostPort(addr, strconv.Itoa(defport)), nil 863 } 864 865 // isMissingPort returns true if an error is a "missing port" error from 866 // net.SplitHostPort. 867 func isMissingPort(err error) bool { 868 // matches error const in net/ipsock.go 869 const missingPort = "missing port in address" 870 return err != nil && strings.Contains(err.Error(), missingPort) 871 } 872 873 // Merge is used to merge two server configs together 874 func (a *ServerConfig) Merge(b *ServerConfig) *ServerConfig { 875 result := *a 876 877 if b.Enabled { 878 result.Enabled = true 879 } 880 if b.BootstrapExpect > 0 { 881 result.BootstrapExpect = b.BootstrapExpect 882 } 883 if b.DataDir != "" { 884 result.DataDir = b.DataDir 885 } 886 if b.ProtocolVersion != 0 { 887 result.ProtocolVersion = b.ProtocolVersion 888 } 889 if b.NumSchedulers != 0 { 890 result.NumSchedulers = b.NumSchedulers 891 } 892 if b.NodeGCThreshold != "" { 893 result.NodeGCThreshold = b.NodeGCThreshold 894 } 895 if b.JobGCThreshold != "" { 896 result.JobGCThreshold = b.JobGCThreshold 897 } 898 if b.EvalGCThreshold != "" { 899 result.EvalGCThreshold = b.EvalGCThreshold 900 } 901 if b.HeartbeatGrace != "" { 902 result.HeartbeatGrace = b.HeartbeatGrace 903 } 904 if b.RetryMaxAttempts != 0 { 905 result.RetryMaxAttempts = b.RetryMaxAttempts 906 } 907 if b.RetryInterval != "" { 908 result.RetryInterval = b.RetryInterval 909 result.retryInterval = b.retryInterval 910 } 911 if b.RejoinAfterLeave { 912 result.RejoinAfterLeave = true 913 } 914 if b.EncryptKey != "" { 915 result.EncryptKey = b.EncryptKey 916 } 917 918 // Add the schedulers 919 result.EnabledSchedulers = append(result.EnabledSchedulers, b.EnabledSchedulers...) 920 921 // Copy the start join addresses 922 result.StartJoin = make([]string, 0, len(a.StartJoin)+len(b.StartJoin)) 923 result.StartJoin = append(result.StartJoin, a.StartJoin...) 924 result.StartJoin = append(result.StartJoin, b.StartJoin...) 925 926 // Copy the retry join addresses 927 result.RetryJoin = make([]string, 0, len(a.RetryJoin)+len(b.RetryJoin)) 928 result.RetryJoin = append(result.RetryJoin, a.RetryJoin...) 929 result.RetryJoin = append(result.RetryJoin, b.RetryJoin...) 930 931 return &result 932 } 933 934 // Merge is used to merge two client configs together 935 func (a *ClientConfig) Merge(b *ClientConfig) *ClientConfig { 936 result := *a 937 938 if b.Enabled { 939 result.Enabled = true 940 } 941 if b.StateDir != "" { 942 result.StateDir = b.StateDir 943 } 944 if b.AllocDir != "" { 945 result.AllocDir = b.AllocDir 946 } 947 if b.NodeClass != "" { 948 result.NodeClass = b.NodeClass 949 } 950 if b.NetworkInterface != "" { 951 result.NetworkInterface = b.NetworkInterface 952 } 953 if b.NetworkSpeed != 0 { 954 result.NetworkSpeed = b.NetworkSpeed 955 } 956 if b.CpuCompute != 0 { 957 result.CpuCompute = b.CpuCompute 958 } 959 if b.MaxKillTimeout != "" { 960 result.MaxKillTimeout = b.MaxKillTimeout 961 } 962 if b.ClientMaxPort != 0 { 963 result.ClientMaxPort = b.ClientMaxPort 964 } 965 if b.ClientMinPort != 0 { 966 result.ClientMinPort = b.ClientMinPort 967 } 968 if result.Reserved == nil && b.Reserved != nil { 969 reserved := *b.Reserved 970 result.Reserved = &reserved 971 } else if b.Reserved != nil { 972 result.Reserved = result.Reserved.Merge(b.Reserved) 973 } 974 if b.GCInterval != 0 { 975 result.GCInterval = b.GCInterval 976 } 977 if b.GCParallelDestroys != 0 { 978 result.GCParallelDestroys = b.GCParallelDestroys 979 } 980 if b.GCDiskUsageThreshold != 0 { 981 result.GCDiskUsageThreshold = b.GCDiskUsageThreshold 982 } 983 if b.GCInodeUsageThreshold != 0 { 984 result.GCInodeUsageThreshold = b.GCInodeUsageThreshold 985 } 986 if b.NoHostUUID { 987 result.NoHostUUID = b.NoHostUUID 988 } 989 990 // Add the servers 991 result.Servers = append(result.Servers, b.Servers...) 992 993 // Add the options map values 994 if result.Options == nil { 995 result.Options = make(map[string]string) 996 } 997 for k, v := range b.Options { 998 result.Options[k] = v 999 } 1000 1001 // Add the meta map values 1002 if result.Meta == nil { 1003 result.Meta = make(map[string]string) 1004 } 1005 for k, v := range b.Meta { 1006 result.Meta[k] = v 1007 } 1008 1009 // Add the chroot_env map values 1010 if result.ChrootEnv == nil { 1011 result.ChrootEnv = make(map[string]string) 1012 } 1013 for k, v := range b.ChrootEnv { 1014 result.ChrootEnv[k] = v 1015 } 1016 1017 return &result 1018 } 1019 1020 // Merge is used to merge two telemetry configs together 1021 func (a *Telemetry) Merge(b *Telemetry) *Telemetry { 1022 result := *a 1023 1024 if b.StatsiteAddr != "" { 1025 result.StatsiteAddr = b.StatsiteAddr 1026 } 1027 if b.StatsdAddr != "" { 1028 result.StatsdAddr = b.StatsdAddr 1029 } 1030 if b.DataDogAddr != "" { 1031 result.DataDogAddr = b.DataDogAddr 1032 } 1033 if b.DisableHostname { 1034 result.DisableHostname = true 1035 } 1036 if b.CollectionInterval != "" { 1037 result.CollectionInterval = b.CollectionInterval 1038 } 1039 if b.collectionInterval != 0 { 1040 result.collectionInterval = b.collectionInterval 1041 } 1042 if b.PublishNodeMetrics { 1043 result.PublishNodeMetrics = true 1044 } 1045 if b.PublishAllocationMetrics { 1046 result.PublishAllocationMetrics = true 1047 } 1048 if b.CirconusAPIToken != "" { 1049 result.CirconusAPIToken = b.CirconusAPIToken 1050 } 1051 if b.CirconusAPIApp != "" { 1052 result.CirconusAPIApp = b.CirconusAPIApp 1053 } 1054 if b.CirconusAPIURL != "" { 1055 result.CirconusAPIURL = b.CirconusAPIURL 1056 } 1057 if b.CirconusCheckSubmissionURL != "" { 1058 result.CirconusCheckSubmissionURL = b.CirconusCheckSubmissionURL 1059 } 1060 if b.CirconusSubmissionInterval != "" { 1061 result.CirconusSubmissionInterval = b.CirconusSubmissionInterval 1062 } 1063 if b.CirconusCheckID != "" { 1064 result.CirconusCheckID = b.CirconusCheckID 1065 } 1066 if b.CirconusCheckForceMetricActivation != "" { 1067 result.CirconusCheckForceMetricActivation = b.CirconusCheckForceMetricActivation 1068 } 1069 if b.CirconusCheckInstanceID != "" { 1070 result.CirconusCheckInstanceID = b.CirconusCheckInstanceID 1071 } 1072 if b.CirconusCheckSearchTag != "" { 1073 result.CirconusCheckSearchTag = b.CirconusCheckSearchTag 1074 } 1075 if b.CirconusCheckTags != "" { 1076 result.CirconusCheckTags = b.CirconusCheckTags 1077 } 1078 if b.CirconusCheckDisplayName != "" { 1079 result.CirconusCheckDisplayName = b.CirconusCheckDisplayName 1080 } 1081 if b.CirconusBrokerID != "" { 1082 result.CirconusBrokerID = b.CirconusBrokerID 1083 } 1084 if b.CirconusBrokerSelectTag != "" { 1085 result.CirconusBrokerSelectTag = b.CirconusBrokerSelectTag 1086 } 1087 return &result 1088 } 1089 1090 // Merge is used to merge two port configurations. 1091 func (a *Ports) Merge(b *Ports) *Ports { 1092 result := *a 1093 1094 if b.HTTP != 0 { 1095 result.HTTP = b.HTTP 1096 } 1097 if b.RPC != 0 { 1098 result.RPC = b.RPC 1099 } 1100 if b.Serf != 0 { 1101 result.Serf = b.Serf 1102 } 1103 return &result 1104 } 1105 1106 // Merge is used to merge two address configs together. 1107 func (a *Addresses) Merge(b *Addresses) *Addresses { 1108 result := *a 1109 1110 if b.HTTP != "" { 1111 result.HTTP = b.HTTP 1112 } 1113 if b.RPC != "" { 1114 result.RPC = b.RPC 1115 } 1116 if b.Serf != "" { 1117 result.Serf = b.Serf 1118 } 1119 return &result 1120 } 1121 1122 // Merge merges two advertise addrs configs together. 1123 func (a *AdvertiseAddrs) Merge(b *AdvertiseAddrs) *AdvertiseAddrs { 1124 result := *a 1125 1126 if b.RPC != "" { 1127 result.RPC = b.RPC 1128 } 1129 if b.Serf != "" { 1130 result.Serf = b.Serf 1131 } 1132 if b.HTTP != "" { 1133 result.HTTP = b.HTTP 1134 } 1135 return &result 1136 } 1137 1138 // Merge merges two Atlas configurations together. 1139 func (a *AtlasConfig) Merge(b *AtlasConfig) *AtlasConfig { 1140 result := *a 1141 1142 if b.Infrastructure != "" { 1143 result.Infrastructure = b.Infrastructure 1144 } 1145 if b.Token != "" { 1146 result.Token = b.Token 1147 } 1148 if b.Join { 1149 result.Join = true 1150 } 1151 if b.Endpoint != "" { 1152 result.Endpoint = b.Endpoint 1153 } 1154 return &result 1155 } 1156 1157 func (r *Resources) Merge(b *Resources) *Resources { 1158 result := *r 1159 if b.CPU != 0 { 1160 result.CPU = b.CPU 1161 } 1162 if b.MemoryMB != 0 { 1163 result.MemoryMB = b.MemoryMB 1164 } 1165 if b.DiskMB != 0 { 1166 result.DiskMB = b.DiskMB 1167 } 1168 if b.IOPS != 0 { 1169 result.IOPS = b.IOPS 1170 } 1171 if b.ReservedPorts != "" { 1172 result.ReservedPorts = b.ReservedPorts 1173 } 1174 if len(b.ParsedReservedPorts) != 0 { 1175 result.ParsedReservedPorts = b.ParsedReservedPorts 1176 } 1177 return &result 1178 } 1179 1180 // LoadConfig loads the configuration at the given path, regardless if 1181 // its a file or directory. 1182 func LoadConfig(path string) (*Config, error) { 1183 fi, err := os.Stat(path) 1184 if err != nil { 1185 return nil, err 1186 } 1187 1188 if fi.IsDir() { 1189 return LoadConfigDir(path) 1190 } 1191 1192 cleaned := filepath.Clean(path) 1193 config, err := ParseConfigFile(cleaned) 1194 if err != nil { 1195 return nil, fmt.Errorf("Error loading %s: %s", cleaned, err) 1196 } 1197 1198 config.Files = append(config.Files, cleaned) 1199 return config, nil 1200 } 1201 1202 // LoadConfigDir loads all the configurations in the given directory 1203 // in alphabetical order. 1204 func LoadConfigDir(dir string) (*Config, error) { 1205 f, err := os.Open(dir) 1206 if err != nil { 1207 return nil, err 1208 } 1209 defer f.Close() 1210 1211 fi, err := f.Stat() 1212 if err != nil { 1213 return nil, err 1214 } 1215 if !fi.IsDir() { 1216 return nil, fmt.Errorf( 1217 "configuration path must be a directory: %s", dir) 1218 } 1219 1220 var files []string 1221 err = nil 1222 for err != io.EOF { 1223 var fis []os.FileInfo 1224 fis, err = f.Readdir(128) 1225 if err != nil && err != io.EOF { 1226 return nil, err 1227 } 1228 1229 for _, fi := range fis { 1230 // Ignore directories 1231 if fi.IsDir() { 1232 continue 1233 } 1234 1235 // Only care about files that are valid to load. 1236 name := fi.Name() 1237 skip := true 1238 if strings.HasSuffix(name, ".hcl") { 1239 skip = false 1240 } else if strings.HasSuffix(name, ".json") { 1241 skip = false 1242 } 1243 if skip || isTemporaryFile(name) { 1244 continue 1245 } 1246 1247 path := filepath.Join(dir, name) 1248 files = append(files, path) 1249 } 1250 } 1251 1252 // Fast-path if we have no files 1253 if len(files) == 0 { 1254 return &Config{}, nil 1255 } 1256 1257 sort.Strings(files) 1258 1259 var result *Config 1260 for _, f := range files { 1261 config, err := ParseConfigFile(f) 1262 if err != nil { 1263 return nil, fmt.Errorf("Error loading %s: %s", f, err) 1264 } 1265 config.Files = append(config.Files, f) 1266 1267 if result == nil { 1268 result = config 1269 } else { 1270 result = result.Merge(config) 1271 } 1272 } 1273 1274 return result, nil 1275 } 1276 1277 // isTemporaryFile returns true or false depending on whether the 1278 // provided file name is a temporary file for the following editors: 1279 // emacs or vim. 1280 func isTemporaryFile(name string) bool { 1281 return strings.HasSuffix(name, "~") || // vim 1282 strings.HasPrefix(name, ".#") || // emacs 1283 (strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#")) // emacs 1284 }