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