Kubernetes: Updating an existing ConfigMap using kubectl replace

Creating a ConfigMap using ‘kubectl create configmap’ is a straightforward operation.   However, there is not a corresponding ‘kubectl apply’ that can easily update that ConfigMap.

As an example, here are the commands for the creation of a simple ConfigMap using a file named “ConfigMap-test1.yaml“.

$ cat ConfigMap-test1.yaml
test1:
  foo: bar

# create and then show ConfigMap
$ kubectl create configmap test1 --from-file=ConfigMap-test1.yaml
$ kubectl describe ConfigMap test1

But there is not a corresponding ‘apply’ or ‘update’ that can be run if you tweaked the file and wanted to update the ConfigMap.  However, this can be achieved by using “–dry-run” and “kubectl replace”.

# file with change made
$ cat ConfigMap-test1.yaml
test1:
  foo: nowchanged

# apply update to ConfigMap using dry-run and replace
kubectl create configmap test1 --from-file=ConfigMap-test1.yaml -o yaml --dry-run | kubectl replace -f -

# validate that change was made
$ kubectl describe ConfigMap test1

And if you wanted to just update a portion of the content without using a file, then you could use jq/sed as described in one of my previous articles.  Below is an example for replacing the DNS resolv.conf value embedded in ‘data.kubelet’ of the ConfigMap.

kubectl get cm -n kube-system kubelet-config -o=json | \
jq 'del(.metadata.resourceVersion,.metadata.uid,.metadata.selfLink,.metadata.creationTimestamp,.metadata.annotations,.metadata.generation,.metadata.ownerReferences,.status)' | \
sed -E 's#resolvConf: [^\n ]*\\n#resolvConf: /etc/kubeadm-resolv.conf\\n#' | \
kubectl replace -f -

Here is an example Ansible playbook “playbook-k8s-configmap-update.yaml” that registers a variable which controls creation versus update.

---
- hosts: localhost
  connection: local

  tasks:

  # make sure kubectl context already set
  - command: kubectl get nodes

  - name: does configmap exist?
    command: kubectl describe configmap test1
    failed_when: not configmap_res.rc in [0,1]
    register: configmap_res
     
  - name: show configmap results, 0=found,1=not found
    debug:
      msg: "{{configmap_res.rc}}"

  - name: kubectl to create new configmap
    command: kubectl create configmap test1 --from-file=ConfigMap-test1.yaml
    when: configmap_res.rc != 0

  - name: kubectl to update existing configmap
    shell: 'kubectl create configmap test1 --from-file=ConfigMap-test1.yaml -o yaml --dry-run | kubectl replace -f -'
    when: configmap_res.rc == 0

  - name: show final configmap
    command: kubectl describe ConfigMap test1
    register: final_output

  - debug:
      msg: "{{final_output.stdout_lines}}"

Which would be run with the following command:

ansible-playbook playbook-k8s-configmap-update.yml --connection=local

 

REFERENCES

stackoverflow, updating already existing ConfigMap using kubectl

stackoverflow, updating existing ConfigMap using kubectl dry-run and apply

atomist.com, updating a K8S ConfigMap or Secret

blog questionable.services, ConfigMap change triggering deployment change using spec.annotation hashed value

NOTES

replacing TLS secret using apply

kubectl -n kube-system create secret tls ingress-tls-cert --key $domain.key --cert $domain.crt --dry-run=client -o=yaml | kubectl apply -f -