Kubernetes: kustomize transformations with patchesStrategicMerge

The power of kustomize lies in its ability to transform yaml, and one of the methods for this is  patchesStrategicMerge.

Where the strategic merge patch excels is in inserting elements and replacing values, allowing you to specify the desired patch using the same indentation level as the target, which makes the intended result very intuitive.

Although this patch type has support for replace, merge, and delete, I would recommend you use it primarily for merge which can replace existing values and add new elements.  The syntax for other operations is clunky and documentation is thin.  If you need delete and replace operations, consider a Json6902 patch instead.

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-strategic-merge

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 patches are specified, and you can preview the results using the built-in kustomize.

kubectl kustomize

Adding patches in kustomization.yaml

To add strategic merge patches, you can either place them under the ‘patchesStrategicMerge’ element, OR the ‘patches’ element where their type will be automatically sensed.

# could also use this keyword
#patchesStrategicMerge:
# but 'patches' keyword auto-senses patch types
patches:

# patches using local files
- patch-selector.yaml
- patch-labels.yaml

# patches using inline definitions
- |-
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: simple-deployment
  ...

The patch operations can be specified either in a separate local file or the definition can be inlined, but I won’t use the inline version in this article because in my opinion it negates one of the major benefits of this patch type which is being able to use the exact yaml indentation of the desired patch.

Strategic merge for Map/Dictionary

Referring back up to our deployment.yaml source, the spec.template.metadata.labels has two keys.

app: simple-deployment
tier: web

Below is a strategic merge that changes ‘web’ to ‘api’, and then adds three more keys: ‘foo’, ‘key_added_6902’ and ‘key_added_6902b’.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-deployment
spec:
  template:
    metadata:
      labels:
        tier: api
        foo: bar
        key_added_6902: value1
        key_added_6902b: value

Replace for Map/Dictionary

Referring back up to our deployment.yaml source, the spec.selector.matchLabels has four keys.

app: simple-deployment
to_del: to_be_removed
to_del_6902: to_be_removed
tier: web

To change ‘web’ to ‘api’ and remove the two ‘to_del’ keys, we can use the replace operation (as opposed to the default merge behavior) which replaces all the matchLabels values with our newly specified ones.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-deployment
spec:
  selector:
    matchLabels:
      $patch: replace
      tier: api
      app: simple-deployment

Strategic Merge for Array

The spec.template.spec.containers[0].env contains two key/value pairs.

- name: my_key
  value: My value
- name: key_todel
  value: will delete with patch

If you wanted to add another item to this array (‘my_new_key’).

apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-deployment
spec:
  template:
    spec:
      containers:
      - name: simple-app
        env:
        - name: my_new_key
          value: my new value

This would result in a total of 3 items in the array.

Replace for Array

If you wanted to:

  • rename ‘my_key’ as ‘my_replaced_key’
  • remove the ‘key_todel’
  • add a new key ‘my_patched_key’

Then you would need to change the behavior to ‘$patch: replace’ instead of merge which would replace all the items in the env array with our own.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-deployment
spec:
  template:
    spec:
      containers:
      - name: simple-app
        env:
        - $patch: replace
        - name: my_replaced_key
          value: My new patched value
        - name: my_patched_key
          value: "true"

Summary

Strategic merge patches excel in their ability to insert elements and replace values, and their match of the target yaml indentation makes them very intuitive to understand as well as copy-paste.

If you feel the syntax is not expressive enough for the operation you are attempting, I would urge you to look at JSON6902 patches.

 

REFERENCES

kustomize docs, patchesStrategicMerge

kustomize, patch types supported

github sig, strategicMerge patch

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