Implement generic patches in Kustomization

Allow patching multiple resources instead of a single existing one as
StrategicMerge & JSON6902 are forced to target existing named resources.

Signed-off-by: Guillaume Le Biller <glebiller@Traveldoo.com>
This commit is contained in:
Guillaume Le Biller 2021-06-10 19:38:11 +02:00
parent 30dd0c33b0
commit b797eac907
No known key found for this signature in database
GPG Key ID: A4E075C4D8E6F708
7 changed files with 171 additions and 14 deletions

View File

@ -83,6 +83,10 @@ type KustomizationSpec struct {
// +optional // +optional
HealthChecks []meta.NamespacedObjectKindReference `json:"healthChecks,omitempty"` HealthChecks []meta.NamespacedObjectKindReference `json:"healthChecks,omitempty"`
// Patches (also called overlays), defined as inline YAML objects.
// +optional
Patches []kustomize.Patch `json:"patches,omitempty"`
// Strategic merge patches, defined as inline YAML objects. // Strategic merge patches, defined as inline YAML objects.
// +optional // +optional
PatchesStrategicMerge []apiextensionsv1.JSON `json:"patchesStrategicMerge,omitempty"` PatchesStrategicMerge []apiextensionsv1.JSON `json:"patchesStrategicMerge,omitempty"`

View File

@ -173,6 +173,11 @@ func (in *KustomizationSpec) DeepCopyInto(out *KustomizationSpec) {
*out = make([]meta.NamespacedObjectKindReference, len(*in)) *out = make([]meta.NamespacedObjectKindReference, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.Patches != nil {
in, out := &in.Patches, &out.Patches
*out = make([]kustomize.Patch, len(*in))
copy(*out, *in)
}
if in.PatchesStrategicMerge != nil { if in.PatchesStrategicMerge != nil {
in, out := &in.PatchesStrategicMerge, &out.PatchesStrategicMerge in, out := &in.PatchesStrategicMerge, &out.PatchesStrategicMerge
*out = make([]apiextensionsv1.JSON, len(*in)) *out = make([]apiextensionsv1.JSON, len(*in))

View File

@ -142,6 +142,41 @@ spec:
- name - name
type: object type: object
type: object type: object
patches:
description: Patches (also called overlays), defined as inline YAML objects.
items:
description: Patch contains either a StrategicMerge or a JSON6902 patch, either a file or inline, and the target the patch should be applied to.
properties:
patch:
description: Patch contains the JSON6902 patch document with an array of operation objects.
type: string
target:
description: Target points to the resources that the patch document should be applied to.
properties:
annotationSelector:
description: AnnotationSelector is a string that follows the label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api It matches with the resource annotations.
type: string
group:
description: Group is the API group to select resources from. Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
kind:
description: Kind of the API Group to select resources from. Together with Group and Version it is capable of unambiguously identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
labelSelector:
description: LabelSelector is a string that follows the label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api It matches with the resource labels.
type: string
name:
description: Name to match resources with.
type: string
namespace:
description: Namespace to select resources from.
type: string
version:
description: Version of the API Group to select resources from. Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
type: object
type: object
type: array
patchesJson6902: patchesJson6902:
description: JSON 6902 patches, defined as inline YAML objects. description: JSON 6902 patches, defined as inline YAML objects.
items: items:

View File

@ -144,10 +144,17 @@ var _ = Describe("KustomizationReconciler", func() {
Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(Equal("ghcr.io/stefanprodan/podinfo:5.2.0")) Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(Equal("ghcr.io/stefanprodan/podinfo:5.2.0"))
}) })
It("strategic merge patches", func() { It("patches as JSON", func() {
kustomization.Spec.PatchesStrategicMerge = []apiextensionsv1.JSON{ kustomization.Spec.Patches = []kustomize.Patch{
{ {
Raw: []byte(`{"kind":"Deployment","apiVersion":"apps/v1","metadata":{"name":"podinfo","labels":{"xxxx":"yyyy"}}}`), Patch: `
- op: add
path: /metadata/labels/patch
value: inline-json
`,
Target: kustomize.Selector{
LabelSelector: "app=podinfo",
},
}, },
} }
Expect(k8sClient.Create(context.TODO(), kustomization)).To(Succeed()) Expect(k8sClient.Create(context.TODO(), kustomization)).To(Succeed())
@ -160,15 +167,59 @@ var _ = Describe("KustomizationReconciler", func() {
var deployment appsv1.Deployment var deployment appsv1.Deployment
Expect(k8sClient.Get(context.TODO(), client.ObjectKey{Name: "podinfo", Namespace: namespace.Name}, &deployment)).To(Succeed()) Expect(k8sClient.Get(context.TODO(), client.ObjectKey{Name: "podinfo", Namespace: namespace.Name}, &deployment)).To(Succeed())
Expect(deployment.ObjectMeta.Labels["xxxx"]).To(Equal("yyyy")) Expect(deployment.ObjectMeta.Labels["patch"]).To(Equal("inline-json"))
})
It("patches as YAML", func() {
kustomization.Spec.Patches = []kustomize.Patch{
{
Patch: `
apiVersion: v1
kind: Pod
metadata:
name: podinfo
labels:
patch: inline-yaml
`,
},
}
Expect(k8sClient.Create(context.TODO(), kustomization)).To(Succeed())
Eventually(func() bool {
var obj kustomizev1.Kustomization
_ = k8sClient.Get(context.Background(), ObjectKey(kustomization), &obj)
return obj.Status.LastAppliedRevision == "main/"+artifactChecksum
}, timeout, time.Second).Should(BeTrue())
var deployment appsv1.Deployment
Expect(k8sClient.Get(context.TODO(), client.ObjectKey{Name: "podinfo", Namespace: namespace.Name}, &deployment)).To(Succeed())
Expect(deployment.ObjectMeta.Labels["patch"]).To(Equal("inline-yaml"))
})
It("strategic merge patches", func() {
kustomization.Spec.PatchesStrategicMerge = []apiextensionsv1.JSON{
{
Raw: []byte(`{"kind":"Deployment","apiVersion":"apps/v1","metadata":{"name":"podinfo","labels":{"patch":"strategic-merge"}}}`),
},
}
Expect(k8sClient.Create(context.TODO(), kustomization)).To(Succeed())
Eventually(func() bool {
var obj kustomizev1.Kustomization
_ = k8sClient.Get(context.Background(), ObjectKey(kustomization), &obj)
return obj.Status.LastAppliedRevision == "main/"+artifactChecksum
}, timeout, time.Second).Should(BeTrue())
var deployment appsv1.Deployment
Expect(k8sClient.Get(context.TODO(), client.ObjectKey{Name: "podinfo", Namespace: namespace.Name}, &deployment)).To(Succeed())
Expect(deployment.ObjectMeta.Labels["patch"]).To(Equal("strategic-merge"))
}) })
It("JSON6902 patches", func() { It("JSON6902 patches", func() {
kustomization.Spec.PatchesJSON6902 = []kustomize.JSON6902Patch{ kustomization.Spec.PatchesJSON6902 = []kustomize.JSON6902Patch{
{ {
Patch: []kustomize.JSON6902{ Patch: []kustomize.JSON6902{
{Op: "add", Path: "/metadata/labels/yyyy", Value: &apiextensionsv1.JSON{Raw: []byte(`"xxxx"`)}}, {Op: "add", Path: "/metadata/labels/patch", Value: &apiextensionsv1.JSON{Raw: []byte(`"json6902"`)}},
{Op: "replace", Path: "/spec/replicas", Value: &apiextensionsv1.JSON{Raw: []byte("2")}}, {Op: "replace", Path: "/spec/replicas", Value: &apiextensionsv1.JSON{Raw: []byte("2")}},
}, },
Target: kustomize.Selector{ Target: kustomize.Selector{
@ -189,7 +240,7 @@ var _ = Describe("KustomizationReconciler", func() {
var deployment appsv1.Deployment var deployment appsv1.Deployment
Expect(k8sClient.Get(context.TODO(), client.ObjectKey{Name: "podinfo", Namespace: namespace.Name}, &deployment)).To(Succeed()) Expect(k8sClient.Get(context.TODO(), client.ObjectKey{Name: "podinfo", Namespace: namespace.Name}, &deployment)).To(Succeed())
Expect(deployment.ObjectMeta.Labels["yyyy"]).To(Equal("xxxx")) Expect(deployment.ObjectMeta.Labels["patch"]).To(Equal("json6902"))
Expect(*deployment.Spec.Replicas).To(Equal(int32(2))) Expect(*deployment.Spec.Replicas).To(Equal(int32(2)))
}) })
}) })

View File

@ -104,6 +104,13 @@ func (kg *KustomizeGenerator) WriteFile(ctx context.Context, dirPath string) (st
kus.Namespace = kg.kustomization.Spec.TargetNamespace kus.Namespace = kg.kustomization.Spec.TargetNamespace
} }
for _, m := range kg.kustomization.Spec.Patches {
kus.Patches = append(kus.Patches, kustypes.Patch{
Patch: m.Patch,
Target: adaptSelector(&m.Target),
})
}
for _, m := range kg.kustomization.Spec.PatchesStrategicMerge { for _, m := range kg.kustomization.Spec.PatchesStrategicMerge {
kus.PatchesStrategicMerge = append(kus.PatchesStrategicMerge, kustypes.PatchStrategicMerge(m.Raw)) kus.PatchesStrategicMerge = append(kus.PatchesStrategicMerge, kustypes.PatchStrategicMerge(m.Raw))
} }

View File

@ -198,6 +198,20 @@ bool
</tr> </tr>
<tr> <tr>
<td> <td>
<code>patches</code><br>
<em>
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/kustomize#Patch">
[]github.com/fluxcd/pkg/apis/kustomize.Patch
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Patches (also called overlays), defined as inline YAML objects.</p>
</td>
</tr>
<tr>
<td>
<code>patchesStrategicMerge</code><br> <code>patchesStrategicMerge</code><br>
<em> <em>
<a href="https://pkg.go.dev/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1?tab=doc#JSON"> <a href="https://pkg.go.dev/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1?tab=doc#JSON">
@ -657,6 +671,20 @@ bool
</tr> </tr>
<tr> <tr>
<td> <td>
<code>patches</code><br>
<em>
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/kustomize#Patch">
[]github.com/fluxcd/pkg/apis/kustomize.Patch
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Patches (also called overlays), defined as inline YAML objects.</p>
</td>
</tr>
<tr>
<td>
<code>patchesStrategicMerge</code><br> <code>patchesStrategicMerge</code><br>
<em> <em>
<a href="https://pkg.go.dev/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1?tab=doc#JSON"> <a href="https://pkg.go.dev/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1?tab=doc#JSON">

View File

@ -598,14 +598,15 @@ The Kustomization has a set of fields to extend and/or override the Kustomize
patches and namespace on all the Kubernetes objects reconciled by the resource, patches and namespace on all the Kubernetes objects reconciled by the resource,
offering support for the following Kustomize directives: offering support for the following Kustomize directives:
- [namespace](https://kubectl.docs.kubernetes.io/references/kustomize/namespace/) - [namespace](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/namespace/)
- [patches](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/patches/)
- [patchesStrategicMerge](https://kubectl.docs.kubernetes.io/references/kustomize/patchesstrategicmerge/) - [patchesStrategicMerge](https://kubectl.docs.kubernetes.io/references/kustomize/patchesstrategicmerge/)
- [patchesJson6902](https://kubectl.docs.kubernetes.io/references/kustomize/patchesjson6902/) - [patchesJson6902](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/patchesjson6902/)
- [images](https://kubectl.docs.kubernetes.io/references/kustomize/images/) - [images](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/images/)
### Target namespace ### Target namespace
To configure the [Kustomize `namespace`](https://kubectl.docs.kubernetes.io/references/kustomize/namespace/) To configure the [Kustomize `namespace`](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/namespace/)
and overwrite the namespace of all the Kubernetes objects reconciled by the `Kustomization`, and overwrite the namespace of all the Kubernetes objects reconciled by the `Kustomization`,
`spec.targetNamespace` can be defined: `spec.targetNamespace` can be defined:
@ -622,9 +623,35 @@ spec:
The `targetNamespace` is expected to exist. The `targetNamespace` is expected to exist.
### Patches
To add [Kustomize `patches` entries](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/patches/)
to the configuration, and patch resources using either a [strategic merge](https://kubectl.docs.kubernetes.io/references/kustomize/glossary#patchstrategicmerge)
patch or a [JSON](https://kubectl.docs.kubernetes.io/references/kustomize/glossary#patchjson6902) patch,
`spec.patches` items must contain a `target` selector and a `patch` document.
The patch can target a single resource or multiple resources
```yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1beta1
kind: Kustomization
metadata:
name: podinfo
namespace: flux-system
spec:
# ...omitted for brevity
patches:
- patch: |-
apiVersion: v1
kind: Pod
metadata:
name: not-used
labels:
app.kubernetes.io/part-of: test-app
```
### Strategic Merge patches ### Strategic Merge patches
To add [Kustomize `patchesStrategicMerge` entries](https://kubectl.docs.kubernetes.io/references/kustomize/patchesstrategicmerge/) To add [Kustomize `patchesStrategicMerge` entries](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/patchesstrategicmerge/)
to the configuration, `spec.patchesStrategicMerge` can be defined with a list to the configuration, `spec.patchesStrategicMerge` can be defined with a list
of strategic merge patches in YAML format: of strategic merge patches in YAML format:
@ -649,7 +676,7 @@ spec:
### JSON 6902 patches ### JSON 6902 patches
To add [Kustomize `patchesJson6902` entries](https://kubectl.docs.kubernetes.io/references/kustomize/patchesjson6902/) To add [Kustomize `patchesJson6902` entries](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/patchesjson6902/)
to the configuration, and patch resources using the [JSON 6902 standard](https://tools.ietf.org/html/rfc6902), to the configuration, and patch resources using the [JSON 6902 standard](https://tools.ietf.org/html/rfc6902),
`spec.patchesJson6902`, the items must contain a `target` selector and JSON 6902 `spec.patchesJson6902`, the items must contain a `target` selector and JSON 6902
`patch` document: `patch` document:
@ -675,7 +702,7 @@ spec:
### Images ### Images
To add [Kustomize `images` entries](https://kubectl.docs.kubernetes.io/references/kustomize/images/) To add [Kustomize `images` entries](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/images/)
to the configuration, and overwrite the name, tag or digest of container images to the configuration, and overwrite the name, tag or digest of container images
without creating patches, `spec.images` can be defined: without creating patches, `spec.images` can be defined: