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.
This commit is contained in:
Sam Dowell
2022-01-06 00:59:58 +00:00
parent c996d1fcab
commit fbd949a95d
2 changed files with 97 additions and 4 deletions

View File

@@ -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,

View File

@@ -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()
}
})
}
}