github.com/sl1pm4t/terraform@v0.6.4-0.20170725213156-870617d22df3/config/loader_hcl.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  
     7  	"github.com/hashicorp/go-multierror"
     8  	"github.com/hashicorp/hcl"
     9  	"github.com/hashicorp/hcl/hcl/ast"
    10  	"github.com/mitchellh/mapstructure"
    11  )
    12  
    13  // hclConfigurable is an implementation of configurable that knows
    14  // how to turn HCL configuration into a *Config object.
    15  type hclConfigurable struct {
    16  	File string
    17  	Root *ast.File
    18  }
    19  
    20  var ReservedResourceFields = []string{
    21  	"connection",
    22  	"count",
    23  	"depends_on",
    24  	"lifecycle",
    25  	"provider",
    26  	"provisioner",
    27  }
    28  
    29  var ReservedProviderFields = []string{
    30  	"alias",
    31  	"version",
    32  }
    33  
    34  func (t *hclConfigurable) Config() (*Config, error) {
    35  	validKeys := map[string]struct{}{
    36  		"atlas":     struct{}{},
    37  		"data":      struct{}{},
    38  		"module":    struct{}{},
    39  		"output":    struct{}{},
    40  		"provider":  struct{}{},
    41  		"resource":  struct{}{},
    42  		"terraform": struct{}{},
    43  		"variable":  struct{}{},
    44  	}
    45  
    46  	// Top-level item should be the object list
    47  	list, ok := t.Root.Node.(*ast.ObjectList)
    48  	if !ok {
    49  		return nil, fmt.Errorf("error parsing: file doesn't contain a root object")
    50  	}
    51  
    52  	// Start building up the actual configuration.
    53  	config := new(Config)
    54  
    55  	// Terraform config
    56  	if o := list.Filter("terraform"); len(o.Items) > 0 {
    57  		var err error
    58  		config.Terraform, err = loadTerraformHcl(o)
    59  		if err != nil {
    60  			return nil, err
    61  		}
    62  	}
    63  
    64  	// Build the variables
    65  	if vars := list.Filter("variable"); len(vars.Items) > 0 {
    66  		var err error
    67  		config.Variables, err = loadVariablesHcl(vars)
    68  		if err != nil {
    69  			return nil, err
    70  		}
    71  	}
    72  
    73  	// Get Atlas configuration
    74  	if atlas := list.Filter("atlas"); len(atlas.Items) > 0 {
    75  		var err error
    76  		config.Atlas, err = loadAtlasHcl(atlas)
    77  		if err != nil {
    78  			return nil, err
    79  		}
    80  	}
    81  
    82  	// Build the modules
    83  	if modules := list.Filter("module"); len(modules.Items) > 0 {
    84  		var err error
    85  		config.Modules, err = loadModulesHcl(modules)
    86  		if err != nil {
    87  			return nil, err
    88  		}
    89  	}
    90  
    91  	// Build the provider configs
    92  	if providers := list.Filter("provider"); len(providers.Items) > 0 {
    93  		var err error
    94  		config.ProviderConfigs, err = loadProvidersHcl(providers)
    95  		if err != nil {
    96  			return nil, err
    97  		}
    98  	}
    99  
   100  	// Build the resources
   101  	{
   102  		var err error
   103  		managedResourceConfigs := list.Filter("resource")
   104  		dataResourceConfigs := list.Filter("data")
   105  
   106  		config.Resources = make(
   107  			[]*Resource, 0,
   108  			len(managedResourceConfigs.Items)+len(dataResourceConfigs.Items),
   109  		)
   110  
   111  		managedResources, err := loadManagedResourcesHcl(managedResourceConfigs)
   112  		if err != nil {
   113  			return nil, err
   114  		}
   115  		dataResources, err := loadDataResourcesHcl(dataResourceConfigs)
   116  		if err != nil {
   117  			return nil, err
   118  		}
   119  
   120  		config.Resources = append(config.Resources, dataResources...)
   121  		config.Resources = append(config.Resources, managedResources...)
   122  	}
   123  
   124  	// Build the outputs
   125  	if outputs := list.Filter("output"); len(outputs.Items) > 0 {
   126  		var err error
   127  		config.Outputs, err = loadOutputsHcl(outputs)
   128  		if err != nil {
   129  			return nil, err
   130  		}
   131  	}
   132  
   133  	// Check for invalid keys
   134  	for _, item := range list.Items {
   135  		if len(item.Keys) == 0 {
   136  			// Not sure how this would happen, but let's avoid a panic
   137  			continue
   138  		}
   139  
   140  		k := item.Keys[0].Token.Value().(string)
   141  		if _, ok := validKeys[k]; ok {
   142  			continue
   143  		}
   144  
   145  		config.unknownKeys = append(config.unknownKeys, k)
   146  	}
   147  
   148  	return config, nil
   149  }
   150  
   151  // loadFileHcl is a fileLoaderFunc that knows how to read HCL
   152  // files and turn them into hclConfigurables.
   153  func loadFileHcl(root string) (configurable, []string, error) {
   154  	// Read the HCL file and prepare for parsing
   155  	d, err := ioutil.ReadFile(root)
   156  	if err != nil {
   157  		return nil, nil, fmt.Errorf(
   158  			"Error reading %s: %s", root, err)
   159  	}
   160  
   161  	// Parse it
   162  	hclRoot, err := hcl.Parse(string(d))
   163  	if err != nil {
   164  		return nil, nil, fmt.Errorf(
   165  			"Error parsing %s: %s", root, err)
   166  	}
   167  
   168  	// Start building the result
   169  	result := &hclConfigurable{
   170  		File: root,
   171  		Root: hclRoot,
   172  	}
   173  
   174  	// Dive in, find the imports. This is disabled for now since
   175  	// imports were removed prior to Terraform 0.1. The code is
   176  	// remaining here commented for historical purposes.
   177  	/*
   178  		imports := obj.Get("import")
   179  		if imports == nil {
   180  			result.Object.Ref()
   181  			return result, nil, nil
   182  		}
   183  
   184  		if imports.Type() != libucl.ObjectTypeString {
   185  			imports.Close()
   186  
   187  			return nil, nil, fmt.Errorf(
   188  				"Error in %s: all 'import' declarations should be in the format\n"+
   189  					"`import \"foo\"` (Got type %s)",
   190  				root,
   191  				imports.Type())
   192  		}
   193  
   194  		// Gather all the import paths
   195  		importPaths := make([]string, 0, imports.Len())
   196  		iter := imports.Iterate(false)
   197  		for imp := iter.Next(); imp != nil; imp = iter.Next() {
   198  			path := imp.ToString()
   199  			if !filepath.IsAbs(path) {
   200  				// Relative paths are relative to the Terraform file itself
   201  				dir := filepath.Dir(root)
   202  				path = filepath.Join(dir, path)
   203  			}
   204  
   205  			importPaths = append(importPaths, path)
   206  			imp.Close()
   207  		}
   208  		iter.Close()
   209  		imports.Close()
   210  
   211  		result.Object.Ref()
   212  	*/
   213  
   214  	return result, nil, nil
   215  }
   216  
   217  // Given a handle to a HCL object, this transforms it into the Terraform config
   218  func loadTerraformHcl(list *ast.ObjectList) (*Terraform, error) {
   219  	if len(list.Items) > 1 {
   220  		return nil, fmt.Errorf("only one 'terraform' block allowed per module")
   221  	}
   222  
   223  	// Get our one item
   224  	item := list.Items[0]
   225  
   226  	// This block should have an empty top level ObjectItem.  If there are keys
   227  	// here, it's likely because we have a flattened JSON object, and we can
   228  	// lift this into a nested ObjectList to decode properly.
   229  	if len(item.Keys) > 0 {
   230  		item = &ast.ObjectItem{
   231  			Val: &ast.ObjectType{
   232  				List: &ast.ObjectList{
   233  					Items: []*ast.ObjectItem{item},
   234  				},
   235  			},
   236  		}
   237  	}
   238  
   239  	// We need the item value as an ObjectList
   240  	var listVal *ast.ObjectList
   241  	if ot, ok := item.Val.(*ast.ObjectType); ok {
   242  		listVal = ot.List
   243  	} else {
   244  		return nil, fmt.Errorf("terraform block: should be an object")
   245  	}
   246  
   247  	// NOTE: We purposely don't validate unknown HCL keys here so that
   248  	// we can potentially read _future_ Terraform version config (to
   249  	// still be able to validate the required version).
   250  	//
   251  	// We should still keep track of unknown keys to validate later, but
   252  	// HCL doesn't currently support that.
   253  
   254  	var config Terraform
   255  	if err := hcl.DecodeObject(&config, item.Val); err != nil {
   256  		return nil, fmt.Errorf(
   257  			"Error reading terraform config: %s",
   258  			err)
   259  	}
   260  
   261  	// If we have provisioners, then parse those out
   262  	if os := listVal.Filter("backend"); len(os.Items) > 0 {
   263  		var err error
   264  		config.Backend, err = loadTerraformBackendHcl(os)
   265  		if err != nil {
   266  			return nil, fmt.Errorf(
   267  				"Error reading backend config for terraform block: %s",
   268  				err)
   269  		}
   270  	}
   271  
   272  	return &config, nil
   273  }
   274  
   275  // Loads the Backend configuration from an object list.
   276  func loadTerraformBackendHcl(list *ast.ObjectList) (*Backend, error) {
   277  	if len(list.Items) > 1 {
   278  		return nil, fmt.Errorf("only one 'backend' block allowed")
   279  	}
   280  
   281  	// Get our one item
   282  	item := list.Items[0]
   283  
   284  	// Verify the keys
   285  	if len(item.Keys) != 1 {
   286  		return nil, fmt.Errorf(
   287  			"position %s: 'backend' must be followed by exactly one string: a type",
   288  			item.Pos())
   289  	}
   290  
   291  	typ := item.Keys[0].Token.Value().(string)
   292  
   293  	// Decode the raw config
   294  	var config map[string]interface{}
   295  	if err := hcl.DecodeObject(&config, item.Val); err != nil {
   296  		return nil, fmt.Errorf(
   297  			"Error reading backend config: %s",
   298  			err)
   299  	}
   300  
   301  	rawConfig, err := NewRawConfig(config)
   302  	if err != nil {
   303  		return nil, fmt.Errorf(
   304  			"Error reading backend config: %s",
   305  			err)
   306  	}
   307  
   308  	b := &Backend{
   309  		Type:      typ,
   310  		RawConfig: rawConfig,
   311  	}
   312  	b.Hash = b.Rehash()
   313  
   314  	return b, nil
   315  }
   316  
   317  // Given a handle to a HCL object, this transforms it into the Atlas
   318  // configuration.
   319  func loadAtlasHcl(list *ast.ObjectList) (*AtlasConfig, error) {
   320  	if len(list.Items) > 1 {
   321  		return nil, fmt.Errorf("only one 'atlas' block allowed")
   322  	}
   323  
   324  	// Get our one item
   325  	item := list.Items[0]
   326  
   327  	var config AtlasConfig
   328  	if err := hcl.DecodeObject(&config, item.Val); err != nil {
   329  		return nil, fmt.Errorf(
   330  			"Error reading atlas config: %s",
   331  			err)
   332  	}
   333  
   334  	return &config, nil
   335  }
   336  
   337  // Given a handle to a HCL object, this recurses into the structure
   338  // and pulls out a list of modules.
   339  //
   340  // The resulting modules may not be unique, but each module
   341  // represents exactly one module definition in the HCL configuration.
   342  // We leave it up to another pass to merge them together.
   343  func loadModulesHcl(list *ast.ObjectList) ([]*Module, error) {
   344  	if err := assertAllBlocksHaveNames("module", list); err != nil {
   345  		return nil, err
   346  	}
   347  
   348  	list = list.Children()
   349  	if len(list.Items) == 0 {
   350  		return nil, nil
   351  	}
   352  
   353  	// Where all the results will go
   354  	var result []*Module
   355  
   356  	// Now go over all the types and their children in order to get
   357  	// all of the actual resources.
   358  	for _, item := range list.Items {
   359  		k := item.Keys[0].Token.Value().(string)
   360  
   361  		var listVal *ast.ObjectList
   362  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   363  			listVal = ot.List
   364  		} else {
   365  			return nil, fmt.Errorf("module '%s': should be an object", k)
   366  		}
   367  
   368  		var config map[string]interface{}
   369  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   370  			return nil, fmt.Errorf(
   371  				"Error reading config for %s: %s",
   372  				k,
   373  				err)
   374  		}
   375  
   376  		// Remove the fields we handle specially
   377  		delete(config, "source")
   378  
   379  		rawConfig, err := NewRawConfig(config)
   380  		if err != nil {
   381  			return nil, fmt.Errorf(
   382  				"Error reading config for %s: %s",
   383  				k,
   384  				err)
   385  		}
   386  
   387  		// If we have a count, then figure it out
   388  		var source string
   389  		if o := listVal.Filter("source"); len(o.Items) > 0 {
   390  			err = hcl.DecodeObject(&source, o.Items[0].Val)
   391  			if err != nil {
   392  				return nil, fmt.Errorf(
   393  					"Error parsing source for %s: %s",
   394  					k,
   395  					err)
   396  			}
   397  		}
   398  
   399  		result = append(result, &Module{
   400  			Name:      k,
   401  			Source:    source,
   402  			RawConfig: rawConfig,
   403  		})
   404  	}
   405  
   406  	return result, nil
   407  }
   408  
   409  // LoadOutputsHcl recurses into the given HCL object and turns
   410  // it into a mapping of outputs.
   411  func loadOutputsHcl(list *ast.ObjectList) ([]*Output, error) {
   412  	if err := assertAllBlocksHaveNames("output", list); err != nil {
   413  		return nil, err
   414  	}
   415  
   416  	list = list.Children()
   417  
   418  	// Go through each object and turn it into an actual result.
   419  	result := make([]*Output, 0, len(list.Items))
   420  	for _, item := range list.Items {
   421  		n := item.Keys[0].Token.Value().(string)
   422  
   423  		var listVal *ast.ObjectList
   424  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   425  			listVal = ot.List
   426  		} else {
   427  			return nil, fmt.Errorf("output '%s': should be an object", n)
   428  		}
   429  
   430  		var config map[string]interface{}
   431  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   432  			return nil, err
   433  		}
   434  
   435  		// Delete special keys
   436  		delete(config, "depends_on")
   437  
   438  		rawConfig, err := NewRawConfig(config)
   439  		if err != nil {
   440  			return nil, fmt.Errorf(
   441  				"Error reading config for output %s: %s",
   442  				n,
   443  				err)
   444  		}
   445  
   446  		// If we have depends fields, then add those in
   447  		var dependsOn []string
   448  		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
   449  			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
   450  			if err != nil {
   451  				return nil, fmt.Errorf(
   452  					"Error reading depends_on for output %q: %s",
   453  					n,
   454  					err)
   455  			}
   456  		}
   457  
   458  		result = append(result, &Output{
   459  			Name:      n,
   460  			RawConfig: rawConfig,
   461  			DependsOn: dependsOn,
   462  		})
   463  	}
   464  
   465  	return result, nil
   466  }
   467  
   468  // LoadVariablesHcl recurses into the given HCL object and turns
   469  // it into a list of variables.
   470  func loadVariablesHcl(list *ast.ObjectList) ([]*Variable, error) {
   471  	if err := assertAllBlocksHaveNames("variable", list); err != nil {
   472  		return nil, err
   473  	}
   474  
   475  	list = list.Children()
   476  
   477  	// hclVariable is the structure each variable is decoded into
   478  	type hclVariable struct {
   479  		DeclaredType string `hcl:"type"`
   480  		Default      interface{}
   481  		Description  string
   482  		Fields       []string `hcl:",decodedFields"`
   483  	}
   484  
   485  	// Go through each object and turn it into an actual result.
   486  	result := make([]*Variable, 0, len(list.Items))
   487  	for _, item := range list.Items {
   488  		// Clean up items from JSON
   489  		unwrapHCLObjectKeysFromJSON(item, 1)
   490  
   491  		// Verify the keys
   492  		if len(item.Keys) != 1 {
   493  			return nil, fmt.Errorf(
   494  				"position %s: 'variable' must be followed by exactly one strings: a name",
   495  				item.Pos())
   496  		}
   497  
   498  		n := item.Keys[0].Token.Value().(string)
   499  		if !NameRegexp.MatchString(n) {
   500  			return nil, fmt.Errorf(
   501  				"position %s: 'variable' name must match regular expression: %s",
   502  				item.Pos(), NameRegexp)
   503  		}
   504  
   505  		// Check for invalid keys
   506  		valid := []string{"type", "default", "description"}
   507  		if err := checkHCLKeys(item.Val, valid); err != nil {
   508  			return nil, multierror.Prefix(err, fmt.Sprintf(
   509  				"variable[%s]:", n))
   510  		}
   511  
   512  		// Decode into hclVariable to get typed values
   513  		var hclVar hclVariable
   514  		if err := hcl.DecodeObject(&hclVar, item.Val); err != nil {
   515  			return nil, err
   516  		}
   517  
   518  		// Defaults turn into a slice of map[string]interface{} and
   519  		// we need to make sure to convert that down into the
   520  		// proper type for Config.
   521  		if ms, ok := hclVar.Default.([]map[string]interface{}); ok {
   522  			def := make(map[string]interface{})
   523  			for _, m := range ms {
   524  				for k, v := range m {
   525  					def[k] = v
   526  				}
   527  			}
   528  
   529  			hclVar.Default = def
   530  		}
   531  
   532  		// Build the new variable and do some basic validation
   533  		newVar := &Variable{
   534  			Name:         n,
   535  			DeclaredType: hclVar.DeclaredType,
   536  			Default:      hclVar.Default,
   537  			Description:  hclVar.Description,
   538  		}
   539  		if err := newVar.ValidateTypeAndDefault(); err != nil {
   540  			return nil, err
   541  		}
   542  
   543  		result = append(result, newVar)
   544  	}
   545  
   546  	return result, nil
   547  }
   548  
   549  // LoadProvidersHcl recurses into the given HCL object and turns
   550  // it into a mapping of provider configs.
   551  func loadProvidersHcl(list *ast.ObjectList) ([]*ProviderConfig, error) {
   552  	if err := assertAllBlocksHaveNames("provider", list); err != nil {
   553  		return nil, err
   554  	}
   555  
   556  	list = list.Children()
   557  	if len(list.Items) == 0 {
   558  		return nil, nil
   559  	}
   560  
   561  	// Go through each object and turn it into an actual result.
   562  	result := make([]*ProviderConfig, 0, len(list.Items))
   563  	for _, item := range list.Items {
   564  		n := item.Keys[0].Token.Value().(string)
   565  
   566  		var listVal *ast.ObjectList
   567  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   568  			listVal = ot.List
   569  		} else {
   570  			return nil, fmt.Errorf("module '%s': should be an object", n)
   571  		}
   572  
   573  		var config map[string]interface{}
   574  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   575  			return nil, err
   576  		}
   577  
   578  		delete(config, "alias")
   579  		delete(config, "version")
   580  
   581  		rawConfig, err := NewRawConfig(config)
   582  		if err != nil {
   583  			return nil, fmt.Errorf(
   584  				"Error reading config for provider config %s: %s",
   585  				n,
   586  				err)
   587  		}
   588  
   589  		// If we have an alias field, then add those in
   590  		var alias string
   591  		if a := listVal.Filter("alias"); len(a.Items) > 0 {
   592  			err := hcl.DecodeObject(&alias, a.Items[0].Val)
   593  			if err != nil {
   594  				return nil, fmt.Errorf(
   595  					"Error reading alias for provider[%s]: %s",
   596  					n,
   597  					err)
   598  			}
   599  		}
   600  
   601  		// If we have a version field then extract it
   602  		var version string
   603  		if a := listVal.Filter("version"); len(a.Items) > 0 {
   604  			err := hcl.DecodeObject(&version, a.Items[0].Val)
   605  			if err != nil {
   606  				return nil, fmt.Errorf(
   607  					"Error reading version for provider[%s]: %s",
   608  					n,
   609  					err)
   610  			}
   611  		}
   612  
   613  		result = append(result, &ProviderConfig{
   614  			Name:      n,
   615  			Alias:     alias,
   616  			Version:   version,
   617  			RawConfig: rawConfig,
   618  		})
   619  	}
   620  
   621  	return result, nil
   622  }
   623  
   624  // Given a handle to a HCL object, this recurses into the structure
   625  // and pulls out a list of data sources.
   626  //
   627  // The resulting data sources may not be unique, but each one
   628  // represents exactly one data definition in the HCL configuration.
   629  // We leave it up to another pass to merge them together.
   630  func loadDataResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
   631  	if err := assertAllBlocksHaveNames("data", list); err != nil {
   632  		return nil, err
   633  	}
   634  
   635  	list = list.Children()
   636  	if len(list.Items) == 0 {
   637  		return nil, nil
   638  	}
   639  
   640  	// Where all the results will go
   641  	var result []*Resource
   642  
   643  	// Now go over all the types and their children in order to get
   644  	// all of the actual resources.
   645  	for _, item := range list.Items {
   646  		if len(item.Keys) != 2 {
   647  			return nil, fmt.Errorf(
   648  				"position %s: 'data' must be followed by exactly two strings: a type and a name",
   649  				item.Pos())
   650  		}
   651  
   652  		t := item.Keys[0].Token.Value().(string)
   653  		k := item.Keys[1].Token.Value().(string)
   654  
   655  		var listVal *ast.ObjectList
   656  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   657  			listVal = ot.List
   658  		} else {
   659  			return nil, fmt.Errorf("data sources %s[%s]: should be an object", t, k)
   660  		}
   661  
   662  		var config map[string]interface{}
   663  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   664  			return nil, fmt.Errorf(
   665  				"Error reading config for %s[%s]: %s",
   666  				t,
   667  				k,
   668  				err)
   669  		}
   670  
   671  		// Remove the fields we handle specially
   672  		delete(config, "depends_on")
   673  		delete(config, "provider")
   674  		delete(config, "count")
   675  
   676  		rawConfig, err := NewRawConfig(config)
   677  		if err != nil {
   678  			return nil, fmt.Errorf(
   679  				"Error reading config for %s[%s]: %s",
   680  				t,
   681  				k,
   682  				err)
   683  		}
   684  
   685  		// If we have a count, then figure it out
   686  		var count string = "1"
   687  		if o := listVal.Filter("count"); len(o.Items) > 0 {
   688  			err = hcl.DecodeObject(&count, o.Items[0].Val)
   689  			if err != nil {
   690  				return nil, fmt.Errorf(
   691  					"Error parsing count for %s[%s]: %s",
   692  					t,
   693  					k,
   694  					err)
   695  			}
   696  		}
   697  		countConfig, err := NewRawConfig(map[string]interface{}{
   698  			"count": count,
   699  		})
   700  		if err != nil {
   701  			return nil, err
   702  		}
   703  		countConfig.Key = "count"
   704  
   705  		// If we have depends fields, then add those in
   706  		var dependsOn []string
   707  		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
   708  			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
   709  			if err != nil {
   710  				return nil, fmt.Errorf(
   711  					"Error reading depends_on for %s[%s]: %s",
   712  					t,
   713  					k,
   714  					err)
   715  			}
   716  		}
   717  
   718  		// If we have a provider, then parse it out
   719  		var provider string
   720  		if o := listVal.Filter("provider"); len(o.Items) > 0 {
   721  			err := hcl.DecodeObject(&provider, o.Items[0].Val)
   722  			if err != nil {
   723  				return nil, fmt.Errorf(
   724  					"Error reading provider for %s[%s]: %s",
   725  					t,
   726  					k,
   727  					err)
   728  			}
   729  		}
   730  
   731  		result = append(result, &Resource{
   732  			Mode:         DataResourceMode,
   733  			Name:         k,
   734  			Type:         t,
   735  			RawCount:     countConfig,
   736  			RawConfig:    rawConfig,
   737  			Provider:     provider,
   738  			Provisioners: []*Provisioner{},
   739  			DependsOn:    dependsOn,
   740  			Lifecycle:    ResourceLifecycle{},
   741  		})
   742  	}
   743  
   744  	return result, nil
   745  }
   746  
   747  // Given a handle to a HCL object, this recurses into the structure
   748  // and pulls out a list of managed resources.
   749  //
   750  // The resulting resources may not be unique, but each resource
   751  // represents exactly one "resource" block in the HCL configuration.
   752  // We leave it up to another pass to merge them together.
   753  func loadManagedResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
   754  	list = list.Children()
   755  	if len(list.Items) == 0 {
   756  		return nil, nil
   757  	}
   758  
   759  	// Where all the results will go
   760  	var result []*Resource
   761  
   762  	// Now go over all the types and their children in order to get
   763  	// all of the actual resources.
   764  	for _, item := range list.Items {
   765  		// GH-4385: We detect a pure provisioner resource and give the user
   766  		// an error about how to do it cleanly.
   767  		if len(item.Keys) == 4 && item.Keys[2].Token.Value().(string) == "provisioner" {
   768  			return nil, fmt.Errorf(
   769  				"position %s: provisioners in a resource should be wrapped in a list\n\n"+
   770  					"Example: \"provisioner\": [ { \"local-exec\": ... } ]",
   771  				item.Pos())
   772  		}
   773  
   774  		// Fix up JSON input
   775  		unwrapHCLObjectKeysFromJSON(item, 2)
   776  
   777  		if len(item.Keys) != 2 {
   778  			return nil, fmt.Errorf(
   779  				"position %s: resource must be followed by exactly two strings, a type and a name",
   780  				item.Pos())
   781  		}
   782  
   783  		t := item.Keys[0].Token.Value().(string)
   784  		k := item.Keys[1].Token.Value().(string)
   785  
   786  		var listVal *ast.ObjectList
   787  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   788  			listVal = ot.List
   789  		} else {
   790  			return nil, fmt.Errorf("resources %s[%s]: should be an object", t, k)
   791  		}
   792  
   793  		var config map[string]interface{}
   794  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   795  			return nil, fmt.Errorf(
   796  				"Error reading config for %s[%s]: %s",
   797  				t,
   798  				k,
   799  				err)
   800  		}
   801  
   802  		// Remove the fields we handle specially
   803  		delete(config, "connection")
   804  		delete(config, "count")
   805  		delete(config, "depends_on")
   806  		delete(config, "provisioner")
   807  		delete(config, "provider")
   808  		delete(config, "lifecycle")
   809  
   810  		rawConfig, err := NewRawConfig(config)
   811  		if err != nil {
   812  			return nil, fmt.Errorf(
   813  				"Error reading config for %s[%s]: %s",
   814  				t,
   815  				k,
   816  				err)
   817  		}
   818  
   819  		// If we have a count, then figure it out
   820  		var count string = "1"
   821  		if o := listVal.Filter("count"); len(o.Items) > 0 {
   822  			err = hcl.DecodeObject(&count, o.Items[0].Val)
   823  			if err != nil {
   824  				return nil, fmt.Errorf(
   825  					"Error parsing count for %s[%s]: %s",
   826  					t,
   827  					k,
   828  					err)
   829  			}
   830  		}
   831  		countConfig, err := NewRawConfig(map[string]interface{}{
   832  			"count": count,
   833  		})
   834  		if err != nil {
   835  			return nil, err
   836  		}
   837  		countConfig.Key = "count"
   838  
   839  		// If we have depends fields, then add those in
   840  		var dependsOn []string
   841  		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
   842  			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
   843  			if err != nil {
   844  				return nil, fmt.Errorf(
   845  					"Error reading depends_on for %s[%s]: %s",
   846  					t,
   847  					k,
   848  					err)
   849  			}
   850  		}
   851  
   852  		// If we have connection info, then parse those out
   853  		var connInfo map[string]interface{}
   854  		if o := listVal.Filter("connection"); len(o.Items) > 0 {
   855  			err := hcl.DecodeObject(&connInfo, o.Items[0].Val)
   856  			if err != nil {
   857  				return nil, fmt.Errorf(
   858  					"Error reading connection info for %s[%s]: %s",
   859  					t,
   860  					k,
   861  					err)
   862  			}
   863  		}
   864  
   865  		// If we have provisioners, then parse those out
   866  		var provisioners []*Provisioner
   867  		if os := listVal.Filter("provisioner"); len(os.Items) > 0 {
   868  			var err error
   869  			provisioners, err = loadProvisionersHcl(os, connInfo)
   870  			if err != nil {
   871  				return nil, fmt.Errorf(
   872  					"Error reading provisioners for %s[%s]: %s",
   873  					t,
   874  					k,
   875  					err)
   876  			}
   877  		}
   878  
   879  		// If we have a provider, then parse it out
   880  		var provider string
   881  		if o := listVal.Filter("provider"); len(o.Items) > 0 {
   882  			err := hcl.DecodeObject(&provider, o.Items[0].Val)
   883  			if err != nil {
   884  				return nil, fmt.Errorf(
   885  					"Error reading provider for %s[%s]: %s",
   886  					t,
   887  					k,
   888  					err)
   889  			}
   890  		}
   891  
   892  		// Check if the resource should be re-created before
   893  		// destroying the existing instance
   894  		var lifecycle ResourceLifecycle
   895  		if o := listVal.Filter("lifecycle"); len(o.Items) > 0 {
   896  			if len(o.Items) > 1 {
   897  				return nil, fmt.Errorf(
   898  					"%s[%s]: Multiple lifecycle blocks found, expected one",
   899  					t, k)
   900  			}
   901  
   902  			// Check for invalid keys
   903  			valid := []string{"create_before_destroy", "ignore_changes", "prevent_destroy"}
   904  			if err := checkHCLKeys(o.Items[0].Val, valid); err != nil {
   905  				return nil, multierror.Prefix(err, fmt.Sprintf(
   906  					"%s[%s]:", t, k))
   907  			}
   908  
   909  			var raw map[string]interface{}
   910  			if err = hcl.DecodeObject(&raw, o.Items[0].Val); err != nil {
   911  				return nil, fmt.Errorf(
   912  					"Error parsing lifecycle for %s[%s]: %s",
   913  					t,
   914  					k,
   915  					err)
   916  			}
   917  
   918  			if err := mapstructure.WeakDecode(raw, &lifecycle); err != nil {
   919  				return nil, fmt.Errorf(
   920  					"Error parsing lifecycle for %s[%s]: %s",
   921  					t,
   922  					k,
   923  					err)
   924  			}
   925  		}
   926  
   927  		result = append(result, &Resource{
   928  			Mode:         ManagedResourceMode,
   929  			Name:         k,
   930  			Type:         t,
   931  			RawCount:     countConfig,
   932  			RawConfig:    rawConfig,
   933  			Provisioners: provisioners,
   934  			Provider:     provider,
   935  			DependsOn:    dependsOn,
   936  			Lifecycle:    lifecycle,
   937  		})
   938  	}
   939  
   940  	return result, nil
   941  }
   942  
   943  func loadProvisionersHcl(list *ast.ObjectList, connInfo map[string]interface{}) ([]*Provisioner, error) {
   944  	if err := assertAllBlocksHaveNames("provisioner", list); err != nil {
   945  		return nil, err
   946  	}
   947  
   948  	list = list.Children()
   949  	if len(list.Items) == 0 {
   950  		return nil, nil
   951  	}
   952  
   953  	// Go through each object and turn it into an actual result.
   954  	result := make([]*Provisioner, 0, len(list.Items))
   955  	for _, item := range list.Items {
   956  		n := item.Keys[0].Token.Value().(string)
   957  
   958  		var listVal *ast.ObjectList
   959  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   960  			listVal = ot.List
   961  		} else {
   962  			return nil, fmt.Errorf("provisioner '%s': should be an object", n)
   963  		}
   964  
   965  		var config map[string]interface{}
   966  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   967  			return nil, err
   968  		}
   969  
   970  		// Parse the "when" value
   971  		when := ProvisionerWhenCreate
   972  		if v, ok := config["when"]; ok {
   973  			switch v {
   974  			case "create":
   975  				when = ProvisionerWhenCreate
   976  			case "destroy":
   977  				when = ProvisionerWhenDestroy
   978  			default:
   979  				return nil, fmt.Errorf(
   980  					"position %s: 'provisioner' when must be 'create' or 'destroy'",
   981  					item.Pos())
   982  			}
   983  		}
   984  
   985  		// Parse the "on_failure" value
   986  		onFailure := ProvisionerOnFailureFail
   987  		if v, ok := config["on_failure"]; ok {
   988  			switch v {
   989  			case "continue":
   990  				onFailure = ProvisionerOnFailureContinue
   991  			case "fail":
   992  				onFailure = ProvisionerOnFailureFail
   993  			default:
   994  				return nil, fmt.Errorf(
   995  					"position %s: 'provisioner' on_failure must be 'continue' or 'fail'",
   996  					item.Pos())
   997  			}
   998  		}
   999  
  1000  		// Delete fields we special case
  1001  		delete(config, "connection")
  1002  		delete(config, "when")
  1003  		delete(config, "on_failure")
  1004  
  1005  		rawConfig, err := NewRawConfig(config)
  1006  		if err != nil {
  1007  			return nil, err
  1008  		}
  1009  
  1010  		// Check if we have a provisioner-level connection
  1011  		// block that overrides the resource-level
  1012  		var subConnInfo map[string]interface{}
  1013  		if o := listVal.Filter("connection"); len(o.Items) > 0 {
  1014  			err := hcl.DecodeObject(&subConnInfo, o.Items[0].Val)
  1015  			if err != nil {
  1016  				return nil, err
  1017  			}
  1018  		}
  1019  
  1020  		// Inherit from the resource connInfo any keys
  1021  		// that are not explicitly overriden.
  1022  		if connInfo != nil && subConnInfo != nil {
  1023  			for k, v := range connInfo {
  1024  				if _, ok := subConnInfo[k]; !ok {
  1025  					subConnInfo[k] = v
  1026  				}
  1027  			}
  1028  		} else if subConnInfo == nil {
  1029  			subConnInfo = connInfo
  1030  		}
  1031  
  1032  		// Parse the connInfo
  1033  		connRaw, err := NewRawConfig(subConnInfo)
  1034  		if err != nil {
  1035  			return nil, err
  1036  		}
  1037  
  1038  		result = append(result, &Provisioner{
  1039  			Type:      n,
  1040  			RawConfig: rawConfig,
  1041  			ConnInfo:  connRaw,
  1042  			When:      when,
  1043  			OnFailure: onFailure,
  1044  		})
  1045  	}
  1046  
  1047  	return result, nil
  1048  }
  1049  
  1050  /*
  1051  func hclObjectMap(os *hclobj.Object) map[string]ast.ListNode {
  1052  	objects := make(map[string][]*hclobj.Object)
  1053  
  1054  	for _, o := range os.Elem(false) {
  1055  		for _, elem := range o.Elem(true) {
  1056  			val, ok := objects[elem.Key]
  1057  			if !ok {
  1058  				val = make([]*hclobj.Object, 0, 1)
  1059  			}
  1060  
  1061  			val = append(val, elem)
  1062  			objects[elem.Key] = val
  1063  		}
  1064  	}
  1065  
  1066  	return objects
  1067  }
  1068  */
  1069  
  1070  // assertAllBlocksHaveNames returns an error if any of the items in
  1071  // the given object list are blocks without keys (like "module {}")
  1072  // or simple assignments (like "module = 1"). It returns nil if
  1073  // neither of these things are true.
  1074  //
  1075  // The given name is used in any generated error messages, and should
  1076  // be the name of the block we're dealing with. The given list should
  1077  // be the result of calling .Filter on an object list with that same
  1078  // name.
  1079  func assertAllBlocksHaveNames(name string, list *ast.ObjectList) error {
  1080  	if elem := list.Elem(); len(elem.Items) != 0 {
  1081  		switch et := elem.Items[0].Val.(type) {
  1082  		case *ast.ObjectType:
  1083  			pos := et.Lbrace
  1084  			return fmt.Errorf("%s: %q must be followed by a name", pos, name)
  1085  		default:
  1086  			pos := elem.Items[0].Val.Pos()
  1087  			return fmt.Errorf("%s: %q must be a configuration block", pos, name)
  1088  		}
  1089  	}
  1090  	return nil
  1091  }
  1092  
  1093  func checkHCLKeys(node ast.Node, valid []string) error {
  1094  	var list *ast.ObjectList
  1095  	switch n := node.(type) {
  1096  	case *ast.ObjectList:
  1097  		list = n
  1098  	case *ast.ObjectType:
  1099  		list = n.List
  1100  	default:
  1101  		return fmt.Errorf("cannot check HCL keys of type %T", n)
  1102  	}
  1103  
  1104  	validMap := make(map[string]struct{}, len(valid))
  1105  	for _, v := range valid {
  1106  		validMap[v] = struct{}{}
  1107  	}
  1108  
  1109  	var result error
  1110  	for _, item := range list.Items {
  1111  		key := item.Keys[0].Token.Value().(string)
  1112  		if _, ok := validMap[key]; !ok {
  1113  			result = multierror.Append(result, fmt.Errorf(
  1114  				"invalid key: %s", key))
  1115  		}
  1116  	}
  1117  
  1118  	return result
  1119  }
  1120  
  1121  // unwrapHCLObjectKeysFromJSON cleans up an edge case that can occur when
  1122  // parsing JSON as input: if we're parsing JSON then directly nested
  1123  // items will show up as additional "keys".
  1124  //
  1125  // For objects that expect a fixed number of keys, this breaks the
  1126  // decoding process. This function unwraps the object into what it would've
  1127  // looked like if it came directly from HCL by specifying the number of keys
  1128  // you expect.
  1129  //
  1130  // Example:
  1131  //
  1132  // { "foo": { "baz": {} } }
  1133  //
  1134  // Will show up with Keys being: []string{"foo", "baz"}
  1135  // when we really just want the first two. This function will fix this.
  1136  func unwrapHCLObjectKeysFromJSON(item *ast.ObjectItem, depth int) {
  1137  	if len(item.Keys) > depth && item.Keys[0].Token.JSON {
  1138  		for len(item.Keys) > depth {
  1139  			// Pop off the last key
  1140  			n := len(item.Keys)
  1141  			key := item.Keys[n-1]
  1142  			item.Keys[n-1] = nil
  1143  			item.Keys = item.Keys[:n-1]
  1144  
  1145  			// Wrap our value in a list
  1146  			item.Val = &ast.ObjectType{
  1147  				List: &ast.ObjectList{
  1148  					Items: []*ast.ObjectItem{
  1149  						&ast.ObjectItem{
  1150  							Keys: []*ast.ObjectKey{key},
  1151  							Val:  item.Val,
  1152  						},
  1153  					},
  1154  				},
  1155  			}
  1156  		}
  1157  	}
  1158  }