How to change a ConfigMap of a Deployment using a script?

10/12/2017

There are Deployments that may use a configmap with the name like cm-myapp-*. How to write a script that looks at all Deployments and reconfigures them from using some of their cm-myapp-* to the new specific cm-myapp-123?

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: myapp
spec:
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: myapp:2
        volumeMounts:
          - name: config-volume
            mountPath: /etc/myapp/
      volumes:
        - name: config-volume
          configMap:
            name: cm-myapp-9375546193
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-myapp-123
data:
  myapp.conf: |
    hi

There is kubectl patch that accepts 'JSON patches', and there is kubectl edit that looks like interactive-only. Some kubectl commands accept go-templates, but they aren't for editing. Dumping the whole config gives some superfluous fields.

Can extract some stuff:

kubectl get deployment -o go-template --template="{{range .items}}{{\$deploymentName := .metadata.name}}{{range .spec.template.spec.volumes}}{{if .configMap}}{{\$deploymentName}} {{.configMap}}:{{end}}{{end}}{{end}}" | tr ':' '\n'

kubectl get deployment myapp -ojsonpath="{.spec.template.spec.volumes[0].configMap.name}}"

Need to patch it (not working):

kubectl patch deployment myapp -p '{ "op": "replace", "path": ".spec.template.spec.volumes[0].name", "value": "cf" }'

So how can it be done? What's the syntax of kubectl patch?

-- Velkan
go
json
kubernetes

4 Answers

10/12/2017

Outputs an old config name, its index in the 'volumes' array and the name of the deployment. Filters out configs that we aren't interested in, patches all deployments.

#!/bin/bash

name=cm-myapp
unique_name=cm-myapp-123

# Columns: ConfigMap name, index in volumes, Deployment name.
kubectl get deployment -o go-template --template="{{range .items}}{{\$deploymentName := .metadata.name}}{{range \$i, \$v := .spec.template.spec.volumes}}{{if .configMap}}{{.configMap.name}} {{\$i}} {{\$deploymentName}}:{{end}}{{end}}{{end}}" | tr ':' '\n' |
    egrep "^$name-[^-]+ " | while read l; do
        i=$(printf '%s\n' "$l" | awk '{print $2}')
        deployment=$(printf '%s\n' "$l" | awk '{print $3}')
        kubectl patch deployment $deployment --type=json -p "[{ \"op\": \"replace\", \"path\": \"/spec/template/spec/volumes/$i/configMap/name\", \"value\": \"$unique_name\" }]"
    done
-- Velkan
Source: StackOverflow

10/12/2017

In my opinion it would be optimal if you knew the deployments in advance. You should generate your manifests that you apply from some templating solution (I would suggest getting familiar with helm which is much more then just templates) and then have the configmap managed with the templating.

-- Radek 'Goblin' Pieczonka
Source: StackOverflow

10/13/2017

Here's some kubectl patch syntax I've used to patch images by container name:

-p "{\"spec\":{\"template\":{\"spec\":{\"volumes\":[{\"name\":\"myapp\",\"image\":\"$imageUri\"}]}}}}"

The same thing might work for you by patching the volumes key instead:

-p "{\"spec\":{\"template\":{\"spec\":{\"volumes\":[{\"name\":\"config-volume\",\"configMap\":{\"name\":\"myapp-123\"}}]}}}}"

What's the syntax of kubectl patch?

The official documentation is here with examples here. According to that guide, you might try setting --type=json on your patch command.

There are two syntax: JSON Patch and JSON Merge Patch.

-- Taylor Wood
Source: StackOverflow

10/12/2017

Use jq, the "awk for json" to tranform the JSON document(s). I'm not sure which fields you want to change exactly, but how to adjust it should be clear from the jq argument.

$ cat x.json 
{
  "apiVersion": "apps/v1beta1",
  "kind": "Deployment",
  "foo": "myapp"
  "metadata": {
    "name": "myapp"
  },
  "spec": {
    "template": {
      "metadata": {
        "labels": {
          "app": "myapp"
        }
      }
    }
  }
}

$ jq '
  .metadata.name = "cm-myapp-123"
| .spec.template.metadata.labels.app = "cm-myapp-123"
| .
' < x.json 
{
  "apiVersion": "apps/v1beta1",
  "kind": "Deployment",
  "foo": "myapp"
  "metadata": {
    "name": "cm-myapp-123"
  },
  "spec": {
    "template": {
      "metadata": {
        "labels": {
          "app": "cm-myapp-123"
        }
      }
    }
  }
}
-- Peter
Source: StackOverflow