From 846dfbca266004cc53440b5be85872199eb74e6c Mon Sep 17 00:00:00 2001 From: wata_mac Date: Sun, 23 Apr 2017 16:37:05 +0900 Subject: [PATCH] Interpolate module attributes --- evaluator/evaluator.go | 11 ++-- evaluator/module.go | 31 +++++++++- evaluator/module_test.go | 123 ++++++++++++++++++++++++++++++++++++++- evaluator/variable.go | 10 +++- 4 files changed, 164 insertions(+), 11 deletions(-) diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index a390a19b1..83c0d9ded 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -21,10 +21,6 @@ func NewEvaluator(templates map[string]*hclast.File, varfile []*hclast.File, c * if err != nil { return nil, err } - moduleMap, err := detectModules(templates, c) - if err != nil { - return nil, err - } evaluator := &Evaluator{ Config: hil.EvalConfig{ @@ -32,9 +28,14 @@ func NewEvaluator(templates map[string]*hclast.File, varfile []*hclast.File, c * VarMap: varMap, }, }, - ModuleConfig: moduleMap, } + moduleMap, err := evaluator.detectModules(templates, c) + if err != nil { + return nil, err + } + evaluator.ModuleConfig = moduleMap + return evaluator, nil } diff --git a/evaluator/module.go b/evaluator/module.go index 71e822949..322cb6985 100644 --- a/evaluator/module.go +++ b/evaluator/module.go @@ -5,6 +5,8 @@ import ( "encoding/hex" "fmt" + "reflect" + "github.com/hashicorp/hcl" hclast "github.com/hashicorp/hcl/hcl/ast" "github.com/hashicorp/hil" @@ -22,7 +24,7 @@ type hclModule struct { Templates map[string]*hclast.File } -func detectModules(templates map[string]*hclast.File, c *config.Config) (map[string]*hclModule, error) { +func (e *Evaluator) detectModules(templates map[string]*hclast.File, c *config.Config) (map[string]*hclModule, error) { moduleMap := make(map[string]*hclModule) for file, template := range templates { @@ -51,7 +53,11 @@ func detectModules(templates map[string]*hclast.File, c *config.Config) (map[str varMap := make(map[string]hilast.Variable) for k, v := range module { varName := "var." + k - varMap[varName] = parseVariable(v, "") + ev, err := e.evalModuleAttr(k, v) + if err != nil { + return nil, err + } + varMap[varName] = parseVariable(ev, "") } moduleMap[moduleKey] = &hclModule{ @@ -72,6 +78,27 @@ func detectModules(templates map[string]*hclast.File, c *config.Config) (map[str return moduleMap, nil } +func (e *Evaluator) evalModuleAttr(key string, val interface{}) (interface{}, error) { + if v, ok := val.(string); ok { + ev, err := e.Eval(v) + if err != nil { + return nil, err + } + if estr, ok := ev.(string); ok && estr == "[NOT EVALUABLE]" { + ev = e + } + + // In parseVariable function, map is expected to be in slice. + switch reflect.ValueOf(ev).Kind() { + case reflect.Map: + return []interface{}{ev}, nil + default: + return ev, nil + } + } + return val, nil +} + func moduleKey(name string, source string) string { base := "root." + name + "-" + source sum := md5.Sum([]byte(base)) // #nosec diff --git a/evaluator/module_test.go b/evaluator/module_test.go index 201969a6c..9797f6aae 100644 --- a/evaluator/module_test.go +++ b/evaluator/module_test.go @@ -110,6 +110,126 @@ module "ec2_instance" { }, Error: false, }, + { + Name: "detect module with string variable", + Input: map[string]string{ + "module.tf": ` +variable "ami" { + default = "ami-12345" +} + +module "ec2_instance" { + source = "./tf_aws_ec2_instance" + ami = "${var.ami}" +}`, + }, + Result: map[string]*hclModule{ + "960d94c2f60d34845dc3051edfad76e1": { + Name: "ec2_instance", + Source: "./tf_aws_ec2_instance", + File: "module.tf", + Config: hil.EvalConfig{ + GlobalScope: &hilast.BasicScope{ + VarMap: map[string]hilast.Variable{ + "var.ami": { + Type: hilast.TypeString, + Value: "ami-12345", + }, + }, + }, + }, + Templates: map[string]*hclast.File{}, + }, + }, + Error: false, + }, + { + Name: "detect module with list variable", + Input: map[string]string{ + "module.tf": ` +variable "amis" { + default = ["ami-12345", "ami-54321"] +} + +module "ec2_instance" { + source = "./tf_aws_ec2_instance" + ami = "${var.amis}" +}`, + }, + Result: map[string]*hclModule{ + "960d94c2f60d34845dc3051edfad76e1": { + Name: "ec2_instance", + Source: "./tf_aws_ec2_instance", + File: "module.tf", + Config: hil.EvalConfig{ + GlobalScope: &hilast.BasicScope{ + VarMap: map[string]hilast.Variable{ + "var.ami": { + Type: hilast.TypeList, + Value: []hilast.Variable{ + { + Type: hilast.TypeString, + Value: "ami-12345", + }, + { + Type: hilast.TypeString, + Value: "ami-54321", + }, + }, + }, + }, + }, + }, + Templates: map[string]*hclast.File{}, + }, + }, + Error: false, + }, + { + Name: "detect module with map variable", + Input: map[string]string{ + "module.tf": ` +variable "ami_info" { + default = { + name = "awesome image" + value = "ami-12345" + } +} + +module "ec2_instance" { + source = "./tf_aws_ec2_instance" + ami = "${var.ami_info}" +}`, + }, + Result: map[string]*hclModule{ + "960d94c2f60d34845dc3051edfad76e1": { + Name: "ec2_instance", + Source: "./tf_aws_ec2_instance", + File: "module.tf", + Config: hil.EvalConfig{ + GlobalScope: &hilast.BasicScope{ + VarMap: map[string]hilast.Variable{ + "var.ami": { + Type: hilast.TypeMap, + Value: map[string]hilast.Variable{ + "name": { + Type: hilast.TypeString, + Value: "awesome image", + }, + "value": { + Type: hilast.TypeString, + Value: "ami-12345", + }, + }, + }, + }, + }, + }, + Templates: map[string]*hclast.File{}, + }, + }, + Error: false, + }, { Name: "invalid source", Input: map[string]string{ @@ -148,7 +268,8 @@ module "ec2_instances" { for k, v := range tc.Input { templates[k], _ = parser.Parse([]byte(v)) } - result, err := detectModules(templates, config.Init()) + evaluator, _ := NewEvaluator(templates, []*hclast.File{}, config.Init()) + result, err := evaluator.detectModules(templates, config.Init()) if tc.Error && err == nil { t.Fatalf("\nshould be happen error.\n\ntestcase: %s", tc.Name) continue diff --git a/evaluator/variable.go b/evaluator/variable.go index 83d45ae80..96e25dba0 100644 --- a/evaluator/variable.go +++ b/evaluator/variable.go @@ -124,9 +124,13 @@ func parseVariable(val interface{}, varType string) hilast.Variable { // Correct: // // []map[string]string{ - // map[string]string{ - // "name": "test", - // "value": "hcl", + // { + // "key": []map[string]string{ + // { + // "name": "test", + // "value": "hcl", + // }, + // }, // }, // } //