Merge pull request #13502 from hashicorp/jbardin/dedupe-depends_on

Remove duplicate entries from resource Dependencies
This commit is contained in:
James Bardin 2017-04-10 12:06:54 -04:00 committed by GitHub
commit 7d4cceb787
7 changed files with 159 additions and 2 deletions

View File

@ -8100,3 +8100,32 @@ func TestContext2Apply_terraformEnv(t *testing.T) {
t.Fatalf("bad: \n%s", actual)
}
}
// verify that multiple config references only create a single depends_on entry
func TestContext2Apply_multiRef(t *testing.T) {
m := testModule(t, "apply-multi-ref")
p := testProvider("aws")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
ctx := testContext2(t, &ContextOpts{
Module: m,
Providers: map[string]ResourceProviderFactory{
"aws": 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)
}
deps := state.Modules[0].Resources["aws_instance.other"].Dependencies
if len(deps) > 1 || deps[0] != "aws_instance.create" {
t.Fatalf("expected 1 depends_on entry for aws_instance.create, got %q", deps)
}
}

View File

@ -102,7 +102,7 @@ func (n *NodeAbstractResource) References() []string {
}
}
return result
return uniqueStrings(result)
}
// If we have state, that is our next source

View File

@ -1163,6 +1163,8 @@ func (m *ModuleState) prune() {
delete(m.Outputs, k)
}
}
m.Dependencies = uniqueStrings(m.Dependencies)
}
func (m *ModuleState) sort() {
@ -1526,8 +1528,9 @@ func (s *ResourceState) prune() {
i--
}
}
s.Deposed = s.Deposed[:n]
s.Dependencies = uniqueStrings(s.Dependencies)
}
func (s *ResourceState) sort() {

View File

@ -1898,6 +1898,62 @@ func TestReadState_prune(t *testing.T) {
}
}
func TestReadState_pruneDependencies(t *testing.T) {
state := &State{
Serial: 9,
Lineage: "5d1ad1a1-4027-4665-a908-dbe6adff11d8",
Remote: &RemoteState{
Type: "http",
Config: map[string]string{
"url": "http://my-cool-server.com/",
},
},
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Dependencies: []string{
"aws_instance.bar",
"aws_instance.bar",
},
Resources: map[string]*ResourceState{
"foo": &ResourceState{
Dependencies: []string{
"aws_instance.baz",
"aws_instance.baz",
},
Primary: &InstanceState{
ID: "bar",
},
},
},
},
},
}
state.init()
buf := new(bytes.Buffer)
if err := WriteState(state, buf); err != nil {
t.Fatalf("err: %s", err)
}
actual, err := ReadState(buf)
if err != nil {
t.Fatalf("err: %s", err)
}
// make sure the duplicate Dependencies are filtered
modDeps := actual.Modules[0].Dependencies
resourceDeps := actual.Modules[0].Resources["foo"].Dependencies
if len(modDeps) > 1 || modDeps[0] != "aws_instance.bar" {
t.Fatalf("expected 1 module depends_on entry, got %q", modDeps)
}
if len(resourceDeps) > 1 || resourceDeps[0] != "aws_instance.baz" {
t.Fatalf("expected 1 resource depends_on entry, got %q", resourceDeps)
}
}
func TestResourceNameSort(t *testing.T) {
names := []string{
"a",

View File

@ -0,0 +1,8 @@
resource "aws_instance" "create" {
bar = "abc"
}
resource "aws_instance" "other" {
var = "${aws_instance.create.id}"
foo = "${aws_instance.create.bar}"
}

View File

@ -1,6 +1,7 @@
package terraform
import (
"sort"
"strings"
)
@ -73,3 +74,20 @@ func strSliceContains(haystack []string, needle string) bool {
}
return false
}
// deduplicate a slice of strings
func uniqueStrings(s []string) []string {
if len(s) < 2 {
return s
}
sort.Strings(s)
result := make([]string, 1, len(s))
result[0] = s[0]
for i := 1; i < len(s); i++ {
if s[i] != result[len(result)-1] {
result = append(result, s[i])
}
}
return result
}

View File

@ -1,6 +1,8 @@
package terraform
import (
"fmt"
"reflect"
"testing"
"time"
)
@ -96,3 +98,44 @@ func TestUtilResourceProvider(t *testing.T) {
}
}
}
func TestUniqueStrings(t *testing.T) {
cases := []struct {
Input []string
Expected []string
}{
{
[]string{},
[]string{},
},
{
[]string{"x"},
[]string{"x"},
},
{
[]string{"a", "b", "c"},
[]string{"a", "b", "c"},
},
{
[]string{"a", "a", "a"},
[]string{"a"},
},
{
[]string{"a", "b", "a", "b", "a", "a"},
[]string{"a", "b"},
},
{
[]string{"c", "b", "a", "c", "b"},
[]string{"a", "b", "c"},
},
}
for i, tc := range cases {
t.Run(fmt.Sprintf("unique-%d", i), func(t *testing.T) {
actual := uniqueStrings(tc.Input)
if !reflect.DeepEqual(tc.Expected, actual) {
t.Fatalf("Expected: %q\nGot: %q", tc.Expected, actual)
}
})
}
}