github.com/smithx10/nomad@v0.9.1-rc1/command/agent/config.go (about)

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