github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/terraform/evaluate.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"path/filepath"
     8  	"sync"
     9  
    10  	"github.com/agext/levenshtein"
    11  	"github.com/hashicorp/hcl/v2"
    12  	"github.com/zclconf/go-cty/cty"
    13  	"github.com/zclconf/go-cty/cty/convert"
    14  
    15  	"github.com/muratcelep/terraform/not-internal/addrs"
    16  	"github.com/muratcelep/terraform/not-internal/configs"
    17  	"github.com/muratcelep/terraform/not-internal/configs/configschema"
    18  	"github.com/muratcelep/terraform/not-internal/instances"
    19  	"github.com/muratcelep/terraform/not-internal/lang"
    20  	"github.com/muratcelep/terraform/not-internal/lang/marks"
    21  	"github.com/muratcelep/terraform/not-internal/plans"
    22  	"github.com/muratcelep/terraform/not-internal/states"
    23  	"github.com/muratcelep/terraform/not-internal/tfdiags"
    24  )
    25  
    26  // Evaluator provides the necessary contextual data for evaluating expressions
    27  // for a particular walk operation.
    28  type Evaluator struct {
    29  	// Operation defines what type of operation this evaluator is being used
    30  	// for.
    31  	Operation walkOperation
    32  
    33  	// Meta is contextual metadata about the current operation.
    34  	Meta *ContextMeta
    35  
    36  	// Config is the root node in the configuration tree.
    37  	Config *configs.Config
    38  
    39  	// VariableValues is a map from variable names to their associated values,
    40  	// within the module indicated by ModulePath. VariableValues is modified
    41  	// concurrently, and so it must be accessed only while holding
    42  	// VariableValuesLock.
    43  	//
    44  	// The first map level is string representations of addr.ModuleInstance
    45  	// values, while the second level is variable names.
    46  	VariableValues     map[string]map[string]cty.Value
    47  	VariableValuesLock *sync.Mutex
    48  
    49  	// Plugins is the library of available plugin components (providers and
    50  	// provisioners) that we have available to help us evaluate expressions
    51  	// that interact with plugin-provided objects.
    52  	//
    53  	// From this we only access the schemas of the plugins, and don't otherwise
    54  	// interact with plugin instances.
    55  	Plugins *contextPlugins
    56  
    57  	// State is the current state, embedded in a wrapper that ensures that
    58  	// it can be safely accessed and modified concurrently.
    59  	State *states.SyncState
    60  
    61  	// Changes is the set of proposed changes, embedded in a wrapper that
    62  	// ensures they can be safely accessed and modified concurrently.
    63  	Changes *plans.ChangesSync
    64  }
    65  
    66  // Scope creates an evaluation scope for the given module path and optional
    67  // resource.
    68  //
    69  // If the "self" argument is nil then the "self" object is not available
    70  // in evaluated expressions. Otherwise, it behaves as an alias for the given
    71  // address.
    72  func (e *Evaluator) Scope(data lang.Data, self addrs.Referenceable) *lang.Scope {
    73  	return &lang.Scope{
    74  		Data:     data,
    75  		SelfAddr: self,
    76  		PureOnly: e.Operation != walkApply && e.Operation != walkDestroy && e.Operation != walkEval,
    77  		BaseDir:  ".", // Always current working directory for now.
    78  	}
    79  }
    80  
    81  // evaluationStateData is an implementation of lang.Data that resolves
    82  // references primarily (but not exclusively) using information from a State.
    83  type evaluationStateData struct {
    84  	Evaluator *Evaluator
    85  
    86  	// ModulePath is the path through the dynamic module tree to the module
    87  	// that references will be resolved relative to.
    88  	ModulePath addrs.ModuleInstance
    89  
    90  	// InstanceKeyData describes the values, if any, that are accessible due
    91  	// to repetition of a containing object using "count" or "for_each"
    92  	// arguments. (It is _not_ used for the for_each inside "dynamic" blocks,
    93  	// since the user specifies in that case which variable name to locally
    94  	// shadow.)
    95  	InstanceKeyData InstanceKeyEvalData
    96  
    97  	// Operation records the type of walk the evaluationStateData is being used
    98  	// for.
    99  	Operation walkOperation
   100  }
   101  
   102  // InstanceKeyEvalData is the old name for instances.RepetitionData, aliased
   103  // here for compatibility. In new code, use instances.RepetitionData instead.
   104  type InstanceKeyEvalData = instances.RepetitionData
   105  
   106  // EvalDataForInstanceKey constructs a suitable InstanceKeyEvalData for
   107  // evaluating in a context that has the given instance key.
   108  //
   109  // The forEachMap argument can be nil when preparing for evaluation
   110  // in a context where each.value is prohibited, such as a destroy-time
   111  // provisioner. In that case, the returned EachValue will always be
   112  // cty.NilVal.
   113  func EvalDataForInstanceKey(key addrs.InstanceKey, forEachMap map[string]cty.Value) InstanceKeyEvalData {
   114  	var evalData InstanceKeyEvalData
   115  	if key == nil {
   116  		return evalData
   117  	}
   118  
   119  	keyValue := key.Value()
   120  	switch keyValue.Type() {
   121  	case cty.String:
   122  		evalData.EachKey = keyValue
   123  		evalData.EachValue = forEachMap[keyValue.AsString()]
   124  	case cty.Number:
   125  		evalData.CountIndex = keyValue
   126  	}
   127  	return evalData
   128  }
   129  
   130  // EvalDataForNoInstanceKey is a value of InstanceKeyData that sets no instance
   131  // key values at all, suitable for use in contexts where no keyed instance
   132  // is relevant.
   133  var EvalDataForNoInstanceKey = InstanceKeyEvalData{}
   134  
   135  // evaluationStateData must implement lang.Data
   136  var _ lang.Data = (*evaluationStateData)(nil)
   137  
   138  func (d *evaluationStateData) GetCountAttr(addr addrs.CountAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   139  	var diags tfdiags.Diagnostics
   140  	switch addr.Name {
   141  
   142  	case "index":
   143  		idxVal := d.InstanceKeyData.CountIndex
   144  		if idxVal == cty.NilVal {
   145  			diags = diags.Append(&hcl.Diagnostic{
   146  				Severity: hcl.DiagError,
   147  				Summary:  `Reference to "count" in non-counted context`,
   148  				Detail:   `The "count" object can only be used in "module", "resource", and "data" blocks, and only when the "count" argument is set.`,
   149  				Subject:  rng.ToHCL().Ptr(),
   150  			})
   151  			return cty.UnknownVal(cty.Number), diags
   152  		}
   153  		return idxVal, diags
   154  
   155  	default:
   156  		diags = diags.Append(&hcl.Diagnostic{
   157  			Severity: hcl.DiagError,
   158  			Summary:  `Invalid "count" attribute`,
   159  			Detail:   fmt.Sprintf(`The "count" object does not have an attribute named %q. The only supported attribute is count.index, which is the index of each instance of a resource block that has the "count" argument set.`, addr.Name),
   160  			Subject:  rng.ToHCL().Ptr(),
   161  		})
   162  		return cty.DynamicVal, diags
   163  	}
   164  }
   165  
   166  func (d *evaluationStateData) GetForEachAttr(addr addrs.ForEachAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   167  	var diags tfdiags.Diagnostics
   168  	var returnVal cty.Value
   169  	switch addr.Name {
   170  
   171  	case "key":
   172  		returnVal = d.InstanceKeyData.EachKey
   173  	case "value":
   174  		returnVal = d.InstanceKeyData.EachValue
   175  
   176  		if returnVal == cty.NilVal {
   177  			diags = diags.Append(&hcl.Diagnostic{
   178  				Severity: hcl.DiagError,
   179  				Summary:  `each.value cannot be used in this context`,
   180  				Detail:   `A reference to "each.value" has been used in a context in which it unavailable, such as when the configuration no longer contains the value in its "for_each" expression. Remove this reference to each.value in your configuration to work around this error.`,
   181  				Subject:  rng.ToHCL().Ptr(),
   182  			})
   183  			return cty.UnknownVal(cty.DynamicPseudoType), diags
   184  		}
   185  	default:
   186  		diags = diags.Append(&hcl.Diagnostic{
   187  			Severity: hcl.DiagError,
   188  			Summary:  `Invalid "each" attribute`,
   189  			Detail:   fmt.Sprintf(`The "each" object does not have an attribute named %q. The supported attributes are each.key and each.value, the current key and value pair of the "for_each" attribute set.`, addr.Name),
   190  			Subject:  rng.ToHCL().Ptr(),
   191  		})
   192  		return cty.DynamicVal, diags
   193  	}
   194  
   195  	if returnVal == cty.NilVal {
   196  		diags = diags.Append(&hcl.Diagnostic{
   197  			Severity: hcl.DiagError,
   198  			Summary:  `Reference to "each" in context without for_each`,
   199  			Detail:   `The "each" object can be used only in "module" or "resource" blocks, and only when the "for_each" argument is set.`,
   200  			Subject:  rng.ToHCL().Ptr(),
   201  		})
   202  		return cty.UnknownVal(cty.DynamicPseudoType), diags
   203  	}
   204  	return returnVal, diags
   205  }
   206  
   207  func (d *evaluationStateData) GetInputVariable(addr addrs.InputVariable, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   208  	var diags tfdiags.Diagnostics
   209  
   210  	// First we'll make sure the requested value is declared in configuration,
   211  	// so we can produce a nice message if not.
   212  	moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath)
   213  	if moduleConfig == nil {
   214  		// should never happen, since we can't be evaluating in a module
   215  		// that wasn't mentioned in configuration.
   216  		panic(fmt.Sprintf("input variable read from %s, which has no configuration", d.ModulePath))
   217  	}
   218  
   219  	config := moduleConfig.Module.Variables[addr.Name]
   220  	if config == nil {
   221  		var suggestions []string
   222  		for k := range moduleConfig.Module.Variables {
   223  			suggestions = append(suggestions, k)
   224  		}
   225  		suggestion := nameSuggestion(addr.Name, suggestions)
   226  		if suggestion != "" {
   227  			suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
   228  		} else {
   229  			suggestion = fmt.Sprintf(" This variable can be declared with a variable %q {} block.", addr.Name)
   230  		}
   231  
   232  		diags = diags.Append(&hcl.Diagnostic{
   233  			Severity: hcl.DiagError,
   234  			Summary:  `Reference to undeclared input variable`,
   235  			Detail:   fmt.Sprintf(`An input variable with the name %q has not been declared.%s`, addr.Name, suggestion),
   236  			Subject:  rng.ToHCL().Ptr(),
   237  		})
   238  		return cty.DynamicVal, diags
   239  	}
   240  	d.Evaluator.VariableValuesLock.Lock()
   241  	defer d.Evaluator.VariableValuesLock.Unlock()
   242  
   243  	// During the validate walk, input variables are always unknown so
   244  	// that we are validating the configuration for all possible input values
   245  	// rather than for a specific set. Checking against a specific set of
   246  	// input values then happens during the plan walk.
   247  	//
   248  	// This is important because otherwise the validation walk will tend to be
   249  	// overly strict, requiring expressions throughout the configuration to
   250  	// be complicated to accommodate all possible inputs, whereas returning
   251  	// known here allows for simpler patterns like using input values as
   252  	// guards to broadly enable/disable resources, avoid processing things
   253  	// that are disabled, etc. Terraform's static validation leans towards
   254  	// being liberal in what it accepts because the subsequent plan walk has
   255  	// more information available and so can be more conservative.
   256  	if d.Operation == walkValidate {
   257  		// Ensure variable sensitivity is captured in the validate walk
   258  		if config.Sensitive {
   259  			return cty.UnknownVal(config.Type).Mark(marks.Sensitive), diags
   260  		}
   261  		return cty.UnknownVal(config.Type), diags
   262  	}
   263  
   264  	moduleAddrStr := d.ModulePath.String()
   265  	vals := d.Evaluator.VariableValues[moduleAddrStr]
   266  	if vals == nil {
   267  		return cty.UnknownVal(config.Type), diags
   268  	}
   269  
   270  	val, isSet := vals[addr.Name]
   271  	switch {
   272  	case !isSet:
   273  		// The config loader will ensure there is a default if the value is not
   274  		// set at all.
   275  		val = config.Default
   276  
   277  	case val.IsNull() && !config.Nullable && config.Default != cty.NilVal:
   278  		// If nullable=false a null value will use the configured default.
   279  		val = config.Default
   280  	}
   281  
   282  	var err error
   283  	val, err = convert.Convert(val, config.ConstraintType)
   284  	if err != nil {
   285  		// We should never get here because this problem should've been caught
   286  		// during earlier validation, but we'll do something reasonable anyway.
   287  		diags = diags.Append(&hcl.Diagnostic{
   288  			Severity: hcl.DiagError,
   289  			Summary:  `Incorrect variable type`,
   290  			Detail:   fmt.Sprintf(`The resolved value of variable %q is not appropriate: %s.`, addr.Name, err),
   291  			Subject:  &config.DeclRange,
   292  		})
   293  		val = cty.UnknownVal(config.Type)
   294  	}
   295  
   296  	// Mark if sensitive
   297  	if config.Sensitive {
   298  		val = val.Mark(marks.Sensitive)
   299  	}
   300  
   301  	return val, diags
   302  }
   303  
   304  func (d *evaluationStateData) GetLocalValue(addr addrs.LocalValue, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   305  	var diags tfdiags.Diagnostics
   306  
   307  	// First we'll make sure the requested value is declared in configuration,
   308  	// so we can produce a nice message if not.
   309  	moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath)
   310  	if moduleConfig == nil {
   311  		// should never happen, since we can't be evaluating in a module
   312  		// that wasn't mentioned in configuration.
   313  		panic(fmt.Sprintf("local value read from %s, which has no configuration", d.ModulePath))
   314  	}
   315  
   316  	config := moduleConfig.Module.Locals[addr.Name]
   317  	if config == nil {
   318  		var suggestions []string
   319  		for k := range moduleConfig.Module.Locals {
   320  			suggestions = append(suggestions, k)
   321  		}
   322  		suggestion := nameSuggestion(addr.Name, suggestions)
   323  		if suggestion != "" {
   324  			suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
   325  		}
   326  
   327  		diags = diags.Append(&hcl.Diagnostic{
   328  			Severity: hcl.DiagError,
   329  			Summary:  `Reference to undeclared local value`,
   330  			Detail:   fmt.Sprintf(`A local value with the name %q has not been declared.%s`, addr.Name, suggestion),
   331  			Subject:  rng.ToHCL().Ptr(),
   332  		})
   333  		return cty.DynamicVal, diags
   334  	}
   335  
   336  	val := d.Evaluator.State.LocalValue(addr.Absolute(d.ModulePath))
   337  	if val == cty.NilVal {
   338  		// Not evaluated yet?
   339  		val = cty.DynamicVal
   340  	}
   341  
   342  	return val, diags
   343  }
   344  
   345  func (d *evaluationStateData) GetModule(addr addrs.ModuleCall, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   346  	var diags tfdiags.Diagnostics
   347  
   348  	// Output results live in the module that declares them, which is one of
   349  	// the child module instances of our current module path.
   350  	moduleAddr := d.ModulePath.Module().Child(addr.Name)
   351  
   352  	parentCfg := d.Evaluator.Config.DescendentForInstance(d.ModulePath)
   353  	callConfig, ok := parentCfg.Module.ModuleCalls[addr.Name]
   354  	if !ok {
   355  		diags = diags.Append(&hcl.Diagnostic{
   356  			Severity: hcl.DiagError,
   357  			Summary:  `Reference to undeclared module`,
   358  			Detail:   fmt.Sprintf(`The configuration contains no %s.`, moduleAddr),
   359  			Subject:  rng.ToHCL().Ptr(),
   360  		})
   361  		return cty.DynamicVal, diags
   362  	}
   363  
   364  	// We'll consult the configuration to see what output names we are
   365  	// expecting, so we can ensure the resulting object is of the expected
   366  	// type even if our data is incomplete for some reason.
   367  	moduleConfig := d.Evaluator.Config.Descendent(moduleAddr)
   368  	if moduleConfig == nil {
   369  		// should never happen, since we have a valid module call above, this
   370  		// should be caught during static validation.
   371  		panic(fmt.Sprintf("output value read from %s, which has no configuration", moduleAddr))
   372  	}
   373  	outputConfigs := moduleConfig.Module.Outputs
   374  
   375  	// Collect all the relevant outputs that current exist in the state.
   376  	// We know the instance path up to this point, and the child module name,
   377  	// so we only need to store these by instance key.
   378  	stateMap := map[addrs.InstanceKey]map[string]cty.Value{}
   379  	for _, output := range d.Evaluator.State.ModuleOutputs(d.ModulePath, addr) {
   380  		_, callInstance := output.Addr.Module.CallInstance()
   381  		instance, ok := stateMap[callInstance.Key]
   382  		if !ok {
   383  			instance = map[string]cty.Value{}
   384  			stateMap[callInstance.Key] = instance
   385  		}
   386  
   387  		instance[output.Addr.OutputValue.Name] = output.Value
   388  	}
   389  
   390  	// Get all changes that reside for this module call within our path.
   391  	// The change contains the full addr, so we can key these with strings.
   392  	changesMap := map[addrs.InstanceKey]map[string]*plans.OutputChangeSrc{}
   393  	for _, change := range d.Evaluator.Changes.GetOutputChanges(d.ModulePath, addr) {
   394  		_, callInstance := change.Addr.Module.CallInstance()
   395  		instance, ok := changesMap[callInstance.Key]
   396  		if !ok {
   397  			instance = map[string]*plans.OutputChangeSrc{}
   398  			changesMap[callInstance.Key] = instance
   399  		}
   400  
   401  		instance[change.Addr.OutputValue.Name] = change
   402  	}
   403  
   404  	// Build up all the module objects, creating a map of values for each
   405  	// module instance.
   406  	moduleInstances := map[addrs.InstanceKey]map[string]cty.Value{}
   407  
   408  	// create a dummy object type for validation below
   409  	unknownMap := map[string]cty.Type{}
   410  
   411  	// the structure is based on the configuration, so iterate through all the
   412  	// defined outputs, and add any instance state or changes we find.
   413  	for _, cfg := range outputConfigs {
   414  		// record the output names for validation
   415  		unknownMap[cfg.Name] = cty.DynamicPseudoType
   416  
   417  		// get all instance output for this path from the state
   418  		for key, states := range stateMap {
   419  			outputState, ok := states[cfg.Name]
   420  			if !ok {
   421  				continue
   422  			}
   423  
   424  			instance, ok := moduleInstances[key]
   425  			if !ok {
   426  				instance = map[string]cty.Value{}
   427  				moduleInstances[key] = instance
   428  			}
   429  
   430  			instance[cfg.Name] = outputState
   431  
   432  			if cfg.Sensitive {
   433  				instance[cfg.Name] = outputState.Mark(marks.Sensitive)
   434  			}
   435  		}
   436  
   437  		// any pending changes override the state state values
   438  		for key, changes := range changesMap {
   439  			changeSrc, ok := changes[cfg.Name]
   440  			if !ok {
   441  				continue
   442  			}
   443  
   444  			instance, ok := moduleInstances[key]
   445  			if !ok {
   446  				instance = map[string]cty.Value{}
   447  				moduleInstances[key] = instance
   448  			}
   449  
   450  			change, err := changeSrc.Decode()
   451  			if err != nil {
   452  				// This should happen only if someone has tampered with a plan
   453  				// file, so we won't bother with a pretty error for it.
   454  				diags = diags.Append(fmt.Errorf("planned change for %s could not be decoded: %s", addr, err))
   455  				instance[cfg.Name] = cty.DynamicVal
   456  				continue
   457  			}
   458  
   459  			instance[cfg.Name] = change.After
   460  
   461  			if change.Sensitive {
   462  				instance[cfg.Name] = change.After.Mark(marks.Sensitive)
   463  			}
   464  		}
   465  	}
   466  
   467  	var ret cty.Value
   468  
   469  	// compile the outputs into the correct value type for the each mode
   470  	switch {
   471  	case callConfig.Count != nil:
   472  		// figure out what the last index we have is
   473  		length := -1
   474  		for key := range moduleInstances {
   475  			intKey, ok := key.(addrs.IntKey)
   476  			if !ok {
   477  				// old key from state which is being dropped
   478  				continue
   479  			}
   480  			if int(intKey) >= length {
   481  				length = int(intKey) + 1
   482  			}
   483  		}
   484  
   485  		if length > 0 {
   486  			vals := make([]cty.Value, length)
   487  			for key, instance := range moduleInstances {
   488  				intKey, ok := key.(addrs.IntKey)
   489  				if !ok {
   490  					// old key from state which is being dropped
   491  					continue
   492  				}
   493  
   494  				vals[int(intKey)] = cty.ObjectVal(instance)
   495  			}
   496  
   497  			// Insert unknown values where there are any missing instances
   498  			for i, v := range vals {
   499  				if v.IsNull() {
   500  					vals[i] = cty.DynamicVal
   501  					continue
   502  				}
   503  			}
   504  			ret = cty.TupleVal(vals)
   505  		} else {
   506  			ret = cty.EmptyTupleVal
   507  		}
   508  
   509  	case callConfig.ForEach != nil:
   510  		vals := make(map[string]cty.Value)
   511  		for key, instance := range moduleInstances {
   512  			strKey, ok := key.(addrs.StringKey)
   513  			if !ok {
   514  				continue
   515  			}
   516  
   517  			vals[string(strKey)] = cty.ObjectVal(instance)
   518  		}
   519  
   520  		if len(vals) > 0 {
   521  			ret = cty.ObjectVal(vals)
   522  		} else {
   523  			ret = cty.EmptyObjectVal
   524  		}
   525  
   526  	default:
   527  		val, ok := moduleInstances[addrs.NoKey]
   528  		if !ok {
   529  			// create the object if there wasn't one known
   530  			val = map[string]cty.Value{}
   531  			for k := range outputConfigs {
   532  				val[k] = cty.DynamicVal
   533  			}
   534  		}
   535  
   536  		ret = cty.ObjectVal(val)
   537  	}
   538  
   539  	// The module won't be expanded during validation, so we need to return an
   540  	// unknown value. This will ensure the types looks correct, since we built
   541  	// the objects based on the configuration.
   542  	if d.Operation == walkValidate {
   543  		// While we know the type here and it would be nice to validate whether
   544  		// indexes are valid or not, because tuples and objects have fixed
   545  		// numbers of elements we can't simply return an unknown value of the
   546  		// same type since we have not expanded any instances during
   547  		// validation.
   548  		//
   549  		// In order to validate the expression a little precisely, we'll create
   550  		// an unknown map or list here to get more type information.
   551  		ty := cty.Object(unknownMap)
   552  		switch {
   553  		case callConfig.Count != nil:
   554  			ret = cty.UnknownVal(cty.List(ty))
   555  		case callConfig.ForEach != nil:
   556  			ret = cty.UnknownVal(cty.Map(ty))
   557  		default:
   558  			ret = cty.UnknownVal(ty)
   559  		}
   560  	}
   561  
   562  	return ret, diags
   563  }
   564  
   565  func (d *evaluationStateData) GetPathAttr(addr addrs.PathAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   566  	var diags tfdiags.Diagnostics
   567  	switch addr.Name {
   568  
   569  	case "cwd":
   570  		var err error
   571  		var wd string
   572  		if d.Evaluator.Meta != nil {
   573  			// Meta is always non-nil in the normal case, but some test cases
   574  			// are not so realistic.
   575  			wd = d.Evaluator.Meta.OriginalWorkingDir
   576  		}
   577  		if wd == "" {
   578  			wd, err = os.Getwd()
   579  			if err != nil {
   580  				diags = diags.Append(&hcl.Diagnostic{
   581  					Severity: hcl.DiagError,
   582  					Summary:  `Failed to get working directory`,
   583  					Detail:   fmt.Sprintf(`The value for path.cwd cannot be determined due to a system error: %s`, err),
   584  					Subject:  rng.ToHCL().Ptr(),
   585  				})
   586  				return cty.DynamicVal, diags
   587  			}
   588  		}
   589  		// The current working directory should always be absolute, whether we
   590  		// just looked it up or whether we were relying on ContextMeta's
   591  		// (possibly non-normalized) path.
   592  		wd, err = filepath.Abs(wd)
   593  		if err != nil {
   594  			diags = diags.Append(&hcl.Diagnostic{
   595  				Severity: hcl.DiagError,
   596  				Summary:  `Failed to get working directory`,
   597  				Detail:   fmt.Sprintf(`The value for path.cwd cannot be determined due to a system error: %s`, err),
   598  				Subject:  rng.ToHCL().Ptr(),
   599  			})
   600  			return cty.DynamicVal, diags
   601  		}
   602  
   603  		return cty.StringVal(filepath.ToSlash(wd)), diags
   604  
   605  	case "module":
   606  		moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath)
   607  		if moduleConfig == nil {
   608  			// should never happen, since we can't be evaluating in a module
   609  			// that wasn't mentioned in configuration.
   610  			panic(fmt.Sprintf("module.path read from module %s, which has no configuration", d.ModulePath))
   611  		}
   612  		sourceDir := moduleConfig.Module.SourceDir
   613  		return cty.StringVal(filepath.ToSlash(sourceDir)), diags
   614  
   615  	case "root":
   616  		sourceDir := d.Evaluator.Config.Module.SourceDir
   617  		return cty.StringVal(filepath.ToSlash(sourceDir)), diags
   618  
   619  	default:
   620  		suggestion := nameSuggestion(addr.Name, []string{"cwd", "module", "root"})
   621  		if suggestion != "" {
   622  			suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
   623  		}
   624  		diags = diags.Append(&hcl.Diagnostic{
   625  			Severity: hcl.DiagError,
   626  			Summary:  `Invalid "path" attribute`,
   627  			Detail:   fmt.Sprintf(`The "path" object does not have an attribute named %q.%s`, addr.Name, suggestion),
   628  			Subject:  rng.ToHCL().Ptr(),
   629  		})
   630  		return cty.DynamicVal, diags
   631  	}
   632  }
   633  
   634  func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   635  	var diags tfdiags.Diagnostics
   636  	// First we'll consult the configuration to see if an resource of this
   637  	// name is declared at all.
   638  	moduleAddr := d.ModulePath
   639  	moduleConfig := d.Evaluator.Config.DescendentForInstance(moduleAddr)
   640  	if moduleConfig == nil {
   641  		// should never happen, since we can't be evaluating in a module
   642  		// that wasn't mentioned in configuration.
   643  		panic(fmt.Sprintf("resource value read from %s, which has no configuration", moduleAddr))
   644  	}
   645  
   646  	config := moduleConfig.Module.ResourceByAddr(addr)
   647  	if config == nil {
   648  		diags = diags.Append(&hcl.Diagnostic{
   649  			Severity: hcl.DiagError,
   650  			Summary:  `Reference to undeclared resource`,
   651  			Detail:   fmt.Sprintf(`A resource %q %q has not been declared in %s`, addr.Type, addr.Name, moduleDisplayAddr(moduleAddr)),
   652  			Subject:  rng.ToHCL().Ptr(),
   653  		})
   654  		return cty.DynamicVal, diags
   655  	}
   656  
   657  	// Build the provider address from configuration, since we may not have
   658  	// state available in all cases.
   659  	// We need to build an abs provider address, but we can use a default
   660  	// instance since we're only interested in the schema.
   661  	schema := d.getResourceSchema(addr, config.Provider)
   662  	if schema == nil {
   663  		// This shouldn't happen, since validation before we get here should've
   664  		// taken care of it, but we'll show a reasonable error message anyway.
   665  		diags = diags.Append(&hcl.Diagnostic{
   666  			Severity: hcl.DiagError,
   667  			Summary:  `Missing resource type schema`,
   668  			Detail:   fmt.Sprintf("No schema is available for %s in %s. This is a bug in Terraform and should be reported.", addr, config.Provider),
   669  			Subject:  rng.ToHCL().Ptr(),
   670  		})
   671  		return cty.DynamicVal, diags
   672  	}
   673  	ty := schema.ImpliedType()
   674  
   675  	rs := d.Evaluator.State.Resource(addr.Absolute(d.ModulePath))
   676  
   677  	if rs == nil {
   678  		switch d.Operation {
   679  		case walkPlan, walkApply:
   680  			// During plan and apply as we evaluate each removed instance they
   681  			// are removed from the working state. Since we know there are no
   682  			// instances, return an empty container of the expected type.
   683  			switch {
   684  			case config.Count != nil:
   685  				return cty.EmptyTupleVal, diags
   686  			case config.ForEach != nil:
   687  				return cty.EmptyObjectVal, diags
   688  			default:
   689  				// While we can reference an expanded resource with 0
   690  				// instances, we cannot reference instances that do not exist.
   691  				// Due to the fact that we may have direct references to
   692  				// instances that may end up in a root output during destroy
   693  				// (since a planned destroy cannot yet remove root outputs), we
   694  				// need to return a dynamic value here to allow evaluation to
   695  				// continue.
   696  				log.Printf("[ERROR] unknown instance %q referenced during %s", addr.Absolute(d.ModulePath), d.Operation)
   697  				return cty.DynamicVal, diags
   698  			}
   699  		default:
   700  			if d.Operation != walkValidate {
   701  				log.Printf("[ERROR] missing state for %q while in %s\n", addr.Absolute(d.ModulePath), d.Operation)
   702  			}
   703  
   704  			// Validation is done with only the configuration, so generate
   705  			// unknown values of the correct shape for evaluation.
   706  			switch {
   707  			case config.Count != nil:
   708  				return cty.UnknownVal(cty.List(ty)), diags
   709  			case config.ForEach != nil:
   710  				return cty.UnknownVal(cty.Map(ty)), diags
   711  			default:
   712  				return cty.UnknownVal(ty), diags
   713  			}
   714  		}
   715  	}
   716  
   717  	// Decode all instances in the current state
   718  	instances := map[addrs.InstanceKey]cty.Value{}
   719  	pendingDestroy := d.Evaluator.Changes.IsFullDestroy()
   720  	for key, is := range rs.Instances {
   721  		if is == nil || is.Current == nil {
   722  			// Assume we're dealing with an instance that hasn't been created yet.
   723  			instances[key] = cty.UnknownVal(ty)
   724  			continue
   725  		}
   726  
   727  		instAddr := addr.Instance(key).Absolute(d.ModulePath)
   728  
   729  		change := d.Evaluator.Changes.GetResourceInstanceChange(instAddr, states.CurrentGen)
   730  		if change != nil {
   731  			// Don't take any resources that are yet to be deleted into account.
   732  			// If the referenced resource is CreateBeforeDestroy, then orphaned
   733  			// instances will be in the state, as they are not destroyed until
   734  			// after their dependants are updated.
   735  			if change.Action == plans.Delete {
   736  				if !pendingDestroy {
   737  					continue
   738  				}
   739  			}
   740  		}
   741  
   742  		// Planned resources are temporarily stored in state with empty values,
   743  		// and need to be replaced by the planned value here.
   744  		if is.Current.Status == states.ObjectPlanned {
   745  			if change == nil {
   746  				// If the object is in planned status then we should not get
   747  				// here, since we should have found a pending value in the plan
   748  				// above instead.
   749  				diags = diags.Append(&hcl.Diagnostic{
   750  					Severity: hcl.DiagError,
   751  					Summary:  "Missing pending object in plan",
   752  					Detail:   fmt.Sprintf("Instance %s is marked as having a change pending but that change is not recorded in the plan. This is a bug in Terraform; please report it.", instAddr),
   753  					Subject:  &config.DeclRange,
   754  				})
   755  				continue
   756  			}
   757  			val, err := change.After.Decode(ty)
   758  			if err != nil {
   759  				diags = diags.Append(&hcl.Diagnostic{
   760  					Severity: hcl.DiagError,
   761  					Summary:  "Invalid resource instance data in plan",
   762  					Detail:   fmt.Sprintf("Instance %s data could not be decoded from the plan: %s.", instAddr, err),
   763  					Subject:  &config.DeclRange,
   764  				})
   765  				continue
   766  			}
   767  
   768  			// If our provider schema contains sensitive values, mark those as sensitive
   769  			afterMarks := change.AfterValMarks
   770  			if schema.ContainsSensitive() {
   771  				afterMarks = append(afterMarks, schema.ValueMarks(val, nil)...)
   772  			}
   773  
   774  			instances[key] = val.MarkWithPaths(afterMarks)
   775  			continue
   776  		}
   777  
   778  		ios, err := is.Current.Decode(ty)
   779  		if err != nil {
   780  			// This shouldn't happen, since by the time we get here we
   781  			// should have upgraded the state data already.
   782  			diags = diags.Append(&hcl.Diagnostic{
   783  				Severity: hcl.DiagError,
   784  				Summary:  "Invalid resource instance data in state",
   785  				Detail:   fmt.Sprintf("Instance %s data could not be decoded from the state: %s.", instAddr, err),
   786  				Subject:  &config.DeclRange,
   787  			})
   788  			continue
   789  		}
   790  
   791  		val := ios.Value
   792  
   793  		// If our schema contains sensitive values, mark those as sensitive.
   794  		// Since decoding the instance object can also apply sensitivity marks,
   795  		// we must remove and combine those before remarking to avoid a double-
   796  		// mark error.
   797  		if schema.ContainsSensitive() {
   798  			var marks []cty.PathValueMarks
   799  			val, marks = val.UnmarkDeepWithPaths()
   800  			marks = append(marks, schema.ValueMarks(val, nil)...)
   801  			val = val.MarkWithPaths(marks)
   802  		}
   803  		instances[key] = val
   804  	}
   805  
   806  	var ret cty.Value
   807  
   808  	switch {
   809  	case config.Count != nil:
   810  		// figure out what the last index we have is
   811  		length := -1
   812  		for key := range instances {
   813  			intKey, ok := key.(addrs.IntKey)
   814  			if !ok {
   815  				continue
   816  			}
   817  			if int(intKey) >= length {
   818  				length = int(intKey) + 1
   819  			}
   820  		}
   821  
   822  		if length > 0 {
   823  			vals := make([]cty.Value, length)
   824  			for key, instance := range instances {
   825  				intKey, ok := key.(addrs.IntKey)
   826  				if !ok {
   827  					// old key from state, which isn't valid for evaluation
   828  					continue
   829  				}
   830  
   831  				vals[int(intKey)] = instance
   832  			}
   833  
   834  			// Insert unknown values where there are any missing instances
   835  			for i, v := range vals {
   836  				if v == cty.NilVal {
   837  					vals[i] = cty.UnknownVal(ty)
   838  				}
   839  			}
   840  			ret = cty.TupleVal(vals)
   841  		} else {
   842  			ret = cty.EmptyTupleVal
   843  		}
   844  
   845  	case config.ForEach != nil:
   846  		vals := make(map[string]cty.Value)
   847  		for key, instance := range instances {
   848  			strKey, ok := key.(addrs.StringKey)
   849  			if !ok {
   850  				// old key that is being dropped and not used for evaluation
   851  				continue
   852  			}
   853  			vals[string(strKey)] = instance
   854  		}
   855  
   856  		if len(vals) > 0 {
   857  			// We use an object rather than a map here because resource schemas
   858  			// may include dynamically-typed attributes, which will then cause
   859  			// each instance to potentially have a different runtime type even
   860  			// though they all conform to the static schema.
   861  			ret = cty.ObjectVal(vals)
   862  		} else {
   863  			ret = cty.EmptyObjectVal
   864  		}
   865  
   866  	default:
   867  		val, ok := instances[addrs.NoKey]
   868  		if !ok {
   869  			// if the instance is missing, insert an unknown value
   870  			val = cty.UnknownVal(ty)
   871  		}
   872  
   873  		ret = val
   874  	}
   875  
   876  	return ret, diags
   877  }
   878  
   879  func (d *evaluationStateData) getResourceSchema(addr addrs.Resource, providerAddr addrs.Provider) *configschema.Block {
   880  	schema, _, err := d.Evaluator.Plugins.ResourceTypeSchema(providerAddr, addr.Mode, addr.Type)
   881  	if err != nil {
   882  		// We have plently other codepaths that will detect and report
   883  		// schema lookup errors before we'd reach this point, so we'll just
   884  		// treat a failure here the same as having no schema.
   885  		return nil
   886  	}
   887  	return schema
   888  }
   889  
   890  func (d *evaluationStateData) GetTerraformAttr(addr addrs.TerraformAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   891  	var diags tfdiags.Diagnostics
   892  	switch addr.Name {
   893  
   894  	case "workspace":
   895  		workspaceName := d.Evaluator.Meta.Env
   896  		return cty.StringVal(workspaceName), diags
   897  
   898  	case "env":
   899  		// Prior to Terraform 0.12 there was an attribute "env", which was
   900  		// an alias name for "workspace". This was deprecated and is now
   901  		// removed.
   902  		diags = diags.Append(&hcl.Diagnostic{
   903  			Severity: hcl.DiagError,
   904  			Summary:  `Invalid "terraform" attribute`,
   905  			Detail:   `The terraform.env attribute was deprecated in v0.10 and removed in v0.12. The "state environment" concept was renamed to "workspace" in v0.12, and so the workspace name can now be accessed using the terraform.workspace attribute.`,
   906  			Subject:  rng.ToHCL().Ptr(),
   907  		})
   908  		return cty.DynamicVal, diags
   909  
   910  	default:
   911  		diags = diags.Append(&hcl.Diagnostic{
   912  			Severity: hcl.DiagError,
   913  			Summary:  `Invalid "terraform" attribute`,
   914  			Detail:   fmt.Sprintf(`The "terraform" object does not have an attribute named %q. The only supported attribute is terraform.workspace, the name of the currently-selected workspace.`, addr.Name),
   915  			Subject:  rng.ToHCL().Ptr(),
   916  		})
   917  		return cty.DynamicVal, diags
   918  	}
   919  }
   920  
   921  // nameSuggestion tries to find a name from the given slice of suggested names
   922  // that is close to the given name and returns it if found. If no suggestion
   923  // is close enough, returns the empty string.
   924  //
   925  // The suggestions are tried in order, so earlier suggestions take precedence
   926  // if the given string is similar to two or more suggestions.
   927  //
   928  // This function is intended to be used with a relatively-small number of
   929  // suggestions. It's not optimized for hundreds or thousands of them.
   930  func nameSuggestion(given string, suggestions []string) string {
   931  	for _, suggestion := range suggestions {
   932  		dist := levenshtein.Distance(given, suggestion, nil)
   933  		if dist < 3 { // threshold determined experimentally
   934  			return suggestion
   935  		}
   936  	}
   937  	return ""
   938  }
   939  
   940  // moduleDisplayAddr returns a string describing the given module instance
   941  // address that is appropriate for returning to users in situations where the
   942  // root module is possible. Specifically, it returns "the root module" if the
   943  // root module instance is given, or a string representation of the module
   944  // address otherwise.
   945  func moduleDisplayAddr(addr addrs.ModuleInstance) string {
   946  	switch {
   947  	case addr.IsRoot():
   948  		return "the root module"
   949  	default:
   950  		return addr.String()
   951  	}
   952  }