Helm iterate over keys - kubernetes-helm

I'm trying to iterate over list of secrets in my values files and mount it as env variables on the pods but having hard time with it.
The helm template command which spits the sample template lists the keys & value as I expect it to be but when i deploy only the "name" key gets mounted on the pods but nothing after that like the valuefrom and secretkeyref. Really appreciate any help on this :)
**DEPLOYMENT YAML**
{{- if $root.Values.secrets }}
{{- range $secrets := $root.Values.secrets }}
{{- range $data := $secrets.data }}
- name: {{ $data.name }}
valuefrom:
secretKeyRef:
name: {{ $secrets.name }}
key: {{ $data.name }}
{{ end }}
{{ end }}
{{ end }}
secrets:
- name: aws-secrets
data:
- key: "secret1"
name: DATABASE_HOST
- key: "secret2"
name: DATABASE_NAME

I fat fingered should be "valueFrom". 🤦

Related

Transform a helm dict into a list

I'm using a Helm chart to control what environment variables are set for a certain container in a deployment.
In my Values.yaml, I have an entry called env which is a dictionary:
image:
repository: xxxx.yyyyy.com/myimage
pullPolicy: IfNotPresent
# Enviroment variables that will be passed to the container.
env: {}
Now, I'll pass variables to the env dict using --set:
helm upgrade mydeployment chart --set env.VARIABLE=test
However, this must be transformed into a list to adhere to Kubernetes yaml:
spec:
template:
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
# This should come from that dict
env:
- name: VARIABLE
value: "test"
I don't know how to use the template language from Helm (sprig / go) to achieve that. Is it even possible?
To iterate through the map, the core Go text/template language provides a range keyword that can iterate through maps or arrays.
{{ range $key, $value := .Values.env }}
...
{{ end }}
Inside of this you can put arbitrary text. Helm doesn't require this to be any particular kind of YAML construct, just so long as the final result is valid YAML. For this setup a typical loop would look like
env:
{{- range $key, $value := .Values.env }}
- name: {{ quote $key }}
value: {{ quote $value }}
{{- end }}
You do need to be careful with indentation here. As a rule of thumb it often will work to include a - "swallow whitespace" indicator inside the open {{ and to not include one inside the close }}. The - name: must be at least as indented as the env: above it (ignoring the range line), and value: must be aligned with name:. I might put all of the template-language lines (the range and end) starting at the first column, even if they're embedded in a structure that's nested more.
spec:
template:
spec:
containers:
- name: {{ template "chart.fullname" . }}
env:
{{- range $key, $value := .Values.env }}
- name: {{ quote $key }}
value: {{ quote $value }}
{{- end }}
image: {{ .Values.registry }}/{{ .Values.image }}:{{ .Values.tag }}

How do I get the name of a parent element from my values.yaml in a Helm template?

I have a section in my values.yaml that looks like this:
env:
normal:
ENV_VAR_1: value
ENV_VAR_2: otherValue
secret:
secret-location:
ENV_VAR_USERNAME: username
ENV_VAR_PASSWORD: password
different-secret:
ENV_CONN_STRING: my_conn_string
I have a _helpers.tpl function like so:
{{/*
Add environment variables
*/}}
{{- define "helpers.list-env-variables" }}
{{- range $key, $val := .Values.env.normal }}
- name: {{ $key }}
value: {{ $val }}
{{- end }}
{{- range $name := .Values.env.secret }}
{{- }}
{{- range $key, $val := $name }}
- name: {{ $key }}
valueFrom:
secretKeyRef:
name: {{ $name }}
key: {{ $val }}
{{- end }}
{{- end }}
{{- end }}
and it's called in the deployment.yaml like you'd expect:
env:
{{- include "helpers.list-env-variables" . | indent 12 }}
So the normal environment variables get added just fine, but the secret ones don't. It specifies the secretKeyRef.name: value as map[ENV_VAR_USERNAME:username ENV_VAR_PASSWORD:password] instead of secret-key-ref.
helm debug output:
...
env:
- name: ENV_VAR_1
value: value
- name: ENV_VAR_2
value: otherValue
- name: ENV_VAR_PASSWORD
valueFrom:
secretKeyRef:
name: map[ENV_VAR_USERNAME:username ENV_VAR_PASSWORD:password]
key: password
- name: ENV_VAR_USERNAME
valueFrom:
secretKeyRef:
name: map[ENV_VAR_USERNAME:username ENV_VAR_PASSWORD:password]
key: username
- name: ENV_CONN_STRING
valueFrom:
secretKeyRef:
name: map[ENV_CONN_STRING:my_conn_string]
key: my_conn_string
I've already got all these secrets loaded into my K8s cluster (they get shared across a number of microservices, so I don't need to add new secrets which is what the default Helm template seems to want to do) - I just need to reference existing secrets.
My assumption here is that there's something I'm missing in my function to get the value of an element (e.g. "secret-location"), I just can't figure out what it is. If there's a different way I can structure my values.yaml to accomplish this, I'm game for that as well.
When obtaining secret data, it can also be obtained in the form of key-val.
Try replacing _helpers.tpl with the following
{{/*
Add environment variables
*/}}
{{- define "helpers.list-env-variables" }}
{{- range $key, $val := .Values.env.normal }}
- name: {{ $key }}
value: {{ $val }}
{{- end }}
{{- range $k, $v := .Values.env.secret }}
{{- }}
{{- range $key, $val := $v }}
- name: {{ $key }}
valueFrom:
secretKeyRef:
name: {{ $k }}
key: {{ $val }}
{{- end }}
{{- end }}
{{- end }}

Helm Chart environment from values file

I have the following values file:
MYVAR: 12123
MYVAR2: 214123
I want to iterate over them and use them as env variables in my deployment template:
env:
{{- range .Values.examplemap }}
- name: {{ .name }}
value: {{ .value }}
{{- end }}
I tried this
For iterate over a map in helm you can try put this in the values.yaml
extraEnvs:
- name: ENV_NAME_1
value: value123
- name: ENV_NAME_2
value: value123
So in your template you must iterate the extraEnvs like this:
extraEnvs:
{{- range .Values.image.extraEnvs }}
- name: {{ .name | quote }}
value: {{ .value | quote }}
{{- end }}
In the core Go text/template language, the range operator can iterate over either a list or a map. There's specific syntax to assign the key-value pairs in a map to local variables:
env:
{{- $k, $v := range .Values.examplemap }}
- name: {{ $k }}
value: {{ $v }}
{{- end }}

Helm referring to kubernetes secrets in enviroment variables

I have some environment variables that I'm using in a helm installation and want to hide the password using a k8s secret.
values.yaml
env:
USER_EMAIL: "test#test.com"
USER_PASSWORD: "p8ssword"
I want to add the password via a kubernetes secret mysecrets, created using
# file: mysecrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysecrets
type: Opaque
data:
test_user_password: cGFzc3dvcmQ=
and then add this to values.yaml
- name: TEST_USER_PASSWORD
valueFrom:
secretKeyRef:
name: mysecrets
key: test_user_password
I then use the following in the deployment
env:
{{- range $key, $value := $.Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
Is it possible to mix formats for environment variables in values.yaml i.e.,
env:
USER_EMAIL: "test#test.com"
- name: USER_PASSWORD
valueFrom:
secretKeyRef:
name: mysecrets
key: test_user_password
Or is there a way of referring to the secret in line in the original format?
Plan 1 :
One of the simplest implementation methods
You can directly use the yaml file injection method, put the env part here as it is, so you can write the kv form value and the ref form value in the values in the required format.
As follows:
values.yaml
env:
- name: "USER_EMAIL"
value: "test#test.com"
- name: "USER_PASSWORD"
valueFrom:
secretKeyRef:
name: mysecrets
key: test_user_password
deployment.yaml
containers:
- name: {{ .Chart.Name }}
env:
{{ toYaml .Values.env | nindent xxx }}
{{- end }}
(ps: xxx --> actual indent)
Plan 2:
Distinguish the scene by judging the type.
As follows:
values.yaml
env:
USER_EMAIL:
type: "kv"
value: "test#test.com"
USER_PASSWORD:
type: "secretRef"
name: mysecrets
key: p8ssword
USER_CONFIG:
type: "configmapRef"
name: myconfigmap
key: mycm
deployment.yaml
containers:
- name: {{ .Chart.Name }}
env:
{{- range $k, $v := .Values.env }}
- name: {{ $k | quote }}
{{- if eq $v.type "kv" }}
value: {{ $v.value | quote }}
{{- else if eq $v.type "secretRef" }}
valueFrom:
secretKeyRef:
name: {{ $v.name | quote }}
key: {{ $v.key | quote }}
{{- else if eq $v.type "configmapRef" }}
valueFrom:
configMapKeyRef:
name: {{ $v.name | quote }}
key: {{ $v.key | quote }}
{{- end }}
{{- end }}

How to pass dynamic arguments to a helm chart that runs a job

I'd like to allow our developers to pass dynamic arguments to a helm template (Kubernetes job). Currently my arguments in the helm template are somewhat static (apart from certain values) and look like this
Args:
--arg1
value1
--arg2
value2
--sql-cmd
select * from db
If I were run a task using the docker container without Kubernetes, I would pass parameters like so:
docker run my-image --arg1 value1 --arg2 value2 --sql-cmd "select * from db"
Is there any way to templatize arguments in a helm chart in such way that any number of arguments could be passed to a template.
For example.
cat values.yaml
...
arguments: --arg1 value1 --arg2 value2 --sql-cmd "select * from db"
...
or
cat values.yaml
...
arguments: --arg3 value3
...
I've tried a few approaches but was not successful. Here is one example:
Args:
{{ range .Values.arguments }}
{{ . }}
{{ end }}
Yes. In values.yaml you need to give it an array instead of a space delimited string.
cat values.yaml
...
arguments: ['--arg3', 'value3', '--arg2', 'value2']
...
or
cat values.yaml
...
arguments:
- --arg3
- value3
- --arg2
- value2
...
and then you like you mentioned in the template should do it:
args:
{{ range .Values.arguments }}
- {{ . }}
{{ end }}
If you want to override the arguments on the command line you can pass an array with --set like this:
--set arguments={--arg1, value1, --arg2, value2, --arg3, value3, ....}
In your values file define arguments as:
extraArgs:
argument1: value1
argument2: value2
booleanArg1:
In your template do:
args:
{{- range $key, $value := .Values.extraArgs }}
{{- if $value }}
- --{{ $key }}={{ $value }}
{{- else }}
- --{{ $key }}
{{- end }}
{{- end }}
Rico's answer needed to be improved.
Using the previous example I've received errors like:
templates/deployment.yaml: error converting YAML to JSON: yaml or
failed to get versionedObject: unable to convert unstructured object to apps/v1beta2, Kind=Deployment: cannot restore slice from string
This is my working setup with coma in elements:
( the vertical format for the list is more readable )
cat values.yaml
...
arguments: [
"--arg3,",
"value3,",
"--arg2,",
"value2,",
]
...
in the template should do it:
args: [
{{ range .Values.arguments }}
{{ . }}
{{ end }}
]
because of some limitations, I had to work with split and to use a delimiter, so in my case:
deployment.yaml :
{{- if .Values.deployment.args }}
args:
{{- range (split " " .Values.deployment.args) }}
- {{ . }}
{{- end }}
{{- end }}
when use --set:
helm install --set deployment.args="--inspect server.js" ...
results with:
- args:
- --inspect
- server.js
The arguments format needs to be kept consistent in such cases.
Here is my case and it works fine.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.app.name }}
labels:
app: {{ .Values.app.name }}
instance: test
spec:
replicas: {{ .Values.master.replicaCount }}
selector:
matchLabels:
app: {{ .Values.app.name }}
instance: test
template:
metadata:
labels:
app: {{ .Values.app.name }}
instance: test
spec:
imagePullSecrets:
- name: gcr-pull-secret
containers:
- name: {{ .Values.app.name }}
image: {{ .Values.app.image }}
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
args:
[
"--users={{int .Values.cmd.users}}",
"--spawn-rate={{int .Values.cmd.rate}}",
"--host={{.Values.cmd.host}}",
"--logfile={{.Values.cmd.logfile}}",
"--{{.Values.cmd.role}}"]
ports:
- containerPort: {{ .Values.container.port }}
resources:
requests:
memory: {{ .Values.container.requests.memory }}
cpu: {{ .Values.container.requests.cpu }}
limits:
memory: {{ .Values.container.limits.memory }}
cpu: {{ .Values.container.limits.cpu }}
Unfortunately following mixed args format does not work within container construct -
mycommand -ArgA valA --ArgB valB --ArgBool1 -ArgBool2 --ArgC=valC
The correct format of above command expected is -
mycommand --ArgA=valA --ArgB=valB --ArgC=valC --ArgBool1 --ArgBool2
This can be achieved by following constructs -
#Dockerfile last line
ENTRYPOINT [mycommand]
#deployment.yaml
containers:
- name: {{ .Values.app.name }}
image: {{ .Values.app.image }}
args: [
"--ArgA={{ .Values.cmd.ArgA }}",
"--ArgB={{ .Values.cmd.ArgB }}",
"--ArgC={{ .Values.cmd.ArgC }}",
"--{{ .Values.cmd.ArgBool1 }}",
"--{{ .Values.cmd.ArgBool2 }}" ]
#values.yaml
cmd:
ArgA: valA
ArgB: valB
ArgC: valC
ArgBool1: "ArgBool1"
ArgBool2: "ArgBool2"
helm install --name "airflow" stable/airflow --set secrets.database=mydatabase,secrets.password=mypassword
So this is the helm chart in question: https://github.com/helm/charts/tree/master/stable/airflow
Now I want to overwrite the default values in the helm chart
secrets.database and
secrets.password so I use --set argument and then it is key=value pairs separated by commas.
helm install --name "<name for your chart>" <chart> --set key0=value0,key1=value1,key2=value2,key3=value3
Did you try this?
{{ range .Values.arguments }}
{{ . | quote }}
{{ end }}
Acid R's key/value solution was the only thing that worked for me.
I ended up with this:
values.yaml
arguments:
url1: 'http://something1.example.com'
url2: 'http://something2.example.com'
url3: 'http://something3.example.com'
url4: 'http://something3.example.com'
And in my template:
args:
{{- range $key, $value := .Values.arguments }}
- --url={{ $value }}
{{- end }}