Skip to content

Commit

Permalink
Implement generic patches in Kustomization
Browse files Browse the repository at this point in the history
Allow patching multiple resources instead of a single existing one as
StrategicMerge & JSON6902 are forced to target existing named resources.
  • Loading branch information
glebiller committed Jun 10, 2021
1 parent 30dd0c3 commit 9bbfbed
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 12 deletions.
4 changes: 4 additions & 0 deletions api/v1beta1/kustomization_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ type KustomizationSpec struct {
// +optional
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.
// +optional
PatchesStrategicMerge []apiextensionsv1.JSON `json:"patchesStrategicMerge,omitempty"`
Expand Down
5 changes: 5 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions config/crd/bases/kustomize.toolkit.fluxcd.io_kustomizations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,41 @@ spec:
- name
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:
description: JSON 6902 patches, defined as inline YAML objects.
items:
Expand Down
61 changes: 56 additions & 5 deletions controllers/kustomization_controller_patch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,62 @@ var _ = Describe("KustomizationReconciler", func() {
Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(Equal("ghcr.io/stefanprodan/podinfo:5.2.0"))
})

It("patches as JSON", func() {
kustomization.Spec.Patches = []kustomize.Patch{
{
Patch: `
- op: add
path: /metadata/labels/patch
value: inline-json
`,
Target: kustomize.Selector{
LabelSelector: "app=podinfo",
},
},
}
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-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":{"xxxx":"yyyy"}}}`),
Raw: []byte(`{"kind":"Deployment","apiVersion":"apps/v1","metadata":{"name":"podinfo","labels":{"patch":"strategic-merge"}}}`),
},
}
Expect(k8sClient.Create(context.TODO(), kustomization)).To(Succeed())
Expand All @@ -160,15 +212,14 @@ var _ = Describe("KustomizationReconciler", func() {

var deployment appsv1.Deployment
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("strategic-merge"))
})

It("JSON6902 patches", func() {
kustomization.Spec.PatchesJSON6902 = []kustomize.JSON6902Patch{
{

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")}},
},
Target: kustomize.Selector{
Expand All @@ -189,7 +240,7 @@ var _ = Describe("KustomizationReconciler", func() {

var deployment appsv1.Deployment
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)))
})
})
Expand Down
7 changes: 7 additions & 0 deletions controllers/kustomization_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ func (kg *KustomizeGenerator) WriteFile(ctx context.Context, dirPath string) (st
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 {
kus.PatchesStrategicMerge = append(kus.PatchesStrategicMerge, kustypes.PatchStrategicMerge(m.Raw))
}
Expand Down
28 changes: 28 additions & 0 deletions docs/api/kustomize.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,20 @@ bool
</tr>
<tr>
<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>
<em>
<a href="https://pkg.go.dev/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1?tab=doc#JSON">
Expand Down Expand Up @@ -657,6 +671,20 @@ bool
</tr>
<tr>
<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>
<em>
<a href="https://pkg.go.dev/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1?tab=doc#JSON">
Expand Down
41 changes: 34 additions & 7 deletions docs/spec/v1beta1/kustomization.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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/)
- [patchesJson6902](https://kubectl.docs.kubernetes.io/references/kustomize/patchesjson6902/)
- [images](https://kubectl.docs.kubernetes.io/references/kustomize/images/)
- [patchesJson6902](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/patchesjson6902/)
- [images](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/images/)

### 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`,
`spec.targetNamespace` can be defined:

Expand All @@ -622,9 +623,35 @@ spec:

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: dummy-app
labels:
app.kubernetes.io/part-of: test-app
```

### 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
of strategic merge patches in YAML format:

Expand All @@ -649,7 +676,7 @@ spec:

### 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),
`spec.patchesJson6902`, the items must contain a `target` selector and JSON 6902
`patch` document:
Expand All @@ -675,7 +702,7 @@ spec:

### 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
without creating patches, `spec.images` can be defined:

Expand Down

0 comments on commit 9bbfbed

Please sign in to comment.