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