export unstructured helper function nestedFieldNoCopy and add unit tests

Signed-off-by: Steve Kriss <steve@heptio.com>
pull/8/head
Steve Kriss 2018-04-09 10:29:29 -07:00
parent d4ded15f8a
commit d2e28006e3
3 changed files with 89 additions and 10 deletions

View File

@ -34,14 +34,17 @@ import (
// Returns false if the value is missing.
// No error is returned for a nil field.
func NestedFieldCopy(obj map[string]interface{}, fields ...string) (interface{}, bool, error) {
val, found, err := nestedFieldNoCopy(obj, fields...)
val, found, err := NestedFieldNoCopy(obj, fields...)
if !found || err != nil {
return nil, found, err
}
return runtime.DeepCopyJSONValue(val), true, nil
}
func nestedFieldNoCopy(obj map[string]interface{}, fields ...string) (interface{}, bool, error) {
// NestedFieldNoCopy returns a reference to a nested field.
// Returns false if value is not found and an error if unable
// to traverse obj.
func NestedFieldNoCopy(obj map[string]interface{}, fields ...string) (interface{}, bool, error) {
var val interface{} = obj
for i, field := range fields {
@ -60,7 +63,7 @@ func nestedFieldNoCopy(obj map[string]interface{}, fields ...string) (interface{
// NestedString returns the string value of a nested field.
// Returns false if value is not found and an error if not a string.
func NestedString(obj map[string]interface{}, fields ...string) (string, bool, error) {
val, found, err := nestedFieldNoCopy(obj, fields...)
val, found, err := NestedFieldNoCopy(obj, fields...)
if !found || err != nil {
return "", found, err
}
@ -74,7 +77,7 @@ func NestedString(obj map[string]interface{}, fields ...string) (string, bool, e
// NestedBool returns the bool value of a nested field.
// Returns false if value is not found and an error if not a bool.
func NestedBool(obj map[string]interface{}, fields ...string) (bool, bool, error) {
val, found, err := nestedFieldNoCopy(obj, fields...)
val, found, err := NestedFieldNoCopy(obj, fields...)
if !found || err != nil {
return false, found, err
}
@ -88,7 +91,7 @@ func NestedBool(obj map[string]interface{}, fields ...string) (bool, bool, error
// NestedFloat64 returns the float64 value of a nested field.
// Returns false if value is not found and an error if not a float64.
func NestedFloat64(obj map[string]interface{}, fields ...string) (float64, bool, error) {
val, found, err := nestedFieldNoCopy(obj, fields...)
val, found, err := NestedFieldNoCopy(obj, fields...)
if !found || err != nil {
return 0, found, err
}
@ -102,7 +105,7 @@ func NestedFloat64(obj map[string]interface{}, fields ...string) (float64, bool,
// NestedInt64 returns the int64 value of a nested field.
// Returns false if value is not found and an error if not an int64.
func NestedInt64(obj map[string]interface{}, fields ...string) (int64, bool, error) {
val, found, err := nestedFieldNoCopy(obj, fields...)
val, found, err := NestedFieldNoCopy(obj, fields...)
if !found || err != nil {
return 0, found, err
}
@ -116,7 +119,7 @@ func NestedInt64(obj map[string]interface{}, fields ...string) (int64, bool, err
// NestedStringSlice returns a copy of []string value of a nested field.
// Returns false if value is not found and an error if not a []interface{} or contains non-string items in the slice.
func NestedStringSlice(obj map[string]interface{}, fields ...string) ([]string, bool, error) {
val, found, err := nestedFieldNoCopy(obj, fields...)
val, found, err := NestedFieldNoCopy(obj, fields...)
if !found || err != nil {
return nil, found, err
}
@ -138,7 +141,7 @@ func NestedStringSlice(obj map[string]interface{}, fields ...string) ([]string,
// NestedSlice returns a deep copy of []interface{} value of a nested field.
// Returns false if value is not found and an error if not a []interface{}.
func NestedSlice(obj map[string]interface{}, fields ...string) ([]interface{}, bool, error) {
val, found, err := nestedFieldNoCopy(obj, fields...)
val, found, err := NestedFieldNoCopy(obj, fields...)
if !found || err != nil {
return nil, found, err
}
@ -180,7 +183,7 @@ func NestedMap(obj map[string]interface{}, fields ...string) (map[string]interfa
// nestedMapNoCopy returns a map[string]interface{} value of a nested field.
// Returns false if value is not found and an error if not a map[string]interface{}.
func nestedMapNoCopy(obj map[string]interface{}, fields ...string) (map[string]interface{}, bool, error) {
val, found, err := nestedFieldNoCopy(obj, fields...)
val, found, err := NestedFieldNoCopy(obj, fields...)
if !found || err != nil {
return nil, found, err
}

View File

@ -58,3 +58,79 @@ func TestRemoveNestedField(t *testing.T) {
RemoveNestedField(obj, "x") // Remove of a non-existent field
assert.Empty(t, obj)
}
func TestNestedFieldNoCopy(t *testing.T) {
target := map[string]interface{}{"foo": "bar"}
obj := map[string]interface{}{
"a": map[string]interface{}{
"b": target,
"c": nil,
"d": []interface{}{"foo"},
},
}
// case 1: field exists and is non-nil
res, exists, err := NestedFieldNoCopy(obj, "a", "b")
assert.True(t, exists)
assert.Nil(t, err)
assert.Equal(t, target, res)
target["foo"] = "baz"
assert.Equal(t, target["foo"], res.(map[string]interface{})["foo"], "result should be a reference to the expected item")
// case 2: field exists and is nil
res, exists, err = NestedFieldNoCopy(obj, "a", "c")
assert.True(t, exists)
assert.Nil(t, err)
assert.Nil(t, res)
// case 3: error traversing obj
res, exists, err = NestedFieldNoCopy(obj, "a", "d", "foo")
assert.False(t, exists)
assert.NotNil(t, err)
assert.Nil(t, res)
// case 4: field does not exist
res, exists, err = NestedFieldNoCopy(obj, "a", "e")
assert.False(t, exists)
assert.Nil(t, err)
assert.Nil(t, res)
}
func TestNestedFieldCopy(t *testing.T) {
target := map[string]interface{}{"foo": "bar"}
obj := map[string]interface{}{
"a": map[string]interface{}{
"b": target,
"c": nil,
"d": []interface{}{"foo"},
},
}
// case 1: field exists and is non-nil
res, exists, err := NestedFieldCopy(obj, "a", "b")
assert.True(t, exists)
assert.Nil(t, err)
assert.Equal(t, target, res)
target["foo"] = "baz"
assert.NotEqual(t, target["foo"], res.(map[string]interface{})["foo"], "result should be a copy of the expected item")
// case 2: field exists and is nil
res, exists, err = NestedFieldCopy(obj, "a", "c")
assert.True(t, exists)
assert.Nil(t, err)
assert.Nil(t, res)
// case 3: error traversing obj
res, exists, err = NestedFieldCopy(obj, "a", "d", "foo")
assert.False(t, exists)
assert.NotNil(t, err)
assert.Nil(t, res)
// case 4: field does not exist
res, exists, err = NestedFieldCopy(obj, "a", "e")
assert.False(t, exists)
assert.Nil(t, err)
assert.Nil(t, res)
}

View File

@ -138,7 +138,7 @@ func (u *Unstructured) setNestedMap(value map[string]string, fields ...string) {
}
func (u *Unstructured) GetOwnerReferences() []metav1.OwnerReference {
field, found, err := nestedFieldNoCopy(u.Object, "metadata", "ownerReferences")
field, found, err := NestedFieldNoCopy(u.Object, "metadata", "ownerReferences")
if !found || err != nil {
return nil
}