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