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
|
||||
}
|
||||
|
||||
w := &interpolationWalker{F: fn, Replace: true}
|
||||
if err := reflectwalk.Walk(tc.Input, w); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
t.Run(fmt.Sprintf("walk-%d", i), func(t *testing.T) {
|
||||
w := &interpolationWalker{F: fn, Replace: true}
|
||||
if err := reflectwalk.Walk(tc.Input, w); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tc.Input, tc.Output) {
|
||||
t.Fatalf("%d: bad:\n\nexpected:%#v\ngot:%#v", i, tc.Output, tc.Input)
|
||||
}
|
||||
if !reflect.DeepEqual(tc.Input, tc.Output) {
|
||||
t.Fatalf("%d: bad:\n\nexpected:%#v\ngot:%#v", i, tc.Output, tc.Input)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/mitchellh/copystructure"
|
||||
)
|
||||
|
||||
// ResourceProvisionerConfig is used to pair a provisioner
|
||||
|
@ -93,6 +94,45 @@ func NewResourceConfig(c *config.RawConfig) *ResourceConfig {
|
|||
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
|
||||
// properly set. If not, errors are returned for each unset key.
|
||||
//
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
|
@ -208,10 +209,33 @@ func TestResourceConfigGet(t *testing.T) {
|
|||
rc := NewResourceConfig(rawC)
|
||||
rc.interpolateForce()
|
||||
|
||||
v, _ := rc.Get(tc.Key)
|
||||
if !reflect.DeepEqual(v, tc.Value) {
|
||||
t.Fatalf("%d bad: %#v", i, v)
|
||||
// Test getting a key
|
||||
t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) {
|
||||
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 (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -272,11 +273,13 @@ func TestStateDeepCopy(t *testing.T) {
|
|||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
actual := tc.F(tc.One.DeepCopy())
|
||||
expected := tc.F(tc.Two)
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Fatalf("Bad: %d\n\n%s\n\n%s", i, actual, expected)
|
||||
}
|
||||
t.Run(fmt.Sprintf("copy-%d", i), func(t *testing.T) {
|
||||
actual := tc.F(tc.One.DeepCopy())
|
||||
expected := tc.F(tc.Two)
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// the struct field in Exit...
|
||||
w.valPush(reflect.ValueOf(f))
|
||||
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 {
|
||||
return w.ignoreDepth > 0 && w.depth >= w.ignoreDepth
|
||||
}
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
// those elements.
|
||||
package reflectwalk
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
import "reflect"
|
||||
|
||||
// PrimitiveWalker implementations are able to handle primitive values
|
||||
// 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) {
|
||||
// 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
|
||||
if v.Kind() == reflect.Ptr {
|
||||
pointerV := v
|
||||
if pointerV.Kind() == reflect.Interface {
|
||||
pointerV = pointerV.Elem()
|
||||
}
|
||||
if pointerV.Kind() == reflect.Ptr {
|
||||
pointer = true
|
||||
v = reflect.Indirect(v)
|
||||
v = reflect.Indirect(pointerV)
|
||||
}
|
||||
if pw, ok := w.(PointerWalker); ok {
|
||||
if err = pw.PointerEnter(pointer); err != nil {
|
||||
|
|
|
@ -1472,10 +1472,10 @@
|
|||
"revision": "8631ce90f28644f54aeedcb3e389a85174e067d1"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Vfkp+PcZ1wZ4+D6AsHTpKkdsQG0=",
|
||||
"checksumSHA1": "EDAtec3XSbTjw6gWG+NNScows9M=",
|
||||
"path": "github.com/mitchellh/copystructure",
|
||||
"revision": "501dcbdc7c358c4d0bfa066018834bedca79fde3",
|
||||
"revisionTime": "2016-09-16T19:51:24Z"
|
||||
"revision": "49a4444999946bce1882f9db0eb3ba0a44ed1fbb",
|
||||
"revisionTime": "2016-09-28T02:49:35Z"
|
||||
},
|
||||
{
|
||||
"path": "github.com/mitchellh/go-homedir",
|
||||
|
@ -1507,8 +1507,10 @@
|
|||
"revision": "6e6954073784f7ee67b28f2d22749d6479151ed7"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "kXh6sdGViiRK0REpIWydJvpsyY0=",
|
||||
"path": "github.com/mitchellh/reflectwalk",
|
||||
"revision": "eecf4c70c626c7cfbb95c90195bc34d386c74ac6"
|
||||
"revision": "0c9480f65513be815a88d6076a3d8d95d4274236",
|
||||
"revisionTime": "2016-09-28T02:49:03Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "/iig5lYSPCL3C8J7e4nTAevYNDE=",
|
||||
|
|
Loading…
Reference in New Issue