From fbd949a95daf3012b94bd706de77ffb480723105 Mon Sep 17 00:00:00 2001 From: Sam Dowell Date: Thu, 6 Jan 2022 00:59:58 +0000 Subject: [PATCH] feat: add SetEntryCallback to labels filter Add a configurable callback that is invoked each time a label is applied by the labels filter. This is useful for scenarios such as tracking labels as they are applied. --- api/filters/labels/labels.go | 17 ++++++- api/filters/labels/labels_test.go | 84 +++++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 4 deletions(-) diff --git a/api/filters/labels/labels.go b/api/filters/labels/labels.go index 92f298bfe..6b19369d9 100644 --- a/api/filters/labels/labels.go +++ b/api/filters/labels/labels.go @@ -20,10 +20,25 @@ type Filter struct { // FsSlice identifies the label fields. FsSlice types.FsSlice + + // SetEntryCallback is invoked each time a label is applied + // Example use cases: + // - Tracking all paths where labels have been applied + SetEntryCallback func(key, value, tag string, node *yaml.RNode) } var _ kio.Filter = Filter{} +func (f Filter) setEntry(key, value, tag string) filtersutil.SetFn { + baseSetEntryFunc := filtersutil.SetEntry(key, value, tag) + return func(node *yaml.RNode) error { + if f.SetEntryCallback != nil { + f.SetEntryCallback(key, value, tag, node) + } + return baseSetEntryFunc(node) + } +} + func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { keys := yaml.SortedMapKeys(f.Labels) _, err := kio.FilterAll(yaml.FilterFunc( @@ -31,7 +46,7 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { for _, k := range keys { if err := node.PipeE(fsslice.Filter{ FsSlice: f.FsSlice, - SetValue: filtersutil.SetEntry( + SetValue: f.setEntry( k, f.Labels[k], yaml.NodeTagString), CreateKind: yaml.MappingNode, // Labels are MappingNodes. CreateTag: yaml.NodeTagMap, diff --git a/api/filters/labels/labels_test.go b/api/filters/labels/labels_test.go index c84b5421a..77eb7e5c1 100644 --- a/api/filters/labels/labels_test.go +++ b/api/filters/labels/labels_test.go @@ -11,13 +11,33 @@ import ( filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/kyaml/resid" + "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) { testCases := map[string]struct { - input string - expectedOutput string - filter Filter + input string + expectedOutput string + filter Filter + expectedSetEntryArgs []setEntryArg }{ "add": { input: ` @@ -399,15 +419,73 @@ metadata: }, }, }, + + // test usage of SetEntryCallback + "set_entry_callback": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + witcher: geralt +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + witcher: geralt + mage: yennefer +a: + b: + mage: yennefer +`, + filter: Filter{ + Labels: labelMap{ + "mage": "yennefer", + }, + FsSlice: []types.FieldSpec{ + { + Path: "metadata/labels", + CreateIfNotPresent: true, + }, + { + Path: "a/b", + CreateIfNotPresent: true, + }, + }, + SetEntryCallback: setEntryCallbackStub, + }, + expectedSetEntryArgs: []setEntryArg{ + { + Key: "mage", + Value: "yennefer", + Tag: "!!str", + NodePath: []string{"metadata", "labels"}, + }, + { + Key: "mage", + Value: "yennefer", + Tag: "!!str", + NodePath: []string{"a", "b"}, + }, + }, + }, } for tn, tc := range testCases { + setEntryArgs = nil t.Run(tn, func(t *testing.T) { if !assert.Equal(t, strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, tc.filter))) { t.FailNow() } + if !assert.Equal(t, tc.expectedSetEntryArgs, setEntryArgs) { + t.FailNow() + } }) } }