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