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