From bf97d23a0013fe95f5f3bf38fc267a81b72ee169 Mon Sep 17 00:00:00 2001 From: Sam Dowell Date: Thu, 13 Jan 2022 00:30:37 +0000 Subject: [PATCH 1/2] refactor: use SetScalar in imagetag filter This change refactors imagetag to reuse the abstraction provided by filtersutil. This change is intended to make imagetag more consistent with other filters by using the same layer of abstraction (filtersutil), and to prepare for upcoming changes which are planned to be implemented at the filtersutil layer. --- api/filters/imagetag/imagetag.go | 13 +++---------- api/filters/imagetag/updater.go | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/api/filters/imagetag/imagetag.go b/api/filters/imagetag/imagetag.go index 0770317544..699fdc18da 100644 --- a/api/filters/imagetag/imagetag.go +++ b/api/filters/imagetag/imagetag.go @@ -4,7 +4,6 @@ package imagetag import ( - "sigs.k8s.io/kustomize/api/filters/filtersutil" "sigs.k8s.io/kustomize/api/filters/fsslice" "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/kyaml/kio" @@ -41,7 +40,9 @@ func (f Filter) filter(node *yaml.RNode) (*yaml.RNode, error) { } if err := node.PipeE(fsslice.Filter{ FsSlice: f.FsSlice, - SetValue: updateImageTagFn(f.ImageTag), + SetValue: imageTagUpdater{ + ImageTag: f.ImageTag, + }.SetImageValue, }); err != nil { return nil, err } @@ -59,11 +60,3 @@ func (f Filter) isOnDenyList(node *yaml.RNode) bool { // https://github.com/kubernetes-sigs/kustomize/issues/890 return meta.Kind == `CustomResourceDefinition` } - -func updateImageTagFn(imageTag types.Image) filtersutil.SetFn { - return func(node *yaml.RNode) error { - return node.PipeE(imageTagUpdater{ - ImageTag: imageTag, - }) - } -} diff --git a/api/filters/imagetag/updater.go b/api/filters/imagetag/updater.go index 1c3637cdec..af8de43938 100644 --- a/api/filters/imagetag/updater.go +++ b/api/filters/imagetag/updater.go @@ -4,6 +4,7 @@ package imagetag import ( + "sigs.k8s.io/kustomize/api/filters/filtersutil" "sigs.k8s.io/kustomize/api/image" "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/kyaml/yaml" @@ -17,15 +18,15 @@ type imageTagUpdater struct { ImageTag types.Image `yaml:"imageTag,omitempty"` } -func (u imageTagUpdater) Filter(rn *yaml.RNode) (*yaml.RNode, error) { +func (u imageTagUpdater) SetImageValue(rn *yaml.RNode) error { if err := yaml.ErrorIfInvalid(rn, yaml.ScalarNode); err != nil { - return nil, err + return err } value := rn.YNode().Value if !image.IsImageMatched(value, u.ImageTag.Name) { - return rn, nil + return nil } name, tag := image.Split(value) @@ -39,5 +40,12 @@ func (u imageTagUpdater) Filter(rn *yaml.RNode) (*yaml.RNode, error) { tag = "@" + u.ImageTag.Digest } - return rn.Pipe(yaml.FieldSetter{StringValue: name + tag}) + return filtersutil.SetScalar(name + tag)(rn) +} + +func (u imageTagUpdater) Filter(rn *yaml.RNode) (*yaml.RNode, error) { + if err := u.SetImageValue(rn); err != nil { + return nil, err + } + return rn, nil } From 302cc866adcc6f4db00180fda9c3689b22893b97 Mon Sep 17 00:00:00 2001 From: Sam Dowell Date: Mon, 24 Jan 2022 22:37:31 +0000 Subject: [PATCH 2/2] implement TrackableFilter interface with imagetag This change updates the imagetag filter to implement the TrackableFilter interface. This provides the functionality for the user to track which fields were updated by the imagetag filter. --- api/filters/imagetag/imagetag.go | 14 ++- api/filters/imagetag/imagetag_test.go | 120 +++++++++++++++++++++++++- api/filters/imagetag/updater.go | 7 +- 3 files changed, 132 insertions(+), 9 deletions(-) diff --git a/api/filters/imagetag/imagetag.go b/api/filters/imagetag/imagetag.go index 699fdc18da..24ab99f740 100644 --- a/api/filters/imagetag/imagetag.go +++ b/api/filters/imagetag/imagetag.go @@ -4,6 +4,7 @@ package imagetag import ( + "sigs.k8s.io/kustomize/api/filters/filtersutil" "sigs.k8s.io/kustomize/api/filters/fsslice" "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/kyaml/kio" @@ -22,9 +23,17 @@ type Filter struct { // FsSlice contains the FieldSpecs to locate an image field, // e.g. Path: "spec/myContainers[]/image" 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 (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) { + f.trackableSetter.WithMutationTracker(callback) +} func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { _, err := kio.FilterAll(yaml.FilterFunc(f.filter)).Filter(nodes) @@ -39,9 +48,10 @@ func (f Filter) filter(node *yaml.RNode) (*yaml.RNode, error) { return node, nil } if err := node.PipeE(fsslice.Filter{ - FsSlice: f.FsSlice, + FsSlice: f.FsSlice, SetValue: imageTagUpdater{ - ImageTag: f.ImageTag, + ImageTag: f.ImageTag, + trackableSetter: f.trackableSetter, }.SetImageValue, }); err != nil { return nil, err diff --git a/api/filters/imagetag/imagetag_test.go b/api/filters/imagetag/imagetag_test.go index 96d68366a9..a62f95a9fa 100644 --- a/api/filters/imagetag/imagetag_test.go +++ b/api/filters/imagetag/imagetag_test.go @@ -10,14 +10,35 @@ import ( "github.com/stretchr/testify/assert" filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest" "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/kustomize/kyaml/yaml" ) +type setValueArg struct { + Key string + Value string + Tag string + PrevValue string +} + +var setValueArgs []setValueArg + +func setValueCallbackStub(key, value, tag string, node *yaml.RNode) { + setValueArgs = append(setValueArgs, setValueArg{ + Key: key, + Value: value, + Tag: tag, + PrevValue: node.YNode().Value, + }) +} + func TestImageTagUpdater_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 + setValueCallback func(key, value, tag string, node *yaml.RNode) + expectedSetValueArgs []setValueArg }{ "ignore CustomResourceDefinition": { input: ` @@ -658,17 +679,108 @@ spec: }, }, }, + "mutation tracker": { + input: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: nginx:1.7.9 + name: nginx-tagged + - image: nginx:latest + name: nginx-latest + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb + initContainers: + - image: nginx + name: nginx-notag + - image: nginx@sha256:111111111111111111 + name: nginx-sha256 + - image: alpine:1.8.0 + name: init-alpine +`, + expectedOutput: ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + template: + spec: + containers: + - image: busybox:v3 + name: nginx-tagged + - image: busybox:v3 + name: nginx-latest + - image: foobar:1 + name: replaced-with-digest + - image: postgres:1.8.0 + name: postgresdb + initContainers: + - image: busybox:v3 + name: nginx-notag + - image: busybox:v3 + name: nginx-sha256 + - image: alpine:1.8.0 + name: init-alpine +`, + filter: Filter{ + ImageTag: types.Image{ + Name: "nginx", + NewName: "busybox", + NewTag: "v3", + }, + }, + fsSlice: []types.FieldSpec{ + { + Path: "spec/template/spec/containers[]/image", + }, + { + Path: "spec/template/spec/initContainers[]/image", + }, + }, + setValueCallback: setValueCallbackStub, + expectedSetValueArgs: []setValueArg{ + { + Value: "busybox:v3", + PrevValue: "nginx:1.7.9", + }, + { + Value: "busybox:v3", + PrevValue: "nginx:latest", + }, + { + Value: "busybox:v3", + PrevValue: "nginx", + }, + { + Value: "busybox:v3", + PrevValue: "nginx@sha256:111111111111111111", + }, + }, + }, } for tn, tc := range testCases { + setValueArgs = nil t.Run(tn, func(t *testing.T) { filter := tc.filter + filter.WithMutationTracker(tc.setValueCallback) filter.FsSlice = tc.fsSlice if !assert.Equal(t, strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(filtertest.RunFilter(t, tc.input, filter))) { t.FailNow() } + assert.Equal(t, tc.expectedSetValueArgs, setValueArgs) }) } } diff --git a/api/filters/imagetag/updater.go b/api/filters/imagetag/updater.go index af8de43938..50c0dcdc8b 100644 --- a/api/filters/imagetag/updater.go +++ b/api/filters/imagetag/updater.go @@ -14,8 +14,9 @@ import ( // that will update the value of the yaml node based on the provided // ImageTag if the current value matches the format of an image reference. type imageTagUpdater struct { - Kind string `yaml:"kind,omitempty"` - ImageTag types.Image `yaml:"imageTag,omitempty"` + Kind string `yaml:"kind,omitempty"` + ImageTag types.Image `yaml:"imageTag,omitempty"` + trackableSetter filtersutil.TrackableSetter } func (u imageTagUpdater) SetImageValue(rn *yaml.RNode) error { @@ -40,7 +41,7 @@ func (u imageTagUpdater) SetImageValue(rn *yaml.RNode) error { tag = "@" + u.ImageTag.Digest } - return filtersutil.SetScalar(name + tag)(rn) + return u.trackableSetter.SetScalar(name + tag)(rn) } func (u imageTagUpdater) Filter(rn *yaml.RNode) (*yaml.RNode, error) {