From 51b767b06e74a3664f505a3b1deff8b5c2d8f063 Mon Sep 17 00:00:00 2001 From: Sam Dowell Date: Fri, 28 Jan 2022 20:52:40 +0000 Subject: [PATCH] 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()) }) } }