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