github.com/tomaszheflik/terraform@v0.7.3-0.20160827060421-32f990b41594/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  
   461  		rawConfig, err := NewRawConfig(config)
   462  		if err != nil {
   463  			return nil, fmt.Errorf(
   464  				"Error reading config for %s[%s]: %s",
   465  				t,
   466  				k,
   467  				err)
   468  		}
   469  
   470  		// If we have a count, then figure it out
   471  		var count string = "1"
   472  		if o := listVal.Filter("count"); len(o.Items) > 0 {
   473  			err = hcl.DecodeObject(&count, o.Items[0].Val)
   474  			if err != nil {
   475  				return nil, fmt.Errorf(
   476  					"Error parsing count for %s[%s]: %s",
   477  					t,
   478  					k,
   479  					err)
   480  			}
   481  		}
   482  		countConfig, err := NewRawConfig(map[string]interface{}{
   483  			"count": count,
   484  		})
   485  		if err != nil {
   486  			return nil, err
   487  		}
   488  		countConfig.Key = "count"
   489  
   490  		// If we have depends fields, then add those in
   491  		var dependsOn []string
   492  		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
   493  			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
   494  			if err != nil {
   495  				return nil, fmt.Errorf(
   496  					"Error reading depends_on for %s[%s]: %s",
   497  					t,
   498  					k,
   499  					err)
   500  			}
   501  		}
   502  
   503  		// If we have a provider, then parse it out
   504  		var provider string
   505  		if o := listVal.Filter("provider"); len(o.Items) > 0 {
   506  			err := hcl.DecodeObject(&provider, o.Items[0].Val)
   507  			if err != nil {
   508  				return nil, fmt.Errorf(
   509  					"Error reading provider for %s[%s]: %s",
   510  					t,
   511  					k,
   512  					err)
   513  			}
   514  		}
   515  
   516  		result = append(result, &Resource{
   517  			Mode:         DataResourceMode,
   518  			Name:         k,
   519  			Type:         t,
   520  			RawCount:     countConfig,
   521  			RawConfig:    rawConfig,
   522  			Provider:     provider,
   523  			Provisioners: []*Provisioner{},
   524  			DependsOn:    dependsOn,
   525  			Lifecycle:    ResourceLifecycle{},
   526  		})
   527  	}
   528  
   529  	return result, nil
   530  }
   531  
   532  // Given a handle to a HCL object, this recurses into the structure
   533  // and pulls out a list of managed resources.
   534  //
   535  // The resulting resources may not be unique, but each resource
   536  // represents exactly one "resource" block in the HCL configuration.
   537  // We leave it up to another pass to merge them together.
   538  func loadManagedResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
   539  	list = list.Children()
   540  	if len(list.Items) == 0 {
   541  		return nil, nil
   542  	}
   543  
   544  	// Where all the results will go
   545  	var result []*Resource
   546  
   547  	// Now go over all the types and their children in order to get
   548  	// all of the actual resources.
   549  	for _, item := range list.Items {
   550  		// GH-4385: We detect a pure provisioner resource and give the user
   551  		// an error about how to do it cleanly.
   552  		if len(item.Keys) == 4 && item.Keys[2].Token.Value().(string) == "provisioner" {
   553  			return nil, fmt.Errorf(
   554  				"position %s: provisioners in a resource should be wrapped in a list\n\n"+
   555  					"Example: \"provisioner\": [ { \"local-exec\": ... } ]",
   556  				item.Pos())
   557  		}
   558  
   559  		// HCL special case: if we're parsing JSON then directly nested
   560  		// items will show up as additional "keys". We need to unwrap them
   561  		// since we expect only two keys. Example:
   562  		//
   563  		// { "foo": { "bar": { "baz": {} } } }
   564  		//
   565  		// Will show up with Keys being: []string{"foo", "bar", "baz"}
   566  		// when we really just want the first two. To fix this we unwrap
   567  		// them into the right value.
   568  		if len(item.Keys) > 2 && item.Keys[0].Token.JSON {
   569  			for len(item.Keys) > 2 {
   570  				// Pop off the last key
   571  				n := len(item.Keys)
   572  				key := item.Keys[n-1]
   573  				item.Keys[n-1] = nil
   574  				item.Keys = item.Keys[:n-1]
   575  
   576  				// Wrap our value in a list
   577  				item.Val = &ast.ObjectType{
   578  					List: &ast.ObjectList{
   579  						Items: []*ast.ObjectItem{
   580  							&ast.ObjectItem{
   581  								Keys: []*ast.ObjectKey{key},
   582  								Val:  item.Val,
   583  							},
   584  						},
   585  					},
   586  				}
   587  			}
   588  		}
   589  
   590  		if len(item.Keys) != 2 {
   591  			return nil, fmt.Errorf(
   592  				"position %s: resource must be followed by exactly two strings, a type and a name",
   593  				item.Pos())
   594  		}
   595  
   596  		t := item.Keys[0].Token.Value().(string)
   597  		k := item.Keys[1].Token.Value().(string)
   598  
   599  		var listVal *ast.ObjectList
   600  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   601  			listVal = ot.List
   602  		} else {
   603  			return nil, fmt.Errorf("resources %s[%s]: should be an object", t, k)
   604  		}
   605  
   606  		var config map[string]interface{}
   607  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   608  			return nil, fmt.Errorf(
   609  				"Error reading config for %s[%s]: %s",
   610  				t,
   611  				k,
   612  				err)
   613  		}
   614  
   615  		// Remove the fields we handle specially
   616  		delete(config, "connection")
   617  		delete(config, "count")
   618  		delete(config, "depends_on")
   619  		delete(config, "provisioner")
   620  		delete(config, "provider")
   621  		delete(config, "lifecycle")
   622  
   623  		rawConfig, err := NewRawConfig(config)
   624  		if err != nil {
   625  			return nil, fmt.Errorf(
   626  				"Error reading config for %s[%s]: %s",
   627  				t,
   628  				k,
   629  				err)
   630  		}
   631  
   632  		// If we have a count, then figure it out
   633  		var count string = "1"
   634  		if o := listVal.Filter("count"); len(o.Items) > 0 {
   635  			err = hcl.DecodeObject(&count, o.Items[0].Val)
   636  			if err != nil {
   637  				return nil, fmt.Errorf(
   638  					"Error parsing count for %s[%s]: %s",
   639  					t,
   640  					k,
   641  					err)
   642  			}
   643  		}
   644  		countConfig, err := NewRawConfig(map[string]interface{}{
   645  			"count": count,
   646  		})
   647  		if err != nil {
   648  			return nil, err
   649  		}
   650  		countConfig.Key = "count"
   651  
   652  		// If we have depends fields, then add those in
   653  		var dependsOn []string
   654  		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
   655  			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
   656  			if err != nil {
   657  				return nil, fmt.Errorf(
   658  					"Error reading depends_on for %s[%s]: %s",
   659  					t,
   660  					k,
   661  					err)
   662  			}
   663  		}
   664  
   665  		// If we have connection info, then parse those out
   666  		var connInfo map[string]interface{}
   667  		if o := listVal.Filter("connection"); len(o.Items) > 0 {
   668  			err := hcl.DecodeObject(&connInfo, o.Items[0].Val)
   669  			if err != nil {
   670  				return nil, fmt.Errorf(
   671  					"Error reading connection info for %s[%s]: %s",
   672  					t,
   673  					k,
   674  					err)
   675  			}
   676  		}
   677  
   678  		// If we have provisioners, then parse those out
   679  		var provisioners []*Provisioner
   680  		if os := listVal.Filter("provisioner"); len(os.Items) > 0 {
   681  			var err error
   682  			provisioners, err = loadProvisionersHcl(os, connInfo)
   683  			if err != nil {
   684  				return nil, fmt.Errorf(
   685  					"Error reading provisioners for %s[%s]: %s",
   686  					t,
   687  					k,
   688  					err)
   689  			}
   690  		}
   691  
   692  		// If we have a provider, then parse it out
   693  		var provider string
   694  		if o := listVal.Filter("provider"); len(o.Items) > 0 {
   695  			err := hcl.DecodeObject(&provider, o.Items[0].Val)
   696  			if err != nil {
   697  				return nil, fmt.Errorf(
   698  					"Error reading provider for %s[%s]: %s",
   699  					t,
   700  					k,
   701  					err)
   702  			}
   703  		}
   704  
   705  		// Check if the resource should be re-created before
   706  		// destroying the existing instance
   707  		var lifecycle ResourceLifecycle
   708  		if o := listVal.Filter("lifecycle"); len(o.Items) > 0 {
   709  			// Check for invalid keys
   710  			valid := []string{"create_before_destroy", "ignore_changes", "prevent_destroy"}
   711  			if err := checkHCLKeys(o.Items[0].Val, valid); err != nil {
   712  				return nil, multierror.Prefix(err, fmt.Sprintf(
   713  					"%s[%s]:", t, k))
   714  			}
   715  
   716  			var raw map[string]interface{}
   717  			if err = hcl.DecodeObject(&raw, o.Items[0].Val); err != nil {
   718  				return nil, fmt.Errorf(
   719  					"Error parsing lifecycle for %s[%s]: %s",
   720  					t,
   721  					k,
   722  					err)
   723  			}
   724  
   725  			if err := mapstructure.WeakDecode(raw, &lifecycle); err != nil {
   726  				return nil, fmt.Errorf(
   727  					"Error parsing lifecycle for %s[%s]: %s",
   728  					t,
   729  					k,
   730  					err)
   731  			}
   732  		}
   733  
   734  		result = append(result, &Resource{
   735  			Mode:         ManagedResourceMode,
   736  			Name:         k,
   737  			Type:         t,
   738  			RawCount:     countConfig,
   739  			RawConfig:    rawConfig,
   740  			Provisioners: provisioners,
   741  			Provider:     provider,
   742  			DependsOn:    dependsOn,
   743  			Lifecycle:    lifecycle,
   744  		})
   745  	}
   746  
   747  	return result, nil
   748  }
   749  
   750  func loadProvisionersHcl(list *ast.ObjectList, connInfo map[string]interface{}) ([]*Provisioner, error) {
   751  	list = list.Children()
   752  	if len(list.Items) == 0 {
   753  		return nil, nil
   754  	}
   755  
   756  	// Go through each object and turn it into an actual result.
   757  	result := make([]*Provisioner, 0, len(list.Items))
   758  	for _, item := range list.Items {
   759  		n := item.Keys[0].Token.Value().(string)
   760  
   761  		var listVal *ast.ObjectList
   762  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   763  			listVal = ot.List
   764  		} else {
   765  			return nil, fmt.Errorf("provisioner '%s': should be an object", n)
   766  		}
   767  
   768  		var config map[string]interface{}
   769  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   770  			return nil, err
   771  		}
   772  
   773  		// Delete the "connection" section, handle separately
   774  		delete(config, "connection")
   775  
   776  		rawConfig, err := NewRawConfig(config)
   777  		if err != nil {
   778  			return nil, err
   779  		}
   780  
   781  		// Check if we have a provisioner-level connection
   782  		// block that overrides the resource-level
   783  		var subConnInfo map[string]interface{}
   784  		if o := listVal.Filter("connection"); len(o.Items) > 0 {
   785  			err := hcl.DecodeObject(&subConnInfo, o.Items[0].Val)
   786  			if err != nil {
   787  				return nil, err
   788  			}
   789  		}
   790  
   791  		// Inherit from the resource connInfo any keys
   792  		// that are not explicitly overriden.
   793  		if connInfo != nil && subConnInfo != nil {
   794  			for k, v := range connInfo {
   795  				if _, ok := subConnInfo[k]; !ok {
   796  					subConnInfo[k] = v
   797  				}
   798  			}
   799  		} else if subConnInfo == nil {
   800  			subConnInfo = connInfo
   801  		}
   802  
   803  		// Parse the connInfo
   804  		connRaw, err := NewRawConfig(subConnInfo)
   805  		if err != nil {
   806  			return nil, err
   807  		}
   808  
   809  		result = append(result, &Provisioner{
   810  			Type:      n,
   811  			RawConfig: rawConfig,
   812  			ConnInfo:  connRaw,
   813  		})
   814  	}
   815  
   816  	return result, nil
   817  }
   818  
   819  /*
   820  func hclObjectMap(os *hclobj.Object) map[string]ast.ListNode {
   821  	objects := make(map[string][]*hclobj.Object)
   822  
   823  	for _, o := range os.Elem(false) {
   824  		for _, elem := range o.Elem(true) {
   825  			val, ok := objects[elem.Key]
   826  			if !ok {
   827  				val = make([]*hclobj.Object, 0, 1)
   828  			}
   829  
   830  			val = append(val, elem)
   831  			objects[elem.Key] = val
   832  		}
   833  	}
   834  
   835  	return objects
   836  }
   837  */
   838  
   839  func checkHCLKeys(node ast.Node, valid []string) error {
   840  	var list *ast.ObjectList
   841  	switch n := node.(type) {
   842  	case *ast.ObjectList:
   843  		list = n
   844  	case *ast.ObjectType:
   845  		list = n.List
   846  	default:
   847  		return fmt.Errorf("cannot check HCL keys of type %T", n)
   848  	}
   849  
   850  	validMap := make(map[string]struct{}, len(valid))
   851  	for _, v := range valid {
   852  		validMap[v] = struct{}{}
   853  	}
   854  
   855  	var result error
   856  	for _, item := range list.Items {
   857  		key := item.Keys[0].Token.Value().(string)
   858  		if _, ok := validMap[key]; !ok {
   859  			result = multierror.Append(result, fmt.Errorf(
   860  				"invalid key: %s", key))
   861  		}
   862  	}
   863  
   864  	return result
   865  }