github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/terraform/eval_context_builtin.go (about)

     1  package terraform
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"sync"
     8  
     9  	"github.com/hashicorp/terraform/internal/checks"
    10  	"github.com/hashicorp/terraform/internal/instances"
    11  	"github.com/hashicorp/terraform/internal/plans"
    12  	"github.com/hashicorp/terraform/internal/providers"
    13  	"github.com/hashicorp/terraform/internal/provisioners"
    14  	"github.com/hashicorp/terraform/internal/refactoring"
    15  	"github.com/hashicorp/terraform/version"
    16  
    17  	"github.com/hashicorp/terraform/internal/states"
    18  
    19  	"github.com/hashicorp/hcl/v2"
    20  	"github.com/hashicorp/terraform/internal/configs/configschema"
    21  	"github.com/hashicorp/terraform/internal/lang"
    22  	"github.com/hashicorp/terraform/internal/tfdiags"
    23  
    24  	"github.com/hashicorp/terraform/internal/addrs"
    25  	"github.com/zclconf/go-cty/cty"
    26  )
    27  
    28  // BuiltinEvalContext is an EvalContext implementation that is used by
    29  // Terraform by default.
    30  type BuiltinEvalContext struct {
    31  	// StopContext is the context used to track whether we're complete
    32  	StopContext context.Context
    33  
    34  	// PathValue is the Path that this context is operating within.
    35  	PathValue addrs.ModuleInstance
    36  
    37  	// pathSet indicates that this context was explicitly created for a
    38  	// specific path, and can be safely used for evaluation. This lets us
    39  	// differentiate between PathValue being unset, and the zero value which is
    40  	// equivalent to RootModuleInstance.  Path and Evaluation methods will
    41  	// panic if this is not set.
    42  	pathSet bool
    43  
    44  	// Evaluator is used for evaluating expressions within the scope of this
    45  	// eval context.
    46  	Evaluator *Evaluator
    47  
    48  	// VariableValues contains the variable values across all modules. This
    49  	// structure is shared across the entire containing context, and so it
    50  	// may be accessed only when holding VariableValuesLock.
    51  	// The keys of the first level of VariableValues are the string
    52  	// representations of addrs.ModuleInstance values. The second-level keys
    53  	// are variable names within each module instance.
    54  	VariableValues     map[string]map[string]cty.Value
    55  	VariableValuesLock *sync.Mutex
    56  
    57  	// Plugins is a library of plugin components (providers and provisioners)
    58  	// available for use during a graph walk.
    59  	Plugins *contextPlugins
    60  
    61  	Hooks                 []Hook
    62  	InputValue            UIInput
    63  	ProviderCache         map[string]providers.Interface
    64  	ProviderInputConfig   map[string]map[string]cty.Value
    65  	ProviderLock          *sync.Mutex
    66  	ProvisionerCache      map[string]provisioners.Interface
    67  	ProvisionerLock       *sync.Mutex
    68  	ChangesValue          *plans.ChangesSync
    69  	StateValue            *states.SyncState
    70  	ChecksValue           *checks.State
    71  	RefreshStateValue     *states.SyncState
    72  	PrevRunStateValue     *states.SyncState
    73  	InstanceExpanderValue *instances.Expander
    74  	MoveResultsValue      refactoring.MoveResults
    75  }
    76  
    77  // BuiltinEvalContext implements EvalContext
    78  var _ EvalContext = (*BuiltinEvalContext)(nil)
    79  
    80  func (ctx *BuiltinEvalContext) WithPath(path addrs.ModuleInstance) EvalContext {
    81  	newCtx := *ctx
    82  	newCtx.pathSet = true
    83  	newCtx.PathValue = path
    84  	return &newCtx
    85  }
    86  
    87  func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} {
    88  	// This can happen during tests. During tests, we just block forever.
    89  	if ctx.StopContext == nil {
    90  		return nil
    91  	}
    92  
    93  	return ctx.StopContext.Done()
    94  }
    95  
    96  func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
    97  	for _, h := range ctx.Hooks {
    98  		action, err := fn(h)
    99  		if err != nil {
   100  			return err
   101  		}
   102  
   103  		switch action {
   104  		case HookActionContinue:
   105  			continue
   106  		case HookActionHalt:
   107  			// Return an early exit error to trigger an early exit
   108  			log.Printf("[WARN] Early exit triggered by hook: %T", h)
   109  			return nil
   110  		}
   111  	}
   112  
   113  	return nil
   114  }
   115  
   116  func (ctx *BuiltinEvalContext) Input() UIInput {
   117  	return ctx.InputValue
   118  }
   119  
   120  func (ctx *BuiltinEvalContext) InitProvider(addr addrs.AbsProviderConfig) (providers.Interface, error) {
   121  	// If we already initialized, it is an error
   122  	if p := ctx.Provider(addr); p != nil {
   123  		return nil, fmt.Errorf("%s is already initialized", addr)
   124  	}
   125  
   126  	// Warning: make sure to acquire these locks AFTER the call to Provider
   127  	// above, since it also acquires locks.
   128  	ctx.ProviderLock.Lock()
   129  	defer ctx.ProviderLock.Unlock()
   130  
   131  	key := addr.String()
   132  
   133  	p, err := ctx.Plugins.NewProviderInstance(addr.Provider)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	log.Printf("[TRACE] BuiltinEvalContext: Initialized %q provider for %s", addr.String(), addr)
   139  	ctx.ProviderCache[key] = p
   140  
   141  	return p, nil
   142  }
   143  
   144  func (ctx *BuiltinEvalContext) Provider(addr addrs.AbsProviderConfig) providers.Interface {
   145  	ctx.ProviderLock.Lock()
   146  	defer ctx.ProviderLock.Unlock()
   147  
   148  	return ctx.ProviderCache[addr.String()]
   149  }
   150  
   151  func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) (*ProviderSchema, error) {
   152  	return ctx.Plugins.ProviderSchema(addr.Provider)
   153  }
   154  
   155  func (ctx *BuiltinEvalContext) CloseProvider(addr addrs.AbsProviderConfig) error {
   156  	ctx.ProviderLock.Lock()
   157  	defer ctx.ProviderLock.Unlock()
   158  
   159  	key := addr.String()
   160  	provider := ctx.ProviderCache[key]
   161  	if provider != nil {
   162  		delete(ctx.ProviderCache, key)
   163  		return provider.Close()
   164  	}
   165  
   166  	return nil
   167  }
   168  
   169  func (ctx *BuiltinEvalContext) ConfigureProvider(addr addrs.AbsProviderConfig, cfg cty.Value) tfdiags.Diagnostics {
   170  	var diags tfdiags.Diagnostics
   171  	if !addr.Module.Equal(ctx.Path().Module()) {
   172  		// This indicates incorrect use of ConfigureProvider: it should be used
   173  		// only from the module that the provider configuration belongs to.
   174  		panic(fmt.Sprintf("%s configured by wrong module %s", addr, ctx.Path()))
   175  	}
   176  
   177  	p := ctx.Provider(addr)
   178  	if p == nil {
   179  		diags = diags.Append(fmt.Errorf("%s not initialized", addr))
   180  		return diags
   181  	}
   182  
   183  	providerSchema, err := ctx.ProviderSchema(addr)
   184  	if err != nil {
   185  		diags = diags.Append(fmt.Errorf("failed to read schema for %s: %s", addr, err))
   186  		return diags
   187  	}
   188  	if providerSchema == nil {
   189  		diags = diags.Append(fmt.Errorf("schema for %s is not available", addr))
   190  		return diags
   191  	}
   192  
   193  	req := providers.ConfigureProviderRequest{
   194  		TerraformVersion: version.String(),
   195  		Config:           cfg,
   196  	}
   197  
   198  	resp := p.ConfigureProvider(req)
   199  	return resp.Diagnostics
   200  }
   201  
   202  func (ctx *BuiltinEvalContext) ProviderInput(pc addrs.AbsProviderConfig) map[string]cty.Value {
   203  	ctx.ProviderLock.Lock()
   204  	defer ctx.ProviderLock.Unlock()
   205  
   206  	if !pc.Module.Equal(ctx.Path().Module()) {
   207  		// This indicates incorrect use of InitProvider: it should be used
   208  		// only from the module that the provider configuration belongs to.
   209  		panic(fmt.Sprintf("%s initialized by wrong module %s", pc, ctx.Path()))
   210  	}
   211  
   212  	if !ctx.Path().IsRoot() {
   213  		// Only root module provider configurations can have input.
   214  		return nil
   215  	}
   216  
   217  	return ctx.ProviderInputConfig[pc.String()]
   218  }
   219  
   220  func (ctx *BuiltinEvalContext) SetProviderInput(pc addrs.AbsProviderConfig, c map[string]cty.Value) {
   221  	absProvider := pc
   222  	if !pc.Module.IsRoot() {
   223  		// Only root module provider configurations can have input.
   224  		log.Printf("[WARN] BuiltinEvalContext: attempt to SetProviderInput for non-root module")
   225  		return
   226  	}
   227  
   228  	// Save the configuration
   229  	ctx.ProviderLock.Lock()
   230  	ctx.ProviderInputConfig[absProvider.String()] = c
   231  	ctx.ProviderLock.Unlock()
   232  }
   233  
   234  func (ctx *BuiltinEvalContext) Provisioner(n string) (provisioners.Interface, error) {
   235  	ctx.ProvisionerLock.Lock()
   236  	defer ctx.ProvisionerLock.Unlock()
   237  
   238  	p, ok := ctx.ProvisionerCache[n]
   239  	if !ok {
   240  		var err error
   241  		p, err = ctx.Plugins.NewProvisionerInstance(n)
   242  		if err != nil {
   243  			return nil, err
   244  		}
   245  
   246  		ctx.ProvisionerCache[n] = p
   247  	}
   248  
   249  	return p, nil
   250  }
   251  
   252  func (ctx *BuiltinEvalContext) ProvisionerSchema(n string) (*configschema.Block, error) {
   253  	return ctx.Plugins.ProvisionerSchema(n)
   254  }
   255  
   256  func (ctx *BuiltinEvalContext) CloseProvisioners() error {
   257  	var diags tfdiags.Diagnostics
   258  	ctx.ProvisionerLock.Lock()
   259  	defer ctx.ProvisionerLock.Unlock()
   260  
   261  	for name, prov := range ctx.ProvisionerCache {
   262  		err := prov.Close()
   263  		if err != nil {
   264  			diags = diags.Append(fmt.Errorf("provisioner.Close %s: %s", name, err))
   265  		}
   266  	}
   267  
   268  	return diags.Err()
   269  }
   270  
   271  func (ctx *BuiltinEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) {
   272  	var diags tfdiags.Diagnostics
   273  	scope := ctx.EvaluationScope(self, keyData)
   274  	body, evalDiags := scope.ExpandBlock(body, schema)
   275  	diags = diags.Append(evalDiags)
   276  	val, evalDiags := scope.EvalBlock(body, schema)
   277  	diags = diags.Append(evalDiags)
   278  	return val, body, diags
   279  }
   280  
   281  func (ctx *BuiltinEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) {
   282  	scope := ctx.EvaluationScope(self, EvalDataForNoInstanceKey)
   283  	return scope.EvalExpr(expr, wantType)
   284  }
   285  
   286  func (ctx *BuiltinEvalContext) EvaluateReplaceTriggeredBy(expr hcl.Expression, repData instances.RepetitionData) (*addrs.Reference, bool, tfdiags.Diagnostics) {
   287  
   288  	// get the reference to lookup changes in the plan
   289  	ref, diags := evalReplaceTriggeredByExpr(expr, repData)
   290  	if diags.HasErrors() {
   291  		return nil, false, diags
   292  	}
   293  
   294  	var changes []*plans.ResourceInstanceChangeSrc
   295  	// store the address once we get it for validation
   296  	var resourceAddr addrs.Resource
   297  
   298  	// The reference is either a resource or resource instance
   299  	switch sub := ref.Subject.(type) {
   300  	case addrs.Resource:
   301  		resourceAddr = sub
   302  		rc := sub.Absolute(ctx.Path())
   303  		changes = ctx.Changes().GetChangesForAbsResource(rc)
   304  	case addrs.ResourceInstance:
   305  		resourceAddr = sub.ContainingResource()
   306  		rc := sub.Absolute(ctx.Path())
   307  		change := ctx.Changes().GetResourceInstanceChange(rc, states.CurrentGen)
   308  		if change != nil {
   309  			// we'll generate an error below if there was no change
   310  			changes = append(changes, change)
   311  		}
   312  	}
   313  
   314  	// Do some validation to make sure we are expecting a change at all
   315  	cfg := ctx.Evaluator.Config.Descendent(ctx.Path().Module())
   316  	resCfg := cfg.Module.ResourceByAddr(resourceAddr)
   317  	if resCfg == nil {
   318  		diags = diags.Append(&hcl.Diagnostic{
   319  			Severity: hcl.DiagError,
   320  			Summary:  `Reference to undeclared resource`,
   321  			Detail:   fmt.Sprintf(`A resource %s has not been declared in %s`, ref.Subject, moduleDisplayAddr(ctx.Path())),
   322  			Subject:  expr.Range().Ptr(),
   323  		})
   324  		return nil, false, diags
   325  	}
   326  
   327  	if len(changes) == 0 {
   328  		// If the resource is valid there should always be at least one change.
   329  		diags = diags.Append(fmt.Errorf("no change found for %s in %s", ref.Subject, moduleDisplayAddr(ctx.Path())))
   330  		return nil, false, diags
   331  	}
   332  
   333  	// If we don't have a traversal beyond the resource, then we can just look
   334  	// for any change.
   335  	if len(ref.Remaining) == 0 {
   336  		for _, c := range changes {
   337  			switch c.ChangeSrc.Action {
   338  			// Only immediate changes to the resource will trigger replacement.
   339  			case plans.Update, plans.DeleteThenCreate, plans.CreateThenDelete:
   340  				return ref, true, diags
   341  			}
   342  		}
   343  
   344  		// no change triggered
   345  		return nil, false, diags
   346  	}
   347  
   348  	// This must be an instances to have a remaining traversal, which means a
   349  	// single change.
   350  	change := changes[0]
   351  
   352  	// Make sure the change is actionable. A create or delete action will have
   353  	// a change in value, but are not valid for our purposes here.
   354  	switch change.ChangeSrc.Action {
   355  	case plans.Update, plans.DeleteThenCreate, plans.CreateThenDelete:
   356  		// OK
   357  	default:
   358  		return nil, false, diags
   359  	}
   360  
   361  	// Since we have a traversal after the resource reference, we will need to
   362  	// decode the changes, which means we need a schema.
   363  	providerAddr := change.ProviderAddr
   364  	schema, err := ctx.ProviderSchema(providerAddr)
   365  	if err != nil {
   366  		diags = diags.Append(err)
   367  		return nil, false, diags
   368  	}
   369  
   370  	resAddr := change.Addr.ContainingResource().Resource
   371  	resSchema, _ := schema.SchemaForResourceType(resAddr.Mode, resAddr.Type)
   372  	ty := resSchema.ImpliedType()
   373  
   374  	before, err := change.ChangeSrc.Before.Decode(ty)
   375  	if err != nil {
   376  		diags = diags.Append(err)
   377  		return nil, false, diags
   378  	}
   379  
   380  	after, err := change.ChangeSrc.After.Decode(ty)
   381  	if err != nil {
   382  		diags = diags.Append(err)
   383  		return nil, false, diags
   384  	}
   385  
   386  	path := traversalToPath(ref.Remaining)
   387  	attrBefore, _ := path.Apply(before)
   388  	attrAfter, _ := path.Apply(after)
   389  
   390  	if attrBefore == cty.NilVal || attrAfter == cty.NilVal {
   391  		replace := attrBefore != attrAfter
   392  		return ref, replace, diags
   393  	}
   394  
   395  	replace := !attrBefore.RawEquals(attrAfter)
   396  
   397  	return ref, replace, diags
   398  }
   399  
   400  func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, keyData instances.RepetitionData) *lang.Scope {
   401  	if !ctx.pathSet {
   402  		panic("context path not set")
   403  	}
   404  	data := &evaluationStateData{
   405  		Evaluator:       ctx.Evaluator,
   406  		ModulePath:      ctx.PathValue,
   407  		InstanceKeyData: keyData,
   408  		Operation:       ctx.Evaluator.Operation,
   409  	}
   410  	scope := ctx.Evaluator.Scope(data, self)
   411  
   412  	// ctx.PathValue is the path of the module that contains whatever
   413  	// expression the caller will be trying to evaluate, so this will
   414  	// activate only the experiments from that particular module, to
   415  	// be consistent with how experiment checking in the "configs"
   416  	// package itself works. The nil check here is for robustness in
   417  	// incompletely-mocked testing situations; mc should never be nil in
   418  	// real situations.
   419  	if mc := ctx.Evaluator.Config.DescendentForInstance(ctx.PathValue); mc != nil {
   420  		scope.SetActiveExperiments(mc.Module.ActiveExperiments)
   421  	}
   422  	return scope
   423  }
   424  
   425  func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance {
   426  	if !ctx.pathSet {
   427  		panic("context path not set")
   428  	}
   429  	return ctx.PathValue
   430  }
   431  
   432  func (ctx *BuiltinEvalContext) SetRootModuleArgument(addr addrs.InputVariable, v cty.Value) {
   433  	ctx.VariableValuesLock.Lock()
   434  	defer ctx.VariableValuesLock.Unlock()
   435  
   436  	log.Printf("[TRACE] BuiltinEvalContext: Storing final value for variable %s", addr.Absolute(addrs.RootModuleInstance))
   437  	key := addrs.RootModuleInstance.String()
   438  	args := ctx.VariableValues[key]
   439  	if args == nil {
   440  		args = make(map[string]cty.Value)
   441  		ctx.VariableValues[key] = args
   442  	}
   443  	args[addr.Name] = v
   444  }
   445  
   446  func (ctx *BuiltinEvalContext) SetModuleCallArgument(callAddr addrs.ModuleCallInstance, varAddr addrs.InputVariable, v cty.Value) {
   447  	ctx.VariableValuesLock.Lock()
   448  	defer ctx.VariableValuesLock.Unlock()
   449  
   450  	if !ctx.pathSet {
   451  		panic("context path not set")
   452  	}
   453  
   454  	childPath := callAddr.ModuleInstance(ctx.PathValue)
   455  	log.Printf("[TRACE] BuiltinEvalContext: Storing final value for variable %s", varAddr.Absolute(childPath))
   456  	key := childPath.String()
   457  	args := ctx.VariableValues[key]
   458  	if args == nil {
   459  		args = make(map[string]cty.Value)
   460  		ctx.VariableValues[key] = args
   461  	}
   462  	args[varAddr.Name] = v
   463  }
   464  
   465  func (ctx *BuiltinEvalContext) GetVariableValue(addr addrs.AbsInputVariableInstance) cty.Value {
   466  	ctx.VariableValuesLock.Lock()
   467  	defer ctx.VariableValuesLock.Unlock()
   468  
   469  	modKey := addr.Module.String()
   470  	modVars := ctx.VariableValues[modKey]
   471  	val, ok := modVars[addr.Variable.Name]
   472  	if !ok {
   473  		return cty.DynamicVal
   474  	}
   475  	return val
   476  }
   477  
   478  func (ctx *BuiltinEvalContext) Changes() *plans.ChangesSync {
   479  	return ctx.ChangesValue
   480  }
   481  
   482  func (ctx *BuiltinEvalContext) State() *states.SyncState {
   483  	return ctx.StateValue
   484  }
   485  
   486  func (ctx *BuiltinEvalContext) Checks() *checks.State {
   487  	return ctx.ChecksValue
   488  }
   489  
   490  func (ctx *BuiltinEvalContext) RefreshState() *states.SyncState {
   491  	return ctx.RefreshStateValue
   492  }
   493  
   494  func (ctx *BuiltinEvalContext) PrevRunState() *states.SyncState {
   495  	return ctx.PrevRunStateValue
   496  }
   497  
   498  func (ctx *BuiltinEvalContext) InstanceExpander() *instances.Expander {
   499  	return ctx.InstanceExpanderValue
   500  }
   501  
   502  func (ctx *BuiltinEvalContext) MoveResults() refactoring.MoveResults {
   503  	return ctx.MoveResultsValue
   504  }