Merge pull request #10765 from hashicorp/jbardin/list-order

Sort interpolated lists by index number
This commit is contained in:
James Bardin 2016-12-15 16:59:22 -05:00 committed by GitHub
commit 64cd09d29a
5 changed files with 127 additions and 5 deletions

View File

@ -2921,3 +2921,30 @@ func TestContext2Plan_createBeforeDestroy_depends_datasource(t *testing.T) {
t.Fatalf("missing diff for data.aws_vpc.bar.1") t.Fatalf("missing diff for data.aws_vpc.bar.1")
} }
} }
// interpolated lists need to be stored in the original order.
func TestContext2Plan_listOrder(t *testing.T) {
m := testModule(t, "plan-list-order")
p := testProvider("aws")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
ctx := testContext2(t, &ContextOpts{
Module: m,
Providers: map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
})
plan, err := ctx.Plan()
if err != nil {
t.Fatalf("err: %s", err)
}
rDiffs := plan.Diff.Modules[0].Resources
rDiffA := rDiffs["aws_instance.a"]
rDiffB := rDiffs["aws_instance.b"]
if !rDiffA.Equal(rDiffB) {
t.Fatal("aws_instance.a and aws_instance.b diffs should match:\n", plan)
}
}

View File

@ -210,10 +210,6 @@ func testDiffFn(
} }
for k, v := range c.Raw { for k, v := range c.Raw {
if _, ok := v.(string); !ok {
continue
}
// Ignore __-prefixed keys since they're used for magic // Ignore __-prefixed keys since they're used for magic
if k[0] == '_' && k[1] == '_' { if k[0] == '_' && k[1] == '_' {
continue continue

View File

@ -620,6 +620,33 @@ func (i *Interpolater) computeResourceMultiVariable(
return &variable, err return &variable, err
} }
type indexKeys []string
// we need to separate the index integer from the ID, and sort numerically
func (i indexKeys) Less(j, k int) bool {
jDot := strings.LastIndex(i[j], ".")
kDot := strings.LastIndex(i[j], ".")
// These should all be properly formatted, but check the indexes and return
// a safe value just in case.
if jDot < 0 || kDot < 0 {
return i[j] < i[k]
}
jIdx, _ := strconv.Atoi(i[j][jDot+1:])
kIdx, _ := strconv.Atoi(i[k][kDot+1:])
return jIdx < kIdx
}
func (i indexKeys) Swap(j, k int) {
i[j], i[k] = i[k], i[j]
}
func (i indexKeys) Len() int {
return len(i)
}
func (i *Interpolater) interpolateComplexTypeAttribute( func (i *Interpolater) interpolateComplexTypeAttribute(
resourceID string, resourceID string,
attributes map[string]string) (ast.Variable, error) { attributes map[string]string) (ast.Variable, error) {
@ -648,7 +675,9 @@ func (i *Interpolater) interpolateComplexTypeAttribute(
keys = append(keys, id) keys = append(keys, id)
} }
} }
sort.Strings(keys)
// sort the keys by their index number, rather than lexicographically by the key
sort.Sort(indexKeys(keys))
var members []string var members []string
for _, key := range keys { for _, key := range keys {

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"reflect" "reflect"
"sort"
"sync" "sync"
"testing" "testing"
@ -674,6 +675,68 @@ func TestInterpolater_selfVarWithoutResource(t *testing.T) {
} }
} }
// Verify sorting by key index number
func TestInterpolator_indexKeySort(t *testing.T) {
keys := []string{"a.1", "a.2", "a.10", "a.20", "a.3"}
sorted := []string{"a.1", "a.2", "a.3", "a.10", "a.20"}
sort.Sort(indexKeys(keys))
for i := range keys {
if keys[i] != sorted[i] {
t.Fatalf("indexes out of order\nexpected: %q\ngot: %q", sorted, keys)
}
}
}
func TestInterpolator_interpolatedListOrder(t *testing.T) {
state := &State{
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Resources: map[string]*ResourceState{
"aws_route53_zone.list": &ResourceState{
Type: "aws_route53_zone",
Dependencies: []string{},
Primary: &InstanceState{
ID: "null",
Attributes: map[string]string{
"foo.#": "12",
"foo.0": "a",
"foo.1": "b",
"foo.2": "c",
"foo.3": "d",
"foo.4": "e",
"foo.5": "f",
"foo.6": "g",
"foo.7": "h",
"foo.8": "i",
"foo.9": "j",
"foo.10": "k",
"foo.11": "l",
},
},
},
},
},
},
}
i := &Interpolater{
Module: testModule(t, "interpolate-multi-vars"),
StateLock: new(sync.RWMutex),
State: state,
}
scope := &InterpolationScope{
Path: rootModulePath,
}
list := []interface{}{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"}
testInterpolate(t, i, scope, "aws_route53_zone.list.foo",
interfaceToVariableSwallowError(list))
}
func getInterpolaterFixture(t *testing.T) *Interpolater { func getInterpolaterFixture(t *testing.T) *Interpolater {
lock := new(sync.RWMutex) lock := new(sync.RWMutex)
state := &State{ state := &State{

View File

@ -0,0 +1,7 @@
resource "aws_instance" "a" {
foo = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 20]
}
resource "aws_instance" "b" {
foo = "${aws_instance.a.foo}"
}