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