From a4d61b8ea7cc2ef41c4568d984813884e430de0c Mon Sep 17 00:00:00 2001 From: laxmikantbpandhare Date: Thu, 22 Dec 2022 12:22:39 -0800 Subject: [PATCH 1/3] rebase Rebase laxmikantbpandhare commits on top of master --- .../builtins/PatchJson6902Transformer.go | 7 + api/internal/builtins/PatchTransformer.go | 7 + api/krusty/extendedpatch_test.go | 198 ++++-------------- .../PatchJson6902Transformer.go | 5 + .../patchtransformer/PatchTransformer.go | 5 + 5 files changed, 70 insertions(+), 152 deletions(-) diff --git a/api/internal/builtins/PatchJson6902Transformer.go b/api/internal/builtins/PatchJson6902Transformer.go index 04625e5109..bbf89f5ddc 100644 --- a/api/internal/builtins/PatchJson6902Transformer.go +++ b/api/internal/builtins/PatchJson6902Transformer.go @@ -24,6 +24,8 @@ type PatchJson6902TransformerPlugin struct { JsonOp string `json:"jsonOp,omitempty" yaml:"jsonOp,omitempty"` } +// noinspection GoUnusedGlobalVariable + func (p *PatchJson6902TransformerPlugin) Config( h *resmap.PluginHelpers, c []byte) (err error) { p.ldr = h.Loader() @@ -78,6 +80,11 @@ func (p *PatchJson6902TransformerPlugin) Transform(m resmap.ResMap) error { if err != nil { return err } + + if len(resources) == 0 { + return fmt.Errorf("patchesJson6902 target not found for %s", p.Target.ResId) + } + for _, res := range resources { internalAnnotations := kioutil.GetInternalAnnotations(&res.RNode) diff --git a/api/internal/builtins/PatchTransformer.go b/api/internal/builtins/PatchTransformer.go index 6161ada864..d31e6daff4 100644 --- a/api/internal/builtins/PatchTransformer.go +++ b/api/internal/builtins/PatchTransformer.go @@ -30,6 +30,8 @@ type PatchTransformerPlugin struct { Options *types.PatchArgs `json:"options,omitempty" yaml:"options,omitempty"` } +// noinspection GoUnusedGlobalVariable + func (p *PatchTransformerPlugin) Config(h *resmap.PluginHelpers, c []byte) error { if err := yaml.Unmarshal(c, p); err != nil { return err @@ -134,6 +136,11 @@ func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap) error { if err != nil { return err } + + if len(resources) == 0 { + return fmt.Errorf("patches target not found for %s", p.Target.ResId) + } + for _, res := range resources { res.StorePreviousId() internalAnnotations := kioutil.GetInternalAnnotations(&res.RNode) diff --git a/api/krusty/extendedpatch_test.go b/api/krusty/extendedpatch_test.go index 955082f617..f229806822 100644 --- a/api/krusty/extendedpatch_test.go +++ b/api/krusty/extendedpatch_test.go @@ -6,6 +6,7 @@ package krusty_test import ( "testing" + "github.com/stretchr/testify/assert" kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" ) @@ -821,82 +822,8 @@ metadata: annotations: new-key: new-value `) - m := th.Run("base", th.MakeDefaultOptions()) - th.AssertActualEqualsExpected(m, ` -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: nginx - name: nginx -spec: - template: - metadata: - labels: - app: nginx - spec: - containers: - - image: nginx - name: nginx - volumeMounts: - - mountPath: /tmp/ps - name: nginx-persistent-storage - volumes: - - emptyDir: {} - name: nginx-persistent-storage - - configMap: - name: configmap-in-base - name: configmap-in-base ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: busybox - name: busybox -spec: - template: - metadata: - labels: - app: busybox - spec: - containers: - - image: busybox - name: busybox - volumeMounts: - - mountPath: /tmp/ps - name: busybox-persistent-storage - volumes: - - emptyDir: {} - name: busybox-persistent-storage - - configMap: - name: configmap-in-base - name: configmap-in-base ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app: nginx - name: nginx -spec: - ports: - - port: 80 - selector: - app: nginx ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app: busybox - name: busybox -spec: - ports: - - port: 8080 - selector: - app: busybox -`) + err := th.RunWithErr("base", th.MakeDefaultOptions()) + assert.Contains(t, err.Error(), "patches target not found for [noKind].[noVer].[noGrp]/no-match.[noNs]") } func TestExtendedPatchWithoutTarget(t *testing.T) { @@ -1021,82 +948,8 @@ metadata: annotations: new-key: new-value `) - m := th.Run("base", th.MakeDefaultOptions()) - th.AssertActualEqualsExpected(m, ` -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: nginx - name: nginx -spec: - template: - metadata: - labels: - app: nginx - spec: - containers: - - image: nginx - name: nginx - volumeMounts: - - mountPath: /tmp/ps - name: nginx-persistent-storage - volumes: - - emptyDir: {} - name: nginx-persistent-storage - - configMap: - name: configmap-in-base - name: configmap-in-base ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: busybox - name: busybox -spec: - template: - metadata: - labels: - app: busybox - spec: - containers: - - image: busybox - name: busybox - volumeMounts: - - mountPath: /tmp/ps - name: busybox-persistent-storage - volumes: - - emptyDir: {} - name: busybox-persistent-storage - - configMap: - name: configmap-in-base - name: configmap-in-base ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app: nginx - name: nginx -spec: - ports: - - port: 80 - selector: - app: nginx ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app: busybox - name: busybox -spec: - ports: - - port: 8080 - selector: - app: busybox -`) + err := th.RunWithErr("base", th.MakeDefaultOptions()) + assert.Contains(t, err.Error(), "patches target not found for [noKind].[noVer].[noGrp]/no-match.[noNs]") } func TestExtendedPatchMultiplePatchOverlapping(t *testing.T) { @@ -1213,3 +1066,44 @@ spec: app: busybox `) } + +func TestTargetMissingPatchJson6902Error(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- servicemonitor.yaml +patchesJson6902: +- target: + group: monitoring.coreos.com + kind: ServiceMonitor + name: starboard-exporter + namespace: starboard + version: v2 + path: patch.0.yaml +`) + th.WriteF("base/servicemonitor.yaml", ` +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + app: starboard-exporter + name: starboard-exporter + namespace: starboard +spec: + endpoints: + - path: /metrics + port: metrics + selector: + matchLabels: + app.kubernetes.io/instance: starboard-exporter + app.kubernetes.io/name: starboard-exporter +`) + th.WriteF("base/patch.0.yaml", ` +- op: add + path: /metadata/labels/release + value: kube-prometheus-stack +`) + err := th.RunWithErr("base", th.MakeDefaultOptions()) + assert.Contains(t, err.Error(), "patchesJson6902 target not found for ServiceMonitor.v2.monitoring.coreos.com/starboard-exporter.starboard") +} diff --git a/plugin/builtin/patchjson6902transformer/PatchJson6902Transformer.go b/plugin/builtin/patchjson6902transformer/PatchJson6902Transformer.go index 285f1796c1..c6c6e4943f 100644 --- a/plugin/builtin/patchjson6902transformer/PatchJson6902Transformer.go +++ b/plugin/builtin/patchjson6902transformer/PatchJson6902Transformer.go @@ -81,6 +81,11 @@ func (p *plugin) Transform(m resmap.ResMap) error { if err != nil { return err } + + if len(resources) == 0 { + return fmt.Errorf("patchesJson6902 target not found for %s", p.Target.ResId) + } + for _, res := range resources { internalAnnotations := kioutil.GetInternalAnnotations(&res.RNode) diff --git a/plugin/builtin/patchtransformer/PatchTransformer.go b/plugin/builtin/patchtransformer/PatchTransformer.go index 77e5439b09..b60868b99e 100644 --- a/plugin/builtin/patchtransformer/PatchTransformer.go +++ b/plugin/builtin/patchtransformer/PatchTransformer.go @@ -137,6 +137,11 @@ func (p *plugin) transformJson6902(m resmap.ResMap) error { if err != nil { return err } + + if len(resources) == 0 { + return fmt.Errorf("patches target not found for %s", p.Target.ResId) + } + for _, res := range resources { res.StorePreviousId() internalAnnotations := kioutil.GetInternalAnnotations(&res.RNode) From 2f127dd34e13e9ad6236c079309b1198ccfaae03 Mon Sep 17 00:00:00 2001 From: laxmikantbpandhare Date: Sun, 23 Jul 2023 10:27:53 -0700 Subject: [PATCH 2/3] updated error message according to new discussion resolved conflicts --- api/internal/builtins/PatchTransformer.go | 5 +++-- plugin/builtin/patchtransformer/PatchTransformer.go | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/api/internal/builtins/PatchTransformer.go b/api/internal/builtins/PatchTransformer.go index d31e6daff4..9ecba5a733 100644 --- a/api/internal/builtins/PatchTransformer.go +++ b/api/internal/builtins/PatchTransformer.go @@ -5,6 +5,7 @@ package builtins import ( "fmt" + "log" "strings" jsonpatch "gopkg.in/evanphx/json-patch.v4" @@ -137,8 +138,8 @@ func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap) error { return err } - if len(resources) == 0 { - return fmt.Errorf("patches target not found for %s", p.Target.ResId) + if p.Options["allowNoTargetMatch"] { + log.Println("Warning: patches target not found for Target") } for _, res := range resources { diff --git a/plugin/builtin/patchtransformer/PatchTransformer.go b/plugin/builtin/patchtransformer/PatchTransformer.go index b60868b99e..a2a12d99c2 100644 --- a/plugin/builtin/patchtransformer/PatchTransformer.go +++ b/plugin/builtin/patchtransformer/PatchTransformer.go @@ -6,6 +6,7 @@ package main import ( "fmt" + "log" "strings" jsonpatch "gopkg.in/evanphx/json-patch.v4" @@ -138,8 +139,8 @@ func (p *plugin) transformJson6902(m resmap.ResMap) error { return err } - if len(resources) == 0 { - return fmt.Errorf("patches target not found for %s", p.Target.ResId) + if p.Options["allowNoTargetMatch"] { + log.Println("Warning: patches target not found for Target") } for _, res := range resources { From f329f91f35b927448f52540712bbc78b3d40298f Mon Sep 17 00:00:00 2001 From: Adoram Shoval Date: Tue, 12 Aug 2025 09:24:03 -0400 Subject: [PATCH 3/3] feat: allowNoTargetMatch feature for PatchTransformer and Path6902Transformer This commit makes use of the new PatchArgs type and the new Patch option `allowNoTargetMatch`. This option, if true, allows rendering kustomization even if no resource is matched against the given target, while printing a warning to stderr. By default this option is set to false meaning that if no resource is matched against the given target an error is raised. This commit includes the extra tests to test this new feature. --- .../builtins/PatchJson6902Transformer.go | 2 - api/internal/builtins/PatchTransformer.go | 54 ++- api/krusty/extendedpatch_test.go | 307 ++++++++++++++++-- api/types/patch_test.go | 15 +- api/types/patchargs.go | 3 + .../patchtransformer/PatchTransformer.go | 52 ++- 6 files changed, 367 insertions(+), 66 deletions(-) diff --git a/api/internal/builtins/PatchJson6902Transformer.go b/api/internal/builtins/PatchJson6902Transformer.go index bbf89f5ddc..a5c0fbad8f 100644 --- a/api/internal/builtins/PatchJson6902Transformer.go +++ b/api/internal/builtins/PatchJson6902Transformer.go @@ -24,8 +24,6 @@ type PatchJson6902TransformerPlugin struct { JsonOp string `json:"jsonOp,omitempty" yaml:"jsonOp,omitempty"` } -// noinspection GoUnusedGlobalVariable - func (p *PatchJson6902TransformerPlugin) Config( h *resmap.PluginHelpers, c []byte) (err error) { p.ldr = h.Loader() diff --git a/api/internal/builtins/PatchTransformer.go b/api/internal/builtins/PatchTransformer.go index 9ecba5a733..08481273b8 100644 --- a/api/internal/builtins/PatchTransformer.go +++ b/api/internal/builtins/PatchTransformer.go @@ -5,7 +5,7 @@ package builtins import ( "fmt" - "log" + "os" "strings" jsonpatch "gopkg.in/evanphx/json-patch.v4" @@ -31,8 +31,6 @@ type PatchTransformerPlugin struct { Options *types.PatchArgs `json:"options,omitempty" yaml:"options,omitempty"` } -// noinspection GoUnusedGlobalVariable - func (p *PatchTransformerPlugin) Config(h *resmap.PluginHelpers, c []byte) error { if err := yaml.Unmarshal(c, p); err != nil { return err @@ -72,18 +70,7 @@ func (p *PatchTransformerPlugin) Config(h *resmap.PluginHelpers, c []byte) error } if errSM == nil { p.smPatches = patchesSM - for _, loadedPatch := range p.smPatches { - if p.Options == nil { - continue - } - - if p.Options.AllowNameChange { - loadedPatch.AllowNameChange() - } - if p.Options.AllowKindChange { - loadedPatch.AllowKindChange() - } - } + p.loadPatchOptions(p.smPatches) } else { p.jsonPatches = patchesJson } @@ -113,6 +100,9 @@ func (p *PatchTransformerPlugin) transformStrategicMerge(m resmap.ResMap) error if err != nil { return fmt.Errorf("unable to find patch target %q in `resources`: %w", p.Target, err) } + if err := p.allowNoTargetMatch(selected); err != nil { + return err + } return errors.Wrap(m.ApplySmPatch(resource.MakeIdSet(selected), patch)) } @@ -138,8 +128,8 @@ func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap) error { return err } - if p.Options["allowNoTargetMatch"] { - log.Println("Warning: patches target not found for Target") + if err := p.allowNoTargetMatch(resources); err != nil { + return err } for _, res := range resources { @@ -181,6 +171,36 @@ func jsonPatchFromBytes(in []byte) (jsonpatch.Patch, error) { return jsonpatch.DecodePatch([]byte(ops)) } +// allowNoTargetMatch checks whether no target match is allowed and return an error in case the patch violates that. +func (p *PatchTransformerPlugin) allowNoTargetMatch(resources []*resource.Resource) error { + if len(resources) > 0 { + return nil + } + + if p.Options == nil || !p.Options.AllowNoTargetMatch { + return fmt.Errorf("patches target not found for %s", p.Target.ResId) + } + + fmt.Fprintf(os.Stderr, "# %v %s\n", "Warning: patches target not found for Target", p.Target.ResId) + return nil +} + +// loadPatchOptions, given a list of resources, enables the available patch options for each resource. +func (p *PatchTransformerPlugin) loadPatchOptions(patches []*resource.Resource) { + if p.Options == nil { + return + } + + for _, patch := range patches { + if p.Options.AllowNameChange { + patch.AllowNameChange() + } + if p.Options.AllowKindChange { + patch.AllowKindChange() + } + } +} + func NewPatchTransformerPlugin() resmap.TransformerPlugin { return &PatchTransformerPlugin{} } diff --git a/api/krusty/extendedpatch_test.go b/api/krusty/extendedpatch_test.go index f229806822..d3b99d3140 100644 --- a/api/krusty/extendedpatch_test.go +++ b/api/krusty/extendedpatch_test.go @@ -826,6 +826,106 @@ metadata: assert.Contains(t, err.Error(), "patches target not found for [noKind].[noVer].[noGrp]/no-match.[noNs]") } +func TestExtendedPatchAllowNoMatch(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- deployment.yaml +- service.yaml +patches: +- path: patch.yaml + target: + name: no-match + options: + allowNoTargetMatch: true +`) + th.WriteF("base/patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: busybox + annotations: + new-key: new-value +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: nginx + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + volumeMounts: + - mountPath: /tmp/ps + name: nginx-persistent-storage + volumes: + - emptyDir: {} + name: nginx-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: busybox + name: busybox +spec: + template: + metadata: + labels: + app: busybox + spec: + containers: + - image: busybox + name: busybox + volumeMounts: + - mountPath: /tmp/ps + name: busybox-persistent-storage + volumes: + - emptyDir: {} + name: busybox-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: nginx + name: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: busybox + name: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) +} + func TestExtendedPatchWithoutTarget(t *testing.T) { th := kusttest_test.MakeHarness(t) makeCommonFileForExtendedPatchTest(th) @@ -952,6 +1052,112 @@ metadata: assert.Contains(t, err.Error(), "patches target not found for [noKind].[noVer].[noGrp]/no-match.[noNs]") } +func TestExtendedPatchAllowNoMatchMultiplePatch(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- deployment.yaml +- service.yaml +patches: +- path: patch.yaml + target: + name: no-match + options: + allowNoTargetMatch: true +- path: patch.yaml + target: + name: busybox + kind: Job + options: + allowNoTargetMatch: true +`) + th.WriteF("base/patch.yaml", ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: busybox + annotations: + new-key: new-value +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: nginx + name: nginx +spec: + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + name: nginx + volumeMounts: + - mountPath: /tmp/ps + name: nginx-persistent-storage + volumes: + - emptyDir: {} + name: nginx-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: busybox + name: busybox +spec: + template: + metadata: + labels: + app: busybox + spec: + containers: + - image: busybox + name: busybox + volumeMounts: + - mountPath: /tmp/ps + name: busybox-persistent-storage + volumes: + - emptyDir: {} + name: busybox-persistent-storage + - configMap: + name: configmap-in-base + name: configmap-in-base +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: nginx + name: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: busybox + name: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) +} + func TestExtendedPatchMultiplePatchOverlapping(t *testing.T) { th := kusttest_test.MakeHarness(t) makeCommonFileForExtendedPatchTest(th) @@ -1072,38 +1278,89 @@ func TestTargetMissingPatchJson6902Error(t *testing.T) { makeCommonFileForExtendedPatchTest(th) th.WriteK("base", ` resources: -- servicemonitor.yaml +- service.yaml patchesJson6902: - target: - group: monitoring.coreos.com - kind: ServiceMonitor - name: starboard-exporter - namespace: starboard + kind: Service + name: busybox version: v2 - path: patch.0.yaml + path: patch.yaml `) - th.WriteF("base/servicemonitor.yaml", ` -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - labels: - app: starboard-exporter - name: starboard-exporter - namespace: starboard -spec: - endpoints: - - path: /metrics - port: metrics - selector: - matchLabels: - app.kubernetes.io/instance: starboard-exporter - app.kubernetes.io/name: starboard-exporter + th.WriteF("base/patch.yaml", ` +- op: add + path: /metadata/labels/release + value: this-label-will-not-go-through `) - th.WriteF("base/patch.0.yaml", ` + err := th.RunWithErr("base", th.MakeDefaultOptions()) + assert.Contains(t, err.Error(), "patchesJson6902 target not found for Service.v2.[noGrp]/busybox.[noNs]") +} + +func TestTargetMissingJsonPatchError(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- service.yaml +patches: +- target: + kind: Service + name: busybox + version: v2 + path: patch.yaml +`) + th.WriteF("base/patch.yaml", ` - op: add path: /metadata/labels/release - value: kube-prometheus-stack + value: this-label-will-not-go-through `) err := th.RunWithErr("base", th.MakeDefaultOptions()) - assert.Contains(t, err.Error(), "patchesJson6902 target not found for ServiceMonitor.v2.monitoring.coreos.com/starboard-exporter.starboard") + assert.Contains(t, err.Error(), "patches target not found for Service.v2.[noGrp]/busybox.[noNs]") +} + +func TestAllowNoTargetMatchPatchTransformerOptions(t *testing.T) { + th := kusttest_test.MakeHarness(t) + makeCommonFileForExtendedPatchTest(th) + th.WriteK("base", ` +resources: +- service.yaml +patches: +- target: + kind: Service + name: busybox + version: v2 + path: patch.yaml + options: + allowNoTargetMatch: true +`) + th.WriteF("base/patch.yaml", ` +- op: add + path: /metadata/labels/release + value: this-label-will-not-go-through +`) + m := th.Run("base", th.MakeDefaultOptions()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: Service +metadata: + labels: + app: nginx + name: nginx +spec: + ports: + - port: 80 + selector: + app: nginx +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: busybox + name: busybox +spec: + ports: + - port: 8080 + selector: + app: busybox +`) } diff --git a/api/types/patch_test.go b/api/types/patch_test.go index fbf2af0423..b2ab1adae6 100644 --- a/api/types/patch_test.go +++ b/api/types/patch_test.go @@ -108,8 +108,9 @@ func TestPatchEquals(t *testing.T) { Patch: "bar", Target: &selector, Options: &PatchArgs{ - AllowNameChange: true, - AllowKindChange: true, + AllowNameChange: true, + AllowKindChange: true, + AllowNoTargetMatch: false, }, }, patch2: Patch{ @@ -117,8 +118,9 @@ func TestPatchEquals(t *testing.T) { Patch: "bar", Target: &selector, Options: &PatchArgs{ - AllowNameChange: true, - AllowKindChange: true, + AllowNameChange: true, + AllowKindChange: true, + AllowNoTargetMatch: false, }, }, expect: true, @@ -153,7 +155,6 @@ func TestPatchEquals(t *testing.T) { Patch: "bar", Target: &selector, Options: &PatchArgs{ - AllowNameChange: false, AllowKindChange: true, }, }, @@ -162,8 +163,8 @@ func TestPatchEquals(t *testing.T) { Patch: "bar", Target: &selector, Options: &PatchArgs{ - AllowNameChange: true, - AllowKindChange: true, + AllowKindChange: true, + AllowNoTargetMatch: true, }, }, expect: false, diff --git a/api/types/patchargs.go b/api/types/patchargs.go index 453849fa6b..126d2cb3a3 100644 --- a/api/types/patchargs.go +++ b/api/types/patchargs.go @@ -10,4 +10,7 @@ type PatchArgs struct { // AllowKindChange allows kind changes to the resource. AllowKindChange bool `json:"allowKindChange,omitempty" yaml:"allowKindChange,omitempty"` + + // AllowNoTargetMatch allows files rendering in case of no target (`api/types/selector`) match. + AllowNoTargetMatch bool `json:"allowNoTargetMatch,omitempty" yaml:"allowNoTargetMatch,omitempty"` } diff --git a/plugin/builtin/patchtransformer/PatchTransformer.go b/plugin/builtin/patchtransformer/PatchTransformer.go index a2a12d99c2..28e9338eea 100644 --- a/plugin/builtin/patchtransformer/PatchTransformer.go +++ b/plugin/builtin/patchtransformer/PatchTransformer.go @@ -6,7 +6,7 @@ package main import ( "fmt" - "log" + "os" "strings" jsonpatch "gopkg.in/evanphx/json-patch.v4" @@ -73,18 +73,7 @@ func (p *plugin) Config(h *resmap.PluginHelpers, c []byte) error { } if errSM == nil { p.smPatches = patchesSM - for _, loadedPatch := range p.smPatches { - if p.Options == nil { - continue - } - - if p.Options.AllowNameChange { - loadedPatch.AllowNameChange() - } - if p.Options.AllowKindChange { - loadedPatch.AllowKindChange() - } - } + p.loadPatchOptions(p.smPatches) } else { p.jsonPatches = patchesJson } @@ -114,6 +103,9 @@ func (p *plugin) transformStrategicMerge(m resmap.ResMap) error { if err != nil { return fmt.Errorf("unable to find patch target %q in `resources`: %w", p.Target, err) } + if err := p.allowNoTargetMatch(selected); err != nil { + return err + } return errors.Wrap(m.ApplySmPatch(resource.MakeIdSet(selected), patch)) } @@ -139,8 +131,8 @@ func (p *plugin) transformJson6902(m resmap.ResMap) error { return err } - if p.Options["allowNoTargetMatch"] { - log.Println("Warning: patches target not found for Target") + if err := p.allowNoTargetMatch(resources); err != nil { + return err } for _, res := range resources { @@ -181,3 +173,33 @@ func jsonPatchFromBytes(in []byte) (jsonpatch.Patch, error) { } return jsonpatch.DecodePatch([]byte(ops)) } + +// allowNoTargetMatch checks whether no target match is allowed and return an error in case the patch violates that. +func (p *plugin) allowNoTargetMatch(resources []*resource.Resource) error { + if len(resources) > 0 { + return nil + } + + if p.Options == nil || !p.Options.AllowNoTargetMatch { + return fmt.Errorf("patches target not found for %s", p.Target.ResId) + } + + fmt.Fprintf(os.Stderr, "# %v %s\n", "Warning: patches target not found for Target", p.Target.ResId) + return nil +} + +// loadPatchOptions, given a list of resources, enables the available patch options for each resource. +func (p *plugin) loadPatchOptions(patches []*resource.Resource) { + if p.Options == nil { + return + } + + for _, patch := range patches { + if p.Options.AllowNameChange { + patch.AllowNameChange() + } + if p.Options.AllowKindChange { + patch.AllowKindChange() + } + } +}