trim index steps from RequiresNew paths
Only GetAttrSteps can actually trigger RequiresNew, but the flatmaps paths will point to the indexed value that caused the change.
This commit is contained in:
parent
6383ffc2aa
commit
f959b560a2
|
@ -28,6 +28,10 @@ func RequiresReplace(attrs []string, ty cty.Type) ([]cty.Path, error) {
|
||||||
paths = append(paths, p)
|
paths = append(paths, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now trim off any trailing paths that aren't GetAttrSteps, since only an
|
||||||
|
// attribute itself can require replacement
|
||||||
|
paths = trimPaths(paths)
|
||||||
|
|
||||||
// There may be redundant paths due to set elements or index attributes
|
// There may be redundant paths due to set elements or index attributes
|
||||||
// Do some ugly n^2 filtering, but these are always fairly small sets.
|
// Do some ugly n^2 filtering, but these are always fairly small sets.
|
||||||
for i := 0; i < len(paths)-1; i++ {
|
for i := 0; i < len(paths)-1; i++ {
|
||||||
|
@ -44,6 +48,30 @@ func RequiresReplace(attrs []string, ty cty.Type) ([]cty.Path, error) {
|
||||||
return paths, nil
|
return paths, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// trimPaths removes any trailing steps that aren't of type GetAttrSet, since
|
||||||
|
// only an attribute itself can require replacement
|
||||||
|
func trimPaths(paths []cty.Path) []cty.Path {
|
||||||
|
var trimmed []cty.Path
|
||||||
|
for _, path := range paths {
|
||||||
|
path = trimPath(path)
|
||||||
|
if len(path) > 0 {
|
||||||
|
trimmed = append(trimmed, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trimmed
|
||||||
|
}
|
||||||
|
|
||||||
|
func trimPath(path cty.Path) cty.Path {
|
||||||
|
for len(path) > 0 {
|
||||||
|
_, isGetAttr := path[len(path)-1].(cty.GetAttrStep)
|
||||||
|
if isGetAttr {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
path = path[:len(path)-1]
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
// requiresReplacePath takes a key from a flatmap along with the cty.Type
|
// requiresReplacePath takes a key from a flatmap along with the cty.Type
|
||||||
// describing the structure, and returns the cty.Path that would be used to
|
// describing the structure, and returns the cty.Path that would be used to
|
||||||
// reference the nested value in the data structure.
|
// reference the nested value in the data structure.
|
||||||
|
|
|
@ -6,9 +6,18 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ignoreUnexported = cmpopts.IgnoreUnexported(cty.GetAttrStep{}, cty.IndexStep{})
|
||||||
|
valueComparer = cmp.Comparer(cty.Value.RawEquals)
|
||||||
|
)
|
||||||
|
|
||||||
func TestPathFromFlatmap(t *testing.T) {
|
func TestPathFromFlatmap(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Flatmap string
|
Flatmap string
|
||||||
|
@ -221,3 +230,140 @@ func TestPathFromFlatmap(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRequiresReplace(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
attrs []string
|
||||||
|
expected []cty.Path
|
||||||
|
ty cty.Type
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "basic",
|
||||||
|
attrs: []string{
|
||||||
|
"foo",
|
||||||
|
},
|
||||||
|
ty: cty.Object(map[string]cty.Type{
|
||||||
|
"foo": cty.String,
|
||||||
|
}),
|
||||||
|
expected: []cty.Path{
|
||||||
|
cty.Path{cty.GetAttrStep{Name: "foo"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "two",
|
||||||
|
attrs: []string{
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
},
|
||||||
|
ty: cty.Object(map[string]cty.Type{
|
||||||
|
"foo": cty.String,
|
||||||
|
"bar": cty.String,
|
||||||
|
}),
|
||||||
|
expected: []cty.Path{
|
||||||
|
cty.Path{cty.GetAttrStep{Name: "foo"}},
|
||||||
|
cty.Path{cty.GetAttrStep{Name: "bar"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested object",
|
||||||
|
attrs: []string{
|
||||||
|
"foo.bar",
|
||||||
|
},
|
||||||
|
ty: cty.Object(map[string]cty.Type{
|
||||||
|
"foo": cty.Object(map[string]cty.Type{
|
||||||
|
"bar": cty.String,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
expected: []cty.Path{
|
||||||
|
cty.Path{cty.GetAttrStep{Name: "foo"}, cty.GetAttrStep{Name: "bar"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested objects",
|
||||||
|
attrs: []string{
|
||||||
|
"foo.bar.baz",
|
||||||
|
},
|
||||||
|
ty: cty.Object(map[string]cty.Type{
|
||||||
|
"foo": cty.Object(map[string]cty.Type{
|
||||||
|
"bar": cty.Object(map[string]cty.Type{
|
||||||
|
"baz": cty.String,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
expected: []cty.Path{
|
||||||
|
cty.Path{cty.GetAttrStep{Name: "foo"}, cty.GetAttrStep{Name: "bar"}, cty.GetAttrStep{Name: "baz"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested map",
|
||||||
|
attrs: []string{
|
||||||
|
"foo.%",
|
||||||
|
"foo.bar",
|
||||||
|
},
|
||||||
|
ty: cty.Object(map[string]cty.Type{
|
||||||
|
"foo": cty.Map(cty.String),
|
||||||
|
}),
|
||||||
|
expected: []cty.Path{
|
||||||
|
cty.Path{cty.GetAttrStep{Name: "foo"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested list",
|
||||||
|
attrs: []string{
|
||||||
|
"foo.#",
|
||||||
|
"foo.1",
|
||||||
|
},
|
||||||
|
ty: cty.Object(map[string]cty.Type{
|
||||||
|
"foo": cty.Map(cty.String),
|
||||||
|
}),
|
||||||
|
expected: []cty.Path{
|
||||||
|
cty.Path{cty.GetAttrStep{Name: "foo"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "object in map",
|
||||||
|
attrs: []string{
|
||||||
|
"foo.bar.baz",
|
||||||
|
},
|
||||||
|
ty: cty.Object(map[string]cty.Type{
|
||||||
|
"foo": cty.Map(cty.Object(
|
||||||
|
map[string]cty.Type{
|
||||||
|
"baz": cty.String,
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
}),
|
||||||
|
expected: []cty.Path{
|
||||||
|
cty.Path{cty.GetAttrStep{Name: "foo"}, cty.IndexStep{Key: cty.StringVal("bar")}, cty.GetAttrStep{Name: "baz"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "object in list",
|
||||||
|
attrs: []string{
|
||||||
|
"foo.1.baz",
|
||||||
|
},
|
||||||
|
ty: cty.Object(map[string]cty.Type{
|
||||||
|
"foo": cty.List(cty.Object(
|
||||||
|
map[string]cty.Type{
|
||||||
|
"baz": cty.String,
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
}),
|
||||||
|
expected: []cty.Path{
|
||||||
|
cty.Path{cty.GetAttrStep{Name: "foo"}, cty.IndexStep{Key: cty.NumberIntVal(1)}, cty.GetAttrStep{Name: "baz"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
rp, err := RequiresReplace(tc.attrs, tc.ty)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !cmp.Equal(tc.expected, rp, ignoreUnexported, valueComparer) {
|
||||||
|
t.Fatalf("\nexpected: %#v\ngot: %#v\n", tc.expected, rp)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue