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