kustomize patching a specific container other than by array (/containers/0)
Asked Answered
B

3

12

I'm trying to see if there's a way to apply a kustomize patchTransformer to a specific container in a pod other than using its array index. For example, if I have 3 containers in a pod, (0, 1, 2) and I want to patch container "1" I would normally do something like this:

patch: |-
  - op: add
    path: /spec/containers/1/command
    value:  ["sh", "-c", "tail -f /dev/null"]

That is heavily dependent on that container order remaining static. If container "1" is removed for whatever reason, the array is reshuffled and container "2" suddenly becomes container "1", making my patch no longer applicable.

Is there a way to patch by name, or target a label/annotation, or some other mechanism?

path: /spec/containers/${NAME_OF_CONTAINER}/command

Any insight is greatly appreciated.

Bellis answered 16/9, 2020 at 21:6 Comment(0)
C
17

You may have seen JSONPath syntax like this floating around the internet and hoped that you could select a list item and patch it using Kustomize.

/spec/containers[name=my-app]/command

As @Rico mentioned in his answer: This is a limitation with JSON6902 - it only accepts paths using JSONPointer syntax, defined by JSON6901.

So, no, you cannot currently address a list item using [key=value] syntax when using kustomize's patchesJson6902.

However, the problem presented in the original question around dealing with changes to the order of list items does have a solution using JSONPointer syntax (JSON6901) without moving to Strategic Merge Patch (which can depend on CRD authors correctly annotating how list-item merges should be applied).

Simply add another JSON6902 operation to your patches to test that the item remains at the index you specified.

# First, test that the item is still at the list index you expect
- op: test
  path: /spec/containers/0/name
  value: my-app

# Now that you know your item is still at index-0, it's safe to patch its command
- op: replace
  path: /spec/containers/0/command
  value: ["sh", "-c", "tail -f /dev/null"]

The test operation will fail your patch if the value at the specified path does not match what is provided. This way, you can be sure that your other patch operation's dependency on the item's index is still valid!

I use this trick especially when dealing with custom resources, since I:

  • A) Don't have to give kustomize a whole new openAPI spec, and
  • B) Don't have to depend on the CRD authors having added the correct extension annotation (like: "x-kubernetes-patch-merge-key": "name") to make sure my strategic merge patches on list items work the way I need them to.
Cary answered 7/1, 2023 at 0:59 Comment(0)
T
2

This is more of a Json6902 patch limitation together with the fact that containers are defined in a K8s pod as an Array and not a Hash where something like this would work:

path: /spec/containers/${NAME_OF_CONTAINER}/command

You could just try a StrategicMergePatch. which essentially what kubectl apply does.

cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  selector:
    matchLabels:
      run: my-app
  replicas: 2
  template:
    metadata:
      labels:
        run: my-app
    spec:
      containers:
      - name: my-container
        image: myimage
        ports:
        - containerPort: 80
EOF
cat <<EOF > set_command.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  template:
    spec:
      containers:
      - name: my-app
        command: ["sh", "-c", "tail -f /dev/null"]
EOF
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
patchesStrategicMerge:
- set_command.yaml
EOF

✌️

Thun answered 16/9, 2020 at 22:8 Comment(0)
B
0

There is a workaround for this by utilizing replacements instead of patches.

Imagine the following deployment.yaml where you want to replace the image by the container's name:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels: {}
spec:
  replicas: 1
  selector: {}
  template:
    spec:
      containers:
        - name: nginx
          image: nginx:latest

With the patch approach, you will have a kustomization.yaml like this:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml

patches:
  - patch: |-
      - op: replace
        path: /spec/template/spec/containers/0/image
        value: nginx:alpine
    target:
      group: apps
      version: v1
      kind: Deployment

instead, you can create a "helper"-Configmap where you can source the values afterwards and point to the target container by its name:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml

configMapGenerator:
  - name: nginx-image
    literals:
      - NGINX_IMAGE=nginx:alpine

replacements:
  - source:
      kind: ConfigMap
      name: nginx-image
      fieldPath: data.NGINX_IMAGE
    targets:
        - select:
            group: apps
            version: v1
            kind: Deployment
            name: nginx
          fieldPaths:
            - spec.template.spec.containers.[name=nginx].image

As a side-effect, this approach creates a totally not necessary ConfigMap but deleting the configmap with patch is not working (see kustmize/#1593), probably because the patches gets applied before the replacements.

EDIT: Another solution with a useless label instead of a useless ConfigMap:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml

replacements:
  - source:
      kind: Deployment
      name: nginx
      fieldPath: metadata.labels.image-override
    targets:
        - select:
            group: apps
            version: v1
            kind: Deployment
            name: nginx
          fieldPaths:
            - spec.template.spec.containers.[name=nginx].image

patches:
  - patch: |-
      - op: add
        path: /metadata/labels/image-override
        value: nginx:alpine
    target:
      group: apps
      version: v1
      kind: Deployment
Barger answered 13/7 at 4:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.