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