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