The power of kustomize lies in its ability to transform yaml, and to that end it has built-in support for the JSON 6902 RFC specification.
This RFC specifies the syntax for adding, removing, replacing, and moving elements and values in a yaml file. And since Kubernetes manifests are yaml files, it makes perfect sense to leverage this specification.
In this article, I will provide examples of patching Kubernetes manifests using the 6902 syntax.
Example project
All the source from this article can be found in my github project.
git clone https://github.com/fabianlee/kustomize-overlays-with-reload.git cd kustomize-overlays-with-reload root_dir=$(realpath .) cd $root_dir/base/simple-with-json-patches
The main file we will be manipulating is “deployment.yaml”, which is a very simple Kubernetes deployment using the hello-app image from Google.
apiVersion: apps/v1 kind: Deployment metadata: name: simple-deployment namespace: default labels: app: simple-deployment spec: selector: matchLabels: app: simple-deployment to_del: to_be_removed # will delete to_del_6902: to_be_removed # will delete tier: web # will change to 'api' template: metadata: labels: app: simple-deployment tier: web # will replace with to 'api' #foo: bar TO BE ADDED #key_added_6902 TO BE ADDED from op file #key_added_6902b TO BE ADDED from op file spec: # podAntiAffinity TO BE ADDED containers: - name: simple-app image: gcr.io/google-samples/hello-app:1.0 ports: - containerPort: 8080 env: - name: my_key # will replace with 'my_replaced_key' value: My value - name: key_todel # will delete value: will delete with patch # will add 'my_patched_key' resources: requests: cpu: 200m
The kustomize.yaml file is where the JSON6902 patches are specified, and you can preview the results using the built-in kustomize.
kubectl kustomize
Adding patches in kustomization.yaml
To add patches in the JSON 6902 format, you can either place them under the ‘patchesJson6902’ element, OR the ‘patches’ element where their type will be automatically sensed.
One big difference is that targets found under ‘patchesJson6902’ mandate a ‘name’ selector to identify the target, whereas ‘patches’ allows more flexibility in finding target that are specified just by kind, name, or labelSelector.
# requires 'name' to be specified in target qualification #patchesJson6902: # more flexible target selection patches: # add patch using operations file - target: kind: Deployment path: op-add-map.yaml # add patch using inline patch definition - target: kind: Deployment #name: simple-deployment labelSelector: "app=simple-deployment" patch: |- # add with inline operation - op: add path: /spec/template/metadata/labels/foo value: bar ...
As shown above, the patch operations (add,remove,replace) can be specified either in a local file (using the ‘path’ element) or the definition can be inlined.
Modify Map/Dictionary
Add key/value to map (foo: bar)
# add to map - op: add path: /spec/template/metadata/labels/foo value: bar
Replacing values in map (tier: web -> tier: api)
# replace element in map (need matchLabels to mirror labels) - op: replace path: /spec/selector/matchLabels/tier value: api - op: replace path: /spec/template/metadata/labels/tier value: api
Deleting elements in map (‘to_del’ key/value removed)
# delete element in map - op: remove path: /spec/selector/matchLabels/to_del
Modify Array
Add new item to array (new ‘my_patched_key’)
- op: add path: /spec/template/spec/containers/0/env/- value: # literal value inline name: my_patched_key value: "true"
Replace item in array (first key at [0] now ‘my_replaced_key’)
- op: replace path: /spec/template/spec/containers/0/env/0 value: name: my_replaced_key value: My new patched value
Remove item in array (item at [1] deleted)
- op: remove path: /spec/template/spec/containers/0/env/1
Add complex element
The ‘value’ specified for these operations does not need to be atomic, or a key/value pair, it can also include a complex element. Here is an example of inserting an anti-affinity definition.
- op: add path: '/spec/template/spec/affinity' value: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - simple-deployment topologyKey: "kubernetes.io/hostname"
Final Results
$ kubectl kustomize apiVersion: apps/v1 kind: Deployment metadata: labels: app: simple-deployment name: simple-deployment namespace: simple-with-json spec: selector: matchLabels: app: simple-deployment tier: api template: metadata: labels: app: simple-deployment foo: bar key_added_6902: value1 key_added_6902b: value2 tier: api spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - simple-deployment topologyKey: kubernetes.io/hostname containers: - env: - name: my_replaced_key value: My new patched value - name: my_patched_key value: "true" image: gcr.io/google-samples/hello-app:1.0 name: simple-app ports: - containerPort: 8080 resources: requests: cpu: 200m
REFERENCES
kustomize docs, patchesJSON6902
kustomize, patch types supported
Sergey Kryvets, Kustomize with patches6902
kustomize sig, specify multiple targets with labelSelector, annotationSelector, wildcard name
NOTES
Create TLS secret
secretGenerator: - name: app-tls-namespaced namespace: apps behavior: replace files: - tls.crt=tls.cert - tls.key=tls.key type: kubernetes.io/tls options: disableNameSuffixHash: true