kubernetes patch or add nested structure depending on if it exists

100 Views Asked by At

I'm wondering if there's any way to use a patch to "/root/subdir", which will either create "root" if it doesn't exist (plus "subdir"), or simply add or replace "subdir" if "root" already exists.

Example:

patchesJson6902:
  - target:
      kind: MyKind
      name: config
      version: v1beta1
      group: mygroup.com
    patch: |-
      - op: replace
        path: /spec/array/0/spec/newField
        value: test

If /spec/array/0/spec exists already, then everything succeeds. Otherwise, I get:

% kustomize build --load-restrictor LoadRestrictionsNone
Error: replace operation does not apply: doc is missing path: /spec/array/0/spec/newField: missing value

I realize that "replace" effectively won't do this, but is there a way to achieve this behavior?

1

There are 1 best solutions below

2
Sai Chandini Routhu On

The most frequent issue that arises while using kubectl kustomize for Json6902 patching is the failure to recognize that "add" might occasionally behave like "replace”. The same problem & its solution is explained in this Medium blog by Paul Dally.

By themselves, Kubernetes patches are made to work with resources that already exist; they do not automatically support adding missing directories to the path. However, if the "/root" directory doesn't already exist, you can create it and then use a strategic merge patch to modify "/root/subdir" in order to get the desired behavior.

Strategic Merge patch :

patchesJson6902:
  - target:
      kind: MyKind
      name: config
      version: v1beta1
      group: mygroup.com
    patch: |-
       spec:
          array:
            - spec:{} #Empty object to create the nested structure
                newField: test  #value to be added or replaced 

Refer to this official Kubernetes doc for more information about Strategic Merge patch.

This patch attempts to merge the provided spec section which includes the nested “newfield” if the “spec/array/0/spec” does not exist, it will be created with the specified “newField” value By following this approach you can effectively create or update the nested structure within your kubernetes resources using patches.

EDIT1: (Based on your comment) Your problem with the inline patch style most likely stems from the fact that deep merges are automatically performed by strategic merge patches.

The patch that just contains the /spec/array/0/spec/newfield definition overwrites the entire object with an empty object that only contains the specified field when the /spec field is absent from the original object. As a result, certain current fields are accidentally removed. Here how to adjust your inline patch to achieve the desired behavior.

Method 1 : Patch with existing spec preservation:

   patches:
  -   target:
      kind: MyKind
      name: config
      version: v1beta1
      group: mygroup.com
    patch: |-
       spec:
          array:
            - spec: # Explicitly define the nested structure
                newField: test  

Using the newField key, the patch explicitly specifies the /spec/array/0/spec structure. Since the patch includes the complete /spec section, Kubernetes executes a deep merge, adding the missing nested structure and updating the newField value while retaining any existing fields within the /spec object.

Method 2 : Strategic merge patch with sub fields : A strategic merging patch with sub-fields is used in another method.

  patches:
  -   target:
      kind: MyKind
      name: config
      version: v1beta1
      group: mygroup.com
    patch: |-
       spec:
        array:
            - spec: 
                newField: test  

This patch is similar to the previous one, but it only defines the desired /spec/array/0/spec/newField element.

If you wish to clearly explain the full hierarchical structure, use the first method. For a more condensed patch that assumes the remainder of the /spec object is already available, use the second method.

Refer to this document for more information