From 24beeb429db22003debf9b82a1f1094c4f625fda Mon Sep 17 00:00:00 2001 From: jregan Date: Fri, 21 Aug 2020 17:58:26 -0700 Subject: [PATCH] RNode copier --- kyaml/yaml/rnode.go | 10 ++++++++ kyaml/yaml/rnode_test.go | 28 ++++++++++++++++++++++ kyaml/yaml/types.go | 17 ++++++++++++++ kyaml/yaml/types_test.go | 51 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+) diff --git a/kyaml/yaml/rnode.go b/kyaml/yaml/rnode.go index 6cefce016..982b452c4 100644 --- a/kyaml/yaml/rnode.go +++ b/kyaml/yaml/rnode.go @@ -168,6 +168,16 @@ type RNode struct { Match []string } +// Copy returns a distinct copy. +func (rn *RNode) Copy() *RNode { + if rn == nil { + return nil + } + result := *rn + result.value = CopyYNode(rn.value) + return &result +} + var ErrMissingMetadata = fmt.Errorf("missing Resource metadata") // Field names diff --git a/kyaml/yaml/rnode_test.go b/kyaml/yaml/rnode_test.go index ab4037ee6..295e180f7 100644 --- a/kyaml/yaml/rnode_test.go +++ b/kyaml/yaml/rnode_test.go @@ -4,6 +4,7 @@ package yaml import ( + "reflect" "strings" "testing" @@ -206,6 +207,33 @@ func TestIsMissingOrNull(t *testing.T) { } } +func TestCopy(t *testing.T) { + rn := RNode{ + fieldPath: []string{"fp1", "fp2"}, + value: &Node{ + Kind: 200, + }, + Match: []string{"m1", "m2"}, + } + rnC := rn.Copy() + if !reflect.DeepEqual(&rn, rnC) { + t.Fatalf("copy %v is not deep equal to %v", rnC, rn) + } + tmp := rn.value.Kind + rn.value.Kind = 666 + if reflect.DeepEqual(rn, rnC) { + t.Fatalf("changing component should break equality") + } + rn.value.Kind = tmp + if !reflect.DeepEqual(&rn, rnC) { + t.Fatalf("should be back to normal") + } + rn.fieldPath[0] = "Different" + if reflect.DeepEqual(rn, rnC) { + t.Fatalf("changing fieldpath should break equality") + } +} + func TestFieldRNodes(t *testing.T) { testCases := []struct { testName string diff --git a/kyaml/yaml/types.go b/kyaml/yaml/types.go index ab233fcba..e39086571 100644 --- a/kyaml/yaml/types.go +++ b/kyaml/yaml/types.go @@ -12,6 +12,23 @@ import ( "sigs.k8s.io/kustomize/kyaml/sets" ) +// CopyYNode returns a distinct copy of its argument. +// Use https://github.com/jinzhu/copier instead? +func CopyYNode(n *yaml.Node) *yaml.Node { + if n == nil { + return nil + } + c := *n + if len(n.Content) > 0 { + // Using Go 'copy' here doesn't yield independent slices. + c.Content = make([]*Node, len(n.Content)) + for i, item := range n.Content { + c.Content[i] = CopyYNode(item) + } + } + return &c +} + // IsYNodeTaggedNull returns true if the node is explicitly tagged Null. func IsYNodeTaggedNull(n *yaml.Node) bool { return n != nil && n.Tag == NodeTagNull diff --git a/kyaml/yaml/types_test.go b/kyaml/yaml/types_test.go index 8358fcd46..7182cac86 100644 --- a/kyaml/yaml/types_test.go +++ b/kyaml/yaml/types_test.go @@ -4,9 +4,60 @@ package yaml import ( + "reflect" "testing" ) +func TestCopyYNode(t *testing.T) { + ynSub1 := Node{ + Kind: 100, + } + ynSub2 := Node{ + Kind: 200, + } + ynSub3 := Node{ + Kind: 300, + } + yn := Node{ + Kind: 5000, + Style: 6000, + Tag: "red", + Value: "green", + Anchor: "blue", + Alias: &ynSub3, + Content: []*Node{&ynSub1, &ynSub2}, + HeadComment: "apple", + LineComment: "peach", + FootComment: "banana", + Line: 7000, + Column: 8000, + } + ynAddr := &yn + if !reflect.DeepEqual(&yn, ynAddr) { + t.Fatalf("truly %v should equal %v", &yn, ynAddr) + } + ynC := CopyYNode(&yn) + if !reflect.DeepEqual(yn.Content, ynC.Content) { + t.Fatalf("copy content %v is not deep equal to %v", ynC, yn) + } + if !reflect.DeepEqual(&yn, ynC) { + t.Fatalf("\noriginal: %v\n copy: %v\nShould be equal.", yn, ynC) + } + tmp := yn.Content[0].Kind + yn.Content[0].Kind = 666 + if reflect.DeepEqual(&yn, ynC) { + t.Fatalf("changing component should break equality") + } + yn.Content[0].Kind = tmp + if !reflect.DeepEqual(&yn, ynC) { + t.Fatalf("should be okay now") + } + yn.Tag = "Different" + if yn.Tag == ynC.Tag { + t.Fatalf("field aliased!") + } +} + func TestIsYNodeTaggedNull(t *testing.T) { if IsYNodeTaggedNull(nil) { t.Fatalf("nil cannot be tagged null")