config: variables must be typed

This commit is contained in:
Mitchell Hashimoto 2015-01-14 10:40:43 -08:00
parent dd456871e9
commit 8ae14f06b3
6 changed files with 118 additions and 30 deletions

View File

@ -81,7 +81,7 @@ func interpolationFuncJoin() lang.Function {
// interpolationFuncLookup implements the "lookup" function that allows
// dynamic lookups of map types within a Terraform configuration.
func interpolationFuncLookup(vs map[string]string) lang.Function {
func interpolationFuncLookup(vs map[string]lang.Variable) lang.Function {
return lang.Function{
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
ReturnType: ast.TypeString,
@ -93,8 +93,13 @@ func interpolationFuncLookup(vs map[string]string) lang.Function {
"lookup in '%s' failed to find '%s'",
args[0].(string), args[1].(string))
}
if v.Type != ast.TypeString {
return "", fmt.Errorf(
"lookup in '%s' for '%s' has bad type %s",
args[0].(string), args[1].(string), v.Type)
}
return v, nil
return v.Value.(string), nil
},
}
}

View File

@ -8,6 +8,7 @@ import (
"testing"
"github.com/hashicorp/terraform/config/lang"
"github.com/hashicorp/terraform/config/lang/ast"
)
func TestInterpolateFuncConcat(t *testing.T) {
@ -108,7 +109,12 @@ func TestInterpolateFuncJoin(t *testing.T) {
func TestInterpolateFuncLookup(t *testing.T) {
testFunction(t, testFunctionConfig{
Vars: map[string]string{"var.foo.bar": "baz"},
Vars: map[string]lang.Variable{
"var.foo.bar": lang.Variable{
Value: "baz",
Type: ast.TypeString,
},
},
Cases: []testFunctionCase{
{
`${lookup("foo", "bar")}`,
@ -170,7 +176,7 @@ func TestInterpolateFuncElement(t *testing.T) {
type testFunctionConfig struct {
Cases []testFunctionCase
Vars map[string]string
Vars map[string]lang.Variable
}
type testFunctionCase struct {

View File

@ -251,6 +251,12 @@ func TestParse(t *testing.T) {
true,
nil,
},
{
"${var",
true,
nil,
},
}
for _, tc := range cases {

View File

@ -80,7 +80,7 @@ func (r *RawConfig) Config() map[string]interface{} {
// Any prior calls to Interpolate are replaced with this one.
//
// If a variable key is missing, this will panic.
func (r *RawConfig) Interpolate(vs map[string]string) error {
func (r *RawConfig) Interpolate(vs map[string]lang.Variable) error {
engine := langEngine(vs)
return r.interpolate(func(root ast.Node) (string, error) {
out, _, err := engine.Execute(root)
@ -203,12 +203,7 @@ type gobRawConfig struct {
}
// langEngine returns the lang.Engine to use for evaluating configurations.
func langEngine(vs map[string]string) *lang.Engine {
varMap := make(map[string]lang.Variable)
for k, v := range vs {
varMap[k] = lang.Variable{Value: v, Type: ast.TypeString}
}
func langEngine(vs map[string]lang.Variable) *lang.Engine {
funcMap := make(map[string]lang.Function)
for k, v := range Funcs {
funcMap[k] = v
@ -217,7 +212,7 @@ func langEngine(vs map[string]string) *lang.Engine {
return &lang.Engine{
GlobalScope: &lang.Scope{
VarMap: varMap,
VarMap: vs,
FuncMap: funcMap,
},
}

View File

@ -4,6 +4,9 @@ import (
"encoding/gob"
"reflect"
"testing"
"github.com/hashicorp/terraform/config/lang"
"github.com/hashicorp/terraform/config/lang/ast"
)
func TestNewRawConfig(t *testing.T) {
@ -40,7 +43,12 @@ func TestRawConfig(t *testing.T) {
t.Fatalf("bad: %#v", rc.Config())
}
vars := map[string]string{"var.bar": "baz"}
vars := map[string]lang.Variable{
"var.bar": lang.Variable{
Value: "baz",
Type: ast.TypeString,
},
}
if err := rc.Interpolate(vars); err != nil {
t.Fatalf("err: %s", err)
}
@ -68,7 +76,12 @@ func TestRawConfig_double(t *testing.T) {
t.Fatalf("err: %s", err)
}
vars := map[string]string{"var.bar": "baz"}
vars := map[string]lang.Variable{
"var.bar": lang.Variable{
Value: "baz",
Type: ast.TypeString,
},
}
if err := rc.Interpolate(vars); err != nil {
t.Fatalf("err: %s", err)
}
@ -82,7 +95,12 @@ func TestRawConfig_double(t *testing.T) {
t.Fatalf("bad: %#v", actual)
}
vars = map[string]string{"var.bar": "what"}
vars = map[string]lang.Variable{
"var.bar": lang.Variable{
Value: "what",
Type: ast.TypeString,
},
}
if err := rc.Interpolate(vars); err != nil {
t.Fatalf("err: %s", err)
}
@ -97,6 +115,16 @@ func TestRawConfig_double(t *testing.T) {
}
}
func TestRawConfig_syntax(t *testing.T) {
raw := map[string]interface{}{
"foo": "${var",
}
if _, err := NewRawConfig(raw); err == nil {
t.Fatal("should error")
}
}
func TestRawConfig_unknown(t *testing.T) {
raw := map[string]interface{}{
"foo": "${var.bar}",
@ -107,7 +135,12 @@ func TestRawConfig_unknown(t *testing.T) {
t.Fatalf("err: %s", err)
}
vars := map[string]string{"var.bar": UnknownVariableValue}
vars := map[string]lang.Variable{
"var.bar": lang.Variable{
Value: UnknownVariableValue,
Type: ast.TypeString,
},
}
if err := rc.Interpolate(vars); err != nil {
t.Fatalf("err: %s", err)
}
@ -145,7 +178,12 @@ func TestRawConfigValue(t *testing.T) {
t.Fatalf("err: %#v", rc.Value())
}
vars := map[string]string{"var.bar": "baz"}
vars := map[string]lang.Variable{
"var.bar": lang.Variable{
Value: "baz",
Type: ast.TypeString,
},
}
if err := rc.Interpolate(vars); err != nil {
t.Fatalf("err: %s", err)
}

View File

@ -11,6 +11,8 @@ import (
"sync/atomic"
"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/config/lang"
"github.com/hashicorp/terraform/config/lang/ast"
"github.com/hashicorp/terraform/config/module"
"github.com/hashicorp/terraform/depgraph"
"github.com/hashicorp/terraform/helper/multierror"
@ -1520,9 +1522,12 @@ func (c *walkContext) computeVars(
}
// Copy the default variables
vs := make(map[string]string)
vs := make(map[string]lang.Variable)
for k, v := range c.defaultVariables {
vs[k] = v
vs[k] = lang.Variable{
Value: v,
Type: ast.TypeString,
}
}
// Next, the actual computed variables
@ -1532,12 +1537,18 @@ func (c *walkContext) computeVars(
switch v.Type {
case config.CountValueIndex:
if r != nil {
vs[n] = strconv.FormatInt(int64(r.CountIndex), 10)
vs[n] = lang.Variable{
Value: int(r.CountIndex),
Type: ast.TypeInt,
}
}
}
case *config.ModuleVariable:
if c.Operation == walkValidate {
vs[n] = config.UnknownVariableValue
vs[n] = lang.Variable{
Value: config.UnknownVariableValue,
Type: ast.TypeString,
}
continue
}
@ -1546,7 +1557,10 @@ func (c *walkContext) computeVars(
return err
}
vs[n] = value
vs[n] = lang.Variable{
Value: value,
Type: ast.TypeString,
}
case *config.PathVariable:
switch v.Type {
case config.PathValueCwd:
@ -1557,17 +1571,29 @@ func (c *walkContext) computeVars(
v.FullKey(), err)
}
vs[n] = wd
vs[n] = lang.Variable{
Value: wd,
Type: ast.TypeString,
}
case config.PathValueModule:
if t := c.Context.module.Child(c.Path[1:]); t != nil {
vs[n] = t.Config().Dir
vs[n] = lang.Variable{
Value: t.Config().Dir,
Type: ast.TypeString,
}
}
case config.PathValueRoot:
vs[n] = c.Context.module.Config().Dir
vs[n] = lang.Variable{
Value: c.Context.module.Config().Dir,
Type: ast.TypeString,
}
}
case *config.ResourceVariable:
if c.Operation == walkValidate {
vs[n] = config.UnknownVariableValue
vs[n] = lang.Variable{
Value: config.UnknownVariableValue,
Type: ast.TypeString,
}
continue
}
@ -1582,16 +1608,25 @@ func (c *walkContext) computeVars(
return err
}
vs[n] = attr
vs[n] = lang.Variable{
Value: attr,
Type: ast.TypeString,
}
case *config.UserVariable:
val, ok := c.Variables[v.Name]
if ok {
vs[n] = val
vs[n] = lang.Variable{
Value: val,
Type: ast.TypeString,
}
continue
}
if _, ok := vs[n]; !ok && c.Operation == walkValidate {
vs[n] = config.UnknownVariableValue
vs[n] = lang.Variable{
Value: config.UnknownVariableValue,
Type: ast.TypeString,
}
continue
}
@ -1599,7 +1634,10 @@ func (c *walkContext) computeVars(
// those are map overrides. Include those.
for k, val := range c.Variables {
if strings.HasPrefix(k, v.Name+".") {
vs["var."+k] = val
vs["var."+k] = lang.Variable{
Value: val,
Type: ast.TypeString,
}
}
}
}