github.com/vic3lord/terraform@v0.8.0-rc1.0.20170626102919-16c6dd2cb372/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  	// This block should have an empty top level ObjectItem.  If there are keys
   213  	// here, it's likely because we have a flattened JSON object, and we can
   214  	// lift this into a nested ObjectList to decode properly.
   215  	if len(item.Keys) > 0 {
   216  		item = &ast.ObjectItem{
   217  			Val: &ast.ObjectType{
   218  				List: &ast.ObjectList{
   219  					Items: []*ast.ObjectItem{item},
   220  				},
   221  			},
   222  		}
   223  	}
   224  
   225  	// We need the item value as an ObjectList
   226  	var listVal *ast.ObjectList
   227  	if ot, ok := item.Val.(*ast.ObjectType); ok {
   228  		listVal = ot.List
   229  	} else {
   230  		return nil, fmt.Errorf("terraform block: should be an object")
   231  	}
   232  
   233  	// NOTE: We purposely don't validate unknown HCL keys here so that
   234  	// we can potentially read _future_ Terraform version config (to
   235  	// still be able to validate the required version).
   236  	//
   237  	// We should still keep track of unknown keys to validate later, but
   238  	// HCL doesn't currently support that.
   239  
   240  	var config Terraform
   241  	if err := hcl.DecodeObject(&config, item.Val); err != nil {
   242  		return nil, fmt.Errorf(
   243  			"Error reading terraform config: %s",
   244  			err)
   245  	}
   246  
   247  	// If we have provisioners, then parse those out
   248  	if os := listVal.Filter("backend"); len(os.Items) > 0 {
   249  		var err error
   250  		config.Backend, err = loadTerraformBackendHcl(os)
   251  		if err != nil {
   252  			return nil, fmt.Errorf(
   253  				"Error reading backend config for terraform block: %s",
   254  				err)
   255  		}
   256  	}
   257  
   258  	return &config, nil
   259  }
   260  
   261  // Loads the Backend configuration from an object list.
   262  func loadTerraformBackendHcl(list *ast.ObjectList) (*Backend, error) {
   263  	if len(list.Items) > 1 {
   264  		return nil, fmt.Errorf("only one 'backend' block allowed")
   265  	}
   266  
   267  	// Get our one item
   268  	item := list.Items[0]
   269  
   270  	// Verify the keys
   271  	if len(item.Keys) != 1 {
   272  		return nil, fmt.Errorf(
   273  			"position %s: 'backend' must be followed by exactly one string: a type",
   274  			item.Pos())
   275  	}
   276  
   277  	typ := item.Keys[0].Token.Value().(string)
   278  
   279  	// Decode the raw config
   280  	var config map[string]interface{}
   281  	if err := hcl.DecodeObject(&config, item.Val); err != nil {
   282  		return nil, fmt.Errorf(
   283  			"Error reading backend config: %s",
   284  			err)
   285  	}
   286  
   287  	rawConfig, err := NewRawConfig(config)
   288  	if err != nil {
   289  		return nil, fmt.Errorf(
   290  			"Error reading backend config: %s",
   291  			err)
   292  	}
   293  
   294  	b := &Backend{
   295  		Type:      typ,
   296  		RawConfig: rawConfig,
   297  	}
   298  	b.Hash = b.Rehash()
   299  
   300  	return b, nil
   301  }
   302  
   303  // Given a handle to a HCL object, this transforms it into the Atlas
   304  // configuration.
   305  func loadAtlasHcl(list *ast.ObjectList) (*AtlasConfig, error) {
   306  	if len(list.Items) > 1 {
   307  		return nil, fmt.Errorf("only one 'atlas' block allowed")
   308  	}
   309  
   310  	// Get our one item
   311  	item := list.Items[0]
   312  
   313  	var config AtlasConfig
   314  	if err := hcl.DecodeObject(&config, item.Val); err != nil {
   315  		return nil, fmt.Errorf(
   316  			"Error reading atlas config: %s",
   317  			err)
   318  	}
   319  
   320  	return &config, nil
   321  }
   322  
   323  // Given a handle to a HCL object, this recurses into the structure
   324  // and pulls out a list of modules.
   325  //
   326  // The resulting modules may not be unique, but each module
   327  // represents exactly one module definition in the HCL configuration.
   328  // We leave it up to another pass to merge them together.
   329  func loadModulesHcl(list *ast.ObjectList) ([]*Module, error) {
   330  	if err := assertAllBlocksHaveNames("module", list); err != nil {
   331  		return nil, err
   332  	}
   333  
   334  	list = list.Children()
   335  	if len(list.Items) == 0 {
   336  		return nil, nil
   337  	}
   338  
   339  	// Where all the results will go
   340  	var result []*Module
   341  
   342  	// Now go over all the types and their children in order to get
   343  	// all of the actual resources.
   344  	for _, item := range list.Items {
   345  		k := item.Keys[0].Token.Value().(string)
   346  
   347  		var listVal *ast.ObjectList
   348  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   349  			listVal = ot.List
   350  		} else {
   351  			return nil, fmt.Errorf("module '%s': should be an object", k)
   352  		}
   353  
   354  		var config map[string]interface{}
   355  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   356  			return nil, fmt.Errorf(
   357  				"Error reading config for %s: %s",
   358  				k,
   359  				err)
   360  		}
   361  
   362  		// Remove the fields we handle specially
   363  		delete(config, "source")
   364  
   365  		rawConfig, err := NewRawConfig(config)
   366  		if err != nil {
   367  			return nil, fmt.Errorf(
   368  				"Error reading config for %s: %s",
   369  				k,
   370  				err)
   371  		}
   372  
   373  		// If we have a count, then figure it out
   374  		var source string
   375  		if o := listVal.Filter("source"); len(o.Items) > 0 {
   376  			err = hcl.DecodeObject(&source, o.Items[0].Val)
   377  			if err != nil {
   378  				return nil, fmt.Errorf(
   379  					"Error parsing source for %s: %s",
   380  					k,
   381  					err)
   382  			}
   383  		}
   384  
   385  		result = append(result, &Module{
   386  			Name:      k,
   387  			Source:    source,
   388  			RawConfig: rawConfig,
   389  		})
   390  	}
   391  
   392  	return result, nil
   393  }
   394  
   395  // LoadOutputsHcl recurses into the given HCL object and turns
   396  // it into a mapping of outputs.
   397  func loadOutputsHcl(list *ast.ObjectList) ([]*Output, error) {
   398  	if err := assertAllBlocksHaveNames("output", list); err != nil {
   399  		return nil, err
   400  	}
   401  
   402  	list = list.Children()
   403  
   404  	// Go through each object and turn it into an actual result.
   405  	result := make([]*Output, 0, len(list.Items))
   406  	for _, item := range list.Items {
   407  		n := item.Keys[0].Token.Value().(string)
   408  
   409  		var listVal *ast.ObjectList
   410  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   411  			listVal = ot.List
   412  		} else {
   413  			return nil, fmt.Errorf("output '%s': should be an object", n)
   414  		}
   415  
   416  		var config map[string]interface{}
   417  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   418  			return nil, err
   419  		}
   420  
   421  		// Delete special keys
   422  		delete(config, "depends_on")
   423  
   424  		rawConfig, err := NewRawConfig(config)
   425  		if err != nil {
   426  			return nil, fmt.Errorf(
   427  				"Error reading config for output %s: %s",
   428  				n,
   429  				err)
   430  		}
   431  
   432  		// If we have depends fields, then add those in
   433  		var dependsOn []string
   434  		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
   435  			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
   436  			if err != nil {
   437  				return nil, fmt.Errorf(
   438  					"Error reading depends_on for output %q: %s",
   439  					n,
   440  					err)
   441  			}
   442  		}
   443  
   444  		result = append(result, &Output{
   445  			Name:      n,
   446  			RawConfig: rawConfig,
   447  			DependsOn: dependsOn,
   448  		})
   449  	}
   450  
   451  	return result, nil
   452  }
   453  
   454  // LoadVariablesHcl recurses into the given HCL object and turns
   455  // it into a list of variables.
   456  func loadVariablesHcl(list *ast.ObjectList) ([]*Variable, error) {
   457  	if err := assertAllBlocksHaveNames("variable", list); err != nil {
   458  		return nil, err
   459  	}
   460  
   461  	list = list.Children()
   462  
   463  	// hclVariable is the structure each variable is decoded into
   464  	type hclVariable struct {
   465  		DeclaredType string `hcl:"type"`
   466  		Default      interface{}
   467  		Description  string
   468  		Fields       []string `hcl:",decodedFields"`
   469  	}
   470  
   471  	// Go through each object and turn it into an actual result.
   472  	result := make([]*Variable, 0, len(list.Items))
   473  	for _, item := range list.Items {
   474  		// Clean up items from JSON
   475  		unwrapHCLObjectKeysFromJSON(item, 1)
   476  
   477  		// Verify the keys
   478  		if len(item.Keys) != 1 {
   479  			return nil, fmt.Errorf(
   480  				"position %s: 'variable' must be followed by exactly one strings: a name",
   481  				item.Pos())
   482  		}
   483  
   484  		n := item.Keys[0].Token.Value().(string)
   485  		if !NameRegexp.MatchString(n) {
   486  			return nil, fmt.Errorf(
   487  				"position %s: 'variable' name must match regular expression: %s",
   488  				item.Pos(), NameRegexp)
   489  		}
   490  
   491  		// Check for invalid keys
   492  		valid := []string{"type", "default", "description"}
   493  		if err := checkHCLKeys(item.Val, valid); err != nil {
   494  			return nil, multierror.Prefix(err, fmt.Sprintf(
   495  				"variable[%s]:", n))
   496  		}
   497  
   498  		// Decode into hclVariable to get typed values
   499  		var hclVar hclVariable
   500  		if err := hcl.DecodeObject(&hclVar, item.Val); err != nil {
   501  			return nil, err
   502  		}
   503  
   504  		// Defaults turn into a slice of map[string]interface{} and
   505  		// we need to make sure to convert that down into the
   506  		// proper type for Config.
   507  		if ms, ok := hclVar.Default.([]map[string]interface{}); ok {
   508  			def := make(map[string]interface{})
   509  			for _, m := range ms {
   510  				for k, v := range m {
   511  					def[k] = v
   512  				}
   513  			}
   514  
   515  			hclVar.Default = def
   516  		}
   517  
   518  		// Build the new variable and do some basic validation
   519  		newVar := &Variable{
   520  			Name:         n,
   521  			DeclaredType: hclVar.DeclaredType,
   522  			Default:      hclVar.Default,
   523  			Description:  hclVar.Description,
   524  		}
   525  		if err := newVar.ValidateTypeAndDefault(); err != nil {
   526  			return nil, err
   527  		}
   528  
   529  		result = append(result, newVar)
   530  	}
   531  
   532  	return result, nil
   533  }
   534  
   535  // LoadProvidersHcl recurses into the given HCL object and turns
   536  // it into a mapping of provider configs.
   537  func loadProvidersHcl(list *ast.ObjectList) ([]*ProviderConfig, error) {
   538  	if err := assertAllBlocksHaveNames("provider", list); err != nil {
   539  		return nil, err
   540  	}
   541  
   542  	list = list.Children()
   543  	if len(list.Items) == 0 {
   544  		return nil, nil
   545  	}
   546  
   547  	// Go through each object and turn it into an actual result.
   548  	result := make([]*ProviderConfig, 0, len(list.Items))
   549  	for _, item := range list.Items {
   550  		n := item.Keys[0].Token.Value().(string)
   551  
   552  		var listVal *ast.ObjectList
   553  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   554  			listVal = ot.List
   555  		} else {
   556  			return nil, fmt.Errorf("module '%s': should be an object", n)
   557  		}
   558  
   559  		var config map[string]interface{}
   560  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   561  			return nil, err
   562  		}
   563  
   564  		delete(config, "alias")
   565  		delete(config, "version")
   566  
   567  		rawConfig, err := NewRawConfig(config)
   568  		if err != nil {
   569  			return nil, fmt.Errorf(
   570  				"Error reading config for provider config %s: %s",
   571  				n,
   572  				err)
   573  		}
   574  
   575  		// If we have an alias field, then add those in
   576  		var alias string
   577  		if a := listVal.Filter("alias"); len(a.Items) > 0 {
   578  			err := hcl.DecodeObject(&alias, a.Items[0].Val)
   579  			if err != nil {
   580  				return nil, fmt.Errorf(
   581  					"Error reading alias for provider[%s]: %s",
   582  					n,
   583  					err)
   584  			}
   585  		}
   586  
   587  		// If we have a version field then extract it
   588  		var version string
   589  		if a := listVal.Filter("version"); len(a.Items) > 0 {
   590  			err := hcl.DecodeObject(&version, a.Items[0].Val)
   591  			if err != nil {
   592  				return nil, fmt.Errorf(
   593  					"Error reading version for provider[%s]: %s",
   594  					n,
   595  					err)
   596  			}
   597  		}
   598  
   599  		result = append(result, &ProviderConfig{
   600  			Name:      n,
   601  			Alias:     alias,
   602  			Version:   version,
   603  			RawConfig: rawConfig,
   604  		})
   605  	}
   606  
   607  	return result, nil
   608  }
   609  
   610  // Given a handle to a HCL object, this recurses into the structure
   611  // and pulls out a list of data sources.
   612  //
   613  // The resulting data sources may not be unique, but each one
   614  // represents exactly one data definition in the HCL configuration.
   615  // We leave it up to another pass to merge them together.
   616  func loadDataResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
   617  	if err := assertAllBlocksHaveNames("data", list); err != nil {
   618  		return nil, err
   619  	}
   620  
   621  	list = list.Children()
   622  	if len(list.Items) == 0 {
   623  		return nil, nil
   624  	}
   625  
   626  	// Where all the results will go
   627  	var result []*Resource
   628  
   629  	// Now go over all the types and their children in order to get
   630  	// all of the actual resources.
   631  	for _, item := range list.Items {
   632  		if len(item.Keys) != 2 {
   633  			return nil, fmt.Errorf(
   634  				"position %s: 'data' must be followed by exactly two strings: a type and a name",
   635  				item.Pos())
   636  		}
   637  
   638  		t := item.Keys[0].Token.Value().(string)
   639  		k := item.Keys[1].Token.Value().(string)
   640  
   641  		var listVal *ast.ObjectList
   642  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   643  			listVal = ot.List
   644  		} else {
   645  			return nil, fmt.Errorf("data sources %s[%s]: should be an object", t, k)
   646  		}
   647  
   648  		var config map[string]interface{}
   649  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   650  			return nil, fmt.Errorf(
   651  				"Error reading config for %s[%s]: %s",
   652  				t,
   653  				k,
   654  				err)
   655  		}
   656  
   657  		// Remove the fields we handle specially
   658  		delete(config, "depends_on")
   659  		delete(config, "provider")
   660  		delete(config, "count")
   661  
   662  		rawConfig, err := NewRawConfig(config)
   663  		if err != nil {
   664  			return nil, fmt.Errorf(
   665  				"Error reading config for %s[%s]: %s",
   666  				t,
   667  				k,
   668  				err)
   669  		}
   670  
   671  		// If we have a count, then figure it out
   672  		var count string = "1"
   673  		if o := listVal.Filter("count"); len(o.Items) > 0 {
   674  			err = hcl.DecodeObject(&count, o.Items[0].Val)
   675  			if err != nil {
   676  				return nil, fmt.Errorf(
   677  					"Error parsing count for %s[%s]: %s",
   678  					t,
   679  					k,
   680  					err)
   681  			}
   682  		}
   683  		countConfig, err := NewRawConfig(map[string]interface{}{
   684  			"count": count,
   685  		})
   686  		if err != nil {
   687  			return nil, err
   688  		}
   689  		countConfig.Key = "count"
   690  
   691  		// If we have depends fields, then add those in
   692  		var dependsOn []string
   693  		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
   694  			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
   695  			if err != nil {
   696  				return nil, fmt.Errorf(
   697  					"Error reading depends_on for %s[%s]: %s",
   698  					t,
   699  					k,
   700  					err)
   701  			}
   702  		}
   703  
   704  		// If we have a provider, then parse it out
   705  		var provider string
   706  		if o := listVal.Filter("provider"); len(o.Items) > 0 {
   707  			err := hcl.DecodeObject(&provider, o.Items[0].Val)
   708  			if err != nil {
   709  				return nil, fmt.Errorf(
   710  					"Error reading provider for %s[%s]: %s",
   711  					t,
   712  					k,
   713  					err)
   714  			}
   715  		}
   716  
   717  		result = append(result, &Resource{
   718  			Mode:         DataResourceMode,
   719  			Name:         k,
   720  			Type:         t,
   721  			RawCount:     countConfig,
   722  			RawConfig:    rawConfig,
   723  			Provider:     provider,
   724  			Provisioners: []*Provisioner{},
   725  			DependsOn:    dependsOn,
   726  			Lifecycle:    ResourceLifecycle{},
   727  		})
   728  	}
   729  
   730  	return result, nil
   731  }
   732  
   733  // Given a handle to a HCL object, this recurses into the structure
   734  // and pulls out a list of managed resources.
   735  //
   736  // The resulting resources may not be unique, but each resource
   737  // represents exactly one "resource" block in the HCL configuration.
   738  // We leave it up to another pass to merge them together.
   739  func loadManagedResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
   740  	list = list.Children()
   741  	if len(list.Items) == 0 {
   742  		return nil, nil
   743  	}
   744  
   745  	// Where all the results will go
   746  	var result []*Resource
   747  
   748  	// Now go over all the types and their children in order to get
   749  	// all of the actual resources.
   750  	for _, item := range list.Items {
   751  		// GH-4385: We detect a pure provisioner resource and give the user
   752  		// an error about how to do it cleanly.
   753  		if len(item.Keys) == 4 && item.Keys[2].Token.Value().(string) == "provisioner" {
   754  			return nil, fmt.Errorf(
   755  				"position %s: provisioners in a resource should be wrapped in a list\n\n"+
   756  					"Example: \"provisioner\": [ { \"local-exec\": ... } ]",
   757  				item.Pos())
   758  		}
   759  
   760  		// Fix up JSON input
   761  		unwrapHCLObjectKeysFromJSON(item, 2)
   762  
   763  		if len(item.Keys) != 2 {
   764  			return nil, fmt.Errorf(
   765  				"position %s: resource must be followed by exactly two strings, a type and a name",
   766  				item.Pos())
   767  		}
   768  
   769  		t := item.Keys[0].Token.Value().(string)
   770  		k := item.Keys[1].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("resources %s[%s]: should be an object", t, k)
   777  		}
   778  
   779  		var config map[string]interface{}
   780  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   781  			return nil, fmt.Errorf(
   782  				"Error reading config for %s[%s]: %s",
   783  				t,
   784  				k,
   785  				err)
   786  		}
   787  
   788  		// Remove the fields we handle specially
   789  		delete(config, "connection")
   790  		delete(config, "count")
   791  		delete(config, "depends_on")
   792  		delete(config, "provisioner")
   793  		delete(config, "provider")
   794  		delete(config, "lifecycle")
   795  
   796  		rawConfig, err := NewRawConfig(config)
   797  		if err != nil {
   798  			return nil, fmt.Errorf(
   799  				"Error reading config for %s[%s]: %s",
   800  				t,
   801  				k,
   802  				err)
   803  		}
   804  
   805  		// If we have a count, then figure it out
   806  		var count string = "1"
   807  		if o := listVal.Filter("count"); len(o.Items) > 0 {
   808  			err = hcl.DecodeObject(&count, o.Items[0].Val)
   809  			if err != nil {
   810  				return nil, fmt.Errorf(
   811  					"Error parsing count for %s[%s]: %s",
   812  					t,
   813  					k,
   814  					err)
   815  			}
   816  		}
   817  		countConfig, err := NewRawConfig(map[string]interface{}{
   818  			"count": count,
   819  		})
   820  		if err != nil {
   821  			return nil, err
   822  		}
   823  		countConfig.Key = "count"
   824  
   825  		// If we have depends fields, then add those in
   826  		var dependsOn []string
   827  		if o := listVal.Filter("depends_on"); len(o.Items) > 0 {
   828  			err := hcl.DecodeObject(&dependsOn, o.Items[0].Val)
   829  			if err != nil {
   830  				return nil, fmt.Errorf(
   831  					"Error reading depends_on for %s[%s]: %s",
   832  					t,
   833  					k,
   834  					err)
   835  			}
   836  		}
   837  
   838  		// If we have connection info, then parse those out
   839  		var connInfo map[string]interface{}
   840  		if o := listVal.Filter("connection"); len(o.Items) > 0 {
   841  			err := hcl.DecodeObject(&connInfo, o.Items[0].Val)
   842  			if err != nil {
   843  				return nil, fmt.Errorf(
   844  					"Error reading connection info for %s[%s]: %s",
   845  					t,
   846  					k,
   847  					err)
   848  			}
   849  		}
   850  
   851  		// If we have provisioners, then parse those out
   852  		var provisioners []*Provisioner
   853  		if os := listVal.Filter("provisioner"); len(os.Items) > 0 {
   854  			var err error
   855  			provisioners, err = loadProvisionersHcl(os, connInfo)
   856  			if err != nil {
   857  				return nil, fmt.Errorf(
   858  					"Error reading provisioners for %s[%s]: %s",
   859  					t,
   860  					k,
   861  					err)
   862  			}
   863  		}
   864  
   865  		// If we have a provider, then parse it out
   866  		var provider string
   867  		if o := listVal.Filter("provider"); len(o.Items) > 0 {
   868  			err := hcl.DecodeObject(&provider, o.Items[0].Val)
   869  			if err != nil {
   870  				return nil, fmt.Errorf(
   871  					"Error reading provider for %s[%s]: %s",
   872  					t,
   873  					k,
   874  					err)
   875  			}
   876  		}
   877  
   878  		// Check if the resource should be re-created before
   879  		// destroying the existing instance
   880  		var lifecycle ResourceLifecycle
   881  		if o := listVal.Filter("lifecycle"); len(o.Items) > 0 {
   882  			if len(o.Items) > 1 {
   883  				return nil, fmt.Errorf(
   884  					"%s[%s]: Multiple lifecycle blocks found, expected one",
   885  					t, k)
   886  			}
   887  
   888  			// Check for invalid keys
   889  			valid := []string{"create_before_destroy", "ignore_changes", "prevent_destroy"}
   890  			if err := checkHCLKeys(o.Items[0].Val, valid); err != nil {
   891  				return nil, multierror.Prefix(err, fmt.Sprintf(
   892  					"%s[%s]:", t, k))
   893  			}
   894  
   895  			var raw map[string]interface{}
   896  			if err = hcl.DecodeObject(&raw, o.Items[0].Val); err != nil {
   897  				return nil, fmt.Errorf(
   898  					"Error parsing lifecycle for %s[%s]: %s",
   899  					t,
   900  					k,
   901  					err)
   902  			}
   903  
   904  			if err := mapstructure.WeakDecode(raw, &lifecycle); err != nil {
   905  				return nil, fmt.Errorf(
   906  					"Error parsing lifecycle for %s[%s]: %s",
   907  					t,
   908  					k,
   909  					err)
   910  			}
   911  		}
   912  
   913  		result = append(result, &Resource{
   914  			Mode:         ManagedResourceMode,
   915  			Name:         k,
   916  			Type:         t,
   917  			RawCount:     countConfig,
   918  			RawConfig:    rawConfig,
   919  			Provisioners: provisioners,
   920  			Provider:     provider,
   921  			DependsOn:    dependsOn,
   922  			Lifecycle:    lifecycle,
   923  		})
   924  	}
   925  
   926  	return result, nil
   927  }
   928  
   929  func loadProvisionersHcl(list *ast.ObjectList, connInfo map[string]interface{}) ([]*Provisioner, error) {
   930  	if err := assertAllBlocksHaveNames("provisioner", list); err != nil {
   931  		return nil, err
   932  	}
   933  
   934  	list = list.Children()
   935  	if len(list.Items) == 0 {
   936  		return nil, nil
   937  	}
   938  
   939  	// Go through each object and turn it into an actual result.
   940  	result := make([]*Provisioner, 0, len(list.Items))
   941  	for _, item := range list.Items {
   942  		n := item.Keys[0].Token.Value().(string)
   943  
   944  		var listVal *ast.ObjectList
   945  		if ot, ok := item.Val.(*ast.ObjectType); ok {
   946  			listVal = ot.List
   947  		} else {
   948  			return nil, fmt.Errorf("provisioner '%s': should be an object", n)
   949  		}
   950  
   951  		var config map[string]interface{}
   952  		if err := hcl.DecodeObject(&config, item.Val); err != nil {
   953  			return nil, err
   954  		}
   955  
   956  		// Parse the "when" value
   957  		when := ProvisionerWhenCreate
   958  		if v, ok := config["when"]; ok {
   959  			switch v {
   960  			case "create":
   961  				when = ProvisionerWhenCreate
   962  			case "destroy":
   963  				when = ProvisionerWhenDestroy
   964  			default:
   965  				return nil, fmt.Errorf(
   966  					"position %s: 'provisioner' when must be 'create' or 'destroy'",
   967  					item.Pos())
   968  			}
   969  		}
   970  
   971  		// Parse the "on_failure" value
   972  		onFailure := ProvisionerOnFailureFail
   973  		if v, ok := config["on_failure"]; ok {
   974  			switch v {
   975  			case "continue":
   976  				onFailure = ProvisionerOnFailureContinue
   977  			case "fail":
   978  				onFailure = ProvisionerOnFailureFail
   979  			default:
   980  				return nil, fmt.Errorf(
   981  					"position %s: 'provisioner' on_failure must be 'continue' or 'fail'",
   982  					item.Pos())
   983  			}
   984  		}
   985  
   986  		// Delete fields we special case
   987  		delete(config, "connection")
   988  		delete(config, "when")
   989  		delete(config, "on_failure")
   990  
   991  		rawConfig, err := NewRawConfig(config)
   992  		if err != nil {
   993  			return nil, err
   994  		}
   995  
   996  		// Check if we have a provisioner-level connection
   997  		// block that overrides the resource-level
   998  		var subConnInfo map[string]interface{}
   999  		if o := listVal.Filter("connection"); len(o.Items) > 0 {
  1000  			err := hcl.DecodeObject(&subConnInfo, o.Items[0].Val)
  1001  			if err != nil {
  1002  				return nil, err
  1003  			}
  1004  		}
  1005  
  1006  		// Inherit from the resource connInfo any keys
  1007  		// that are not explicitly overriden.
  1008  		if connInfo != nil && subConnInfo != nil {
  1009  			for k, v := range connInfo {
  1010  				if _, ok := subConnInfo[k]; !ok {
  1011  					subConnInfo[k] = v
  1012  				}
  1013  			}
  1014  		} else if subConnInfo == nil {
  1015  			subConnInfo = connInfo
  1016  		}
  1017  
  1018  		// Parse the connInfo
  1019  		connRaw, err := NewRawConfig(subConnInfo)
  1020  		if err != nil {
  1021  			return nil, err
  1022  		}
  1023  
  1024  		result = append(result, &Provisioner{
  1025  			Type:      n,
  1026  			RawConfig: rawConfig,
  1027  			ConnInfo:  connRaw,
  1028  			When:      when,
  1029  			OnFailure: onFailure,
  1030  		})
  1031  	}
  1032  
  1033  	return result, nil
  1034  }
  1035  
  1036  /*
  1037  func hclObjectMap(os *hclobj.Object) map[string]ast.ListNode {
  1038  	objects := make(map[string][]*hclobj.Object)
  1039  
  1040  	for _, o := range os.Elem(false) {
  1041  		for _, elem := range o.Elem(true) {
  1042  			val, ok := objects[elem.Key]
  1043  			if !ok {
  1044  				val = make([]*hclobj.Object, 0, 1)
  1045  			}
  1046  
  1047  			val = append(val, elem)
  1048  			objects[elem.Key] = val
  1049  		}
  1050  	}
  1051  
  1052  	return objects
  1053  }
  1054  */
  1055  
  1056  // assertAllBlocksHaveNames returns an error if any of the items in
  1057  // the given object list are blocks without keys (like "module {}")
  1058  // or simple assignments (like "module = 1"). It returns nil if
  1059  // neither of these things are true.
  1060  //
  1061  // The given name is used in any generated error messages, and should
  1062  // be the name of the block we're dealing with. The given list should
  1063  // be the result of calling .Filter on an object list with that same
  1064  // name.
  1065  func assertAllBlocksHaveNames(name string, list *ast.ObjectList) error {
  1066  	if elem := list.Elem(); len(elem.Items) != 0 {
  1067  		switch et := elem.Items[0].Val.(type) {
  1068  		case *ast.ObjectType:
  1069  			pos := et.Lbrace
  1070  			return fmt.Errorf("%s: %q must be followed by a name", pos, name)
  1071  		default:
  1072  			pos := elem.Items[0].Val.Pos()
  1073  			return fmt.Errorf("%s: %q must be a configuration block", pos, name)
  1074  		}
  1075  	}
  1076  	return nil
  1077  }
  1078  
  1079  func checkHCLKeys(node ast.Node, valid []string) error {
  1080  	var list *ast.ObjectList
  1081  	switch n := node.(type) {
  1082  	case *ast.ObjectList:
  1083  		list = n
  1084  	case *ast.ObjectType:
  1085  		list = n.List
  1086  	default:
  1087  		return fmt.Errorf("cannot check HCL keys of type %T", n)
  1088  	}
  1089  
  1090  	validMap := make(map[string]struct{}, len(valid))
  1091  	for _, v := range valid {
  1092  		validMap[v] = struct{}{}
  1093  	}
  1094  
  1095  	var result error
  1096  	for _, item := range list.Items {
  1097  		key := item.Keys[0].Token.Value().(string)
  1098  		if _, ok := validMap[key]; !ok {
  1099  			result = multierror.Append(result, fmt.Errorf(
  1100  				"invalid key: %s", key))
  1101  		}
  1102  	}
  1103  
  1104  	return result
  1105  }
  1106  
  1107  // unwrapHCLObjectKeysFromJSON cleans up an edge case that can occur when
  1108  // parsing JSON as input: if we're parsing JSON then directly nested
  1109  // items will show up as additional "keys".
  1110  //
  1111  // For objects that expect a fixed number of keys, this breaks the
  1112  // decoding process. This function unwraps the object into what it would've
  1113  // looked like if it came directly from HCL by specifying the number of keys
  1114  // you expect.
  1115  //
  1116  // Example:
  1117  //
  1118  // { "foo": { "baz": {} } }
  1119  //
  1120  // Will show up with Keys being: []string{"foo", "baz"}
  1121  // when we really just want the first two. This function will fix this.
  1122  func unwrapHCLObjectKeysFromJSON(item *ast.ObjectItem, depth int) {
  1123  	if len(item.Keys) > depth && item.Keys[0].Token.JSON {
  1124  		for len(item.Keys) > depth {
  1125  			// Pop off the last key
  1126  			n := len(item.Keys)
  1127  			key := item.Keys[n-1]
  1128  			item.Keys[n-1] = nil
  1129  			item.Keys = item.Keys[:n-1]
  1130  
  1131  			// Wrap our value in a list
  1132  			item.Val = &ast.ObjectType{
  1133  				List: &ast.ObjectList{
  1134  					Items: []*ast.ObjectItem{
  1135  						&ast.ObjectItem{
  1136  							Keys: []*ast.ObjectKey{key},
  1137  							Val:  item.Val,
  1138  						},
  1139  					},
  1140  				},
  1141  			}
  1142  		}
  1143  	}
  1144  }