terraform: Eval

This commit is contained in:
Mitchell Hashimoto 2015-02-04 11:30:53 -05:00
parent d59ced3c57
commit 012d68923c
5 changed files with 114 additions and 9 deletions

View File

@ -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)
}

View File

@ -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() {

View File

@ -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
}

View File

@ -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)
}

View File

@ -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()
}