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