Merge pull request #9096 from hashicorp/f-resource-config
ResourceConfig.Equal, DeepCopy
This commit is contained in:
commit
88c3554dda
|
@ -179,13 +179,15 @@ func TestInterpolationWalker_replace(t *testing.T) {
|
||||||
return tc.Value, nil
|
return tc.Value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
w := &interpolationWalker{F: fn, Replace: true}
|
t.Run(fmt.Sprintf("walk-%d", i), func(t *testing.T) {
|
||||||
if err := reflectwalk.Walk(tc.Input, w); err != nil {
|
w := &interpolationWalker{F: fn, Replace: true}
|
||||||
t.Fatalf("err: %s", err)
|
if err := reflectwalk.Walk(tc.Input, w); err != nil {
|
||||||
}
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(tc.Input, tc.Output) {
|
if !reflect.DeepEqual(tc.Input, tc.Output) {
|
||||||
t.Fatalf("%d: bad:\n\nexpected:%#v\ngot:%#v", i, tc.Output, tc.Input)
|
t.Fatalf("%d: bad:\n\nexpected:%#v\ngot:%#v", i, tc.Output, tc.Input)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
|
"github.com/mitchellh/copystructure"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ResourceProvisionerConfig is used to pair a provisioner
|
// ResourceProvisionerConfig is used to pair a provisioner
|
||||||
|
@ -93,6 +94,45 @@ func NewResourceConfig(c *config.RawConfig) *ResourceConfig {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopy performs a deep copy of the configuration. This makes it safe
|
||||||
|
// to modify any of the structures that are part of the resource config without
|
||||||
|
// affecting the original configuration.
|
||||||
|
func (c *ResourceConfig) DeepCopy() *ResourceConfig {
|
||||||
|
// Copy, this will copy all the exported attributes
|
||||||
|
copy, err := copystructure.Config{Lock: true}.Copy(c)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force the type
|
||||||
|
result := copy.(*ResourceConfig)
|
||||||
|
|
||||||
|
// For the raw configuration, we can just use its own copy method
|
||||||
|
result.raw = c.raw.Copy()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal checks the equality of two resource configs.
|
||||||
|
func (c *ResourceConfig) Equal(c2 *ResourceConfig) bool {
|
||||||
|
// Two resource configs if their exported properties are equal.
|
||||||
|
// We don't compare "raw" because it is never used again after
|
||||||
|
// initialization and for all intents and purposes they are equal
|
||||||
|
// if the exported properties are equal.
|
||||||
|
check := [][2]interface{}{
|
||||||
|
{c.ComputedKeys, c2.ComputedKeys},
|
||||||
|
{c.Raw, c2.Raw},
|
||||||
|
{c.Config, c2.Config},
|
||||||
|
}
|
||||||
|
for _, pair := range check {
|
||||||
|
if !reflect.DeepEqual(pair[0], pair[1]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// CheckSet checks that the given list of configuration keys is
|
// CheckSet checks that the given list of configuration keys is
|
||||||
// properly set. If not, errors are returned for each unset key.
|
// properly set. If not, errors are returned for each unset key.
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -208,10 +209,33 @@ func TestResourceConfigGet(t *testing.T) {
|
||||||
rc := NewResourceConfig(rawC)
|
rc := NewResourceConfig(rawC)
|
||||||
rc.interpolateForce()
|
rc.interpolateForce()
|
||||||
|
|
||||||
v, _ := rc.Get(tc.Key)
|
// Test getting a key
|
||||||
if !reflect.DeepEqual(v, tc.Value) {
|
t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) {
|
||||||
t.Fatalf("%d bad: %#v", i, v)
|
v, _ := rc.Get(tc.Key)
|
||||||
|
if !reflect.DeepEqual(v, tc.Value) {
|
||||||
|
t.Fatalf("%d bad: %#v", i, v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// If we have vars, we don't test copying
|
||||||
|
if len(tc.Vars) > 0 {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test copying and equality
|
||||||
|
t.Run(fmt.Sprintf("copy-and-equal-%d", i), func(t *testing.T) {
|
||||||
|
copy := rc.DeepCopy()
|
||||||
|
if !reflect.DeepEqual(copy, rc) {
|
||||||
|
t.Fatalf("bad:\n\n%#v\n\n%#v", copy, rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !copy.Equal(rc) {
|
||||||
|
t.Fatalf("copy != rc:\n\n%#v\n\n%#v", copy, rc)
|
||||||
|
}
|
||||||
|
if !rc.Equal(copy) {
|
||||||
|
t.Fatalf("rc != copy:\n\n%#v\n\n%#v", copy, rc)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package terraform
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -272,11 +273,13 @@ func TestStateDeepCopy(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range cases {
|
for i, tc := range cases {
|
||||||
actual := tc.F(tc.One.DeepCopy())
|
t.Run(fmt.Sprintf("copy-%d", i), func(t *testing.T) {
|
||||||
expected := tc.F(tc.Two)
|
actual := tc.F(tc.One.DeepCopy())
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
expected := tc.F(tc.Two)
|
||||||
t.Fatalf("Bad: %d\n\n%s\n\n%s", i, actual, expected)
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
}
|
t.Fatalf("Bad: %d\n\n%s\n\n%s", i, actual, expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -295,12 +295,24 @@ func (w *walker) StructField(f reflect.StructField, v reflect.Value) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If PkgPath is non-empty, this is a private (unexported) field.
|
||||||
|
// We do not set this unexported since the Go runtime doesn't allow us.
|
||||||
|
if f.PkgPath != "" {
|
||||||
|
w.ignore()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Push the field onto the stack, we'll handle it when we exit
|
// Push the field onto the stack, we'll handle it when we exit
|
||||||
// the struct field in Exit...
|
// the struct field in Exit...
|
||||||
w.valPush(reflect.ValueOf(f))
|
w.valPush(reflect.ValueOf(f))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ignore causes the walker to ignore any more values until we exit this on
|
||||||
|
func (w *walker) ignore() {
|
||||||
|
w.ignoreDepth = w.depth
|
||||||
|
}
|
||||||
|
|
||||||
func (w *walker) ignoring() bool {
|
func (w *walker) ignoring() bool {
|
||||||
return w.ignoreDepth > 0 && w.depth >= w.ignoreDepth
|
return w.ignoreDepth > 0 && w.depth >= w.ignoreDepth
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,7 @@
|
||||||
// those elements.
|
// those elements.
|
||||||
package reflectwalk
|
package reflectwalk
|
||||||
|
|
||||||
import (
|
import "reflect"
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PrimitiveWalker implementations are able to handle primitive values
|
// PrimitiveWalker implementations are able to handle primitive values
|
||||||
// within complex structures. Primitive values are numbers, strings,
|
// within complex structures. Primitive values are numbers, strings,
|
||||||
|
@ -79,10 +77,26 @@ func Walk(data, walker interface{}) (err error) {
|
||||||
|
|
||||||
func walk(v reflect.Value, w interface{}) (err error) {
|
func walk(v reflect.Value, w interface{}) (err error) {
|
||||||
// Determine if we're receiving a pointer and if so notify the walker.
|
// Determine if we're receiving a pointer and if so notify the walker.
|
||||||
|
// The logic here is convoluted but very important (tests will fail if
|
||||||
|
// almost any part is changed). I will try to explain here.
|
||||||
|
//
|
||||||
|
// First, we check if the value is an interface, if so, we really need
|
||||||
|
// to check the interface's VALUE to see whether it is a pointer (pointers
|
||||||
|
// to interfaces are not allowed).
|
||||||
|
//
|
||||||
|
// Check whether the value is then an interface. If so, then set pointer
|
||||||
|
// to true to notify the user.
|
||||||
|
//
|
||||||
|
// At this time, we also set "v" to be the dereferenced value. This is
|
||||||
|
// because once we've unwrapped the pointer we want to use that value.
|
||||||
pointer := false
|
pointer := false
|
||||||
if v.Kind() == reflect.Ptr {
|
pointerV := v
|
||||||
|
if pointerV.Kind() == reflect.Interface {
|
||||||
|
pointerV = pointerV.Elem()
|
||||||
|
}
|
||||||
|
if pointerV.Kind() == reflect.Ptr {
|
||||||
pointer = true
|
pointer = true
|
||||||
v = reflect.Indirect(v)
|
v = reflect.Indirect(pointerV)
|
||||||
}
|
}
|
||||||
if pw, ok := w.(PointerWalker); ok {
|
if pw, ok := w.(PointerWalker); ok {
|
||||||
if err = pw.PointerEnter(pointer); err != nil {
|
if err = pw.PointerEnter(pointer); err != nil {
|
||||||
|
|
|
@ -1472,10 +1472,10 @@
|
||||||
"revision": "8631ce90f28644f54aeedcb3e389a85174e067d1"
|
"revision": "8631ce90f28644f54aeedcb3e389a85174e067d1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Vfkp+PcZ1wZ4+D6AsHTpKkdsQG0=",
|
"checksumSHA1": "EDAtec3XSbTjw6gWG+NNScows9M=",
|
||||||
"path": "github.com/mitchellh/copystructure",
|
"path": "github.com/mitchellh/copystructure",
|
||||||
"revision": "501dcbdc7c358c4d0bfa066018834bedca79fde3",
|
"revision": "49a4444999946bce1882f9db0eb3ba0a44ed1fbb",
|
||||||
"revisionTime": "2016-09-16T19:51:24Z"
|
"revisionTime": "2016-09-28T02:49:35Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "github.com/mitchellh/go-homedir",
|
"path": "github.com/mitchellh/go-homedir",
|
||||||
|
@ -1507,8 +1507,10 @@
|
||||||
"revision": "6e6954073784f7ee67b28f2d22749d6479151ed7"
|
"revision": "6e6954073784f7ee67b28f2d22749d6479151ed7"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"checksumSHA1": "kXh6sdGViiRK0REpIWydJvpsyY0=",
|
||||||
"path": "github.com/mitchellh/reflectwalk",
|
"path": "github.com/mitchellh/reflectwalk",
|
||||||
"revision": "eecf4c70c626c7cfbb95c90195bc34d386c74ac6"
|
"revision": "0c9480f65513be815a88d6076a3d8d95d4274236",
|
||||||
|
"revisionTime": "2016-09-28T02:49:03Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "/iig5lYSPCL3C8J7e4nTAevYNDE=",
|
"checksumSHA1": "/iig5lYSPCL3C8J7e4nTAevYNDE=",
|
||||||
|
|
Loading…
Reference in New Issue