From e3160373f05ad9f14261e4ac06a902ccab68233a Mon Sep 17 00:00:00 2001 From: Sam Dowell Date: Fri, 28 Jan 2022 19:33:03 +0000 Subject: [PATCH 1/5] test: add testutil for mutation tracker This change provides a common test util for a mutation tracker stub. This is intended to reduce the duplicated boilerplate as additional filters implement the TrackableFilter interface. --- api/filters/annotations/annotations_test.go | 29 +++---------- api/filters/imagetag/imagetag_test.go | 45 +++++++-------------- api/filters/labels/labels_test.go | 29 +++---------- api/testutils/filtertest/runfilter.go | 30 ++++++++++++++ 4 files changed, 56 insertions(+), 77 deletions(-) diff --git a/api/filters/annotations/annotations_test.go b/api/filters/annotations/annotations_test.go index 39fd8e2e2..d52cd673a 100644 --- a/api/filters/annotations/annotations_test.go +++ b/api/filters/annotations/annotations_test.go @@ -16,32 +16,15 @@ import ( var annosFs = builtinconfig.MakeDefaultConfig().CommonAnnotations -type setEntryArg struct { - Key string - Value string - Tag string - NodePath []string -} - -var setEntryArgs []setEntryArg - -func setEntryCallbackStub(key, value, tag string, node *yaml.RNode) { - setEntryArgs = append(setEntryArgs, setEntryArg{ - Key: key, - Value: value, - Tag: tag, - NodePath: node.FieldPath(), - }) -} - func TestAnnotations_Filter(t *testing.T) { + mutationTrackStub := filtertest_test.MutationTrackerStub{} testCases := map[string]struct { input string expectedOutput string filter Filter fsslice types.FsSlice setEntryCallback func(key, value, tag string, node *yaml.RNode) - expectedSetEntryArgs []setEntryArg + expectedSetEntryArgs []filtertest_test.SetValueArg }{ "add": { input: ` @@ -261,14 +244,14 @@ spec: "b": "b1", }, }, - setEntryCallback: setEntryCallbackStub, + setEntryCallback: mutationTrackStub.MutationTracker, fsslice: []types.FieldSpec{ { Path: "spec/template/metadata/annotations", CreateIfNotPresent: true, }, }, - expectedSetEntryArgs: []setEntryArg{ + expectedSetEntryArgs: []filtertest_test.SetValueArg{ { Key: "a", Value: "a1", @@ -298,7 +281,7 @@ spec: } for tn, tc := range testCases { - setEntryArgs = nil + mutationTrackStub.Reset() t.Run(tn, func(t *testing.T) { filter := tc.filter filter.WithMutationTracker(tc.setEntryCallback) @@ -308,7 +291,7 @@ spec: strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, filter))) { t.FailNow() } - if !assert.Equal(t, tc.expectedSetEntryArgs, setEntryArgs) { + if !assert.Equal(t, tc.expectedSetEntryArgs, mutationTrackStub.SetValueArgs()) { t.FailNow() } }) diff --git a/api/filters/imagetag/imagetag_test.go b/api/filters/imagetag/imagetag_test.go index a62f95a9f..d1f0bc1de 100644 --- a/api/filters/imagetag/imagetag_test.go +++ b/api/filters/imagetag/imagetag_test.go @@ -13,32 +13,15 @@ import ( "sigs.k8s.io/kustomize/kyaml/yaml" ) -type setValueArg struct { - Key string - Value string - Tag string - PrevValue string -} - -var setValueArgs []setValueArg - -func setValueCallbackStub(key, value, tag string, node *yaml.RNode) { - setValueArgs = append(setValueArgs, setValueArg{ - Key: key, - Value: value, - Tag: tag, - PrevValue: node.YNode().Value, - }) -} - func TestImageTagUpdater_Filter(t *testing.T) { + mutationTrackerStub := filtertest.MutationTrackerStub{} testCases := map[string]struct { input string expectedOutput string filter Filter fsSlice types.FsSlice setValueCallback func(key, value, tag string, node *yaml.RNode) - expectedSetValueArgs []setValueArg + expectedSetValueArgs []filtertest.SetValueArg }{ "ignore CustomResourceDefinition": { input: ` @@ -747,30 +730,30 @@ spec: Path: "spec/template/spec/initContainers[]/image", }, }, - setValueCallback: setValueCallbackStub, - expectedSetValueArgs: []setValueArg{ + setValueCallback: mutationTrackerStub.MutationTracker, + expectedSetValueArgs: []filtertest.SetValueArg{ { - Value: "busybox:v3", - PrevValue: "nginx:1.7.9", + Value: "busybox:v3", + NodePath: []string{"spec", "template", "spec", "containers", "image"}, }, { - Value: "busybox:v3", - PrevValue: "nginx:latest", + Value: "busybox:v3", + NodePath: []string{"spec", "template", "spec", "containers", "image"}, }, { - Value: "busybox:v3", - PrevValue: "nginx", + Value: "busybox:v3", + NodePath: []string{"spec", "template", "spec", "initContainers", "image"}, }, { - Value: "busybox:v3", - PrevValue: "nginx@sha256:111111111111111111", + Value: "busybox:v3", + NodePath: []string{"spec", "template", "spec", "initContainers", "image"}, }, }, }, } for tn, tc := range testCases { - setValueArgs = nil + mutationTrackerStub.Reset() t.Run(tn, func(t *testing.T) { filter := tc.filter filter.WithMutationTracker(tc.setValueCallback) @@ -780,7 +763,7 @@ spec: strings.TrimSpace(filtertest.RunFilter(t, tc.input, filter))) { t.FailNow() } - assert.Equal(t, tc.expectedSetValueArgs, setValueArgs) + assert.Equal(t, tc.expectedSetValueArgs, mutationTrackerStub.SetValueArgs()) }) } } diff --git a/api/filters/labels/labels_test.go b/api/filters/labels/labels_test.go index 45f4eff4e..0cdb0d126 100644 --- a/api/filters/labels/labels_test.go +++ b/api/filters/labels/labels_test.go @@ -14,31 +14,14 @@ import ( "sigs.k8s.io/kustomize/kyaml/yaml" ) -type setEntryArg struct { - Key string - Value string - Tag string - NodePath []string -} - -var setEntryArgs []setEntryArg - -func setEntryCallbackStub(key, value, tag string, node *yaml.RNode) { - setEntryArgs = append(setEntryArgs, setEntryArg{ - Key: key, - Value: value, - Tag: tag, - NodePath: node.FieldPath(), - }) -} - func TestLabels_Filter(t *testing.T) { + mutationTrackerStub := filtertest_test.MutationTrackerStub{} testCases := map[string]struct { input string expectedOutput string filter Filter setEntryCallback func(key, value, tag string, node *yaml.RNode) - expectedSetEntryArgs []setEntryArg + expectedSetEntryArgs []filtertest_test.SetValueArg }{ "add": { input: ` @@ -458,8 +441,8 @@ a: }, }, }, - setEntryCallback: setEntryCallbackStub, - expectedSetEntryArgs: []setEntryArg{ + setEntryCallback: mutationTrackerStub.MutationTracker, + expectedSetEntryArgs: []filtertest_test.SetValueArg{ { Key: "mage", Value: "yennefer", @@ -477,7 +460,7 @@ a: } for tn, tc := range testCases { - setEntryArgs = nil + mutationTrackerStub.Reset() t.Run(tn, func(t *testing.T) { tc.filter.WithMutationTracker(tc.setEntryCallback) if !assert.Equal(t, @@ -485,7 +468,7 @@ a: strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, tc.filter))) { t.FailNow() } - if !assert.Equal(t, tc.expectedSetEntryArgs, setEntryArgs) { + if !assert.Equal(t, tc.expectedSetEntryArgs, mutationTrackerStub.SetValueArgs()) { t.FailNow() } }) diff --git a/api/testutils/filtertest/runfilter.go b/api/testutils/filtertest/runfilter.go index 2cf775311..95f8d26c3 100644 --- a/api/testutils/filtertest/runfilter.go +++ b/api/testutils/filtertest/runfilter.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "sigs.k8s.io/kustomize/kyaml/kio" + "sigs.k8s.io/kustomize/kyaml/yaml" ) func run(input string, f kio.Filter) (string, error) { @@ -46,3 +47,32 @@ func RunFilterE(t *testing.T, input string, f kio.Filter) (string, error) { } return output, nil } + +type SetValueArg struct { + Key string + Value string + Tag string + NodePath []string +} + +// MutationTrackerStub to help stub a mutation tracker for kio.TrackableFilter +type MutationTrackerStub struct { + setValueArgs []SetValueArg +} + +func (mts *MutationTrackerStub) MutationTracker(key, value, tag string, node *yaml.RNode) { + mts.setValueArgs = append(mts.setValueArgs, SetValueArg{ + Key: key, + Value: value, + Tag: tag, + NodePath: node.FieldPath(), + }) +} + +func (mts *MutationTrackerStub) SetValueArgs() []SetValueArg { + return mts.setValueArgs +} + +func (mts *MutationTrackerStub) Reset() { + mts.setValueArgs = nil +} From 51b767b06e74a3664f505a3b1deff8b5c2d8f063 Mon Sep 17 00:00:00 2001 From: Sam Dowell Date: Fri, 28 Jan 2022 20:52:40 +0000 Subject: [PATCH 2/5] feat: implement TrackableFilter for namespace This change updates the namespace filter to implement the TrackableFilter interface. This provides the functionality for the user to track which fields were updated by the namespace filter. --- api/filters/namespace/namespace.go | 22 +++++-- api/filters/namespace/namespace_test.go | 84 +++++++++++++++++++++++-- 2 files changed, 96 insertions(+), 10 deletions(-) diff --git a/api/filters/namespace/namespace.go b/api/filters/namespace/namespace.go index e554600bc..85484bf70 100644 --- a/api/filters/namespace/namespace.go +++ b/api/filters/namespace/namespace.go @@ -18,9 +18,17 @@ type Filter struct { // FsSlice contains the FieldSpecs to locate the namespace field FsSlice types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` + + trackableSetter filtersutil.TrackableSetter } var _ kio.Filter = Filter{} +var _ kio.TrackableFilter = &Filter{} + +// WithMutationTracker registers a callback which will be invoked each time a field is mutated +func (ns *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) { + ns.trackableSetter.WithMutationTracker(callback) +} func (ns Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { return kio.FilterAll(yaml.FilterFunc(ns.run)).Filter(nodes) @@ -44,7 +52,7 @@ func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) { // transformations based on data -- :) err := node.PipeE(fsslice.Filter{ FsSlice: ns.FsSlice, - SetValue: filtersutil.SetScalar(ns.Namespace), + SetValue: ns.trackableSetter.SetScalar(ns.Namespace), CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode CreateTag: yaml.NodeTagString, }) @@ -77,7 +85,7 @@ func (ns Filter) metaNamespaceHack(obj *yaml.RNode, gvk resid.Gvk) error { FsSlice: []types.FieldSpec{ {Path: types.MetadataNamespacePath, CreateIfNotPresent: true}, }, - SetValue: filtersutil.SetScalar(ns.Namespace), + SetValue: ns.trackableSetter.SetScalar(ns.Namespace), CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode } _, err := f.Filter(obj) @@ -123,11 +131,15 @@ func (ns Filter) roleBindingHack(obj *yaml.RNode, gvk resid.Gvk) error { } // set the namespace for the default account - v := yaml.NewScalarRNode(ns.Namespace) - return o.PipeE( + node, err := o.Pipe( yaml.LookupCreate(yaml.ScalarNode, "namespace"), - yaml.FieldSetter{Value: v}, ) + if err != nil { + return err + } + + return ns.trackableSetter.SetScalar(ns.Namespace)(node) + }) return err diff --git a/api/filters/namespace/namespace_test.go b/api/filters/namespace/namespace_test.go index e9854811e..302abe035 100644 --- a/api/filters/namespace/namespace_test.go +++ b/api/filters/namespace/namespace_test.go @@ -12,8 +12,11 @@ import ( "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" ) +var mutationTrackerStub = filtertest_test.MutationTrackerStub{} + var tests = []TestCase{ { name: "add", @@ -283,21 +286,91 @@ a: }, }, }, + + { + name: "mutation tracker", + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: RoleBinding +subjects: +- name: default +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + namespace: bar +a: + b: + c: bar +--- +apiVersion: example.com/v1 +kind: RoleBinding +subjects: +- name: default + namespace: bar +metadata: + namespace: bar +a: + b: + c: bar +`, + filter: namespace.Filter{Namespace: "bar"}, + fsslice: []types.FieldSpec{ + { + Path: "a/b/c", + CreateIfNotPresent: true, + }, + }, + mutationTracker: mutationTrackerStub.MutationTracker, + expectedSetValueArgs: []filtertest_test.SetValueArg{ + { + Value: "bar", + NodePath: []string{"metadata", "namespace"}, + }, + { + Value: "bar", + NodePath: []string{"a", "b", "c"}, + }, + { + Value: "bar", + NodePath: []string{"metadata", "namespace"}, + }, + { + Value: "bar", + NodePath: []string{"namespace"}, + }, + { + Value: "bar", + NodePath: []string{"a", "b", "c"}, + }, + }, + }, } type TestCase struct { - name string - input string - expected string - filter namespace.Filter - fsslice types.FsSlice + name string + input string + expected string + filter namespace.Filter + fsslice types.FsSlice + mutationTracker func(key, value, tag string, node *yaml.RNode) + expectedSetValueArgs []filtertest_test.SetValueArg } var config = builtinconfig.MakeDefaultConfig() func TestNamespace_Filter(t *testing.T) { for i := range tests { + mutationTrackerStub.Reset() test := tests[i] + test.filter.WithMutationTracker(test.mutationTracker) t.Run(test.name, func(t *testing.T) { test.filter.FsSlice = append(config.NameSpace, test.fsslice...) if !assert.Equal(t, @@ -306,6 +379,7 @@ func TestNamespace_Filter(t *testing.T) { filtertest_test.RunFilter(t, test.input, test.filter))) { t.FailNow() } + assert.Equal(t, test.expectedSetValueArgs, mutationTrackerStub.SetValueArgs()) }) } } From 90493ec374741306183a33404f41ab61831cd940 Mon Sep 17 00:00:00 2001 From: Sam Dowell Date: Fri, 28 Jan 2022 20:53:45 +0000 Subject: [PATCH 3/5] feat: implement TrackableFilter for prefix This change updates the prefix filter to implement the TrackableFilter interface. This provides the functionality for the user to track which fields were updated by the prefix filter. --- api/filters/prefix/prefix.go | 10 +++++- api/filters/prefix/prefix_test.go | 54 +++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/api/filters/prefix/prefix.go b/api/filters/prefix/prefix.go index dc7fb134f..daa375d1f 100644 --- a/api/filters/prefix/prefix.go +++ b/api/filters/prefix/prefix.go @@ -18,9 +18,17 @@ type Filter struct { Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"` + + trackableSetter filtersutil.TrackableSetter } var _ kio.Filter = Filter{} +var _ kio.TrackableFilter = &Filter{} + +// WithMutationTracker registers a callback which will be invoked each time a field is mutated +func (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) { + f.trackableSetter.WithMutationTracker(callback) +} func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes) @@ -37,6 +45,6 @@ func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) { } func (f Filter) evaluateField(node *yaml.RNode) error { - return filtersutil.SetScalar(fmt.Sprintf( + return f.trackableSetter.SetScalar(fmt.Sprintf( "%s%s", f.Prefix, node.YNode().Value))(node) } diff --git a/api/filters/prefix/prefix_test.go b/api/filters/prefix/prefix_test.go index 2bcdbdeab..607aa757d 100644 --- a/api/filters/prefix/prefix_test.go +++ b/api/filters/prefix/prefix_test.go @@ -11,8 +11,11 @@ import ( "sigs.k8s.io/kustomize/api/filters/prefix" filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" ) +var mutationTrackerStub = filtertest_test.MutationTrackerStub{} + var tests = map[string]TestCase{ "prefix": { input: ` @@ -83,17 +86,61 @@ a: FieldSpec: types.FieldSpec{Path: "a/b/c"}, }, }, + + "mutation tracker": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: foo-instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: foo-instance +`, + filter: prefix.Filter{ + Prefix: "foo-", + FieldSpec: types.FieldSpec{Path: "metadata/name"}, + }, + mutationTracker: mutationTrackerStub.MutationTracker, + expectedSetValueArgs: []filtertest_test.SetValueArg{ + { + Value: "foo-instance", + NodePath: []string{"metadata", "name"}, + }, + { + Value: "foo-instance", + NodePath: []string{"metadata", "name"}, + }, + }, + }, } type TestCase struct { - input string - expected string - filter prefix.Filter + input string + expected string + filter prefix.Filter + mutationTracker func(key, value, tag string, node *yaml.RNode) + expectedSetValueArgs []filtertest_test.SetValueArg } func TestFilter(t *testing.T) { for name := range tests { + mutationTrackerStub.Reset() test := tests[name] + test.filter.WithMutationTracker(test.mutationTracker) t.Run(name, func(t *testing.T) { if !assert.Equal(t, strings.TrimSpace(test.expected), @@ -101,6 +148,7 @@ func TestFilter(t *testing.T) { filtertest_test.RunFilter(t, test.input, test.filter))) { t.FailNow() } + assert.Equal(t, test.expectedSetValueArgs, mutationTrackerStub.SetValueArgs()) }) } } From 851b3fc28c6ad7f415af58f76c79b64d6835a4bf Mon Sep 17 00:00:00 2001 From: Sam Dowell Date: Fri, 28 Jan 2022 20:54:32 +0000 Subject: [PATCH 4/5] feat: implement TrackableFilter for replicacount This change updates the replicacount filter to implement the TrackableFilter interface. This provides the functionality for the user to track which fields were updated by the replicacount filter. --- api/filters/replicacount/replicacount.go | 10 +++- api/filters/replicacount/replicacount_test.go | 46 +++++++++++++++++-- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/api/filters/replicacount/replicacount.go b/api/filters/replicacount/replicacount.go index 8a503966a..84005f26a 100644 --- a/api/filters/replicacount/replicacount.go +++ b/api/filters/replicacount/replicacount.go @@ -14,9 +14,17 @@ import ( type Filter struct { Replica types.Replica `json:"replica,omitempty" yaml:"replica,omitempty"` FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"` + + trackableSetter filtersutil.TrackableSetter } var _ kio.Filter = Filter{} +var _ kio.TrackableFilter = &Filter{} + +// WithMutationTracker registers a callback which will be invoked each time a field is mutated +func (rc *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) { + rc.trackableSetter.WithMutationTracker(callback) +} func (rc Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { return kio.FilterAll(yaml.FilterFunc(rc.run)).Filter(nodes) @@ -33,5 +41,5 @@ func (rc Filter) run(node *yaml.RNode) (*yaml.RNode, error) { } func (rc Filter) set(node *yaml.RNode) error { - return filtersutil.SetScalar(strconv.FormatInt(rc.Replica.Count, 10))(node) + return rc.trackableSetter.SetScalar(strconv.FormatInt(rc.Replica.Count, 10))(node) } diff --git a/api/filters/replicacount/replicacount_test.go b/api/filters/replicacount/replicacount_test.go index a61889e2d..e690192fc 100644 --- a/api/filters/replicacount/replicacount_test.go +++ b/api/filters/replicacount/replicacount_test.go @@ -7,14 +7,17 @@ import ( "github.com/stretchr/testify/assert" filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" ) func TestFilter(t *testing.T) { - + mutationTrackerStub := filtertest_test.MutationTrackerStub{} testCases := map[string]struct { - input string - expected string - filter Filter + input string + expected string + filter Filter + mutationTracker func(key, value, tag string, node *yaml.RNode) + expectedSetValueArgs []filtertest_test.SetValueArg }{ "update field": { input: ` @@ -161,9 +164,43 @@ spec: FieldSpec: types.FieldSpec{Path: "spec/template/replicas"}, }, }, + "mutation tracker": { + input: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +spec: + replicas: 5 +`, + expected: ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep +spec: + replicas: 42 +`, + filter: Filter{ + Replica: types.Replica{ + Name: "dep", + Count: 42, + }, + FieldSpec: types.FieldSpec{Path: "spec/replicas"}, + }, + mutationTracker: mutationTrackerStub.MutationTracker, + expectedSetValueArgs: []filtertest_test.SetValueArg{ + { + Value: "42", + NodePath: []string{"spec", "replicas"}, + }, + }, + }, } for tn, tc := range testCases { + mutationTrackerStub.Reset() + tc.filter.WithMutationTracker(tc.mutationTracker) t.Run(tn, func(t *testing.T) { if !assert.Equal(t, strings.TrimSpace(tc.expected), @@ -171,6 +208,7 @@ spec: filtertest_test.RunFilter(t, tc.input, tc.filter))) { t.FailNow() } + assert.Equal(t, tc.expectedSetValueArgs, mutationTrackerStub.SetValueArgs()) }) } } From ba55d95542505afeaf589cca25b8610c471c0a0c Mon Sep 17 00:00:00 2001 From: Sam Dowell Date: Fri, 28 Jan 2022 20:55:08 +0000 Subject: [PATCH 5/5] feat: implement TrackableFilter for suffix This change updates the suffix filter to implement the TrackableFilter interface. This provides the functionality for the user to track which fields were updated by the suffix filter. --- api/filters/suffix/suffix.go | 10 +++++- api/filters/suffix/suffix_test.go | 54 +++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/api/filters/suffix/suffix.go b/api/filters/suffix/suffix.go index a3df62557..babc257be 100644 --- a/api/filters/suffix/suffix.go +++ b/api/filters/suffix/suffix.go @@ -18,9 +18,17 @@ type Filter struct { Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"` FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"` + + trackableSetter filtersutil.TrackableSetter } var _ kio.Filter = Filter{} +var _ kio.TrackableFilter = &Filter{} + +// WithMutationTracker registers a callback which will be invoked each time a field is mutated +func (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) { + f.trackableSetter.WithMutationTracker(callback) +} func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes) @@ -37,6 +45,6 @@ func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) { } func (f Filter) evaluateField(node *yaml.RNode) error { - return filtersutil.SetScalar(fmt.Sprintf( + return f.trackableSetter.SetScalar(fmt.Sprintf( "%s%s", node.YNode().Value, f.Suffix))(node) } diff --git a/api/filters/suffix/suffix_test.go b/api/filters/suffix/suffix_test.go index 94020ed94..53489c746 100644 --- a/api/filters/suffix/suffix_test.go +++ b/api/filters/suffix/suffix_test.go @@ -11,8 +11,11 @@ import ( "sigs.k8s.io/kustomize/api/filters/suffix" filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" ) +var mutationTrackerStub = filtertest_test.MutationTrackerStub{} + var tests = map[string]TestCase{ "suffix": { input: ` @@ -83,17 +86,61 @@ a: FieldSpec: types.FieldSpec{Path: "a/b/c"}, }, }, + + "mutation tracker": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance-foo +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance-foo +`, + filter: suffix.Filter{ + Suffix: "-foo", + FieldSpec: types.FieldSpec{Path: "metadata/name"}, + }, + mutationTracker: mutationTrackerStub.MutationTracker, + expectedSetValueArgs: []filtertest_test.SetValueArg{ + { + Value: "instance-foo", + NodePath: []string{"metadata", "name"}, + }, + { + Value: "instance-foo", + NodePath: []string{"metadata", "name"}, + }, + }, + }, } type TestCase struct { - input string - expected string - filter suffix.Filter + input string + expected string + filter suffix.Filter + mutationTracker func(key, value, tag string, node *yaml.RNode) + expectedSetValueArgs []filtertest_test.SetValueArg } func TestFilter(t *testing.T) { for name := range tests { + mutationTrackerStub.Reset() test := tests[name] + test.filter.WithMutationTracker(test.mutationTracker) t.Run(name, func(t *testing.T) { if !assert.Equal(t, strings.TrimSpace(test.expected), @@ -101,6 +148,7 @@ func TestFilter(t *testing.T) { filtertest_test.RunFilter(t, test.input, test.filter))) { t.FailNow() } + assert.Equal(t, test.expectedSetValueArgs, mutationTrackerStub.SetValueArgs()) }) } }