mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-09-15 12:18:57 +00:00
Add TrackableFilter interface (#4410)
* add kio filter interface This interface is an extension of the Filter interface which can be used for filters which are capable of tracking which fields they mutate. * add TrackableSetter struct to filtersutil This struct provides an abstraction to help Filters implement the TrackableFilter interface * implement TrackableFilter with annotations This updates the annotations filter to implement the TrackableFilter interface by reusing the TrackableSetter abstraction provided by filtersutil. This is done to provide a generic and consistent experience across the filters * implement TrackableFilter with labels This updates the labels filter to implement the TrackableFilter interface by reusing the TrackableSetter abstraction provided by filtersutil. This is done to provide a generic and consistent experience across the filters
This commit is contained in:
@@ -20,22 +20,15 @@ type Filter struct {
|
|||||||
// FsSlice contains the FieldSpecs to locate the namespace field
|
// FsSlice contains the FieldSpecs to locate the namespace field
|
||||||
FsSlice types.FsSlice
|
FsSlice types.FsSlice
|
||||||
|
|
||||||
// SetEntryCallback is invoked each time an annotation is applied
|
trackableSetter filtersutil.TrackableSetter
|
||||||
// Example use cases:
|
|
||||||
// - Tracking all paths where annotations have been applied
|
|
||||||
SetEntryCallback func(key, value, tag string, node *yaml.RNode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ kio.Filter = Filter{}
|
var _ kio.Filter = Filter{}
|
||||||
|
var _ kio.TrackableFilter = &Filter{}
|
||||||
|
|
||||||
func (f Filter) setEntry(key, value, tag string) filtersutil.SetFn {
|
// WithMutationTracker registers a callback which will be invoked each time a field is mutated
|
||||||
baseSetEntryFunc := filtersutil.SetEntry(key, value, tag)
|
func (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) {
|
||||||
return func(node *yaml.RNode) error {
|
f.trackableSetter.WithMutationTracker(callback)
|
||||||
if f.SetEntryCallback != nil {
|
|
||||||
f.SetEntryCallback(key, value, tag, node)
|
|
||||||
}
|
|
||||||
return baseSetEntryFunc(node)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
@@ -45,7 +38,7 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
|||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
if err := node.PipeE(fsslice.Filter{
|
if err := node.PipeE(fsslice.Filter{
|
||||||
FsSlice: f.FsSlice,
|
FsSlice: f.FsSlice,
|
||||||
SetValue: f.setEntry(
|
SetValue: f.trackableSetter.SetEntry(
|
||||||
k, f.Annotations[k], yaml.NodeTagString),
|
k, f.Annotations[k], yaml.NodeTagString),
|
||||||
CreateKind: yaml.MappingNode, // Annotations are MappingNodes.
|
CreateKind: yaml.MappingNode, // Annotations are MappingNodes.
|
||||||
CreateTag: yaml.NodeTagMap,
|
CreateTag: yaml.NodeTagMap,
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ func TestAnnotations_Filter(t *testing.T) {
|
|||||||
expectedOutput string
|
expectedOutput string
|
||||||
filter Filter
|
filter Filter
|
||||||
fsslice types.FsSlice
|
fsslice types.FsSlice
|
||||||
|
setEntryCallback func(key, value, tag string, node *yaml.RNode)
|
||||||
expectedSetEntryArgs []setEntryArg
|
expectedSetEntryArgs []setEntryArg
|
||||||
}{
|
}{
|
||||||
"add": {
|
"add": {
|
||||||
@@ -259,8 +260,8 @@ spec:
|
|||||||
"a": "a1",
|
"a": "a1",
|
||||||
"b": "b1",
|
"b": "b1",
|
||||||
},
|
},
|
||||||
SetEntryCallback: setEntryCallbackStub,
|
|
||||||
},
|
},
|
||||||
|
setEntryCallback: setEntryCallbackStub,
|
||||||
fsslice: []types.FieldSpec{
|
fsslice: []types.FieldSpec{
|
||||||
{
|
{
|
||||||
Path: "spec/template/metadata/annotations",
|
Path: "spec/template/metadata/annotations",
|
||||||
@@ -300,6 +301,7 @@ spec:
|
|||||||
setEntryArgs = nil
|
setEntryArgs = nil
|
||||||
t.Run(tn, func(t *testing.T) {
|
t.Run(tn, func(t *testing.T) {
|
||||||
filter := tc.filter
|
filter := tc.filter
|
||||||
|
filter.WithMutationTracker(tc.setEntryCallback)
|
||||||
filter.FsSlice = append(annosFs, tc.fsslice...)
|
filter.FsSlice = append(annosFs, tc.fsslice...)
|
||||||
if !assert.Equal(t,
|
if !assert.Equal(t,
|
||||||
strings.TrimSpace(tc.expectedOutput),
|
strings.TrimSpace(tc.expectedOutput),
|
||||||
|
|||||||
@@ -31,3 +31,39 @@ func SetEntry(key, value, tag string) SetFn {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TrackableSetter struct {
|
||||||
|
// SetValueCallback will be invoked each time a field is set
|
||||||
|
setValueCallback func(key, value, tag string, node *yaml.RNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMutationTracker registers a callback which will be invoked each time a field is mutated
|
||||||
|
func (s *TrackableSetter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) {
|
||||||
|
s.setValueCallback = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetScalar returns a SetFn to set a scalar value
|
||||||
|
// if a mutation tracker has been registered, the tracker will be invoked each
|
||||||
|
// time a scalar is set
|
||||||
|
func (s TrackableSetter) SetScalar(value string) SetFn {
|
||||||
|
origSetScalar := SetScalar(value)
|
||||||
|
return func(node *yaml.RNode) error {
|
||||||
|
if s.setValueCallback != nil {
|
||||||
|
s.setValueCallback("", value, "", node)
|
||||||
|
}
|
||||||
|
return origSetScalar(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEntry returns a SetFn to set an entry in a map
|
||||||
|
// if a mutation tracker has been registered, the tracker will be invoked each
|
||||||
|
// time an entry is set
|
||||||
|
func (s TrackableSetter) SetEntry(key, value, tag string) SetFn {
|
||||||
|
origSetEntry := SetEntry(key, value, tag)
|
||||||
|
return func(node *yaml.RNode) error {
|
||||||
|
if s.setValueCallback != nil {
|
||||||
|
s.setValueCallback(key, value, tag, node)
|
||||||
|
}
|
||||||
|
return origSetEntry(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,22 +21,15 @@ type Filter struct {
|
|||||||
// FsSlice identifies the label fields.
|
// FsSlice identifies the label fields.
|
||||||
FsSlice types.FsSlice
|
FsSlice types.FsSlice
|
||||||
|
|
||||||
// SetEntryCallback is invoked each time a label is applied
|
trackableSetter filtersutil.TrackableSetter
|
||||||
// Example use cases:
|
|
||||||
// - Tracking all paths where labels have been applied
|
|
||||||
SetEntryCallback func(key, value, tag string, node *yaml.RNode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ kio.Filter = Filter{}
|
var _ kio.Filter = Filter{}
|
||||||
|
var _ kio.TrackableFilter = &Filter{}
|
||||||
|
|
||||||
func (f Filter) setEntry(key, value, tag string) filtersutil.SetFn {
|
// WithMutationTracker registers a callback which will be invoked each time a field is mutated
|
||||||
baseSetEntryFunc := filtersutil.SetEntry(key, value, tag)
|
func (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) {
|
||||||
return func(node *yaml.RNode) error {
|
f.trackableSetter.WithMutationTracker(callback)
|
||||||
if f.SetEntryCallback != nil {
|
|
||||||
f.SetEntryCallback(key, value, tag, node)
|
|
||||||
}
|
|
||||||
return baseSetEntryFunc(node)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
@@ -46,7 +39,7 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
|||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
if err := node.PipeE(fsslice.Filter{
|
if err := node.PipeE(fsslice.Filter{
|
||||||
FsSlice: f.FsSlice,
|
FsSlice: f.FsSlice,
|
||||||
SetValue: f.setEntry(
|
SetValue: f.trackableSetter.SetEntry(
|
||||||
k, f.Labels[k], yaml.NodeTagString),
|
k, f.Labels[k], yaml.NodeTagString),
|
||||||
CreateKind: yaml.MappingNode, // Labels are MappingNodes.
|
CreateKind: yaml.MappingNode, // Labels are MappingNodes.
|
||||||
CreateTag: yaml.NodeTagMap,
|
CreateTag: yaml.NodeTagMap,
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ func TestLabels_Filter(t *testing.T) {
|
|||||||
input string
|
input string
|
||||||
expectedOutput string
|
expectedOutput string
|
||||||
filter Filter
|
filter Filter
|
||||||
|
setEntryCallback func(key, value, tag string, node *yaml.RNode)
|
||||||
expectedSetEntryArgs []setEntryArg
|
expectedSetEntryArgs []setEntryArg
|
||||||
}{
|
}{
|
||||||
"add": {
|
"add": {
|
||||||
@@ -456,8 +457,8 @@ a:
|
|||||||
CreateIfNotPresent: true,
|
CreateIfNotPresent: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
SetEntryCallback: setEntryCallbackStub,
|
|
||||||
},
|
},
|
||||||
|
setEntryCallback: setEntryCallbackStub,
|
||||||
expectedSetEntryArgs: []setEntryArg{
|
expectedSetEntryArgs: []setEntryArg{
|
||||||
{
|
{
|
||||||
Key: "mage",
|
Key: "mage",
|
||||||
@@ -478,6 +479,7 @@ a:
|
|||||||
for tn, tc := range testCases {
|
for tn, tc := range testCases {
|
||||||
setEntryArgs = nil
|
setEntryArgs = nil
|
||||||
t.Run(tn, func(t *testing.T) {
|
t.Run(tn, func(t *testing.T) {
|
||||||
|
tc.filter.WithMutationTracker(tc.setEntryCallback)
|
||||||
if !assert.Equal(t,
|
if !assert.Equal(t,
|
||||||
strings.TrimSpace(tc.expectedOutput),
|
strings.TrimSpace(tc.expectedOutput),
|
||||||
strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, tc.filter))) {
|
strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, tc.filter))) {
|
||||||
|
|||||||
@@ -57,6 +57,13 @@ type Filter interface {
|
|||||||
Filter([]*yaml.RNode) ([]*yaml.RNode, error)
|
Filter([]*yaml.RNode) ([]*yaml.RNode, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TrackableFilter is an extension of Filter which is also capable of tracking
|
||||||
|
// which fields were mutated by the filter.
|
||||||
|
type TrackableFilter interface {
|
||||||
|
Filter
|
||||||
|
WithMutationTracker(func(key, value, tag string, node *yaml.RNode))
|
||||||
|
}
|
||||||
|
|
||||||
// FilterFunc implements a Filter as a function.
|
// FilterFunc implements a Filter as a function.
|
||||||
type FilterFunc func([]*yaml.RNode) ([]*yaml.RNode, error)
|
type FilterFunc func([]*yaml.RNode) ([]*yaml.RNode, error)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user