terraform: set variables in the proper location

This commit is contained in:
Mitchell Hashimoto 2015-05-01 16:29:19 -07:00
parent 94e1bab65d
commit c207beda36
9 changed files with 63 additions and 67 deletions

View File

@ -58,10 +58,10 @@ type EvalContext interface {
// that is currently being acted upon. // that is currently being acted upon.
Interpolate(*config.RawConfig, *Resource) (*ResourceConfig, error) Interpolate(*config.RawConfig, *Resource) (*ResourceConfig, error)
// SetVariables sets the variables for interpolation. These variables // SetVariables sets the variables for the module within
// should not have a "var." prefix. For example: "var.foo" should be // this context with the name n. This function call is additive:
// "foo" as the key. // the second parameter is merged with any previous call.
SetVariables(map[string]string) SetVariables(string, map[string]string)
// Diff returns the global diff as well as the lock that should // Diff returns the global diff as well as the lock that should
// be used to modify that diff. // be used to modify that diff.

View File

@ -14,6 +14,8 @@ import (
type BuiltinEvalContext struct { type BuiltinEvalContext struct {
PathValue []string PathValue []string
Interpolater *Interpolater Interpolater *Interpolater
InterpolaterVars map[string]map[string]string
InterpolaterVarLock *sync.Mutex
Hooks []Hook Hooks []Hook
InputValue UIInput InputValue UIInput
Providers map[string]ResourceProviderFactory Providers map[string]ResourceProviderFactory
@ -29,8 +31,7 @@ type BuiltinEvalContext struct {
StateValue *State StateValue *State
StateLock *sync.RWMutex StateLock *sync.RWMutex
once sync.Once once sync.Once
varLock sync.Mutex
} }
func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error { func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
@ -238,12 +239,23 @@ func (ctx *BuiltinEvalContext) Path() []string {
return ctx.PathValue return ctx.PathValue
} }
func (ctx *BuiltinEvalContext) SetVariables(vs map[string]string) { func (ctx *BuiltinEvalContext) SetVariables(n string, vs map[string]string) {
ctx.varLock.Lock() ctx.InterpolaterVarLock.Lock()
defer ctx.varLock.Unlock() defer ctx.InterpolaterVarLock.Unlock()
path := make([]string, len(ctx.Path())+1)
copy(path, ctx.Path())
path[len(path)-1] = n
key := PathCacheKey(path)
vars := ctx.InterpolaterVars[key]
if vars == nil {
vars = make(map[string]string)
ctx.InterpolaterVars[key] = vars
}
for k, v := range vs { for k, v := range vs {
ctx.Interpolater.Variables[k] = v vars[k] = v
} }
} }

View File

@ -65,6 +65,7 @@ type MockEvalContext struct {
PathPath []string PathPath []string
SetVariablesCalled bool SetVariablesCalled bool
SetVariablesModule string
SetVariablesVariables map[string]string SetVariablesVariables map[string]string
DiffCalled bool DiffCalled bool
@ -162,8 +163,9 @@ func (c *MockEvalContext) Path() []string {
return c.PathPath return c.PathPath
} }
func (c *MockEvalContext) SetVariables(vs map[string]string) { func (c *MockEvalContext) SetVariables(n string, vs map[string]string) {
c.SetVariablesCalled = true c.SetVariablesCalled = true
c.SetVariablesModule = n
c.SetVariablesVariables = vs c.SetVariablesVariables = vs
} }

View File

@ -11,12 +11,13 @@ import (
// EvalSetVariables is an EvalNode implementation that sets the variables // EvalSetVariables is an EvalNode implementation that sets the variables
// explicitly for interpolation later. // explicitly for interpolation later.
type EvalSetVariables struct { type EvalSetVariables struct {
Module *string
Variables map[string]string Variables map[string]string
} }
// TODO: test // TODO: test
func (n *EvalSetVariables) Eval(ctx EvalContext) (interface{}, error) { func (n *EvalSetVariables) Eval(ctx EvalContext) (interface{}, error) {
ctx.SetVariables(n.Variables) ctx.SetVariables(*n.Module, n.Variables)
return nil, nil return nil, nil
} }

View File

@ -162,7 +162,7 @@ func (g *Graph) walk(walker GraphWalker) error {
// is normally the context of our graph but can be overridden // is normally the context of our graph but can be overridden
// with a GraphNodeSubPath impl. // with a GraphNodeSubPath impl.
vertexCtx := ctx vertexCtx := ctx
if pn, ok := v.(GraphNodeSubPath); ok { if pn, ok := v.(GraphNodeSubPath); ok && len(pn.Path()) > 0 {
vertexCtx = walker.EnterPath(pn.Path()) vertexCtx = walker.EnterPath(pn.Path())
defer walker.ExitPath(pn.Path()) defer walker.ExitPath(pn.Path())
} }

View File

@ -177,7 +177,7 @@ func (n *graphNodeModuleExpanded) FlattenGraph() *Graph {
// If this is a variable, then look it up in the raw configuration. // If this is a variable, then look it up in the raw configuration.
// If it exists in the raw configuration, set the value of it. // If it exists in the raw configuration, set the value of it.
if vn, ok := v.(GraphNodeVariable); ok && input != nil { if vn, ok := v.(*GraphNodeConfigVariable); ok && input != nil {
key := vn.VariableName() key := vn.VariableName()
if v, ok := input.Raw[key]; ok { if v, ok := input.Raw[key]; ok {
config, err := config.NewRawConfig(map[string]interface{}{ config, err := config.NewRawConfig(map[string]interface{}{
@ -189,8 +189,10 @@ func (n *graphNodeModuleExpanded) FlattenGraph() *Graph {
panic(err) panic(err)
} }
// Set the variable value so it is interpolated properly // Set the variable value so it is interpolated properly.
vn.SetVariableValue(config) // Also set the module so we set the value on it properly.
vn.Module = graph.Path[len(graph.Path)-1]
vn.Value = config
} }
} }
} }
@ -209,32 +211,6 @@ type graphNodeModuleSkippable interface {
FlattenSkip() bool FlattenSkip() bool
} }
/*
// graphNodeModuleFlatWrap wraps elements of the module graph
// when they are flattened so that the dependency information is
// correct.
type graphNodeModuleFlatWrap struct {
Vertex dag.Vertex
PathValue []string
NamePrefix string
DependentOnPrefix string
}
// GraphNodeProviderConsumer impl.
func (n *graphNodeModuleFlatWrap) ProvidedBy() []string {
pn, ok := n.Vertex.(GraphNodeProviderConsumer)
if !ok {
return nil
}
result := make([]string, 0, 2)
for _, v := range pn.ProvidedBy() {
result = append(result, fmt.Sprintf("%s.%s", n.NamePrefix, v))
}
return result
}
*/
func modulePrefixStr(p []string) string { func modulePrefixStr(p []string) string {
parts := make([]string, 0, len(p)*2) parts := make([]string, 0, len(p)*2)
for _, p := range p[1:] { for _, p := range p[1:] {

View File

@ -7,20 +7,16 @@ import (
"github.com/hashicorp/terraform/dag" "github.com/hashicorp/terraform/dag"
) )
// GraphNodeVariable is the interface that must be implemented by anything
// that is a variable.
type GraphNodeVariable interface {
VariableName() string
SetVariableValue(*config.RawConfig)
}
// GraphNodeConfigVariable represents a Variable in the config. // GraphNodeConfigVariable represents a Variable in the config.
type GraphNodeConfigVariable struct { type GraphNodeConfigVariable struct {
Variable *config.Variable Variable *config.Variable
// Value, if non-nil, will be used to set the value of the variable // Value, if non-nil, will be used to set the value of the variable
// during evaluation. If this is nil, evaluation will do nothing. // during evaluation. If this is nil, evaluation will do nothing.
Value *config.RawConfig //
// Module is the name of the module to set the variables on.
Module string
Value *config.RawConfig
depPrefix string depPrefix string
} }
@ -55,16 +51,10 @@ func (n *GraphNodeConfigVariable) DependentOn() []string {
return result return result
} }
// GraphNodeVariable impl.
func (n *GraphNodeConfigVariable) VariableName() string { func (n *GraphNodeConfigVariable) VariableName() string {
return n.Variable.Name return n.Variable.Name
} }
// GraphNodeVariable impl.
func (n *GraphNodeConfigVariable) SetVariableValue(v *config.RawConfig) {
n.Value = v
}
// GraphNodeProxy impl. // GraphNodeProxy impl.
func (n *GraphNodeConfigVariable) Proxy() bool { func (n *GraphNodeConfigVariable) Proxy() bool {
return true return true
@ -94,6 +84,7 @@ func (n *GraphNodeConfigVariable) EvalTree() EvalNode {
}, },
&EvalSetVariables{ &EvalSetVariables{
Module: &n.Module,
Variables: variables, Variables: variables,
}, },
}, },
@ -128,7 +119,7 @@ func (n *GraphNodeConfigVariableFlat) DependentOn() []string {
// longer than 2 elements (root, child, more). This is because when // longer than 2 elements (root, child, more). This is because when
// flattened, variables can point outside the graph. // flattened, variables can point outside the graph.
prefix := "" prefix := ""
if len(n.PathValue) <= 2 { if len(n.PathValue) > 2 {
prefix = modulePrefixStr(n.PathValue[:len(n.PathValue)-1]) prefix = modulePrefixStr(n.PathValue[:len(n.PathValue)-1])
} }
@ -138,5 +129,9 @@ func (n *GraphNodeConfigVariableFlat) DependentOn() []string {
} }
func (n *GraphNodeConfigVariableFlat) Path() []string { func (n *GraphNodeConfigVariableFlat) Path() []string {
return n.PathValue if len(n.PathValue) > 2 {
return n.PathValue[:len(n.PathValue)-1]
}
return nil
} }

View File

@ -10,7 +10,6 @@ func TestGraphNodeConfigVariable_impl(t *testing.T) {
var _ dag.Vertex = new(GraphNodeConfigVariable) var _ dag.Vertex = new(GraphNodeConfigVariable)
var _ dag.NamedVertex = new(GraphNodeConfigVariable) var _ dag.NamedVertex = new(GraphNodeConfigVariable)
var _ graphNodeConfig = new(GraphNodeConfigVariable) var _ graphNodeConfig = new(GraphNodeConfigVariable)
var _ GraphNodeVariable = new(GraphNodeConfigVariable)
var _ GraphNodeProxy = new(GraphNodeConfigVariable) var _ GraphNodeProxy = new(GraphNodeConfigVariable)
} }
@ -18,6 +17,5 @@ func TestGraphNodeConfigVariableFlat_impl(t *testing.T) {
var _ dag.Vertex = new(GraphNodeConfigVariableFlat) var _ dag.Vertex = new(GraphNodeConfigVariableFlat)
var _ dag.NamedVertex = new(GraphNodeConfigVariableFlat) var _ dag.NamedVertex = new(GraphNodeConfigVariableFlat)
var _ graphNodeConfig = new(GraphNodeConfigVariableFlat) var _ graphNodeConfig = new(GraphNodeConfigVariableFlat)
var _ GraphNodeVariable = new(GraphNodeConfigVariableFlat)
var _ GraphNodeProxy = new(GraphNodeConfigVariableFlat) var _ GraphNodeProxy = new(GraphNodeConfigVariableFlat)
} }

View File

@ -26,6 +26,8 @@ type ContextGraphWalker struct {
once sync.Once once sync.Once
contexts map[string]*BuiltinEvalContext contexts map[string]*BuiltinEvalContext
contextLock sync.Mutex contextLock sync.Mutex
interpolaterVars map[string]map[string]string
interpolaterVarLock sync.Mutex
providerCache map[string]ResourceProvider providerCache map[string]ResourceProvider
providerConfigCache map[string]*ResourceConfig providerConfigCache map[string]*ResourceConfig
providerLock sync.Mutex providerLock sync.Mutex
@ -45,14 +47,21 @@ func (w *ContextGraphWalker) EnterPath(path []string) EvalContext {
return ctx return ctx
} }
// Variables should be our context variables, but these only apply // Setup the variables for this interpolater
// to the root module. As we enter subgraphs, we don't want to set variables := make(map[string]string)
// variables, which is set by the SetVariables EvalContext function. if len(path) <= 1 {
variables := w.Context.variables for k, v := range w.Context.variables {
if len(path) > 1 { variables[k] = v
// We're in a submodule, the variables should be empty }
variables = make(map[string]string)
} }
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{ ctx := &BuiltinEvalContext{
PathValue: path, PathValue: path,
@ -77,6 +86,8 @@ func (w *ContextGraphWalker) EnterPath(path []string) EvalContext {
StateLock: &w.Context.stateLock, StateLock: &w.Context.stateLock,
Variables: variables, Variables: variables,
}, },
InterpolaterVars: w.interpolaterVars,
InterpolaterVarLock: &w.interpolaterVarLock,
} }
w.contexts[key] = ctx w.contexts[key] = ctx
@ -131,4 +142,5 @@ func (w *ContextGraphWalker) init() {
w.providerCache = make(map[string]ResourceProvider, 5) w.providerCache = make(map[string]ResourceProvider, 5)
w.providerConfigCache = make(map[string]*ResourceConfig, 5) w.providerConfigCache = make(map[string]*ResourceConfig, 5)
w.provisionerCache = make(map[string]ResourceProvisioner, 5) w.provisionerCache = make(map[string]ResourceProvisioner, 5)
w.interpolaterVars = make(map[string]map[string]string, 5)
} }