core: Fix empty multi-variable type

Previously, interpolation of multi-variables was returning an empty
variable if the resource count was 0. The empty variable was defined as
TypeString, Value "". This means that empty resource counts fail type
checking for interpolation functions which operate on lists.

Instead, return an empty list if the count is 0. A context test tests
this against further regression. Also add a regression test covering the
case of a single count multi-variable.

In order to make the context testing framework deal with this change it
was necessary to special case empty lists in the test diff function.

Fixes #7002
This commit is contained in:
James Nugent 2016-06-12 12:33:05 +02:00
parent 2fd4a62cf5
commit 052345abfe
5 changed files with 85 additions and 2 deletions

View File

@ -48,6 +48,69 @@ func TestContext2Apply(t *testing.T) {
} }
} }
func TestContext2Apply_resourceCountOneList(t *testing.T) {
m := testModule(t, "apply-resource-count-one-list")
p := testProvider("null")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
ctx := testContext2(t, &ContextOpts{
Module: m,
Providers: map[string]ResourceProviderFactory{
"null": testProviderFuncFixed(p),
},
})
if _, err := ctx.Plan(); err != nil {
t.Fatalf("err: %s", err)
}
state, err := ctx.Apply()
if err != nil {
t.Fatalf("err: %s", err)
}
actual := strings.TrimSpace(state.String())
expected := strings.TrimSpace(`null_resource.foo:
ID = foo
Outputs:
test = [foo]`)
if actual != expected {
t.Fatalf("expected: \n%s\n\ngot: \n%s\n", expected, actual)
}
}
func TestContext2Apply_resourceCountZeroList(t *testing.T) {
m := testModule(t, "apply-resource-count-zero-list")
p := testProvider("null")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
ctx := testContext2(t, &ContextOpts{
Module: m,
Providers: map[string]ResourceProviderFactory{
"null": testProviderFuncFixed(p),
},
})
if _, err := ctx.Plan(); err != nil {
t.Fatalf("err: %s", err)
}
state, err := ctx.Apply()
if err != nil {
t.Fatalf("err: %s", err)
}
actual := strings.TrimSpace(state.String())
expected := strings.TrimSpace(`<no state>
Outputs:
test = []`)
if actual != expected {
t.Fatalf("expected: \n%s\n\ngot: \n%s\n", expected, actual)
}
}
func TestContext2Apply_mapVarBetweenModules(t *testing.T) { func TestContext2Apply_mapVarBetweenModules(t *testing.T) {
m := testModule(t, "apply-map-var-through-module") m := testModule(t, "apply-map-var-through-module")
p := testProvider("null") p := testProvider("null")

View File

@ -2,6 +2,7 @@ package terraform
import ( import (
"fmt" "fmt"
"reflect"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -166,7 +167,12 @@ func testDiffFn(
attrDiff := &ResourceAttrDiff{ attrDiff := &ResourceAttrDiff{
Old: "", Old: "",
New: v.(string), }
if reflect.DeepEqual(v, []interface{}{}) {
attrDiff.New = ""
} else {
attrDiff.New = v.(string)
} }
if k == "require_new" { if k == "require_new" {

View File

@ -487,7 +487,7 @@ func (i *Interpolater) computeResourceMultiVariable(
// If we have no module in the state yet or count, return empty // If we have no module in the state yet or count, return empty
if module == nil || len(module.Resources) == 0 || count == 0 { if module == nil || len(module.Resources) == 0 || count == 0 {
return &ast.Variable{Type: ast.TypeString, Value: ""}, nil return &ast.Variable{Type: ast.TypeList, Value: []ast.Variable{}}, nil
} }
var values []string var values []string

View File

@ -0,0 +1,7 @@
resource "null_resource" "foo" {
count = 1
}
output "test" {
value = "${sort(null_resource.foo.*.id)}"
}

View File

@ -0,0 +1,7 @@
resource "null_resource" "foo" {
count = 0
}
output "test" {
value = "${sort(null_resource.foo.*.id)}"
}