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