From 012d68923c5575f043bd55faf5158f3a0218f948 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 4 Feb 2015 11:30:53 -0500 Subject: [PATCH] terraform: Eval --- terraform/eval.go | 17 ++++++++++++ terraform/eval_context_builtin.go | 46 ++++++++++++++++++++++++++++--- terraform/eval_test.go | 45 ++++++++++++++++++++++++++++++ terraform/graph.go | 6 +--- terraform/resource.go | 9 ++++++ 5 files changed, 114 insertions(+), 9 deletions(-) diff --git a/terraform/eval.go b/terraform/eval.go index eb1fe5a60..150820842 100644 --- a/terraform/eval.go +++ b/terraform/eval.go @@ -22,3 +22,20 @@ type EvalNode interface { type GraphNodeEvalable interface { EvalTree() EvalNode } + +// Eval evaluates the given EvalNode with the given context, properly +// evaluating all args in the correct order. +func Eval(n EvalNode, ctx EvalContext) (interface{}, error) { + argNodes, _ := n.Args() + args := make([]interface{}, len(argNodes)) + for i, n := range argNodes { + v, err := Eval(n, ctx) + if err != nil { + return nil, err + } + + args[i] = v + } + + return n.Eval(ctx, args) +} diff --git a/terraform/eval_context_builtin.go b/terraform/eval_context_builtin.go index 05b77c08d..d74b490b7 100644 --- a/terraform/eval_context_builtin.go +++ b/terraform/eval_context_builtin.go @@ -5,12 +5,14 @@ import ( "sync" "github.com/hashicorp/terraform/config" + "github.com/hashicorp/terraform/config/lang/ast" ) // BuiltinEvalContext is an EvalContext implementation that is used by // Terraform by default. type BuiltinEvalContext struct { - Providers map[string]ResourceProviderFactory + Providers map[string]ResourceProviderFactory + ComputeMissing bool providers map[string]ResourceProvider once sync.Once @@ -37,9 +39,45 @@ func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider { } func (ctx *BuiltinEvalContext) Interpolate( - config *config.RawConfig) (*ResourceConfig, error) { - // TODO: Actual interpolation, for now we just return it as-is - return NewResourceConfig(config), nil + cfg *config.RawConfig) (*ResourceConfig, error) { + vs := make(map[string]ast.Variable) + + // If we don't have a config, use the blank config + if cfg == nil { + goto INTERPOLATE_RESULT + } + + for n, rawV := range cfg.Variables { + switch rawV.(type) { + case *config.ModuleVariable: + if ctx.ComputeMissing { + vs[n] = ast.Variable{ + Value: config.UnknownVariableValue, + Type: ast.TypeString, + } + } + case *config.ResourceVariable: + if ctx.ComputeMissing { + vs[n] = ast.Variable{ + Value: config.UnknownVariableValue, + Type: ast.TypeString, + } + } + default: + return nil, fmt.Errorf( + "unknown interpolation type: %#v", rawV) + } + } + + // Do the interpolation + if err := cfg.Interpolate(vs); err != nil { + return nil, err + } + +INTERPOLATE_RESULT: + result := NewResourceConfig(cfg) + result.interpolateForce() + return result, nil } func (ctx *BuiltinEvalContext) init() { diff --git a/terraform/eval_test.go b/terraform/eval_test.go index e89f2641b..47154ef2c 100644 --- a/terraform/eval_test.go +++ b/terraform/eval_test.go @@ -7,3 +7,48 @@ import ( func TestMockEvalContext_impl(t *testing.T) { var _ EvalContext = new(MockEvalContext) } + +func TestEval(t *testing.T) { + n := &testEvalAdd{ + Items: []EvalNode{ + &EvalLiteral{Value: 10}, + &EvalLiteral{Value: 32}, + }, + } + + result, err := Eval(n, nil) + if err != nil { + t.Fatalf("err: %s", err) + } + + if result != 42 { + t.Fatalf("bad: %#v", result) + } +} + +type testEvalAdd struct { + Items []EvalNode +} + +func (n *testEvalAdd) Args() ([]EvalNode, []EvalType) { + types := make([]EvalType, len(n.Items)) + for i, _ := range n.Items { + types[i] = EvalTypeInvalid + } + + return n.Items, types +} + +func (n *testEvalAdd) Eval( + ctx EvalContext, args []interface{}) (interface{}, error) { + result := 0 + for _, arg := range args { + result += arg.(int) + } + + return result, nil +} + +func (n *testEvalAdd) Type() EvalType { + return EvalTypeInvalid +} diff --git a/terraform/graph.go b/terraform/graph.go index e59ec87b0..d0e68648c 100644 --- a/terraform/graph.go +++ b/terraform/graph.go @@ -18,7 +18,7 @@ var RootModulePath = []string{RootModuleName} type Graph struct { // Graph is the actual DAG. This is embedded so you can call the DAG // methods directly. - *dag.Graph + dag.AcyclicGraph // Path is the path in the module tree that this Graph represents. // The root is represented by a single element list containing @@ -112,10 +112,6 @@ func (g *Graph) Dependable(n string) dag.Vertex { } func (g *Graph) init() { - if g.Graph == nil { - g.Graph = new(dag.Graph) - } - if g.dependableMap == nil { g.dependableMap = make(map[string]dag.Vertex) } diff --git a/terraform/resource.go b/terraform/resource.go index cbcd7814d..931d1b323 100644 --- a/terraform/resource.go +++ b/terraform/resource.go @@ -226,3 +226,12 @@ func (c *ResourceConfig) interpolate( c.Config = c.raw.Config() return nil } + +// interpolateForce is a temporary thing. We want to get rid of interpolate +// above and likewise this, but it can only be done after the f-ast-graph +// refactor is complete. +func (c *ResourceConfig) interpolateForce() { + c.ComputedKeys = c.raw.UnknownKeys() + c.Raw = c.raw.Raw + c.Config = c.raw.Config() +}