feat: Add SetEntryCallback to annotations filter

Add a configurable callback that is invoked each time an
annotation is applied by the annotations filter. This is useful
for scenarios such as tracking annotations as they are applied.

Issues: GoogleContainerTools/kpt#2448
This commit is contained in:
Sam Dowell
2021-11-24 22:36:04 +00:00
parent 01420768c8
commit 0fe1236e20
2 changed files with 108 additions and 5 deletions

View File

@@ -19,10 +19,25 @@ type Filter struct {
// FsSlice contains the FieldSpecs to locate the namespace field
FsSlice types.FsSlice
// SetEntryCallback is invoked each time an annotation is applied
// Example use cases:
// - Tracking all paths where annotations 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.Annotations)
_, err := kio.FilterAll(yaml.FilterFunc(
@@ -30,7 +45,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.Annotations[k], yaml.NodeTagString),
CreateKind: yaml.MappingNode, // Annotations are MappingNodes.
CreateTag: yaml.NodeTagMap,

View File

@@ -11,16 +11,36 @@ 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 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) {
testCases := map[string]struct {
input string
expectedOutput string
filter Filter
fsslice types.FsSlice
input string
expectedOutput string
filter Filter
fsslice types.FsSlice
expectedSetEntryArgs []setEntryArg
}{
"add": {
input: `
@@ -210,9 +230,74 @@ metadata:
"b": "b1",
}},
},
// test usage of SetEntryCallback
"set_entry_callback": {
input: `
apiVersion: example.com/v1
kind: Foo
metadata:
name: instance
`,
expectedOutput: `
apiVersion: example.com/v1
kind: Foo
metadata:
name: instance
annotations:
a: a1
b: b1
spec:
template:
metadata:
annotations:
a: a1
b: b1
`,
filter: Filter{
Annotations: annoMap{
"a": "a1",
"b": "b1",
},
SetEntryCallback: setEntryCallbackStub,
},
fsslice: []types.FieldSpec{
{
Path: "spec/template/metadata/annotations",
CreateIfNotPresent: true,
},
},
expectedSetEntryArgs: []setEntryArg{
{
Key: "a",
Value: "a1",
Tag: "!!str",
NodePath: []string{"metadata", "annotations"},
},
{
Key: "a",
Value: "a1",
Tag: "!!str",
NodePath: []string{"spec", "template", "metadata", "annotations"},
},
{
Key: "b",
Value: "b1",
Tag: "!!str",
NodePath: []string{"metadata", "annotations"},
},
{
Key: "b",
Value: "b1",
Tag: "!!str",
NodePath: []string{"spec", "template", "metadata", "annotations"},
},
},
},
}
for tn, tc := range testCases {
setEntryArgs = nil
t.Run(tn, func(t *testing.T) {
filter := tc.filter
filter.FsSlice = append(annosFs, tc.fsslice...)
@@ -221,6 +306,9 @@ metadata:
strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, filter))) {
t.FailNow()
}
if !assert.Equal(t, tc.expectedSetEntryArgs, setEntryArgs) {
t.FailNow()
}
})
}
}