github.com/sfdevops1/terrra4orm@v0.11.12-beta1/config/loader_hcl.go (about)

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