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