13

I tried to use kubectl patch to add two more values to the args list of a kubernetes deployment. I've gone over the officially documented (https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/) variants but did not manage to append more than one value at a time.

Assume this simple deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test
spec:
  replicas: 1
  selector:
    matchLabels:
      name: test
  template:
    metadata:
      labels:
        name: test
      name: test
    spec:
      containers:
      - image: alpine
        name: test
        command:
        - echo
        args:
        - my
        - text

I now want to append additional values to the args section. This works for a single value at a time:

Adding a single additional value

kubectl patch deployments.apps test --type=json -p='[{"op": "add", "path": "/spec/t
emplate/spec/containers/0/args/-", "value": "additional" }]'

This works and leaves me with the following:

...
        args:
        - my
        - text
        - additional

But running the patch with an array of values gives me an error:

# running:
k patch deployments.apps test --type=json -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": ["additional","text"] }]'

# results in:
The request is invalid: patch: Invalid value: "...": v1.Deployment.Spec: v1.DeploymentSpec.Template: v1.PodTemplateSpec.Spec: v1.PodSpec.Containers: []v1.Container: v1.Container.Args: []string: ReadString: expects " or n, but found [, error found in #10 byte of ...|itional",["additiona|..., bigger context ...|{"containers":[{"args":["my","text","additional",["additional","text"]],"command":["echo"],"image":"|...

Does anyone know a way to add mutliple values to an array within a single patch command without overwriting the whole args array? Thanks for your help.

jonny
  • 133
  • 1
  • 1
  • 7
  • 2
    jsonpatch does not support adding multiple values to an array. It might be possible to use multiple "add" statements in the patch array to add multiple values. – Burak Serdar Jun 25 '20 at 15:47
  • 1
    According to https://github.com/kubernetes/kubernetes/issues/72663#issuecomment-452325790, it might be possible to add multiple values in a single patch, however, in case of patching the arguements I am not quite sure how it would be achieved. – Dhruv Shah Jun 25 '20 at 15:50

2 Answers2

26

Below uses a single patch but it's not very DRY:

kubectl patch deployment <deployment-name> -n <namespace> --type "json" -p '[
{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"arg-1"},
{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"arg-2"},
{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"arg-3"}]'

I've been doing something similar for cert-manager to allow fully automated TLS:

kubectl patch deployment cert-manager -n cert-manager --type "json" -p '[
{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--default-issuer-name=letsencrypt-prod"},
{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--default-issuer-kind=ClusterIssuer"},
{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--default-issuer-group=cert-manager.io"}]'
GuyC
  • 6,494
  • 1
  • 31
  • 44
  • This is IMHO the correct answer. Unfortunately in the general case, this is not possible - there isn't a way to append to a list that doesn't yet exist, or that is nested within an optional object. For example, it seems impossible to add a node affinity term within a single json/strategic merge/... patch, unless there already are node affinity terms. – misberner Oct 13 '20 at 22:40
  • Hi @GuyC - Any idea I can patch node /status/conditions ? if yes can the value be json ? – Sam May 19 '22 at 21:25
10

The simplest way I've found is to use jq to edit the json, so instead of:

kubectl patch deployment <deployment-name> -n <namespace> --type "json" -p '[
{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"arg-1"},
{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"arg-2"},
{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"arg-3"}]'

You can use:

kubectl get deployment <deployment-name> -n <namespace> -o json \
  | jq '.spec.template.spec.containers[0].args += ["arg-1", "arg-2", "arg-3"]'  \
  | kubectl apply -f -

This has an advantage: it allows to inject even objects such as patching permissions. Example (taken from the requirements for upgrading coredns to 1.8.3):

kubectl get clusterrole system:coredns -n kube-system -o json \
  | jq '.rules += [{"apiGroups":["discovery.k8s.io"],"resources":["endpointslices"],"verbs":["list","watch"]}]' \
  | kubectl apply -f -

And another advantage: it is very easy to test before applying, just removing the kubectl apply -f - part:

kubectl get clusterrole system:coredns -n kube-system -o json \
  | jq '.rules += [{"apiGroups":["discovery.k8s.io"],"resources":["endpointslices"],"verbs":["list","watch"]}]' \
MagMax
  • 1,645
  • 2
  • 17
  • 26