mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-29 10:21:01 -06:00
e57a399d71
This changes the representation of maps in the interpolator from the dotted flatmap form of a string variable named "var.variablename.key" per map element to use native HIL maps instead. This involves porting some of the interpolation functions in order to keep the tests green, and adding support for map outputs. There is one backwards incompatibility: as a result of an implementation detail of maps, one could access an indexed map variable using the syntax "${var.variablename.key}". This is no longer possible - instead HIL native syntax - "${var.variablename["key"]}" must be used. This was previously documented, (though not heavily used) so it must be noted as a backward compatibility issue for Terraform 0.7.
155 lines
4.2 KiB
Go
155 lines
4.2 KiB
Go
package terraform
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"sync"
|
|
|
|
"github.com/hashicorp/errwrap"
|
|
"github.com/hashicorp/terraform/dag"
|
|
)
|
|
|
|
// ContextGraphWalker is the GraphWalker implementation used with the
|
|
// Context struct to walk and evaluate the graph.
|
|
type ContextGraphWalker struct {
|
|
NullGraphWalker
|
|
|
|
// Configurable values
|
|
Context *Context
|
|
Operation walkOperation
|
|
|
|
// Outputs, do not set these. Do not read these while the graph
|
|
// is being walked.
|
|
ValidationWarnings []string
|
|
ValidationErrors []error
|
|
|
|
errorLock sync.Mutex
|
|
once sync.Once
|
|
contexts map[string]*BuiltinEvalContext
|
|
contextLock sync.Mutex
|
|
interpolaterVars map[string]map[string]interface{}
|
|
interpolaterVarLock sync.Mutex
|
|
providerCache map[string]ResourceProvider
|
|
providerConfigCache map[string]*ResourceConfig
|
|
providerLock sync.Mutex
|
|
provisionerCache map[string]ResourceProvisioner
|
|
provisionerLock sync.Mutex
|
|
}
|
|
|
|
func (w *ContextGraphWalker) EnterPath(path []string) EvalContext {
|
|
w.once.Do(w.init)
|
|
|
|
w.contextLock.Lock()
|
|
defer w.contextLock.Unlock()
|
|
|
|
// If we already have a context for this path cached, use that
|
|
key := PathCacheKey(path)
|
|
if ctx, ok := w.contexts[key]; ok {
|
|
return ctx
|
|
}
|
|
|
|
// Setup the variables for this interpolater
|
|
variables := make(map[string]interface{})
|
|
if len(path) <= 1 {
|
|
for k, v := range w.Context.variables {
|
|
variables[k] = v
|
|
}
|
|
}
|
|
w.interpolaterVarLock.Lock()
|
|
if m, ok := w.interpolaterVars[key]; ok {
|
|
for k, v := range m {
|
|
variables[k] = v
|
|
}
|
|
}
|
|
w.interpolaterVars[key] = variables
|
|
w.interpolaterVarLock.Unlock()
|
|
|
|
ctx := &BuiltinEvalContext{
|
|
PathValue: path,
|
|
Hooks: w.Context.hooks,
|
|
InputValue: w.Context.uiInput,
|
|
Providers: w.Context.providers,
|
|
ProviderCache: w.providerCache,
|
|
ProviderConfigCache: w.providerConfigCache,
|
|
ProviderInputConfig: w.Context.providerInputConfig,
|
|
ProviderLock: &w.providerLock,
|
|
Provisioners: w.Context.provisioners,
|
|
ProvisionerCache: w.provisionerCache,
|
|
ProvisionerLock: &w.provisionerLock,
|
|
DiffValue: w.Context.diff,
|
|
DiffLock: &w.Context.diffLock,
|
|
StateValue: w.Context.state,
|
|
StateLock: &w.Context.stateLock,
|
|
Interpolater: &Interpolater{
|
|
Operation: w.Operation,
|
|
Module: w.Context.module,
|
|
State: w.Context.state,
|
|
StateLock: &w.Context.stateLock,
|
|
VariableValues: variables,
|
|
VariableValuesLock: &w.interpolaterVarLock,
|
|
},
|
|
InterpolaterVars: w.interpolaterVars,
|
|
InterpolaterVarLock: &w.interpolaterVarLock,
|
|
}
|
|
|
|
w.contexts[key] = ctx
|
|
return ctx
|
|
}
|
|
|
|
func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode {
|
|
log.Printf("[TRACE] [%s] Entering eval tree: %s",
|
|
w.Operation, dag.VertexName(v))
|
|
|
|
// Acquire a lock on the semaphore
|
|
w.Context.parallelSem.Acquire()
|
|
|
|
// We want to filter the evaluation tree to only include operations
|
|
// that belong in this operation.
|
|
return EvalFilter(n, EvalNodeFilterOp(w.Operation))
|
|
}
|
|
|
|
func (w *ContextGraphWalker) ExitEvalTree(
|
|
v dag.Vertex, output interface{}, err error) error {
|
|
log.Printf("[TRACE] [%s] Exiting eval tree: %s",
|
|
w.Operation, dag.VertexName(v))
|
|
|
|
// Release the semaphore
|
|
w.Context.parallelSem.Release()
|
|
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
// Acquire the lock because anything is going to require a lock.
|
|
w.errorLock.Lock()
|
|
defer w.errorLock.Unlock()
|
|
|
|
// Try to get a validation error out of it. If its not a validation
|
|
// error, then just record the normal error.
|
|
verr, ok := err.(*EvalValidateError)
|
|
if !ok {
|
|
return err
|
|
}
|
|
|
|
for _, msg := range verr.Warnings {
|
|
w.ValidationWarnings = append(
|
|
w.ValidationWarnings,
|
|
fmt.Sprintf("%s: %s", dag.VertexName(v), msg))
|
|
}
|
|
for _, e := range verr.Errors {
|
|
w.ValidationErrors = append(
|
|
w.ValidationErrors,
|
|
errwrap.Wrapf(fmt.Sprintf("%s: {{err}}", dag.VertexName(v)), e))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (w *ContextGraphWalker) init() {
|
|
w.contexts = make(map[string]*BuiltinEvalContext, 5)
|
|
w.providerCache = make(map[string]ResourceProvider, 5)
|
|
w.providerConfigCache = make(map[string]*ResourceConfig, 5)
|
|
w.provisionerCache = make(map[string]ResourceProvisioner, 5)
|
|
w.interpolaterVars = make(map[string]map[string]interface{}, 5)
|
|
}
|