github.com/zhizhiboom/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/command/agent/config_parse.go (about)

     1  package agent
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"time"
    10  
    11  	multierror "github.com/hashicorp/go-multierror"
    12  	"github.com/hashicorp/go-version"
    13  	"github.com/hashicorp/hcl"
    14  	"github.com/hashicorp/hcl/hcl/ast"
    15  	"github.com/hashicorp/nomad/helper"
    16  	"github.com/hashicorp/nomad/helper/tlsutil"
    17  	"github.com/hashicorp/nomad/nomad/structs/config"
    18  	"github.com/mitchellh/mapstructure"
    19  )
    20  
    21  // ParseConfigFile parses the given path as a config file.
    22  func ParseConfigFile(path string) (*Config, error) {
    23  	path, err := filepath.Abs(path)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	f, err := os.Open(path)
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  	defer f.Close()
    33  
    34  	config, err := ParseConfig(f)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  
    39  	return config, nil
    40  }
    41  
    42  // ParseConfig parses the config from the given io.Reader.
    43  //
    44  // Due to current internal limitations, the entire contents of the
    45  // io.Reader will be copied into memory first before parsing.
    46  func ParseConfig(r io.Reader) (*Config, error) {
    47  	// Copy the reader into an in-memory buffer first since HCL requires it.
    48  	var buf bytes.Buffer
    49  	if _, err := io.Copy(&buf, r); err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	// Parse the buffer
    54  	root, err := hcl.Parse(buf.String())
    55  	if err != nil {
    56  		return nil, fmt.Errorf("error parsing: %s", err)
    57  	}
    58  	buf.Reset()
    59  
    60  	// Top-level item should be a list
    61  	list, ok := root.Node.(*ast.ObjectList)
    62  	if !ok {
    63  		return nil, fmt.Errorf("error parsing: root should be an object")
    64  	}
    65  
    66  	var config Config
    67  	if err := parseConfig(&config, list); err != nil {
    68  		return nil, fmt.Errorf("error parsing 'config': %v", err)
    69  	}
    70  
    71  	return &config, nil
    72  }
    73  
    74  func parseConfig(result *Config, list *ast.ObjectList) error {
    75  	// Check for invalid keys
    76  	valid := []string{
    77  		"region",
    78  		"datacenter",
    79  		"name",
    80  		"data_dir",
    81  		"plugin_dir",
    82  		"log_level",
    83  		"bind_addr",
    84  		"enable_debug",
    85  		"ports",
    86  		"addresses",
    87  		"interfaces",
    88  		"advertise",
    89  		"client",
    90  		"server",
    91  		"telemetry",
    92  		"leave_on_interrupt",
    93  		"leave_on_terminate",
    94  		"enable_syslog",
    95  		"syslog_facility",
    96  		"disable_update_check",
    97  		"disable_anonymous_signature",
    98  		"consul",
    99  		"vault",
   100  		"tls",
   101  		"http_api_response_headers",
   102  		"acl",
   103  		"sentinel",
   104  		"autopilot",
   105  		"plugin",
   106  	}
   107  	if err := helper.CheckHCLKeys(list, valid); err != nil {
   108  		return multierror.Prefix(err, "config:")
   109  	}
   110  
   111  	// Decode the full thing into a map[string]interface for ease
   112  	var m map[string]interface{}
   113  	if err := hcl.DecodeObject(&m, list); err != nil {
   114  		return err
   115  	}
   116  	delete(m, "ports")
   117  	delete(m, "addresses")
   118  	delete(m, "interfaces")
   119  	delete(m, "advertise")
   120  	delete(m, "client")
   121  	delete(m, "server")
   122  	delete(m, "telemetry")
   123  	delete(m, "consul")
   124  	delete(m, "vault")
   125  	delete(m, "tls")
   126  	delete(m, "http_api_response_headers")
   127  	delete(m, "acl")
   128  	delete(m, "sentinel")
   129  	delete(m, "autopilot")
   130  	delete(m, "plugin")
   131  
   132  	// Decode the rest
   133  	if err := mapstructure.WeakDecode(m, result); err != nil {
   134  		return err
   135  	}
   136  
   137  	// Parse ports
   138  	if o := list.Filter("ports"); len(o.Items) > 0 {
   139  		if err := parsePorts(&result.Ports, o); err != nil {
   140  			return multierror.Prefix(err, "ports ->")
   141  		}
   142  	}
   143  
   144  	// Parse addresses
   145  	if o := list.Filter("addresses"); len(o.Items) > 0 {
   146  		if err := parseAddresses(&result.Addresses, o); err != nil {
   147  			return multierror.Prefix(err, "addresses ->")
   148  		}
   149  	}
   150  
   151  	// Parse advertise
   152  	if o := list.Filter("advertise"); len(o.Items) > 0 {
   153  		if err := parseAdvertise(&result.AdvertiseAddrs, o); err != nil {
   154  			return multierror.Prefix(err, "advertise ->")
   155  		}
   156  	}
   157  
   158  	// Parse client config
   159  	if o := list.Filter("client"); len(o.Items) > 0 {
   160  		if err := parseClient(&result.Client, o); err != nil {
   161  			return multierror.Prefix(err, "client ->")
   162  		}
   163  	}
   164  
   165  	// Parse server config
   166  	if o := list.Filter("server"); len(o.Items) > 0 {
   167  		if err := parseServer(&result.Server, o); err != nil {
   168  			return multierror.Prefix(err, "server ->")
   169  		}
   170  	}
   171  
   172  	// Parse ACL config
   173  	if o := list.Filter("acl"); len(o.Items) > 0 {
   174  		if err := parseACL(&result.ACL, o); err != nil {
   175  			return multierror.Prefix(err, "acl ->")
   176  		}
   177  	}
   178  
   179  	// Parse telemetry config
   180  	if o := list.Filter("telemetry"); len(o.Items) > 0 {
   181  		if err := parseTelemetry(&result.Telemetry, o); err != nil {
   182  			return multierror.Prefix(err, "telemetry ->")
   183  		}
   184  	}
   185  
   186  	// Parse the consul config
   187  	if o := list.Filter("consul"); len(o.Items) > 0 {
   188  		if err := parseConsulConfig(&result.Consul, o); err != nil {
   189  			return multierror.Prefix(err, "consul ->")
   190  		}
   191  	}
   192  
   193  	// Parse the vault config
   194  	if o := list.Filter("vault"); len(o.Items) > 0 {
   195  		if err := parseVaultConfig(&result.Vault, o); err != nil {
   196  			return multierror.Prefix(err, "vault ->")
   197  		}
   198  	}
   199  
   200  	// Parse the TLS config
   201  	if o := list.Filter("tls"); len(o.Items) > 0 {
   202  		if err := parseTLSConfig(&result.TLSConfig, o); err != nil {
   203  			return multierror.Prefix(err, "tls ->")
   204  		}
   205  	}
   206  
   207  	// Parse Sentinel config
   208  	if o := list.Filter("sentinel"); len(o.Items) > 0 {
   209  		if err := parseSentinel(&result.Sentinel, o); err != nil {
   210  			return multierror.Prefix(err, "sentinel->")
   211  		}
   212  	}
   213  
   214  	// Parse Autopilot config
   215  	if o := list.Filter("autopilot"); len(o.Items) > 0 {
   216  		if err := parseAutopilot(&result.Autopilot, o); err != nil {
   217  			return multierror.Prefix(err, "autopilot->")
   218  		}
   219  	}
   220  
   221  	// Parse Plugin configs
   222  	if o := list.Filter("plugin"); len(o.Items) > 0 {
   223  		if err := parsePlugins(&result.Plugins, o); err != nil {
   224  			return multierror.Prefix(err, "plugin->")
   225  		}
   226  	}
   227  
   228  	// Parse out http_api_response_headers fields. These are in HCL as a list so
   229  	// we need to iterate over them and merge them.
   230  	if headersO := list.Filter("http_api_response_headers"); len(headersO.Items) > 0 {
   231  		for _, o := range headersO.Elem().Items {
   232  			var m map[string]interface{}
   233  			if err := hcl.DecodeObject(&m, o.Val); err != nil {
   234  				return err
   235  			}
   236  			if err := mapstructure.WeakDecode(m, &result.HTTPAPIResponseHeaders); err != nil {
   237  				return err
   238  			}
   239  		}
   240  	}
   241  
   242  	return nil
   243  }
   244  
   245  func parsePorts(result **Ports, list *ast.ObjectList) error {
   246  	list = list.Elem()
   247  	if len(list.Items) > 1 {
   248  		return fmt.Errorf("only one 'ports' block allowed")
   249  	}
   250  
   251  	// Get our ports object
   252  	listVal := list.Items[0].Val
   253  
   254  	// Check for invalid keys
   255  	valid := []string{
   256  		"http",
   257  		"rpc",
   258  		"serf",
   259  	}
   260  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   261  		return err
   262  	}
   263  
   264  	var m map[string]interface{}
   265  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   266  		return err
   267  	}
   268  
   269  	var ports Ports
   270  	if err := mapstructure.WeakDecode(m, &ports); err != nil {
   271  		return err
   272  	}
   273  	*result = &ports
   274  	return nil
   275  }
   276  
   277  func parseAddresses(result **Addresses, list *ast.ObjectList) error {
   278  	list = list.Elem()
   279  	if len(list.Items) > 1 {
   280  		return fmt.Errorf("only one 'addresses' block allowed")
   281  	}
   282  
   283  	// Get our addresses object
   284  	listVal := list.Items[0].Val
   285  
   286  	// Check for invalid keys
   287  	valid := []string{
   288  		"http",
   289  		"rpc",
   290  		"serf",
   291  	}
   292  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   293  		return err
   294  	}
   295  
   296  	var m map[string]interface{}
   297  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   298  		return err
   299  	}
   300  
   301  	var addresses Addresses
   302  	if err := mapstructure.WeakDecode(m, &addresses); err != nil {
   303  		return err
   304  	}
   305  	*result = &addresses
   306  	return nil
   307  }
   308  
   309  func parseAdvertise(result **AdvertiseAddrs, list *ast.ObjectList) error {
   310  	list = list.Elem()
   311  	if len(list.Items) > 1 {
   312  		return fmt.Errorf("only one 'advertise' block allowed")
   313  	}
   314  
   315  	// Get our advertise object
   316  	listVal := list.Items[0].Val
   317  
   318  	// Check for invalid keys
   319  	valid := []string{
   320  		"http",
   321  		"rpc",
   322  		"serf",
   323  	}
   324  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   325  		return err
   326  	}
   327  
   328  	var m map[string]interface{}
   329  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   330  		return err
   331  	}
   332  
   333  	var advertise AdvertiseAddrs
   334  	if err := mapstructure.WeakDecode(m, &advertise); err != nil {
   335  		return err
   336  	}
   337  	*result = &advertise
   338  	return nil
   339  }
   340  
   341  func parseClient(result **ClientConfig, list *ast.ObjectList) error {
   342  	list = list.Elem()
   343  	if len(list.Items) > 1 {
   344  		return fmt.Errorf("only one 'client' block allowed")
   345  	}
   346  
   347  	// Get our client object
   348  	obj := list.Items[0]
   349  
   350  	// Value should be an object
   351  	var listVal *ast.ObjectList
   352  	if ot, ok := obj.Val.(*ast.ObjectType); ok {
   353  		listVal = ot.List
   354  	} else {
   355  		return fmt.Errorf("client value: should be an object")
   356  	}
   357  
   358  	// Check for invalid keys
   359  	valid := []string{
   360  		"enabled",
   361  		"state_dir",
   362  		"alloc_dir",
   363  		"servers",
   364  		"node_class",
   365  		"options",
   366  		"meta",
   367  		"chroot_env",
   368  		"network_interface",
   369  		"network_speed",
   370  		"memory_total_mb",
   371  		"cpu_total_compute",
   372  		"max_kill_timeout",
   373  		"client_max_port",
   374  		"client_min_port",
   375  		"reserved",
   376  		"stats",
   377  		"gc_interval",
   378  		"gc_disk_usage_threshold",
   379  		"gc_inode_usage_threshold",
   380  		"gc_parallel_destroys",
   381  		"gc_max_allocs",
   382  		"no_host_uuid",
   383  		"server_join",
   384  	}
   385  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   386  		return err
   387  	}
   388  
   389  	var m map[string]interface{}
   390  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   391  		return err
   392  	}
   393  
   394  	delete(m, "options")
   395  	delete(m, "meta")
   396  	delete(m, "chroot_env")
   397  	delete(m, "reserved")
   398  	delete(m, "stats")
   399  	delete(m, "server_join")
   400  
   401  	var config ClientConfig
   402  	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   403  		DecodeHook:       mapstructure.StringToTimeDurationHookFunc(),
   404  		WeaklyTypedInput: true,
   405  		Result:           &config,
   406  	})
   407  	if err != nil {
   408  		return err
   409  	}
   410  	if err := dec.Decode(m); err != nil {
   411  		return err
   412  	}
   413  
   414  	// Parse out options fields. These are in HCL as a list so we need to
   415  	// iterate over them and merge them.
   416  	if optionsO := listVal.Filter("options"); len(optionsO.Items) > 0 {
   417  		for _, o := range optionsO.Elem().Items {
   418  			var m map[string]interface{}
   419  			if err := hcl.DecodeObject(&m, o.Val); err != nil {
   420  				return err
   421  			}
   422  			if err := mapstructure.WeakDecode(m, &config.Options); err != nil {
   423  				return err
   424  			}
   425  		}
   426  	}
   427  
   428  	// Parse out options meta. These are in HCL as a list so we need to
   429  	// iterate over them and merge them.
   430  	if metaO := listVal.Filter("meta"); len(metaO.Items) > 0 {
   431  		for _, o := range metaO.Elem().Items {
   432  			var m map[string]interface{}
   433  			if err := hcl.DecodeObject(&m, o.Val); err != nil {
   434  				return err
   435  			}
   436  			if err := mapstructure.WeakDecode(m, &config.Meta); err != nil {
   437  				return err
   438  			}
   439  		}
   440  	}
   441  
   442  	// Parse out chroot_env fields. These are in HCL as a list so we need to
   443  	// iterate over them and merge them.
   444  	if chrootEnvO := listVal.Filter("chroot_env"); len(chrootEnvO.Items) > 0 {
   445  		for _, o := range chrootEnvO.Elem().Items {
   446  			var m map[string]interface{}
   447  			if err := hcl.DecodeObject(&m, o.Val); err != nil {
   448  				return err
   449  			}
   450  			if err := mapstructure.WeakDecode(m, &config.ChrootEnv); err != nil {
   451  				return err
   452  			}
   453  		}
   454  	}
   455  
   456  	// Parse reserved config
   457  	if o := listVal.Filter("reserved"); len(o.Items) > 0 {
   458  		if err := parseReserved(&config.Reserved, o); err != nil {
   459  			return multierror.Prefix(err, "reserved ->")
   460  		}
   461  	}
   462  
   463  	// Parse ServerJoin config
   464  	if o := listVal.Filter("server_join"); len(o.Items) > 0 {
   465  		if err := parseServerJoin(&config.ServerJoin, o); err != nil {
   466  			return multierror.Prefix(err, "server_join->")
   467  		}
   468  	}
   469  
   470  	*result = &config
   471  	return nil
   472  }
   473  
   474  func parseReserved(result **Resources, list *ast.ObjectList) error {
   475  	list = list.Elem()
   476  	if len(list.Items) > 1 {
   477  		return fmt.Errorf("only one 'reserved' block allowed")
   478  	}
   479  
   480  	// Get our reserved object
   481  	obj := list.Items[0]
   482  
   483  	// Value should be an object
   484  	var listVal *ast.ObjectList
   485  	if ot, ok := obj.Val.(*ast.ObjectType); ok {
   486  		listVal = ot.List
   487  	} else {
   488  		return fmt.Errorf("client value: should be an object")
   489  	}
   490  
   491  	// Check for invalid keys
   492  	valid := []string{
   493  		"cpu",
   494  		"memory",
   495  		"disk",
   496  		"iops",
   497  		"reserved_ports",
   498  	}
   499  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   500  		return err
   501  	}
   502  
   503  	var m map[string]interface{}
   504  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   505  		return err
   506  	}
   507  
   508  	var reserved Resources
   509  	if err := mapstructure.WeakDecode(m, &reserved); err != nil {
   510  		return err
   511  	}
   512  	if err := reserved.ParseReserved(); err != nil {
   513  		return err
   514  	}
   515  
   516  	*result = &reserved
   517  	return nil
   518  }
   519  
   520  func parseServer(result **ServerConfig, list *ast.ObjectList) error {
   521  	list = list.Elem()
   522  	if len(list.Items) > 1 {
   523  		return fmt.Errorf("only one 'server' block allowed")
   524  	}
   525  
   526  	// Get our server object
   527  	obj := list.Items[0]
   528  
   529  	// Value should be an object
   530  	var listVal *ast.ObjectList
   531  	if ot, ok := obj.Val.(*ast.ObjectType); ok {
   532  		listVal = ot.List
   533  	} else {
   534  		return fmt.Errorf("client value: should be an object")
   535  	}
   536  
   537  	// Check for invalid keys
   538  	valid := []string{
   539  		"enabled",
   540  		"bootstrap_expect",
   541  		"data_dir",
   542  		"protocol_version",
   543  		"raft_protocol",
   544  		"num_schedulers",
   545  		"enabled_schedulers",
   546  		"node_gc_threshold",
   547  		"eval_gc_threshold",
   548  		"job_gc_threshold",
   549  		"deployment_gc_threshold",
   550  		"heartbeat_grace",
   551  		"min_heartbeat_ttl",
   552  		"max_heartbeats_per_second",
   553  		"rejoin_after_leave",
   554  		"encrypt",
   555  		"authoritative_region",
   556  		"non_voting_server",
   557  		"redundancy_zone",
   558  		"upgrade_version",
   559  
   560  		"server_join",
   561  
   562  		// For backwards compatibility
   563  		"start_join",
   564  		"retry_join",
   565  		"retry_max",
   566  		"retry_interval",
   567  	}
   568  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   569  		return err
   570  	}
   571  
   572  	var m map[string]interface{}
   573  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   574  		return err
   575  	}
   576  
   577  	delete(m, "server_join")
   578  
   579  	var config ServerConfig
   580  	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   581  		DecodeHook:       mapstructure.StringToTimeDurationHookFunc(),
   582  		WeaklyTypedInput: true,
   583  		Result:           &config,
   584  	})
   585  	if err != nil {
   586  		return err
   587  	}
   588  	if err := dec.Decode(m); err != nil {
   589  		return err
   590  	}
   591  
   592  	if config.UpgradeVersion != "" {
   593  		if _, err := version.NewVersion(config.UpgradeVersion); err != nil {
   594  			return fmt.Errorf("error parsing upgrade_version: %v", err)
   595  		}
   596  	}
   597  
   598  	// Parse ServerJoin config
   599  	if o := listVal.Filter("server_join"); len(o.Items) > 0 {
   600  		if err := parseServerJoin(&config.ServerJoin, o); err != nil {
   601  			return multierror.Prefix(err, "server_join->")
   602  		}
   603  	}
   604  
   605  	*result = &config
   606  	return nil
   607  }
   608  
   609  func parseServerJoin(result **ServerJoin, list *ast.ObjectList) error {
   610  	list = list.Elem()
   611  	if len(list.Items) > 1 {
   612  		return fmt.Errorf("only one 'server_join' block allowed")
   613  	}
   614  
   615  	// Get our object
   616  	listVal := list.Items[0].Val
   617  
   618  	// Check for invalid keys
   619  	valid := []string{
   620  		"start_join",
   621  		"retry_join",
   622  		"retry_max",
   623  		"retry_interval",
   624  	}
   625  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   626  		return err
   627  	}
   628  
   629  	var m map[string]interface{}
   630  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   631  		return err
   632  	}
   633  
   634  	var serverJoinInfo ServerJoin
   635  	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   636  		DecodeHook:       mapstructure.StringToTimeDurationHookFunc(),
   637  		WeaklyTypedInput: true,
   638  		Result:           &serverJoinInfo,
   639  	})
   640  	if err != nil {
   641  		return err
   642  	}
   643  	if err := dec.Decode(m); err != nil {
   644  		return err
   645  	}
   646  
   647  	*result = &serverJoinInfo
   648  	return nil
   649  }
   650  
   651  func parseACL(result **ACLConfig, list *ast.ObjectList) error {
   652  	list = list.Elem()
   653  	if len(list.Items) > 1 {
   654  		return fmt.Errorf("only one 'acl' block allowed")
   655  	}
   656  
   657  	// Get our server object
   658  	obj := list.Items[0]
   659  
   660  	// Value should be an object
   661  	var listVal *ast.ObjectList
   662  	if ot, ok := obj.Val.(*ast.ObjectType); ok {
   663  		listVal = ot.List
   664  	} else {
   665  		return fmt.Errorf("acl value: should be an object")
   666  	}
   667  
   668  	// Check for invalid keys
   669  	valid := []string{
   670  		"enabled",
   671  		"token_ttl",
   672  		"policy_ttl",
   673  		"replication_token",
   674  	}
   675  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   676  		return err
   677  	}
   678  
   679  	var m map[string]interface{}
   680  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   681  		return err
   682  	}
   683  
   684  	var config ACLConfig
   685  	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   686  		DecodeHook:       mapstructure.StringToTimeDurationHookFunc(),
   687  		WeaklyTypedInput: true,
   688  		Result:           &config,
   689  	})
   690  	if err != nil {
   691  		return err
   692  	}
   693  	if err := dec.Decode(m); err != nil {
   694  		return err
   695  	}
   696  
   697  	*result = &config
   698  	return nil
   699  }
   700  
   701  func parseTelemetry(result **Telemetry, list *ast.ObjectList) error {
   702  	list = list.Elem()
   703  	if len(list.Items) > 1 {
   704  		return fmt.Errorf("only one 'telemetry' block allowed")
   705  	}
   706  
   707  	// Get our telemetry object
   708  	listVal := list.Items[0].Val
   709  
   710  	// Check for invalid keys
   711  	valid := []string{
   712  		"statsite_address",
   713  		"statsd_address",
   714  		"disable_hostname",
   715  		"use_node_name",
   716  		"collection_interval",
   717  		"publish_allocation_metrics",
   718  		"publish_node_metrics",
   719  		"datadog_address",
   720  		"datadog_tags",
   721  		"prometheus_metrics",
   722  		"circonus_api_token",
   723  		"circonus_api_app",
   724  		"circonus_api_url",
   725  		"circonus_submission_interval",
   726  		"circonus_submission_url",
   727  		"circonus_check_id",
   728  		"circonus_check_force_metric_activation",
   729  		"circonus_check_instance_id",
   730  		"circonus_check_search_tag",
   731  		"circonus_check_display_name",
   732  		"circonus_check_tags",
   733  		"circonus_broker_id",
   734  		"circonus_broker_select_tag",
   735  		"disable_tagged_metrics",
   736  		"backwards_compatible_metrics",
   737  	}
   738  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   739  		return err
   740  	}
   741  
   742  	var m map[string]interface{}
   743  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   744  		return err
   745  	}
   746  
   747  	var telemetry Telemetry
   748  	if err := mapstructure.WeakDecode(m, &telemetry); err != nil {
   749  		return err
   750  	}
   751  	if telemetry.CollectionInterval != "" {
   752  		if dur, err := time.ParseDuration(telemetry.CollectionInterval); err != nil {
   753  			return fmt.Errorf("error parsing value of %q: %v", "collection_interval", err)
   754  		} else {
   755  			telemetry.collectionInterval = dur
   756  		}
   757  	}
   758  	*result = &telemetry
   759  	return nil
   760  }
   761  
   762  func parseConsulConfig(result **config.ConsulConfig, list *ast.ObjectList) error {
   763  	list = list.Elem()
   764  	if len(list.Items) > 1 {
   765  		return fmt.Errorf("only one 'consul' block allowed")
   766  	}
   767  
   768  	// Get our Consul object
   769  	listVal := list.Items[0].Val
   770  
   771  	// Check for invalid keys
   772  	valid := []string{
   773  		"address",
   774  		"auth",
   775  		"auto_advertise",
   776  		"ca_file",
   777  		"cert_file",
   778  		"checks_use_advertise",
   779  		"client_auto_join",
   780  		"client_service_name",
   781  		"client_http_check_name",
   782  		"key_file",
   783  		"server_auto_join",
   784  		"server_service_name",
   785  		"server_http_check_name",
   786  		"server_serf_check_name",
   787  		"server_rpc_check_name",
   788  		"ssl",
   789  		"timeout",
   790  		"token",
   791  		"verify_ssl",
   792  	}
   793  
   794  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   795  		return err
   796  	}
   797  
   798  	var m map[string]interface{}
   799  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   800  		return err
   801  	}
   802  
   803  	consulConfig := config.DefaultConsulConfig()
   804  	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   805  		DecodeHook:       mapstructure.StringToTimeDurationHookFunc(),
   806  		WeaklyTypedInput: true,
   807  		Result:           &consulConfig,
   808  	})
   809  	if err != nil {
   810  		return err
   811  	}
   812  	if err := dec.Decode(m); err != nil {
   813  		return err
   814  	}
   815  
   816  	*result = consulConfig
   817  	return nil
   818  }
   819  
   820  func parseTLSConfig(result **config.TLSConfig, list *ast.ObjectList) error {
   821  	list = list.Elem()
   822  	if len(list.Items) > 1 {
   823  		return fmt.Errorf("only one 'tls' block allowed")
   824  	}
   825  
   826  	// Get the TLS object
   827  	listVal := list.Items[0].Val
   828  
   829  	valid := []string{
   830  		"http",
   831  		"rpc",
   832  		"verify_server_hostname",
   833  		"rpc_upgrade_mode",
   834  		"ca_file",
   835  		"cert_file",
   836  		"key_file",
   837  		"verify_https_client",
   838  		"tls_cipher_suites",
   839  		"tls_min_version",
   840  		"tls_prefer_server_cipher_suites",
   841  	}
   842  
   843  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   844  		return err
   845  	}
   846  
   847  	var m map[string]interface{}
   848  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   849  		return err
   850  	}
   851  
   852  	var tlsConfig config.TLSConfig
   853  	if err := mapstructure.WeakDecode(m, &tlsConfig); err != nil {
   854  		return err
   855  	}
   856  
   857  	if _, err := tlsutil.ParseCiphers(&tlsConfig); err != nil {
   858  		return err
   859  	}
   860  
   861  	if _, err := tlsutil.ParseMinVersion(tlsConfig.TLSMinVersion); err != nil {
   862  		return err
   863  	}
   864  
   865  	*result = &tlsConfig
   866  	return nil
   867  }
   868  
   869  func parseVaultConfig(result **config.VaultConfig, list *ast.ObjectList) error {
   870  	list = list.Elem()
   871  	if len(list.Items) > 1 {
   872  		return fmt.Errorf("only one 'vault' block allowed")
   873  	}
   874  
   875  	// Get our Vault object
   876  	listVal := list.Items[0].Val
   877  
   878  	// Check for invalid keys
   879  	valid := []string{
   880  		"address",
   881  		"allow_unauthenticated",
   882  		"enabled",
   883  		"task_token_ttl",
   884  		"ca_file",
   885  		"ca_path",
   886  		"cert_file",
   887  		"create_from_role",
   888  		"key_file",
   889  		"tls_server_name",
   890  		"tls_skip_verify",
   891  		"token",
   892  	}
   893  
   894  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   895  		return err
   896  	}
   897  
   898  	var m map[string]interface{}
   899  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   900  		return err
   901  	}
   902  
   903  	vaultConfig := config.DefaultVaultConfig()
   904  	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   905  		DecodeHook:       mapstructure.StringToTimeDurationHookFunc(),
   906  		WeaklyTypedInput: true,
   907  		Result:           &vaultConfig,
   908  	})
   909  	if err != nil {
   910  		return err
   911  	}
   912  	if err := dec.Decode(m); err != nil {
   913  		return err
   914  	}
   915  
   916  	*result = vaultConfig
   917  	return nil
   918  }
   919  
   920  func parseSentinel(result **config.SentinelConfig, list *ast.ObjectList) error {
   921  	list = list.Elem()
   922  	if len(list.Items) > 1 {
   923  		return fmt.Errorf("only one 'sentinel' block allowed")
   924  	}
   925  
   926  	// Get our sentinel object
   927  	obj := list.Items[0]
   928  
   929  	// Value should be an object
   930  	var listVal *ast.ObjectList
   931  	if ot, ok := obj.Val.(*ast.ObjectType); ok {
   932  		listVal = ot.List
   933  	} else {
   934  		return fmt.Errorf("sentinel value: should be an object")
   935  	}
   936  
   937  	// Check for invalid keys
   938  	valid := []string{
   939  		"import",
   940  	}
   941  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   942  		return err
   943  	}
   944  
   945  	var config config.SentinelConfig
   946  	if err := hcl.DecodeObject(&config, listVal); err != nil {
   947  		return err
   948  	}
   949  
   950  	*result = &config
   951  	return nil
   952  }
   953  
   954  func parseAutopilot(result **config.AutopilotConfig, list *ast.ObjectList) error {
   955  	list = list.Elem()
   956  	if len(list.Items) > 1 {
   957  		return fmt.Errorf("only one 'autopilot' block allowed")
   958  	}
   959  
   960  	// Get our Autopilot object
   961  	listVal := list.Items[0].Val
   962  
   963  	// Check for invalid keys
   964  	valid := []string{
   965  		"cleanup_dead_servers",
   966  		"server_stabilization_time",
   967  		"last_contact_threshold",
   968  		"max_trailing_logs",
   969  		"enable_redundancy_zones",
   970  		"disable_upgrade_migration",
   971  		"enable_custom_upgrades",
   972  	}
   973  
   974  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   975  		return err
   976  	}
   977  
   978  	var m map[string]interface{}
   979  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   980  		return err
   981  	}
   982  
   983  	autopilotConfig := config.DefaultAutopilotConfig()
   984  	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   985  		DecodeHook:       mapstructure.StringToTimeDurationHookFunc(),
   986  		WeaklyTypedInput: true,
   987  		Result:           &autopilotConfig,
   988  	})
   989  	if err != nil {
   990  		return err
   991  	}
   992  	if err := dec.Decode(m); err != nil {
   993  		return err
   994  	}
   995  
   996  	*result = autopilotConfig
   997  	return nil
   998  }
   999  
  1000  func parsePlugins(result *[]*config.PluginConfig, list *ast.ObjectList) error {
  1001  	listLen := len(list.Items)
  1002  	plugins := make([]*config.PluginConfig, listLen)
  1003  
  1004  	// Check for invalid keys
  1005  	valid := []string{
  1006  		"args",
  1007  		"config",
  1008  	}
  1009  
  1010  	for i := 0; i < listLen; i++ {
  1011  		// Get the current plugin object
  1012  		listVal := list.Items[i]
  1013  
  1014  		if err := helper.CheckHCLKeys(listVal.Val, valid); err != nil {
  1015  			return fmt.Errorf("invalid keys in plugin config %d: %v", i+1, err)
  1016  		}
  1017  
  1018  		// Ensure there is a key
  1019  		if len(listVal.Keys) != 1 {
  1020  			return fmt.Errorf("plugin config %d doesn't incude a name key", i+1)
  1021  		}
  1022  
  1023  		var plugin config.PluginConfig
  1024  		if err := hcl.DecodeObject(&plugin, listVal); err != nil {
  1025  			return fmt.Errorf("error decoding plugin config %d: %v", i+1, err)
  1026  		}
  1027  
  1028  		plugins[i] = &plugin
  1029  	}
  1030  
  1031  	*result = plugins
  1032  	return nil
  1033  }