config: variables must be typed
This commit is contained in:
parent
dd456871e9
commit
8ae14f06b3
|
@ -81,7 +81,7 @@ func interpolationFuncJoin() lang.Function {
|
||||||
|
|
||||||
// interpolationFuncLookup implements the "lookup" function that allows
|
// interpolationFuncLookup implements the "lookup" function that allows
|
||||||
// dynamic lookups of map types within a Terraform configuration.
|
// 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{
|
return lang.Function{
|
||||||
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
|
ArgTypes: []ast.Type{ast.TypeString, ast.TypeString},
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
|
@ -93,8 +93,13 @@ func interpolationFuncLookup(vs map[string]string) lang.Function {
|
||||||
"lookup in '%s' failed to find '%s'",
|
"lookup in '%s' failed to find '%s'",
|
||||||
args[0].(string), args[1].(string))
|
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
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config/lang"
|
"github.com/hashicorp/terraform/config/lang"
|
||||||
|
"github.com/hashicorp/terraform/config/lang/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInterpolateFuncConcat(t *testing.T) {
|
func TestInterpolateFuncConcat(t *testing.T) {
|
||||||
|
@ -108,7 +109,12 @@ func TestInterpolateFuncJoin(t *testing.T) {
|
||||||
|
|
||||||
func TestInterpolateFuncLookup(t *testing.T) {
|
func TestInterpolateFuncLookup(t *testing.T) {
|
||||||
testFunction(t, testFunctionConfig{
|
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{
|
Cases: []testFunctionCase{
|
||||||
{
|
{
|
||||||
`${lookup("foo", "bar")}`,
|
`${lookup("foo", "bar")}`,
|
||||||
|
@ -170,7 +176,7 @@ func TestInterpolateFuncElement(t *testing.T) {
|
||||||
|
|
||||||
type testFunctionConfig struct {
|
type testFunctionConfig struct {
|
||||||
Cases []testFunctionCase
|
Cases []testFunctionCase
|
||||||
Vars map[string]string
|
Vars map[string]lang.Variable
|
||||||
}
|
}
|
||||||
|
|
||||||
type testFunctionCase struct {
|
type testFunctionCase struct {
|
||||||
|
|
|
@ -251,6 +251,12 @@ func TestParse(t *testing.T) {
|
||||||
true,
|
true,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"${var",
|
||||||
|
true,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
|
|
|
@ -80,7 +80,7 @@ func (r *RawConfig) Config() map[string]interface{} {
|
||||||
// Any prior calls to Interpolate are replaced with this one.
|
// Any prior calls to Interpolate are replaced with this one.
|
||||||
//
|
//
|
||||||
// If a variable key is missing, this will panic.
|
// 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)
|
engine := langEngine(vs)
|
||||||
return r.interpolate(func(root ast.Node) (string, error) {
|
return r.interpolate(func(root ast.Node) (string, error) {
|
||||||
out, _, err := engine.Execute(root)
|
out, _, err := engine.Execute(root)
|
||||||
|
@ -203,12 +203,7 @@ type gobRawConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// langEngine returns the lang.Engine to use for evaluating configurations.
|
// langEngine returns the lang.Engine to use for evaluating configurations.
|
||||||
func langEngine(vs map[string]string) *lang.Engine {
|
func langEngine(vs map[string]lang.Variable) *lang.Engine {
|
||||||
varMap := make(map[string]lang.Variable)
|
|
||||||
for k, v := range vs {
|
|
||||||
varMap[k] = lang.Variable{Value: v, Type: ast.TypeString}
|
|
||||||
}
|
|
||||||
|
|
||||||
funcMap := make(map[string]lang.Function)
|
funcMap := make(map[string]lang.Function)
|
||||||
for k, v := range Funcs {
|
for k, v := range Funcs {
|
||||||
funcMap[k] = v
|
funcMap[k] = v
|
||||||
|
@ -217,7 +212,7 @@ func langEngine(vs map[string]string) *lang.Engine {
|
||||||
|
|
||||||
return &lang.Engine{
|
return &lang.Engine{
|
||||||
GlobalScope: &lang.Scope{
|
GlobalScope: &lang.Scope{
|
||||||
VarMap: varMap,
|
VarMap: vs,
|
||||||
FuncMap: funcMap,
|
FuncMap: funcMap,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ import (
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/config/lang"
|
||||||
|
"github.com/hashicorp/terraform/config/lang/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewRawConfig(t *testing.T) {
|
func TestNewRawConfig(t *testing.T) {
|
||||||
|
@ -40,7 +43,12 @@ func TestRawConfig(t *testing.T) {
|
||||||
t.Fatalf("bad: %#v", rc.Config())
|
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 {
|
if err := rc.Interpolate(vars); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -68,7 +76,12 @@ func TestRawConfig_double(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
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 {
|
if err := rc.Interpolate(vars); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -82,7 +95,12 @@ func TestRawConfig_double(t *testing.T) {
|
||||||
t.Fatalf("bad: %#v", actual)
|
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 {
|
if err := rc.Interpolate(vars); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
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) {
|
func TestRawConfig_unknown(t *testing.T) {
|
||||||
raw := map[string]interface{}{
|
raw := map[string]interface{}{
|
||||||
"foo": "${var.bar}",
|
"foo": "${var.bar}",
|
||||||
|
@ -107,7 +135,12 @@ func TestRawConfig_unknown(t *testing.T) {
|
||||||
t.Fatalf("err: %s", err)
|
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 {
|
if err := rc.Interpolate(vars); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -145,7 +178,12 @@ func TestRawConfigValue(t *testing.T) {
|
||||||
t.Fatalf("err: %#v", rc.Value())
|
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 {
|
if err := rc.Interpolate(vars); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config"
|
"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/config/module"
|
||||||
"github.com/hashicorp/terraform/depgraph"
|
"github.com/hashicorp/terraform/depgraph"
|
||||||
"github.com/hashicorp/terraform/helper/multierror"
|
"github.com/hashicorp/terraform/helper/multierror"
|
||||||
|
@ -1520,9 +1522,12 @@ func (c *walkContext) computeVars(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the default variables
|
// Copy the default variables
|
||||||
vs := make(map[string]string)
|
vs := make(map[string]lang.Variable)
|
||||||
for k, v := range c.defaultVariables {
|
for k, v := range c.defaultVariables {
|
||||||
vs[k] = v
|
vs[k] = lang.Variable{
|
||||||
|
Value: v,
|
||||||
|
Type: ast.TypeString,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, the actual computed variables
|
// Next, the actual computed variables
|
||||||
|
@ -1532,12 +1537,18 @@ func (c *walkContext) computeVars(
|
||||||
switch v.Type {
|
switch v.Type {
|
||||||
case config.CountValueIndex:
|
case config.CountValueIndex:
|
||||||
if r != nil {
|
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:
|
case *config.ModuleVariable:
|
||||||
if c.Operation == walkValidate {
|
if c.Operation == walkValidate {
|
||||||
vs[n] = config.UnknownVariableValue
|
vs[n] = lang.Variable{
|
||||||
|
Value: config.UnknownVariableValue,
|
||||||
|
Type: ast.TypeString,
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1546,7 +1557,10 @@ func (c *walkContext) computeVars(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
vs[n] = value
|
vs[n] = lang.Variable{
|
||||||
|
Value: value,
|
||||||
|
Type: ast.TypeString,
|
||||||
|
}
|
||||||
case *config.PathVariable:
|
case *config.PathVariable:
|
||||||
switch v.Type {
|
switch v.Type {
|
||||||
case config.PathValueCwd:
|
case config.PathValueCwd:
|
||||||
|
@ -1557,17 +1571,29 @@ func (c *walkContext) computeVars(
|
||||||
v.FullKey(), err)
|
v.FullKey(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
vs[n] = wd
|
vs[n] = lang.Variable{
|
||||||
|
Value: wd,
|
||||||
|
Type: ast.TypeString,
|
||||||
|
}
|
||||||
case config.PathValueModule:
|
case config.PathValueModule:
|
||||||
if t := c.Context.module.Child(c.Path[1:]); t != nil {
|
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:
|
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:
|
case *config.ResourceVariable:
|
||||||
if c.Operation == walkValidate {
|
if c.Operation == walkValidate {
|
||||||
vs[n] = config.UnknownVariableValue
|
vs[n] = lang.Variable{
|
||||||
|
Value: config.UnknownVariableValue,
|
||||||
|
Type: ast.TypeString,
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1582,16 +1608,25 @@ func (c *walkContext) computeVars(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
vs[n] = attr
|
vs[n] = lang.Variable{
|
||||||
|
Value: attr,
|
||||||
|
Type: ast.TypeString,
|
||||||
|
}
|
||||||
case *config.UserVariable:
|
case *config.UserVariable:
|
||||||
val, ok := c.Variables[v.Name]
|
val, ok := c.Variables[v.Name]
|
||||||
if ok {
|
if ok {
|
||||||
vs[n] = val
|
vs[n] = lang.Variable{
|
||||||
|
Value: val,
|
||||||
|
Type: ast.TypeString,
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := vs[n]; !ok && c.Operation == walkValidate {
|
if _, ok := vs[n]; !ok && c.Operation == walkValidate {
|
||||||
vs[n] = config.UnknownVariableValue
|
vs[n] = lang.Variable{
|
||||||
|
Value: config.UnknownVariableValue,
|
||||||
|
Type: ast.TypeString,
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1599,7 +1634,10 @@ func (c *walkContext) computeVars(
|
||||||
// those are map overrides. Include those.
|
// those are map overrides. Include those.
|
||||||
for k, val := range c.Variables {
|
for k, val := range c.Variables {
|
||||||
if strings.HasPrefix(k, v.Name+".") {
|
if strings.HasPrefix(k, v.Name+".") {
|
||||||
vs["var."+k] = val
|
vs["var."+k] = lang.Variable{
|
||||||
|
Value: val,
|
||||||
|
Type: ast.TypeString,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue