github.com/hashicorp/terraform-plugin-sdk@v1.17.2/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-plugin-sdk/internal/plans"
    10  	"github.com/hashicorp/terraform-plugin-sdk/internal/providers"
    11  	"github.com/hashicorp/terraform-plugin-sdk/internal/provisioners"
    12  	"github.com/hashicorp/terraform-plugin-sdk/internal/version"
    13  
    14  	"github.com/hashicorp/terraform-plugin-sdk/internal/states"
    15  
    16  	"github.com/hashicorp/hcl/v2"
    17  	"github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema"
    18  	"github.com/hashicorp/terraform-plugin-sdk/internal/lang"
    19  	"github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags"
    20  
    21  	"github.com/hashicorp/terraform-plugin-sdk/internal/addrs"
    22  	"github.com/zclconf/go-cty/cty"
    23  )
    24  
    25  // BuiltinEvalContext is an EvalContext implementation that is used by
    26  // Terraform by default.
    27  type BuiltinEvalContext struct {
    28  	// StopContext is the context used to track whether we're complete
    29  	StopContext context.Context
    30  
    31  	// PathValue is the Path that this context is operating within.
    32  	PathValue addrs.ModuleInstance
    33  
    34  	// Evaluator is used for evaluating expressions within the scope of this
    35  	// eval context.
    36  	Evaluator *Evaluator
    37  
    38  	// Schemas is a repository of all of the schemas we should need to
    39  	// decode configuration blocks and expressions. This must be constructed by
    40  	// the caller to include schemas for all of the providers, resource types,
    41  	// data sources and provisioners used by the given configuration and
    42  	// state.
    43  	//
    44  	// This must not be mutated during evaluation.
    45  	Schemas *Schemas
    46  
    47  	// VariableValues contains the variable values across all modules. This
    48  	// structure is shared across the entire containing context, and so it
    49  	// may be accessed only when holding VariableValuesLock.
    50  	// The keys of the first level of VariableValues are the string
    51  	// representations of addrs.ModuleInstance values. The second-level keys
    52  	// are variable names within each module instance.
    53  	VariableValues     map[string]map[string]cty.Value
    54  	VariableValuesLock *sync.Mutex
    55  
    56  	Components          contextComponentFactory
    57  	Hooks               []Hook
    58  	InputValue          UIInput
    59  	ProviderCache       map[string]providers.Interface
    60  	ProviderInputConfig map[string]map[string]cty.Value
    61  	ProviderLock        *sync.Mutex
    62  	ProvisionerCache    map[string]provisioners.Interface
    63  	ProvisionerLock     *sync.Mutex
    64  	ChangesValue        *plans.ChangesSync
    65  	StateValue          *states.SyncState
    66  
    67  	once sync.Once
    68  }
    69  
    70  // BuiltinEvalContext implements EvalContext
    71  var _ EvalContext = (*BuiltinEvalContext)(nil)
    72  
    73  func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} {
    74  	// This can happen during tests. During tests, we just block forever.
    75  	if ctx.StopContext == nil {
    76  		return nil
    77  	}
    78  
    79  	return ctx.StopContext.Done()
    80  }
    81  
    82  func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
    83  	for _, h := range ctx.Hooks {
    84  		action, err := fn(h)
    85  		if err != nil {
    86  			return err
    87  		}
    88  
    89  		switch action {
    90  		case HookActionContinue:
    91  			continue
    92  		case HookActionHalt:
    93  			// Return an early exit error to trigger an early exit
    94  			log.Printf("[WARN] Early exit triggered by hook: %T", h)
    95  			return EvalEarlyExitError{}
    96  		}
    97  	}
    98  
    99  	return nil
   100  }
   101  
   102  func (ctx *BuiltinEvalContext) Input() UIInput {
   103  	return ctx.InputValue
   104  }
   105  
   106  func (ctx *BuiltinEvalContext) InitProvider(typeName string, addr addrs.ProviderConfig) (providers.Interface, error) {
   107  	ctx.once.Do(ctx.init)
   108  	absAddr := addr.Absolute(ctx.Path())
   109  
   110  	// If we already initialized, it is an error
   111  	if p := ctx.Provider(absAddr); p != nil {
   112  		return nil, fmt.Errorf("%s is already initialized", addr)
   113  	}
   114  
   115  	// Warning: make sure to acquire these locks AFTER the call to Provider
   116  	// above, since it also acquires locks.
   117  	ctx.ProviderLock.Lock()
   118  	defer ctx.ProviderLock.Unlock()
   119  
   120  	key := absAddr.String()
   121  
   122  	p, err := ctx.Components.ResourceProvider(typeName, key)
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  
   127  	log.Printf("[TRACE] BuiltinEvalContext: Initialized %q provider for %s", typeName, absAddr)
   128  	ctx.ProviderCache[key] = p
   129  
   130  	return p, nil
   131  }
   132  
   133  func (ctx *BuiltinEvalContext) Provider(addr addrs.AbsProviderConfig) providers.Interface {
   134  	ctx.once.Do(ctx.init)
   135  
   136  	ctx.ProviderLock.Lock()
   137  	defer ctx.ProviderLock.Unlock()
   138  
   139  	return ctx.ProviderCache[addr.String()]
   140  }
   141  
   142  func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) *ProviderSchema {
   143  	ctx.once.Do(ctx.init)
   144  
   145  	return ctx.Schemas.ProviderSchema(addr.ProviderConfig.Type)
   146  }
   147  
   148  func (ctx *BuiltinEvalContext) CloseProvider(addr addrs.ProviderConfig) error {
   149  	ctx.once.Do(ctx.init)
   150  
   151  	ctx.ProviderLock.Lock()
   152  	defer ctx.ProviderLock.Unlock()
   153  
   154  	key := addr.Absolute(ctx.Path()).String()
   155  	provider := ctx.ProviderCache[key]
   156  	if provider != nil {
   157  		delete(ctx.ProviderCache, key)
   158  		return provider.Close()
   159  	}
   160  
   161  	return nil
   162  }
   163  
   164  func (ctx *BuiltinEvalContext) ConfigureProvider(addr addrs.ProviderConfig, cfg cty.Value) tfdiags.Diagnostics {
   165  	var diags tfdiags.Diagnostics
   166  	absAddr := addr.Absolute(ctx.Path())
   167  	p := ctx.Provider(absAddr)
   168  	if p == nil {
   169  		diags = diags.Append(fmt.Errorf("%s not initialized", addr))
   170  		return diags
   171  	}
   172  
   173  	providerSchema := ctx.ProviderSchema(absAddr)
   174  	if providerSchema == nil {
   175  		diags = diags.Append(fmt.Errorf("schema for %s is not available", absAddr))
   176  		return diags
   177  	}
   178  
   179  	req := providers.ConfigureRequest{
   180  		TerraformVersion: version.String(),
   181  		Config:           cfg,
   182  	}
   183  
   184  	resp := p.Configure(req)
   185  	return resp.Diagnostics
   186  }
   187  
   188  func (ctx *BuiltinEvalContext) ProviderInput(pc addrs.ProviderConfig) map[string]cty.Value {
   189  	ctx.ProviderLock.Lock()
   190  	defer ctx.ProviderLock.Unlock()
   191  
   192  	if !ctx.Path().IsRoot() {
   193  		// Only root module provider configurations can have input.
   194  		return nil
   195  	}
   196  
   197  	return ctx.ProviderInputConfig[pc.String()]
   198  }
   199  
   200  func (ctx *BuiltinEvalContext) SetProviderInput(pc addrs.ProviderConfig, c map[string]cty.Value) {
   201  	absProvider := pc.Absolute(ctx.Path())
   202  
   203  	if !ctx.Path().IsRoot() {
   204  		// Only root module provider configurations can have input.
   205  		log.Printf("[WARN] BuiltinEvalContext: attempt to SetProviderInput for non-root module")
   206  		return
   207  	}
   208  
   209  	// Save the configuration
   210  	ctx.ProviderLock.Lock()
   211  	ctx.ProviderInputConfig[absProvider.String()] = c
   212  	ctx.ProviderLock.Unlock()
   213  }
   214  
   215  func (ctx *BuiltinEvalContext) InitProvisioner(n string) (provisioners.Interface, error) {
   216  	ctx.once.Do(ctx.init)
   217  
   218  	// If we already initialized, it is an error
   219  	if p := ctx.Provisioner(n); p != nil {
   220  		return nil, fmt.Errorf("Provisioner '%s' already initialized", n)
   221  	}
   222  
   223  	// Warning: make sure to acquire these locks AFTER the call to Provisioner
   224  	// above, since it also acquires locks.
   225  	ctx.ProvisionerLock.Lock()
   226  	defer ctx.ProvisionerLock.Unlock()
   227  
   228  	key := PathObjectCacheKey(ctx.Path(), n)
   229  
   230  	p, err := ctx.Components.ResourceProvisioner(n, key)
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  
   235  	ctx.ProvisionerCache[key] = p
   236  
   237  	return p, nil
   238  }
   239  
   240  func (ctx *BuiltinEvalContext) Provisioner(n string) provisioners.Interface {
   241  	ctx.once.Do(ctx.init)
   242  
   243  	ctx.ProvisionerLock.Lock()
   244  	defer ctx.ProvisionerLock.Unlock()
   245  
   246  	key := PathObjectCacheKey(ctx.Path(), n)
   247  	return ctx.ProvisionerCache[key]
   248  }
   249  
   250  func (ctx *BuiltinEvalContext) ProvisionerSchema(n string) *configschema.Block {
   251  	ctx.once.Do(ctx.init)
   252  
   253  	return ctx.Schemas.ProvisionerConfig(n)
   254  }
   255  
   256  func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error {
   257  	ctx.once.Do(ctx.init)
   258  
   259  	ctx.ProvisionerLock.Lock()
   260  	defer ctx.ProvisionerLock.Unlock()
   261  
   262  	key := PathObjectCacheKey(ctx.Path(), n)
   263  
   264  	prov := ctx.ProvisionerCache[key]
   265  	if prov != nil {
   266  		return prov.Close()
   267  	}
   268  
   269  	return nil
   270  }
   271  
   272  func (ctx *BuiltinEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) {
   273  	var diags tfdiags.Diagnostics
   274  	scope := ctx.EvaluationScope(self, keyData)
   275  	body, evalDiags := scope.ExpandBlock(body, schema)
   276  	diags = diags.Append(evalDiags)
   277  	val, evalDiags := scope.EvalBlock(body, schema)
   278  	diags = diags.Append(evalDiags)
   279  	return val, body, diags
   280  }
   281  
   282  func (ctx *BuiltinEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) {
   283  	scope := ctx.EvaluationScope(self, EvalDataForNoInstanceKey)
   284  	return scope.EvalExpr(expr, wantType)
   285  }
   286  
   287  func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, keyData InstanceKeyEvalData) *lang.Scope {
   288  	data := &evaluationStateData{
   289  		Evaluator:       ctx.Evaluator,
   290  		ModulePath:      ctx.PathValue,
   291  		InstanceKeyData: keyData,
   292  		Operation:       ctx.Evaluator.Operation,
   293  	}
   294  	return ctx.Evaluator.Scope(data, self)
   295  }
   296  
   297  func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance {
   298  	return ctx.PathValue
   299  }
   300  
   301  func (ctx *BuiltinEvalContext) SetModuleCallArguments(n addrs.ModuleCallInstance, vals map[string]cty.Value) {
   302  	ctx.VariableValuesLock.Lock()
   303  	defer ctx.VariableValuesLock.Unlock()
   304  
   305  	childPath := n.ModuleInstance(ctx.PathValue)
   306  	key := childPath.String()
   307  
   308  	args := ctx.VariableValues[key]
   309  	if args == nil {
   310  		args = make(map[string]cty.Value)
   311  		ctx.VariableValues[key] = vals
   312  		return
   313  	}
   314  
   315  	for k, v := range vals {
   316  		args[k] = v
   317  	}
   318  }
   319  
   320  func (ctx *BuiltinEvalContext) Changes() *plans.ChangesSync {
   321  	return ctx.ChangesValue
   322  }
   323  
   324  func (ctx *BuiltinEvalContext) State() *states.SyncState {
   325  	return ctx.StateValue
   326  }
   327  
   328  func (ctx *BuiltinEvalContext) init() {
   329  }