github.com/hspak/nomad@v0.7.2-0.20180309000617-bc4ae22a39a5/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  		"cpu_total_compute",
   360  		"max_kill_timeout",
   361  		"client_max_port",
   362  		"client_min_port",
   363  		"reserved",
   364  		"stats",
   365  		"gc_interval",
   366  		"gc_disk_usage_threshold",
   367  		"gc_inode_usage_threshold",
   368  		"gc_parallel_destroys",
   369  		"gc_max_allocs",
   370  		"no_host_uuid",
   371  	}
   372  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   373  		return err
   374  	}
   375  
   376  	var m map[string]interface{}
   377  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   378  		return err
   379  	}
   380  
   381  	delete(m, "options")
   382  	delete(m, "meta")
   383  	delete(m, "chroot_env")
   384  	delete(m, "reserved")
   385  	delete(m, "stats")
   386  
   387  	var config ClientConfig
   388  	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   389  		DecodeHook:       mapstructure.StringToTimeDurationHookFunc(),
   390  		WeaklyTypedInput: true,
   391  		Result:           &config,
   392  	})
   393  	if err != nil {
   394  		return err
   395  	}
   396  	if err := dec.Decode(m); err != nil {
   397  		return err
   398  	}
   399  
   400  	// Parse out options fields. These are in HCL as a list so we need to
   401  	// iterate over them and merge them.
   402  	if optionsO := listVal.Filter("options"); len(optionsO.Items) > 0 {
   403  		for _, o := range optionsO.Elem().Items {
   404  			var m map[string]interface{}
   405  			if err := hcl.DecodeObject(&m, o.Val); err != nil {
   406  				return err
   407  			}
   408  			if err := mapstructure.WeakDecode(m, &config.Options); err != nil {
   409  				return err
   410  			}
   411  		}
   412  	}
   413  
   414  	// Parse out options meta. These are in HCL as a list so we need to
   415  	// iterate over them and merge them.
   416  	if metaO := listVal.Filter("meta"); len(metaO.Items) > 0 {
   417  		for _, o := range metaO.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.Meta); err != nil {
   423  				return err
   424  			}
   425  		}
   426  	}
   427  
   428  	// Parse out chroot_env fields. These are in HCL as a list so we need to
   429  	// iterate over them and merge them.
   430  	if chrootEnvO := listVal.Filter("chroot_env"); len(chrootEnvO.Items) > 0 {
   431  		for _, o := range chrootEnvO.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.ChrootEnv); err != nil {
   437  				return err
   438  			}
   439  		}
   440  	}
   441  
   442  	// Parse reserved config
   443  	if o := listVal.Filter("reserved"); len(o.Items) > 0 {
   444  		if err := parseReserved(&config.Reserved, o); err != nil {
   445  			return multierror.Prefix(err, "reserved ->")
   446  		}
   447  	}
   448  
   449  	*result = &config
   450  	return nil
   451  }
   452  
   453  func parseReserved(result **Resources, list *ast.ObjectList) error {
   454  	list = list.Elem()
   455  	if len(list.Items) > 1 {
   456  		return fmt.Errorf("only one 'reserved' block allowed")
   457  	}
   458  
   459  	// Get our reserved object
   460  	obj := list.Items[0]
   461  
   462  	// Value should be an object
   463  	var listVal *ast.ObjectList
   464  	if ot, ok := obj.Val.(*ast.ObjectType); ok {
   465  		listVal = ot.List
   466  	} else {
   467  		return fmt.Errorf("client value: should be an object")
   468  	}
   469  
   470  	// Check for invalid keys
   471  	valid := []string{
   472  		"cpu",
   473  		"memory",
   474  		"disk",
   475  		"iops",
   476  		"reserved_ports",
   477  	}
   478  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   479  		return err
   480  	}
   481  
   482  	var m map[string]interface{}
   483  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   484  		return err
   485  	}
   486  
   487  	var reserved Resources
   488  	if err := mapstructure.WeakDecode(m, &reserved); err != nil {
   489  		return err
   490  	}
   491  	if err := reserved.ParseReserved(); err != nil {
   492  		return err
   493  	}
   494  
   495  	*result = &reserved
   496  	return nil
   497  }
   498  
   499  func parseServer(result **ServerConfig, list *ast.ObjectList) error {
   500  	list = list.Elem()
   501  	if len(list.Items) > 1 {
   502  		return fmt.Errorf("only one 'server' block allowed")
   503  	}
   504  
   505  	// Get our server object
   506  	obj := list.Items[0]
   507  
   508  	// Value should be an object
   509  	var listVal *ast.ObjectList
   510  	if ot, ok := obj.Val.(*ast.ObjectType); ok {
   511  		listVal = ot.List
   512  	} else {
   513  		return fmt.Errorf("client value: should be an object")
   514  	}
   515  
   516  	// Check for invalid keys
   517  	valid := []string{
   518  		"enabled",
   519  		"bootstrap_expect",
   520  		"data_dir",
   521  		"protocol_version",
   522  		"raft_protocol",
   523  		"num_schedulers",
   524  		"enabled_schedulers",
   525  		"node_gc_threshold",
   526  		"eval_gc_threshold",
   527  		"job_gc_threshold",
   528  		"deployment_gc_threshold",
   529  		"heartbeat_grace",
   530  		"min_heartbeat_ttl",
   531  		"max_heartbeats_per_second",
   532  		"start_join",
   533  		"retry_join",
   534  		"retry_max",
   535  		"retry_interval",
   536  		"rejoin_after_leave",
   537  		"encrypt",
   538  		"authoritative_region",
   539  		"non_voting_server",
   540  		"redundancy_zone",
   541  		"upgrade_version",
   542  	}
   543  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   544  		return err
   545  	}
   546  
   547  	var m map[string]interface{}
   548  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   549  		return err
   550  	}
   551  
   552  	var config ServerConfig
   553  	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   554  		DecodeHook:       mapstructure.StringToTimeDurationHookFunc(),
   555  		WeaklyTypedInput: true,
   556  		Result:           &config,
   557  	})
   558  	if err != nil {
   559  		return err
   560  	}
   561  	if err := dec.Decode(m); err != nil {
   562  		return err
   563  	}
   564  
   565  	if config.UpgradeVersion != "" {
   566  		if _, err := version.NewVersion(config.UpgradeVersion); err != nil {
   567  			return fmt.Errorf("error parsing upgrade_version: %v", err)
   568  		}
   569  	}
   570  
   571  	*result = &config
   572  	return nil
   573  }
   574  
   575  func parseACL(result **ACLConfig, list *ast.ObjectList) error {
   576  	list = list.Elem()
   577  	if len(list.Items) > 1 {
   578  		return fmt.Errorf("only one 'acl' block allowed")
   579  	}
   580  
   581  	// Get our server object
   582  	obj := list.Items[0]
   583  
   584  	// Value should be an object
   585  	var listVal *ast.ObjectList
   586  	if ot, ok := obj.Val.(*ast.ObjectType); ok {
   587  		listVal = ot.List
   588  	} else {
   589  		return fmt.Errorf("acl value: should be an object")
   590  	}
   591  
   592  	// Check for invalid keys
   593  	valid := []string{
   594  		"enabled",
   595  		"token_ttl",
   596  		"policy_ttl",
   597  		"replication_token",
   598  	}
   599  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   600  		return err
   601  	}
   602  
   603  	var m map[string]interface{}
   604  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   605  		return err
   606  	}
   607  
   608  	var config ACLConfig
   609  	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   610  		DecodeHook:       mapstructure.StringToTimeDurationHookFunc(),
   611  		WeaklyTypedInput: true,
   612  		Result:           &config,
   613  	})
   614  	if err != nil {
   615  		return err
   616  	}
   617  	if err := dec.Decode(m); err != nil {
   618  		return err
   619  	}
   620  
   621  	*result = &config
   622  	return nil
   623  }
   624  
   625  func parseTelemetry(result **Telemetry, list *ast.ObjectList) error {
   626  	list = list.Elem()
   627  	if len(list.Items) > 1 {
   628  		return fmt.Errorf("only one 'telemetry' block allowed")
   629  	}
   630  
   631  	// Get our telemetry object
   632  	listVal := list.Items[0].Val
   633  
   634  	// Check for invalid keys
   635  	valid := []string{
   636  		"statsite_address",
   637  		"statsd_address",
   638  		"disable_hostname",
   639  		"use_node_name",
   640  		"collection_interval",
   641  		"publish_allocation_metrics",
   642  		"publish_node_metrics",
   643  		"datadog_address",
   644  		"datadog_tags",
   645  		"prometheus_metrics",
   646  		"circonus_api_token",
   647  		"circonus_api_app",
   648  		"circonus_api_url",
   649  		"circonus_submission_interval",
   650  		"circonus_submission_url",
   651  		"circonus_check_id",
   652  		"circonus_check_force_metric_activation",
   653  		"circonus_check_instance_id",
   654  		"circonus_check_search_tag",
   655  		"circonus_check_display_name",
   656  		"circonus_check_tags",
   657  		"circonus_broker_id",
   658  		"circonus_broker_select_tag",
   659  		"disable_tagged_metrics",
   660  		"backwards_compatible_metrics",
   661  	}
   662  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   663  		return err
   664  	}
   665  
   666  	var m map[string]interface{}
   667  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   668  		return err
   669  	}
   670  
   671  	var telemetry Telemetry
   672  	if err := mapstructure.WeakDecode(m, &telemetry); err != nil {
   673  		return err
   674  	}
   675  	if telemetry.CollectionInterval != "" {
   676  		if dur, err := time.ParseDuration(telemetry.CollectionInterval); err != nil {
   677  			return fmt.Errorf("error parsing value of %q: %v", "collection_interval", err)
   678  		} else {
   679  			telemetry.collectionInterval = dur
   680  		}
   681  	}
   682  	*result = &telemetry
   683  	return nil
   684  }
   685  
   686  func parseConsulConfig(result **config.ConsulConfig, list *ast.ObjectList) error {
   687  	list = list.Elem()
   688  	if len(list.Items) > 1 {
   689  		return fmt.Errorf("only one 'consul' block allowed")
   690  	}
   691  
   692  	// Get our Consul object
   693  	listVal := list.Items[0].Val
   694  
   695  	// Check for invalid keys
   696  	valid := []string{
   697  		"address",
   698  		"auth",
   699  		"auto_advertise",
   700  		"ca_file",
   701  		"cert_file",
   702  		"checks_use_advertise",
   703  		"client_auto_join",
   704  		"client_service_name",
   705  		"key_file",
   706  		"server_auto_join",
   707  		"server_service_name",
   708  		"ssl",
   709  		"timeout",
   710  		"token",
   711  		"verify_ssl",
   712  	}
   713  
   714  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   715  		return err
   716  	}
   717  
   718  	var m map[string]interface{}
   719  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   720  		return err
   721  	}
   722  
   723  	consulConfig := config.DefaultConsulConfig()
   724  	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   725  		DecodeHook:       mapstructure.StringToTimeDurationHookFunc(),
   726  		WeaklyTypedInput: true,
   727  		Result:           &consulConfig,
   728  	})
   729  	if err != nil {
   730  		return err
   731  	}
   732  	if err := dec.Decode(m); err != nil {
   733  		return err
   734  	}
   735  
   736  	*result = consulConfig
   737  	return nil
   738  }
   739  
   740  func parseTLSConfig(result **config.TLSConfig, list *ast.ObjectList) error {
   741  	list = list.Elem()
   742  	if len(list.Items) > 1 {
   743  		return fmt.Errorf("only one 'tls' block allowed")
   744  	}
   745  
   746  	// Get the TLS object
   747  	listVal := list.Items[0].Val
   748  
   749  	valid := []string{
   750  		"http",
   751  		"rpc",
   752  		"verify_server_hostname",
   753  		"rpc_upgrade_mode",
   754  		"ca_file",
   755  		"cert_file",
   756  		"key_file",
   757  		"verify_https_client",
   758  	}
   759  
   760  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   761  		return err
   762  	}
   763  
   764  	var m map[string]interface{}
   765  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   766  		return err
   767  	}
   768  
   769  	var tlsConfig config.TLSConfig
   770  	if err := mapstructure.WeakDecode(m, &tlsConfig); err != nil {
   771  		return err
   772  	}
   773  
   774  	*result = &tlsConfig
   775  	return nil
   776  }
   777  
   778  func parseVaultConfig(result **config.VaultConfig, list *ast.ObjectList) error {
   779  	list = list.Elem()
   780  	if len(list.Items) > 1 {
   781  		return fmt.Errorf("only one 'vault' block allowed")
   782  	}
   783  
   784  	// Get our Vault object
   785  	listVal := list.Items[0].Val
   786  
   787  	// Check for invalid keys
   788  	valid := []string{
   789  		"address",
   790  		"allow_unauthenticated",
   791  		"enabled",
   792  		"task_token_ttl",
   793  		"ca_file",
   794  		"ca_path",
   795  		"cert_file",
   796  		"create_from_role",
   797  		"key_file",
   798  		"tls_server_name",
   799  		"tls_skip_verify",
   800  		"token",
   801  	}
   802  
   803  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   804  		return err
   805  	}
   806  
   807  	var m map[string]interface{}
   808  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   809  		return err
   810  	}
   811  
   812  	vaultConfig := config.DefaultVaultConfig()
   813  	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   814  		DecodeHook:       mapstructure.StringToTimeDurationHookFunc(),
   815  		WeaklyTypedInput: true,
   816  		Result:           &vaultConfig,
   817  	})
   818  	if err != nil {
   819  		return err
   820  	}
   821  	if err := dec.Decode(m); err != nil {
   822  		return err
   823  	}
   824  
   825  	*result = vaultConfig
   826  	return nil
   827  }
   828  
   829  func parseSentinel(result **config.SentinelConfig, list *ast.ObjectList) error {
   830  	list = list.Elem()
   831  	if len(list.Items) > 1 {
   832  		return fmt.Errorf("only one 'sentinel' block allowed")
   833  	}
   834  
   835  	// Get our sentinel object
   836  	obj := list.Items[0]
   837  
   838  	// Value should be an object
   839  	var listVal *ast.ObjectList
   840  	if ot, ok := obj.Val.(*ast.ObjectType); ok {
   841  		listVal = ot.List
   842  	} else {
   843  		return fmt.Errorf("sentinel value: should be an object")
   844  	}
   845  
   846  	// Check for invalid keys
   847  	valid := []string{
   848  		"import",
   849  	}
   850  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   851  		return err
   852  	}
   853  
   854  	var config config.SentinelConfig
   855  	if err := hcl.DecodeObject(&config, listVal); err != nil {
   856  		return err
   857  	}
   858  
   859  	*result = &config
   860  	return nil
   861  }
   862  
   863  func parseAutopilot(result **config.AutopilotConfig, list *ast.ObjectList) error {
   864  	list = list.Elem()
   865  	if len(list.Items) > 1 {
   866  		return fmt.Errorf("only one 'autopilot' block allowed")
   867  	}
   868  
   869  	// Get our Autopilot object
   870  	listVal := list.Items[0].Val
   871  
   872  	// Check for invalid keys
   873  	valid := []string{
   874  		"cleanup_dead_servers",
   875  		"server_stabilization_time",
   876  		"last_contact_threshold",
   877  		"max_trailing_logs",
   878  		"enable_redundancy_zones",
   879  		"disable_upgrade_migration",
   880  		"enable_custom_upgrades",
   881  	}
   882  
   883  	if err := helper.CheckHCLKeys(listVal, valid); err != nil {
   884  		return err
   885  	}
   886  
   887  	var m map[string]interface{}
   888  	if err := hcl.DecodeObject(&m, listVal); err != nil {
   889  		return err
   890  	}
   891  
   892  	autopilotConfig := config.DefaultAutopilotConfig()
   893  	dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
   894  		DecodeHook:       mapstructure.StringToTimeDurationHookFunc(),
   895  		WeaklyTypedInput: true,
   896  		Result:           &autopilotConfig,
   897  	})
   898  	if err != nil {
   899  		return err
   900  	}
   901  	if err := dec.Decode(m); err != nil {
   902  		return err
   903  	}
   904  
   905  	*result = autopilotConfig
   906  	return nil
   907  }