github.com/mapuri/terraform@v0.7.6-0.20161012203214-7e0408293f97/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  func (t *hclConfigurable) Config() (*Config, error) {
    21  	validKeys := map[string]struct{}{
    22  		"atlas":    struct{}{},
    23  		"data":     struct{}{},
    24  		"module":   struct{}{},
    25  		"output":   struct{}{},
    26  		"provider": struct{}{},
    27  		"resource": struct{}{},
    28  		"variable": struct{}{},
    29  	}
    30  
    31  	type hclVariable struct {
    32  		Name         string `hcl:",key"`
    33  		Default      interface{}
    34  		Description  string
    35  		DeclaredType string   `hcl:"type"`
    36  		Fields       []string `hcl:",decodedFields"`
    37  	}
    38  
    39  	var rawConfig struct {
    40  		Variable []*hclVariable
    41  	}
    42  
    43  	// Top-level item should be the object list
    44  	list, ok := t.Root.Node.(*ast.ObjectList)
    45  	if !ok {
    46  		return nil, fmt.Errorf("error parsing: file doesn't contain a root object")
    47  	}
    48  
    49  	if err := hcl.DecodeObject(&rawConfig, list); err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	// Start building up the actual configuration. We start with
    54  	// variables.
    55  	// TODO(mitchellh): Make function like loadVariablesHcl so that
    56  	// duplicates aren't overriden
    57  	config := new(Config)
    58  	if len(rawConfig.Variable) > 0 {
    59  		config.Variables = make([]*Variable, 0, len(rawConfig.Variable))
    60  		for _, v := range rawConfig.Variable {
    61  			// Defaults turn into a slice of map[string]interface{} and
    62  			// we need to make sure to convert that down into the
    63  			// proper type for Config.
    64  			if ms, ok := v.Default.([]map[string]interface{}); ok {
    65  				def := make(map[string]interface{})
    66  				for _, m := range ms {
    67  					for k, v := range m {
    68  						def[k] = v
    69  					}
    70  				}
    71  
    72  				v.Default = def
    73  			}
    74  
    75  			newVar := &Variable{
    76  				Name:         v.Name,
    77  				DeclaredType: v.DeclaredType,
    78  				Default:      v.Default,
    79  				Description:  v.Description,
    80  			}
    81  
    82  			if err := newVar.ValidateTypeAndDefault(); err != nil {
    83  				return nil, err
    84  			}
    85  
    86  			config.Variables = append(config.Variables, newVar)
    87  		}
    88  	}
    89  
    90  	// Get Atlas configuration
    91  	if atlas := list.Filter("atlas"); len(atlas.Items) > 0 {
    92  		var err error
    93  		config.Atlas, err = loadAtlasHcl(atlas)
    94  		if err != nil {
    95  			return nil, err
    96  		}
    97  	}
    98  
    99  	// Build the modules
   100  	if modules := list.Filter("module"); len(modules.Items) > 0 {
   101  		var err error
   102  		config.Modules, err = loadModulesHcl(modules)
   103  		if err != nil {
   104  			return nil, err
   105  		}
   106  	}
   107  
   108  	// Build the provider configs
   109  	if providers := list.Filter("provider"); len(providers.Items) > 0 {
   110  		var err error
   111  		config.ProviderConfigs, err = loadProvidersHcl(providers)
   112  		if err != nil {
   113  			return nil, err
   114  		}
   115  	}
   116  
   117  	// Build the resources
   118  	{
   119  		var err error
   120  		managedResourceConfigs := list.Filter("resource")
   121  		dataResourceConfigs := list.Filter("data")
   122  
   123  		config.Resources = make(
   124  			[]*Resource, 0,
   125  			len(managedResourceConfigs.Items)+len(dataResourceConfigs.Items),
   126  		)
   127  
   128  		managedResources, err := loadManagedResourcesHcl(managedResourceConfigs)
   129  		if err != nil {
   130  			return nil, err
   131  		}
   132  		dataResources, err := loadDataResourcesHcl(dataResourceConfigs)
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  
   137  		config.Resources = append(config.Resources, dataResources...)
   138  		config.Resources = append(config.Resources, managedResources...)
   139  	}
   140  
   141  	// Build the outputs
   142  	if outputs := list.Filter("output"); len(outputs.Items) > 0 {
   143  		var err error
   144  		config.Outputs, err = loadOutputsHcl(outputs)
   145  		if err != nil {
   146  			return nil, err
   147  		}
   148  	}
   149  
   150  	// Check for invalid keys
   151  	for _, item := range list.Items {
   152  		if len(item.Keys) == 0 {
   153  			// Not sure how this would happen, but let's avoid a panic
   154  			continue
   155  		}
   156  
   157  		k := item.Keys[0].Token.Value().(string)
   158  		if _, ok := validKeys[k]; ok {
   159  			continue
   160  		}
   161  
   162  		config.unknownKeys = append(config.unknownKeys, k)
   163  	}
   164  
   165  	return config, nil
   166  }
   167  
   168  // loadFileHcl is a fileLoaderFunc that knows how to read HCL
   169  // files and turn them into hclConfigurables.
   170  func loadFileHcl(root string) (configurable, []string, error) {
   171  	// Read the HCL file and prepare for parsing
   172  	d, err := ioutil.ReadFile(root)
   173  	if err != nil {
   174  		return nil, nil, fmt.Errorf(
   175  			"Error reading %s: %s", root, err)
   176  	}
   177  
   178  	// Parse it
   179  	hclRoot, err := hcl.Parse(string(d))
   180  	if err != nil {
   181  		return nil, nil, fmt.Errorf(
   182  			"Error parsing %s: %s", root, err)
   183  	}
   184  
   185  	// Start building the result
   186  	result := &hclConfigurable{
   187  		File: root,
   188  		Root: hclRoot,
   189  	}
   190  
   191  	// Dive in, find the imports. This is disabled for now since
   192  	// imports were removed prior to Terraform 0.1. The code is
   193  	// remaining here commented for historical purposes.
   194  	/*
   195  		imports := obj.Get("import")
   196  		if imports == nil {
   197  			result.Object.Ref()
   198  			return result, nil, nil
   199  		}
   200  
   201  		if imports.Type() != libucl.ObjectTypeString {
   202  			imports.Close()
   203  
   204  			return nil, nil, fmt.Errorf(
   205  				"Error in %s: all 'import' declarations should be in the format\n"+
   206  					"`import \"foo\"` (Got type %s)",
   207  				root,
   208  				imports.Type())
   209  		}
   210  
   211  		// Gather all the import paths
   212  		importPaths := make([]string, 0, imports.Len())
   213  		iter := imports.Iterate(false)
   214  		for imp := iter.Next(); imp != nil; imp = iter.Next() {
   215  			path := imp.ToString()
   216  			if !filepath.IsAbs(path) {
   217  				// Relative paths are relative to the Terraform file itself
   218  				dir := filepath.Dir(root)
   219  				path = filepath.Join(dir, path)
   220  			}
   221  
   222  			importPaths = append(importPaths, path)
   223  			imp.Close()
   224  		}
   225  		iter.Close()
   226  		imports.Close()
   227  
   228  		result.Object.Ref()
   229  	*/
   230  
   231  	return result, nil, nil
   232  }
   233  
   234  // Given a handle to a HCL object, this transforms it into the Atlas
   235  // configuration.
   236  func loadAtlasHcl(list *ast.ObjectList) (*AtlasConfig, error) {
   237  	if len(list.Items) > 1 {
   238  		return nil, fmt.Errorf("only one 'atlas' block allowed")
   239  	}
   240  
   241  	// Get our one item
   242  	item := list.Items[0]
   243  
   244  	var config AtlasConfig
   245  	if err := hcl.DecodeObject(&config, item.Val); err != nil {
   246  		return nil, fmt.Errorf(
   247  			"Error reading atlas config: %s",
   248  			err)
   249  	}
   250  
   251  	return &config, nil
   252  }
   253  
   254  // Given a handle to a HCL object, this recurses into the structure
   255  // and pulls out a list of modules.
   256  //
   257  // The resulting modules may not be unique, but each module
   258  // represents exactly one module definition in the HCL configuration.
   259  // We leave it up to another pass to merge them together.
   260  func loadModulesHcl(list *ast.ObjectList) ([]*Module, error) {
   261  	list = list.Children()
   262  	if len(list.Items) == 0 {
   263  		return nil, nil
   264  	}
   265  
   266  	// Where all the results will go
   267  	var result []*Module
   268  
   269  	// Now go over all the types and their children in order to get
   270  	// all of the actual resources.
   271  	for _, item := range list.Items {
   272  		k := item.Keys[0].Token.Value().(string)
   273  
   274  		var listVal *ast.ObjectList
   275  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   276  			listVal = ot.List
   277  		} else {
   278  			return nil, fmt.Errorf("module '%s': should be an object", k)
   279  		}
   280  
   281  		var config map[string]interface{}
   282  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   283  			return nil, fmt.Errorf(
   284  				"Error reading config for %s: %s",
   285  				k,
   286  				err)
   287  		}
   288  
   289  		// Remove the fields we handle specially
   290  		delete(config, "source")
   291  
   292  		rawConfig, err := NewRawConfig(config)
   293  		if err != nil {
   294  			return nil, fmt.Errorf(
   295  				"Error reading config for %s: %s",
   296  				k,
   297  				err)
   298  		}
   299  
   300  		// If we have a count, then figure it out
   301  		var source string
   302  		if o := listVal.Filter("source"); len(o.Items) > 0 {
   303  			err = hcl.DecodeObject(&source, o.Items[0].Val)
   304  			if err != nil {
   305  				return nil, fmt.Errorf(
   306  					"Error parsing source for %s: %s",
   307  					k,
   308  					err)
   309  			}
   310  		}
   311  
   312  		result = append(result, &Module{
   313  			Name:      k,
   314  			Source:    source,
   315  			RawConfig: rawConfig,
   316  		})
   317  	}
   318  
   319  	return result, nil
   320  }
   321  
   322  // LoadOutputsHcl recurses into the given HCL object and turns
   323  // it into a mapping of outputs.
   324  func loadOutputsHcl(list *ast.ObjectList) ([]*Output, error) {
   325  	list = list.Children()
   326  	if len(list.Items) == 0 {
   327  		return nil, nil
   328  	}
   329  
   330  	// Go through each object and turn it into an actual result.
   331  	result := make([]*Output, 0, len(list.Items))
   332  	for _, item := range list.Items {
   333  		n := item.Keys[0].Token.Value().(string)
   334  
   335  		var config map[string]interface{}
   336  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   337  			return nil, err
   338  		}
   339  
   340  		rawConfig, err := NewRawConfig(config)
   341  		if err != nil {
   342  			return nil, fmt.Errorf(
   343  				"Error reading config for output %s: %s",
   344  				n,
   345  				err)
   346  		}
   347  
   348  		result = append(result, &Output{
   349  			Name:      n,
   350  			RawConfig: rawConfig,
   351  		})
   352  	}
   353  
   354  	return result, nil
   355  }
   356  
   357  // LoadProvidersHcl recurses into the given HCL object and turns
   358  // it into a mapping of provider configs.
   359  func loadProvidersHcl(list *ast.ObjectList) ([]*ProviderConfig, error) {
   360  	list = list.Children()
   361  	if len(list.Items) == 0 {
   362  		return nil, nil
   363  	}
   364  
   365  	// Go through each object and turn it into an actual result.
   366  	result := make([]*ProviderConfig, 0, len(list.Items))
   367  	for _, item := range list.Items {
   368  		n := item.Keys[0].Token.Value().(string)
   369  
   370  		var listVal *ast.ObjectList
   371  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   372  			listVal = ot.List
   373  		} else {
   374  			return nil, fmt.Errorf("module '%s': should be an object", n)
   375  		}
   376  
   377  		var config map[string]interface{}
   378  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   379  			return nil, err
   380  		}
   381  
   382  		delete(config, "alias")
   383  
   384  		rawConfig, err := NewRawConfig(config)
   385  		if err != nil {
   386  			return nil, fmt.Errorf(
   387  				"Error reading config for provider config %s: %s",
   388  				n,
   389  				err)
   390  		}
   391  
   392  		// If we have an alias field, then add those in
   393  		var alias string
   394  		if a := listVal.Filter("alias"); len(a.Items) > 0 {
   395  			err := hcl.DecodeObject(&alias, a.Items[0].Val)
   396  			if err != nil {
   397  				return nil, fmt.Errorf(
   398  					"Error reading alias for provider[%s]: %s",
   399  					n,
   400  					err)
   401  			}
   402  		}
   403  
   404  		result = append(result, &ProviderConfig{
   405  			Name:      n,
   406  			Alias:     alias,
   407  			RawConfig: rawConfig,
   408  		})
   409  	}
   410  
   411  	return result, nil
   412  }
   413  
   414  // Given a handle to a HCL object, this recurses into the structure
   415  // and pulls out a list of data sources.
   416  //
   417  // The resulting data sources may not be unique, but each one
   418  // represents exactly one data definition in the HCL configuration.
   419  // We leave it up to another pass to merge them together.
   420  func loadDataResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
   421  	list = list.Children()
   422  	if len(list.Items) == 0 {
   423  		return nil, nil
   424  	}
   425  
   426  	// Where all the results will go
   427  	var result []*Resource
   428  
   429  	// Now go over all the types and their children in order to get
   430  	// all of the actual resources.
   431  	for _, item := range list.Items {
   432  		if len(item.Keys) != 2 {
   433  			return nil, fmt.Errorf(
   434  				"position %s: 'data' must be followed by exactly two strings: a type and a name",
   435  				item.Pos())
   436  		}
   437  
   438  		t := item.Keys[0].Token.Value().(string)
   439  		k := item.Keys[1].Token.Value().(string)
   440  
   441  		var listVal *ast.ObjectList
   442  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   443  			listVal = ot.List
   444  		} else {
   445  			return nil, fmt.Errorf("data sources %s[%s]: should be an object", t, k)
   446  		}
   447  
   448  		var config map[string]interface{}
   449  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   450  			return nil, fmt.Errorf(
   451  				"Error reading config for %s[%s]: %s",
   452  				t,
   453  				k,
   454  				err)
   455  		}
   456  
   457  		// Remove the fields we handle specially
   458  		delete(config, "depends_on")
   459  		delete(config, "provider")
   460  		delete(config, "count")
   461  
   462  		rawConfig, err := NewRawConfig(config)
   463  		if err != nil {
   464  			return nil, fmt.Errorf(
   465  				"Error reading config for %s[%s]: %s",
   466  				t,
   467  				k,
   468  				err)
   469  		}
   470  
   471  		// If we have a count, then figure it out
   472  		var count string = "1"
   473  		if o := listVal.Filter("count"); len(o.Items) > 0 {
   474  			err = hcl.DecodeObject(&count, o.Items[0].Val)
   475  			if err != nil {
   476  				return nil, fmt.Errorf(
   477  					"Error parsing count for %s[%s]: %s",
   478  					t,
   479  					k,
   480  					err)
   481  			}
   482  		}
   483  		countConfig, err := NewRawConfig(map[string]interface{}{
   484  			"count": count,
   485  		})
   486  		if err != nil {
   487  			return nil, err
   488  		}
   489  		countConfig.Key = "count"
   490  
   491  		// If we have depends fields, then add those in
   492  		var dependsOn []string
   493  		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
   494  			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
   495  			if err != nil {
   496  				return nil, fmt.Errorf(
   497  					"Error reading depends_on for %s[%s]: %s",
   498  					t,
   499  					k,
   500  					err)
   501  			}
   502  		}
   503  
   504  		// If we have a provider, then parse it out
   505  		var provider string
   506  		if o := listVal.Filter("provider"); len(o.Items) > 0 {
   507  			err := hcl.DecodeObject(&provider, o.Items[0].Val)
   508  			if err != nil {
   509  				return nil, fmt.Errorf(
   510  					"Error reading provider for %s[%s]: %s",
   511  					t,
   512  					k,
   513  					err)
   514  			}
   515  		}
   516  
   517  		result = append(result, &Resource{
   518  			Mode:         DataResourceMode,
   519  			Name:         k,
   520  			Type:         t,
   521  			RawCount:     countConfig,
   522  			RawConfig:    rawConfig,
   523  			Provider:     provider,
   524  			Provisioners: []*Provisioner{},
   525  			DependsOn:    dependsOn,
   526  			Lifecycle:    ResourceLifecycle{},
   527  		})
   528  	}
   529  
   530  	return result, nil
   531  }
   532  
   533  // Given a handle to a HCL object, this recurses into the structure
   534  // and pulls out a list of managed resources.
   535  //
   536  // The resulting resources may not be unique, but each resource
   537  // represents exactly one "resource" block in the HCL configuration.
   538  // We leave it up to another pass to merge them together.
   539  func loadManagedResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
   540  	list = list.Children()
   541  	if len(list.Items) == 0 {
   542  		return nil, nil
   543  	}
   544  
   545  	// Where all the results will go
   546  	var result []*Resource
   547  
   548  	// Now go over all the types and their children in order to get
   549  	// all of the actual resources.
   550  	for _, item := range list.Items {
   551  		// GH-4385: We detect a pure provisioner resource and give the user
   552  		// an error about how to do it cleanly.
   553  		if len(item.Keys) == 4 && item.Keys[2].Token.Value().(string) == "provisioner" {
   554  			return nil, fmt.Errorf(
   555  				"position %s: provisioners in a resource should be wrapped in a list\n\n"+
   556  					"Example: \"provisioner\": [ { \"local-exec\": ... } ]",
   557  				item.Pos())
   558  		}
   559  
   560  		// HCL special case: if we're parsing JSON then directly nested
   561  		// items will show up as additional "keys". We need to unwrap them
   562  		// since we expect only two keys. Example:
   563  		//
   564  		// { "foo": { "bar": { "baz": {} } } }
   565  		//
   566  		// Will show up with Keys being: []string{"foo", "bar", "baz"}
   567  		// when we really just want the first two. To fix this we unwrap
   568  		// them into the right value.
   569  		if len(item.Keys) > 2 && item.Keys[0].Token.JSON {
   570  			for len(item.Keys) > 2 {
   571  				// Pop off the last key
   572  				n := len(item.Keys)
   573  				key := item.Keys[n-1]
   574  				item.Keys[n-1] = nil
   575  				item.Keys = item.Keys[:n-1]
   576  
   577  				// Wrap our value in a list
   578  				item.Val = &ast.ObjectType{
   579  					List: &ast.ObjectList{
   580  						Items: []*ast.ObjectItem{
   581  							&ast.ObjectItem{
   582  								Keys: []*ast.ObjectKey{key},
   583  								Val:  item.Val,
   584  							},
   585  						},
   586  					},
   587  				}
   588  			}
   589  		}
   590  
   591  		if len(item.Keys) != 2 {
   592  			return nil, fmt.Errorf(
   593  				"position %s: resource must be followed by exactly two strings, a type and a name",
   594  				item.Pos())
   595  		}
   596  
   597  		t := item.Keys[0].Token.Value().(string)
   598  		k := item.Keys[1].Token.Value().(string)
   599  
   600  		var listVal *ast.ObjectList
   601  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   602  			listVal = ot.List
   603  		} else {
   604  			return nil, fmt.Errorf("resources %s[%s]: should be an object", t, k)
   605  		}
   606  
   607  		var config map[string]interface{}
   608  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   609  			return nil, fmt.Errorf(
   610  				"Error reading config for %s[%s]: %s",
   611  				t,
   612  				k,
   613  				err)
   614  		}
   615  
   616  		// Remove the fields we handle specially
   617  		delete(config, "connection")
   618  		delete(config, "count")
   619  		delete(config, "depends_on")
   620  		delete(config, "provisioner")
   621  		delete(config, "provider")
   622  		delete(config, "lifecycle")
   623  
   624  		rawConfig, err := NewRawConfig(config)
   625  		if err != nil {
   626  			return nil, fmt.Errorf(
   627  				"Error reading config for %s[%s]: %s",
   628  				t,
   629  				k,
   630  				err)
   631  		}
   632  
   633  		// If we have a count, then figure it out
   634  		var count string = "1"
   635  		if o := listVal.Filter("count"); len(o.Items) > 0 {
   636  			err = hcl.DecodeObject(&count, o.Items[0].Val)
   637  			if err != nil {
   638  				return nil, fmt.Errorf(
   639  					"Error parsing count for %s[%s]: %s",
   640  					t,
   641  					k,
   642  					err)
   643  			}
   644  		}
   645  		countConfig, err := NewRawConfig(map[string]interface{}{
   646  			"count": count,
   647  		})
   648  		if err != nil {
   649  			return nil, err
   650  		}
   651  		countConfig.Key = "count"
   652  
   653  		// If we have depends fields, then add those in
   654  		var dependsOn []string
   655  		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
   656  			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
   657  			if err != nil {
   658  				return nil, fmt.Errorf(
   659  					"Error reading depends_on for %s[%s]: %s",
   660  					t,
   661  					k,
   662  					err)
   663  			}
   664  		}
   665  
   666  		// If we have connection info, then parse those out
   667  		var connInfo map[string]interface{}
   668  		if o := listVal.Filter("connection"); len(o.Items) > 0 {
   669  			err := hcl.DecodeObject(&connInfo, o.Items[0].Val)
   670  			if err != nil {
   671  				return nil, fmt.Errorf(
   672  					"Error reading connection info for %s[%s]: %s",
   673  					t,
   674  					k,
   675  					err)
   676  			}
   677  		}
   678  
   679  		// If we have provisioners, then parse those out
   680  		var provisioners []*Provisioner
   681  		if os := listVal.Filter("provisioner"); len(os.Items) > 0 {
   682  			var err error
   683  			provisioners, err = loadProvisionersHcl(os, connInfo)
   684  			if err != nil {
   685  				return nil, fmt.Errorf(
   686  					"Error reading provisioners for %s[%s]: %s",
   687  					t,
   688  					k,
   689  					err)
   690  			}
   691  		}
   692  
   693  		// If we have a provider, then parse it out
   694  		var provider string
   695  		if o := listVal.Filter("provider"); len(o.Items) > 0 {
   696  			err := hcl.DecodeObject(&provider, o.Items[0].Val)
   697  			if err != nil {
   698  				return nil, fmt.Errorf(
   699  					"Error reading provider for %s[%s]: %s",
   700  					t,
   701  					k,
   702  					err)
   703  			}
   704  		}
   705  
   706  		// Check if the resource should be re-created before
   707  		// destroying the existing instance
   708  		var lifecycle ResourceLifecycle
   709  		if o := listVal.Filter("lifecycle"); len(o.Items) > 0 {
   710  			// Check for invalid keys
   711  			valid := []string{"create_before_destroy", "ignore_changes", "prevent_destroy"}
   712  			if err := checkHCLKeys(o.Items[0].Val, valid); err != nil {
   713  				return nil, multierror.Prefix(err, fmt.Sprintf(
   714  					"%s[%s]:", t, k))
   715  			}
   716  
   717  			var raw map[string]interface{}
   718  			if err = hcl.DecodeObject(&raw, o.Items[0].Val); err != nil {
   719  				return nil, fmt.Errorf(
   720  					"Error parsing lifecycle for %s[%s]: %s",
   721  					t,
   722  					k,
   723  					err)
   724  			}
   725  
   726  			if err := mapstructure.WeakDecode(raw, &lifecycle); err != nil {
   727  				return nil, fmt.Errorf(
   728  					"Error parsing lifecycle for %s[%s]: %s",
   729  					t,
   730  					k,
   731  					err)
   732  			}
   733  		}
   734  
   735  		result = append(result, &Resource{
   736  			Mode:         ManagedResourceMode,
   737  			Name:         k,
   738  			Type:         t,
   739  			RawCount:     countConfig,
   740  			RawConfig:    rawConfig,
   741  			Provisioners: provisioners,
   742  			Provider:     provider,
   743  			DependsOn:    dependsOn,
   744  			Lifecycle:    lifecycle,
   745  		})
   746  	}
   747  
   748  	return result, nil
   749  }
   750  
   751  func loadProvisionersHcl(list *ast.ObjectList, connInfo map[string]interface{}) ([]*Provisioner, error) {
   752  	list = list.Children()
   753  	if len(list.Items) == 0 {
   754  		return nil, nil
   755  	}
   756  
   757  	// Go through each object and turn it into an actual result.
   758  	result := make([]*Provisioner, 0, len(list.Items))
   759  	for _, item := range list.Items {
   760  		n := item.Keys[0].Token.Value().(string)
   761  
   762  		var listVal *ast.ObjectList
   763  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   764  			listVal = ot.List
   765  		} else {
   766  			return nil, fmt.Errorf("provisioner '%s': should be an object", n)
   767  		}
   768  
   769  		var config map[string]interface{}
   770  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   771  			return nil, err
   772  		}
   773  
   774  		// Delete the "connection" section, handle separately
   775  		delete(config, "connection")
   776  
   777  		rawConfig, err := NewRawConfig(config)
   778  		if err != nil {
   779  			return nil, err
   780  		}
   781  
   782  		// Check if we have a provisioner-level connection
   783  		// block that overrides the resource-level
   784  		var subConnInfo map[string]interface{}
   785  		if o := listVal.Filter("connection"); len(o.Items) > 0 {
   786  			err := hcl.DecodeObject(&subConnInfo, o.Items[0].Val)
   787  			if err != nil {
   788  				return nil, err
   789  			}
   790  		}
   791  
   792  		// Inherit from the resource connInfo any keys
   793  		// that are not explicitly overriden.
   794  		if connInfo != nil && subConnInfo != nil {
   795  			for k, v := range connInfo {
   796  				if _, ok := subConnInfo[k]; !ok {
   797  					subConnInfo[k] = v
   798  				}
   799  			}
   800  		} else if subConnInfo == nil {
   801  			subConnInfo = connInfo
   802  		}
   803  
   804  		// Parse the connInfo
   805  		connRaw, err := NewRawConfig(subConnInfo)
   806  		if err != nil {
   807  			return nil, err
   808  		}
   809  
   810  		result = append(result, &Provisioner{
   811  			Type:      n,
   812  			RawConfig: rawConfig,
   813  			ConnInfo:  connRaw,
   814  		})
   815  	}
   816  
   817  	return result, nil
   818  }
   819  
   820  /*
   821  func hclObjectMap(os *hclobj.Object) map[string]ast.ListNode {
   822  	objects := make(map[string][]*hclobj.Object)
   823  
   824  	for _, o := range os.Elem(false) {
   825  		for _, elem := range o.Elem(true) {
   826  			val, ok := objects[elem.Key]
   827  			if !ok {
   828  				val = make([]*hclobj.Object, 0, 1)
   829  			}
   830  
   831  			val = append(val, elem)
   832  			objects[elem.Key] = val
   833  		}
   834  	}
   835  
   836  	return objects
   837  }
   838  */
   839  
   840  func checkHCLKeys(node ast.Node, valid []string) error {
   841  	var list *ast.ObjectList
   842  	switch n := node.(type) {
   843  	case *ast.ObjectList:
   844  		list = n
   845  	case *ast.ObjectType:
   846  		list = n.List
   847  	default:
   848  		return fmt.Errorf("cannot check HCL keys of type %T", n)
   849  	}
   850  
   851  	validMap := make(map[string]struct{}, len(valid))
   852  	for _, v := range valid {
   853  		validMap[v] = struct{}{}
   854  	}
   855  
   856  	var result error
   857  	for _, item := range list.Items {
   858  		key := item.Keys[0].Token.Value().(string)
   859  		if _, ok := validMap[key]; !ok {
   860  			result = multierror.Append(result, fmt.Errorf(
   861  				"invalid key: %s", key))
   862  		}
   863  	}
   864  
   865  	return result
   866  }