Kubernetes: kustomize transformations with patchesJson6902

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

kustomize, built-ins

rfc 6902

Sergey Kryvets, Kustomize with patches6902

kustomize, create TLS secret

kustomize sig, specify multiple targets with labelSelector, annotationSelector, wildcard name

Explanation patches690s being the older keyword and ‘patches’ being the newer version which allows multiple targets

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