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