Read env.valueFrom a value in nested object in kubernetes configMap

128 Views Asked by At

I need to have the container environment varible to read the value from configMap which has a nested object/map as below.

Name:         my-configmap
Namespace:    my-namespace
Labels:       <none>
Annotations:  alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:***

Data
====
payment-api-url:
----
dev: dev-url
qa: qa-url
prod: prod-url


BinaryData
====

Events:  <none>  

manifest file for the configMap and the container trying to read the value is as below:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-configmap
data:
  payment-api-url: |
    dev: dev-url
    qa: qa-url
    prod: prod-url
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: my-pod
  replicas: 1
  template:
    metadata:
      labels:
        app.kubernetes.io/name: my-pod
    spec:
      containers:
        - image: my-image
          imagePullPolicy: Always
          name: my-container
          ports:
            - containerPort: 8080
          env:
            - name: PAYMENT_API_URL
              valueFrom:
                configMapKeyRef:
                  name: my-configmap
                  key: payment-api-url.dev
---

But I am getting the below error with pod in CreateContainerConfigError status.

Error: couldn't find key payment-api-url.dev in ConfigMap my-namespace/my-configmap

How should I read this value?

1

There are 1 best solutions below

0
On
data:
  payment-api-url: |
    dev: dev-url
    qa: qa-url
    prod: prod-url

So this bit here is not a nested key. It's actually a string called payment-api-url that happens to contain YAML. I'd recommend breaking this up into separate keys if possible, but if for whatever reason that's not possible you could use a startup container to parse it as YAML and return the key. Something like...

apiVersion: v1
kind: Pod
metadata:
  name: example
spec:
  containers:
  - name: main-container
    image: your-main-image:latest
    volumeMounts:
    - name: config-volume
      mountPath: /env
  initContainers:
  - name: init-container
    image: linuxserver/yq
    command: ["/bin/sh", "-c"]
    args:
    - echo $PAYMENT_API_URL | yq eval '.dev' - > /env/dev
    env:
    - name: PAYMENT_API_URL
      valueFrom:
        configMapKeyRef:
          name: your-config-map
          key: payment-api-url
    volumeMounts:
    - name: config-volume
      mountPath: /env
  volumes:
  - name: config-volume
    emptyDir: {}

...and then if you need it in an env var and not a file, you can override the command of the main container to set that env var.

That said, this is definitely a bit of a hack! You should solve this at the level of Kubernetes objects and have whatever is generating your Kubernetes manifests generate something like...

payment-api-url.dev: dev-url
payment-api-url.qa: qa-url
payment-api-url.prod: prod-url