diff --git a/terraform/context.go b/terraform/context.go index 4885886645..91bc7a7043 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -347,12 +347,11 @@ func (c *Context) State() *State { // receiving context. func (c *Context) Evaluator() *Evaluator { return &Evaluator{ - Operation: walkApply, - Meta: c.meta, - Config: c.config, - State: c.state, - StateLock: &c.stateLock, - RootVariableValues: c.variables, + Operation: walkApply, + Meta: c.meta, + Config: c.config, + State: c.state, + StateLock: &c.stateLock, } } @@ -786,9 +785,10 @@ func (c *Context) walk(graph *Graph, operation walkOperation) (*ContextGraphWalk log.Printf("[DEBUG] Starting graph walk: %s", operation.String()) walker := &ContextGraphWalker{ - Context: realCtx, - Operation: operation, - StopContext: c.runContext, + Context: realCtx, + Operation: operation, + StopContext: c.runContext, + RootVariableValues: c.variables, } // Watch for a stop so we can call the provider Stop() API. diff --git a/terraform/eval_context_builtin.go b/terraform/eval_context_builtin.go index 8dedcc7162..1bffc0d1c6 100644 --- a/terraform/eval_context_builtin.go +++ b/terraform/eval_context_builtin.go @@ -27,8 +27,14 @@ type BuiltinEvalContext struct { // eval context. Evaluator *Evaluator - ChildModuleCallArgs map[string]map[string]cty.Value - ChildModuleCallsLock *sync.Mutex + // VariableValues contains the variable values across all modules. This + // structure is shared across the entire containing context, and so it + // may be accessed only when holding VariableValuesLock. + // The keys of the first level of VariableValues are the string + // representations of addrs.ModuleInstance values. The second-level keys + // are variable names within each module instance. + VariableValues map[string]map[string]cty.Value + VariableValuesLock *sync.Mutex Components contextComponentFactory Hooks []Hook @@ -322,16 +328,17 @@ func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance { } func (ctx *BuiltinEvalContext) SetModuleCallArguments(n addrs.ModuleCallInstance, vals map[string]cty.Value) { - ctx.ChildModuleCallsLock.Lock() - defer ctx.ChildModuleCallsLock.Unlock() + ctx.VariableValuesLock.Lock() + defer ctx.VariableValuesLock.Unlock() - childPath := ctx.Path().Child(n.Call.Name, n.Key) + childPath := n.ModuleInstance(ctx.PathValue) key := childPath.String() - args := ctx.ChildModuleCallArgs[key] + args := ctx.VariableValues[key] if args == nil { args = make(map[string]cty.Value) - ctx.ChildModuleCallArgs[key] = args + ctx.VariableValues[key] = vals + return } for k, v := range vals { diff --git a/terraform/evaluate.go b/terraform/evaluate.go index 288d2c39bc..a23abbec1b 100644 --- a/terraform/evaluate.go +++ b/terraform/evaluate.go @@ -24,10 +24,15 @@ type Evaluator struct { // Config is the root node in the configuration tree. Config *configs.Config - // RootVariableValues is a map of values for variables defined in the - // root module, passed in from external sources. This must not be - // modified during evaluation. - RootVariableValues map[string]*InputValue + // VariableValues is a map from variable names to their associated values, + // within the module indicated by ModulePath. VariableValues is modified + // concurrently, and so it must be accessed only while holding + // VariableValuesLock. + // + // The first map level is string representations of addr.ModuleInstance + // values, while the second level is variable names. + VariableValues map[string]map[string]cty.Value + VariableValuesLock *sync.Mutex // State is the current state. During some operations this structure // is mutated concurrently, and so it must be accessed only while holding diff --git a/terraform/graph_walk_context.go b/terraform/graph_walk_context.go index 68ad549616..bda2b787ba 100644 --- a/terraform/graph_walk_context.go +++ b/terraform/graph_walk_context.go @@ -5,6 +5,8 @@ import ( "log" "sync" + "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/terraform/config/configschema" "github.com/hashicorp/terraform/tfdiags" @@ -19,26 +21,27 @@ type ContextGraphWalker struct { NullGraphWalker // Configurable values - Context *Context - Operation walkOperation - StopContext context.Context + Context *Context + Operation walkOperation + StopContext context.Context + RootVariableValues InputValues // This is an output. Do not set this, nor read it while a graph walk // is in progress. NonFatalDiagnostics tfdiags.Diagnostics - 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 - providerSchemas map[string]*ProviderSchema - providerLock sync.Mutex - provisionerCache map[string]ResourceProvisioner - provisionerSchemas map[string]*configschema.Block - provisionerLock sync.Mutex + errorLock sync.Mutex + once sync.Once + contexts map[string]*BuiltinEvalContext + contextLock sync.Mutex + variableValues map[string]map[string]cty.Value + variableValuesLock sync.Mutex + providerCache map[string]ResourceProvider + providerSchemas map[string]*ProviderSchema + providerLock sync.Mutex + provisionerCache map[string]ResourceProvisioner + provisionerSchemas map[string]*configschema.Block + provisionerLock sync.Mutex } func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext { @@ -53,38 +56,18 @@ func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext { 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() - // Our evaluator shares some locks with the main context and the walker // so that we can safely run multiple evaluations at once across // different modules. evaluator := &Evaluator{ - Meta: w.Context.meta, - Config: w.Context.config, - State: w.Context.state, - StateLock: &w.Context.stateLock, - ProviderSchemas: w.providerSchemas, - ProvidersLock: &w.providerLock, - - // FIXME: This was a design mistake on the evaluator, which should - // get replaced with something like the interpolatorVars thing above - // once we verify exactly how that was used in the old Interpolator - // codepath. - RootVariableValues: map[string]*InputValue{}, + Meta: w.Context.meta, + Config: w.Context.config, + State: w.Context.state, + StateLock: &w.Context.stateLock, + ProviderSchemas: w.providerSchemas, + ProvidersLock: &w.providerLock, + VariableValues: w.variableValues, + VariableValuesLock: &w.variableValuesLock, } ctx := &BuiltinEvalContext{ @@ -104,6 +87,8 @@ func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext { StateValue: w.Context.state, StateLock: &w.Context.stateLock, Evaluator: evaluator, + VariableValues: w.variableValues, + VariableValuesLock: &w.variableValuesLock, } w.contexts[key] = ctx @@ -158,5 +143,12 @@ func (w *ContextGraphWalker) init() { w.providerSchemas = make(map[string]*ProviderSchema) w.provisionerCache = make(map[string]ResourceProvisioner) w.provisionerSchemas = make(map[string]*configschema.Block) - w.interpolaterVars = make(map[string]map[string]interface{}) + w.variableValues = make(map[string]map[string]cty.Value) + + // Populate root module variable values. Other modules will be populated + // during the graph walk. + w.variableValues[""] = make(map[string]cty.Value) + for k, iv := range w.RootVariableValues { + w.variableValues[""][k] = iv.Value + } }