github.com/cycloidio/terraform@v1.1.10-0.20220513142504-76d5c768dc63/terraform/eval_context_builtin.go (about)

     1  package terraform
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"sync"
     8  
     9  	"github.com/cycloidio/terraform/instances"
    10  	"github.com/cycloidio/terraform/plans"
    11  	"github.com/cycloidio/terraform/providers"
    12  	"github.com/cycloidio/terraform/provisioners"
    13  	"github.com/cycloidio/terraform/refactoring"
    14  	"github.com/cycloidio/terraform/version"
    15  
    16  	"github.com/cycloidio/terraform/states"
    17  
    18  	"github.com/hashicorp/hcl/v2"
    19  	"github.com/cycloidio/terraform/configs/configschema"
    20  	"github.com/cycloidio/terraform/lang"
    21  	"github.com/cycloidio/terraform/tfdiags"
    22  
    23  	"github.com/cycloidio/terraform/addrs"
    24  	"github.com/zclconf/go-cty/cty"
    25  )
    26  
    27  // BuiltinEvalContext is an EvalContext implementation that is used by
    28  // Terraform by default.
    29  type BuiltinEvalContext struct {
    30  	// StopContext is the context used to track whether we're complete
    31  	StopContext context.Context
    32  
    33  	// PathValue is the Path that this context is operating within.
    34  	PathValue addrs.ModuleInstance
    35  
    36  	// pathSet indicates that this context was explicitly created for a
    37  	// specific path, and can be safely used for evaluation. This lets us
    38  	// differentiate between PathValue being unset, and the zero value which is
    39  	// equivalent to RootModuleInstance.  Path and Evaluation methods will
    40  	// panic if this is not set.
    41  	pathSet bool
    42  
    43  	// Evaluator is used for evaluating expressions within the scope of this
    44  	// eval context.
    45  	Evaluator *Evaluator
    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  	// Plugins is a library of plugin components (providers and provisioners)
    57  	// available for use during a graph walk.
    58  	Plugins *contextPlugins
    59  
    60  	Hooks                 []Hook
    61  	InputValue            UIInput
    62  	ProviderCache         map[string]providers.Interface
    63  	ProviderInputConfig   map[string]map[string]cty.Value
    64  	ProviderLock          *sync.Mutex
    65  	ProvisionerCache      map[string]provisioners.Interface
    66  	ProvisionerLock       *sync.Mutex
    67  	ChangesValue          *plans.ChangesSync
    68  	StateValue            *states.SyncState
    69  	RefreshStateValue     *states.SyncState
    70  	PrevRunStateValue     *states.SyncState
    71  	InstanceExpanderValue *instances.Expander
    72  	MoveResultsValue      refactoring.MoveResults
    73  }
    74  
    75  // BuiltinEvalContext implements EvalContext
    76  var _ EvalContext = (*BuiltinEvalContext)(nil)
    77  
    78  func (ctx *BuiltinEvalContext) WithPath(path addrs.ModuleInstance) EvalContext {
    79  	newCtx := *ctx
    80  	newCtx.pathSet = true
    81  	newCtx.PathValue = path
    82  	return &newCtx
    83  }
    84  
    85  func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} {
    86  	// This can happen during tests. During tests, we just block forever.
    87  	if ctx.StopContext == nil {
    88  		return nil
    89  	}
    90  
    91  	return ctx.StopContext.Done()
    92  }
    93  
    94  func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
    95  	for _, h := range ctx.Hooks {
    96  		action, err := fn(h)
    97  		if err != nil {
    98  			return err
    99  		}
   100  
   101  		switch action {
   102  		case HookActionContinue:
   103  			continue
   104  		case HookActionHalt:
   105  			// Return an early exit error to trigger an early exit
   106  			log.Printf("[WARN] Early exit triggered by hook: %T", h)
   107  			return nil
   108  		}
   109  	}
   110  
   111  	return nil
   112  }
   113  
   114  func (ctx *BuiltinEvalContext) Input() UIInput {
   115  	return ctx.InputValue
   116  }
   117  
   118  func (ctx *BuiltinEvalContext) InitProvider(addr addrs.AbsProviderConfig) (providers.Interface, error) {
   119  	// If we already initialized, it is an error
   120  	if p := ctx.Provider(addr); p != nil {
   121  		return nil, fmt.Errorf("%s is already initialized", addr)
   122  	}
   123  
   124  	// Warning: make sure to acquire these locks AFTER the call to Provider
   125  	// above, since it also acquires locks.
   126  	ctx.ProviderLock.Lock()
   127  	defer ctx.ProviderLock.Unlock()
   128  
   129  	key := addr.String()
   130  
   131  	p, err := ctx.Plugins.NewProviderInstance(addr.Provider)
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	log.Printf("[TRACE] BuiltinEvalContext: Initialized %q provider for %s", addr.String(), addr)
   137  	ctx.ProviderCache[key] = p
   138  
   139  	return p, nil
   140  }
   141  
   142  func (ctx *BuiltinEvalContext) Provider(addr addrs.AbsProviderConfig) providers.Interface {
   143  	ctx.ProviderLock.Lock()
   144  	defer ctx.ProviderLock.Unlock()
   145  
   146  	return ctx.ProviderCache[addr.String()]
   147  }
   148  
   149  func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) (*ProviderSchema, error) {
   150  	return ctx.Plugins.ProviderSchema(addr.Provider)
   151  }
   152  
   153  func (ctx *BuiltinEvalContext) CloseProvider(addr addrs.AbsProviderConfig) error {
   154  	ctx.ProviderLock.Lock()
   155  	defer ctx.ProviderLock.Unlock()
   156  
   157  	key := addr.String()
   158  	provider := ctx.ProviderCache[key]
   159  	if provider != nil {
   160  		delete(ctx.ProviderCache, key)
   161  		return provider.Close()
   162  	}
   163  
   164  	return nil
   165  }
   166  
   167  func (ctx *BuiltinEvalContext) ConfigureProvider(addr addrs.AbsProviderConfig, cfg cty.Value) tfdiags.Diagnostics {
   168  	var diags tfdiags.Diagnostics
   169  	if !addr.Module.Equal(ctx.Path().Module()) {
   170  		// This indicates incorrect use of ConfigureProvider: it should be used
   171  		// only from the module that the provider configuration belongs to.
   172  		panic(fmt.Sprintf("%s configured by wrong module %s", addr, ctx.Path()))
   173  	}
   174  
   175  	p := ctx.Provider(addr)
   176  	if p == nil {
   177  		diags = diags.Append(fmt.Errorf("%s not initialized", addr))
   178  		return diags
   179  	}
   180  
   181  	providerSchema, err := ctx.ProviderSchema(addr)
   182  	if err != nil {
   183  		diags = diags.Append(fmt.Errorf("failed to read schema for %s: %s", addr, err))
   184  		return diags
   185  	}
   186  	if providerSchema == nil {
   187  		diags = diags.Append(fmt.Errorf("schema for %s is not available", addr))
   188  		return diags
   189  	}
   190  
   191  	req := providers.ConfigureProviderRequest{
   192  		TerraformVersion: version.String(),
   193  		Config:           cfg,
   194  	}
   195  
   196  	resp := p.ConfigureProvider(req)
   197  	return resp.Diagnostics
   198  }
   199  
   200  func (ctx *BuiltinEvalContext) ProviderInput(pc addrs.AbsProviderConfig) map[string]cty.Value {
   201  	ctx.ProviderLock.Lock()
   202  	defer ctx.ProviderLock.Unlock()
   203  
   204  	if !pc.Module.Equal(ctx.Path().Module()) {
   205  		// This indicates incorrect use of InitProvider: it should be used
   206  		// only from the module that the provider configuration belongs to.
   207  		panic(fmt.Sprintf("%s initialized by wrong module %s", pc, ctx.Path()))
   208  	}
   209  
   210  	if !ctx.Path().IsRoot() {
   211  		// Only root module provider configurations can have input.
   212  		return nil
   213  	}
   214  
   215  	return ctx.ProviderInputConfig[pc.String()]
   216  }
   217  
   218  func (ctx *BuiltinEvalContext) SetProviderInput(pc addrs.AbsProviderConfig, c map[string]cty.Value) {
   219  	absProvider := pc
   220  	if !pc.Module.IsRoot() {
   221  		// Only root module provider configurations can have input.
   222  		log.Printf("[WARN] BuiltinEvalContext: attempt to SetProviderInput for non-root module")
   223  		return
   224  	}
   225  
   226  	// Save the configuration
   227  	ctx.ProviderLock.Lock()
   228  	ctx.ProviderInputConfig[absProvider.String()] = c
   229  	ctx.ProviderLock.Unlock()
   230  }
   231  
   232  func (ctx *BuiltinEvalContext) Provisioner(n string) (provisioners.Interface, error) {
   233  	ctx.ProvisionerLock.Lock()
   234  	defer ctx.ProvisionerLock.Unlock()
   235  
   236  	p, ok := ctx.ProvisionerCache[n]
   237  	if !ok {
   238  		var err error
   239  		p, err = ctx.Plugins.NewProvisionerInstance(n)
   240  		if err != nil {
   241  			return nil, err
   242  		}
   243  
   244  		ctx.ProvisionerCache[n] = p
   245  	}
   246  
   247  	return p, nil
   248  }
   249  
   250  func (ctx *BuiltinEvalContext) ProvisionerSchema(n string) (*configschema.Block, error) {
   251  	return ctx.Plugins.ProvisionerSchema(n)
   252  }
   253  
   254  func (ctx *BuiltinEvalContext) CloseProvisioners() error {
   255  	var diags tfdiags.Diagnostics
   256  	ctx.ProvisionerLock.Lock()
   257  	defer ctx.ProvisionerLock.Unlock()
   258  
   259  	for name, prov := range ctx.ProvisionerCache {
   260  		err := prov.Close()
   261  		if err != nil {
   262  			diags = diags.Append(fmt.Errorf("provisioner.Close %s: %s", name, err))
   263  		}
   264  	}
   265  
   266  	return diags.Err()
   267  }
   268  
   269  func (ctx *BuiltinEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) {
   270  	var diags tfdiags.Diagnostics
   271  	scope := ctx.EvaluationScope(self, keyData)
   272  	body, evalDiags := scope.ExpandBlock(body, schema)
   273  	diags = diags.Append(evalDiags)
   274  	val, evalDiags := scope.EvalBlock(body, schema)
   275  	diags = diags.Append(evalDiags)
   276  	return val, body, diags
   277  }
   278  
   279  func (ctx *BuiltinEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) {
   280  	scope := ctx.EvaluationScope(self, EvalDataForNoInstanceKey)
   281  	return scope.EvalExpr(expr, wantType)
   282  }
   283  
   284  func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, keyData InstanceKeyEvalData) *lang.Scope {
   285  	if !ctx.pathSet {
   286  		panic("context path not set")
   287  	}
   288  	data := &evaluationStateData{
   289  		Evaluator:       ctx.Evaluator,
   290  		ModulePath:      ctx.PathValue,
   291  		InstanceKeyData: keyData,
   292  		Operation:       ctx.Evaluator.Operation,
   293  	}
   294  	scope := ctx.Evaluator.Scope(data, self)
   295  
   296  	// ctx.PathValue is the path of the module that contains whatever
   297  	// expression the caller will be trying to evaluate, so this will
   298  	// activate only the experiments from that particular module, to
   299  	// be consistent with how experiment checking in the "configs"
   300  	// package itself works. The nil check here is for robustness in
   301  	// incompletely-mocked testing situations; mc should never be nil in
   302  	// real situations.
   303  	if mc := ctx.Evaluator.Config.DescendentForInstance(ctx.PathValue); mc != nil {
   304  		scope.SetActiveExperiments(mc.Module.ActiveExperiments)
   305  	}
   306  	return scope
   307  }
   308  
   309  func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance {
   310  	if !ctx.pathSet {
   311  		panic("context path not set")
   312  	}
   313  	return ctx.PathValue
   314  }
   315  
   316  func (ctx *BuiltinEvalContext) SetModuleCallArguments(n addrs.ModuleCallInstance, vals map[string]cty.Value) {
   317  	ctx.VariableValuesLock.Lock()
   318  	defer ctx.VariableValuesLock.Unlock()
   319  
   320  	if !ctx.pathSet {
   321  		panic("context path not set")
   322  	}
   323  
   324  	childPath := n.ModuleInstance(ctx.PathValue)
   325  	key := childPath.String()
   326  
   327  	args := ctx.VariableValues[key]
   328  	if args == nil {
   329  		ctx.VariableValues[key] = vals
   330  		return
   331  	}
   332  
   333  	for k, v := range vals {
   334  		args[k] = v
   335  	}
   336  }
   337  
   338  func (ctx *BuiltinEvalContext) GetVariableValue(addr addrs.AbsInputVariableInstance) cty.Value {
   339  	ctx.VariableValuesLock.Lock()
   340  	defer ctx.VariableValuesLock.Unlock()
   341  
   342  	modKey := addr.Module.String()
   343  	modVars := ctx.VariableValues[modKey]
   344  	val, ok := modVars[addr.Variable.Name]
   345  	if !ok {
   346  		return cty.DynamicVal
   347  	}
   348  	return val
   349  }
   350  
   351  func (ctx *BuiltinEvalContext) Changes() *plans.ChangesSync {
   352  	return ctx.ChangesValue
   353  }
   354  
   355  func (ctx *BuiltinEvalContext) State() *states.SyncState {
   356  	return ctx.StateValue
   357  }
   358  
   359  func (ctx *BuiltinEvalContext) RefreshState() *states.SyncState {
   360  	return ctx.RefreshStateValue
   361  }
   362  
   363  func (ctx *BuiltinEvalContext) PrevRunState() *states.SyncState {
   364  	return ctx.PrevRunStateValue
   365  }
   366  
   367  func (ctx *BuiltinEvalContext) InstanceExpander() *instances.Expander {
   368  	return ctx.InstanceExpanderValue
   369  }
   370  
   371  func (ctx *BuiltinEvalContext) MoveResults() refactoring.MoveResults {
   372  	return ctx.MoveResultsValue
   373  }