I have a shell script that triggers a helm upgrade. I have a comma-separated variable in the script that I want to use in help to loop through via range. I tried a comma separate variable but it does not work.
secretkey="aws.id,aws.pwd"
helm upgrade <chartName> ./charts/xxx -f values.yaml --install --set a.secretlist=$secretkey
And I want to loop through this variable in helm. Something like this
{{- range $v := .Values.a.secretlist }}
- name: {{ $v }}
valueFrom:
secretKeyRef:
name: secretname
key: {{ $v }}
optional: true
{{ end }}
I know I can loop through a yaml, but is there any way to loop through a shell variable?
Helm does not support setting non-existent array elements, you can assign values by passing the entire array or set the corresponding number of elements first, and then modify it by --set a.secretlist[i]=xxx.
If you want to set by --set, you need to pass array values you can use curly braces (unix shell require quotes):
--set a.secretlist={"3"\,"4"\,"5"}
Notice!!!
The separator of the array elements , you must remember to add a \
Demo
values.yaml
a:
secretlist:
- "1"
- "2"
tpl.yaml
{{- range $v := .Values.a.secretlist }}
- name: {{ $v }}
valueFrom:
secretKeyRef:
name: secretname
key: {{ $v }}
optional: true
{{ end }}
cmd
secretkey={"aws.id"\,"aws.pwd"}
helm upgrade <chartName> ./charts/xxx -f values.yaml --install --set a.secretlist=$secretkey
output
- name: aws.id
valueFrom:
secretKeyRef:
name: secretname
key: aws.id
optional: true
- name: aws.pwd
valueFrom:
secretKeyRef:
name: secretname
key: aws.pwd
optional: true
Related
I have a general helm chart in my Kubernetes cluster taking a multiline text field with environment variables (identified by KEY=VALUE), translating them into the deployment.yaml like this:
Inside the Rancher dialog:
In the deployment.yaml:
{{- if .Values.envAsMultiline }}
{{- range (split "\n" .Values.envAsMultiline) }}
- name: "{{ (split "=" .)._0 }}"
value: "{{ (split "=" .)._1 }}"
{{- end }}
{{- end }}
This works fine so far. But the problem now is: If I have a "=" in my environment variable (Like in the JAVA_OPTS above), it splits the environment variable value at the second "=" of the line:
JAVA_OPTS=-Xms1024m -Xmx2048m -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=512m
is translated to
-Xms1024m -Xmx2048m -XX:MetaspaceSize
The "=256M -XX:MaxMetaspaceSize=512m" is missing here.
How do I correct my deployment.yaml template accordingly?
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: ENVIRONMENT1
value: "testABC"
- name: JAVA_OPTS
value: "-Xms1024m -Xmx2048m -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=512M"
- name: TZ
value: "Europe/Berlin"
deployment.yaml
containers:
- name: {{ .Chart.Name }}
env:
{{ toYaml .Values.env | nindent xxx }}
(ps: xxx --> actual indent)
Plan 2:
Env is defined in the form of kv, which is rendered in an iterative manner
values.yaml
env:
ENVIRONMENT1: "testABC"
JAVA_OPTS: "-Xms1024m -Xmx2048m -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=512M"
TZ: "Europe/Berlin"
deployment.yaml
containers:
- name: {{ .Chart.Name }}
env:
{{- range $k, $v := .Values.env }}
- name: {{ $k | quote }}
value: {{ $v | quote }}
{{- end }}
Plan 3:
If you still need to follow your previous writing, then you can do this
values.yaml
env: |
ENVIRONMENT1=testABC
JAVA_OPTS=-Xms1024m -Xmx2048m -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=512M
TZ=Europe/Berlin
deployment.yaml
containers:
- name: {{ .Chart.Name }}
{{- if .Values.env }}
env:
{{- range (split "\n" .Values.env) }}
- name: {{ (split "=" .)._0 }}
value: {{ . | trimPrefix (split "=" .)._0 | trimPrefix "=" | quote }}
{{- end }}
{{- end }}
output:
env:
- name: ENVIRONMENT1
value: "testABC"
- name: JAVA_OPTS
value: "-Xms1024m -Xmx2048m -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=512M"
- name: TZ
value: "Europe/Berlin"
This question already has answers here:
How to pass entire JSON string to Helm chart value?
(5 answers)
Closed 1 year ago.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-configmap
data:
{{- range $key, $value := .Values.envVars }}
{{ $key }}: {{ $value | quote }}
{{- end }}
Running
helm upgrade my-app --install --set envVars.CREDS='{"key":"val"}'
with the above configmap results in a configmap as follows:
CREDS = ["key":"val"]
So instead of a JSON, it's an array.
What do I need to configure to pass it as a JSON?
That looks fine, based on this answer regarding json objects representation in yamls: https://stackoverflow.com/a/33989696/11874278
If you want to achieve another thing - this key as json string you could do:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-configmap
data:
{{- range $key, $value := .Values.envVars }}
{{ $key }}: >
{{ $value }}
{{- end }}
In a helm chart want to iterate over a map that contains structured values.
I do know how to iterate over a map with simple string values. I also can iterate over an array that contains structured values (not shown here). But I did not manage to iterate over a map that contains structured values.
This is my directory structure containing 3 files:
templates/test.yaml
Chart.yaml
values.yaml
A simple file Chart.yaml (just for completing the showcase):
---
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: foochart
version: 0.1.0
The file values.yaml with a map that contains simple string values (label) and one that contains structured values (label1):
---
label:
fook: foo
bark: bar
label1:
fook:
name: foo
value: foo1
bark:
name: bar
value: bar2
This template test.yaml works:
---
env:
{{- range $k, $v := .Values.label }}
- name: {{ $k }}
value: {{ $v }}
{{- end }}
But when I substitute .Values.label by .Values.label1, it produces no output.
This is my command for testing:
helm template foochart
Question: Is it possible to process a map with structured values? I would like to use something like $v.name. If yes, how can I do that?
You can in fact use syntax like $v.name, if you know that $v is a variable holding an object.
env:
{{- range $k, $v := .Values.label1 }}
- name: {{ $k }}_{{ $v.name }}
value: {{ $v.value }}
{{- end }}
If you know that it has exactly the syntax you want, there is an underdocumented toYaml function that takes an arbitrary object and returns it as unindented YAML. In your example, each of the values has the form of an env: item, and if you know (or specify) that, you can write out literally:
env:
{{- range .Values.label1 }}
- {{ . | toYaml | indent 4 | trim }}
{{- else }}
[]
{{- end }}
(In this last example: I'm not assigning a variable, so . is temporarily reassigned to each value in the map, and the keys are lost; for each item, I convert it to YAML, indent it by 4 spaces, but then trim out leading and trailing whitespace; and if there are no values, I explicitly write out an empty list.)
It's usually easier to specify a format you want your values to be in, and work with that. If you for some reason can't be sure which form you have, the template language includes functions to test on a value's type, so in principle you can test:
env:
{{- range $k, $v := .Values.labelN }}
{{- if kindIs "string" $v }}
- name: {{ $k }}
value: {{ $v }}
{{- else }}
- name: {{ $v.name }}
value: {{ $v.value }}
{{- end }}
{{- else }}
[]
{{- end }}
I want to be able to pass in env vars plain text and from secrets like this:
envVars:
var1: 'sdfsdfsdf'
var2: 'efwefwefwef'
envVarsSecret:
var3:
secretKeyRef: my-secret
key: key
envVars works but envVarsSecret does not
env:
# This works
{{- range $key, $value := .Values.envVars }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
# This doesn't throw an error but when deployed secretKeyRef and key are blank (the secret was pre-created and exists)
{{- range $key, $value := .Values.envVarsSecret }}
- name: {{ $key }}
valueFrom:
secretKeyRef:
name: {{ $.Values.envVarsSecret.secretKeyRef | quote }}
key: {{ $.Values.envVarsSecret.key | quote }}
{{- end }
$. like that means you are trying to access a root value. You want something like {{ $value.secretKeyRef | quote }} and similar. Also that's not looping :)
I need to loop through a list of instances and create 1 stateful set for every instance. However, inside range I then limit myself to the scope of that loop. I need to access some global values in my statefulset.
I've solved it by just putting all global objects I need in an env variable but... this very seems hacky.
What is the correct way to loop through ranges while still being able to reference global objects?
Example of my loop
{{- $values := .Values -}}
{{- $release := .Release -}}
{{- range .Values.nodes }}
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ $release.Name }} <-- Global Scope
labels:
.
.
.
env:
- name: IP_ADDRESS
value: {{ .ip_address }} <-- From range scope
.
.
.
{{- end }}
Example of values
# Global
image:
repository: ..ecr.....
# Instances
nodes:
- node1:
name: node-1
iP: 1.1.1.1
- node2:
name: node-2
iP: 1.1.1.1
When entering a loop block you lose your global context when using .. You can access the global context by using $. instead.
As written in the Helm docs -
there is one variable that is always global - $ - this variable will always point to the root context. This can be very useful when you are looping in a range and need to know the chart's release name.
In your example, using this would look something like:
{{- range .Values.nodes }}
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ $.Release.Name }}
labels:
.
.
.
env:
- name: IP_ADDRESS
value: {{ .ip_address }}
.
.
.
{{- end }}
The question is about the global scope, but it is possible to keep access to any outer scope by storing it, like this:
{{- $outer := . -}}
Then, if you use named variables for the range, like this:
{{- range $idx, $node := .Values.nodes }}
You don't need ., so you can restore the outer scope, like this:
{{- with $outer -}}
In your example, using this would look something like:
{{- $outer := . -}}
{{- range $idx, $node := .Values.nodes }}
{{- with $outer -}}
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ .Release.Name }}
labels:
.
.
.
env:
- name: IP_ADDRESS
value: {{ $node.ip_address }}
.
.
.
{{- end }}
If you need to access the global scope only, simply add {{- with $ -}} will do.
{{- range $idx, $node := .Values.nodes }}
{{- with $ -}}
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ .Release.Name }}
labels:
.
.
.
env:
- name: IP_ADDRESS
value: {{ $node.ip_address }}
.
.
.
{{- end }}
{{- end }}
The best way is not to call many external objects inside the loop.
you can declare release name at the top in a variable to overcome this issue:
{{- $release_name := .Release.Name -}}