github.com/janma/nomad@v0.11.3/command/agent/config.go (about)

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