helm append variable in a loop [duplicate] - kubernetes

I have the function:
{{- define "myapp.getSubKey" -}}
{{- $map := .source }}
{{ "Before: " }}{{ $map }}
{{- range $key, $value := .keys }}
{{- if kindIs "int" $value }}
{{- $map := index $map (int $value) }}
{{ "After: " }}{{ $map }}
{{- end }}
{{- end }}
{{ $map }}
{{- end }}
I call it with include "myapp.getSubKey" (dict "source" .Values.vars "keys" list(0))
This prints out:
Before: [map[name:MYSQL_ROOT_PASSWORD valueFrom:map[secretKeyRef:map[key:db-pass name:db-creds]]] map[name:MYSQL_ROOT_USER valueFrom:map[secretKeyRef:map[key:db-user name:db-creds]]]]
After: [map[name:MYSQL_ROOT_PASSWORD valueFrom:map[secretKeyRef:map[key:db-pass name:db-creds]]]
[map[name:MYSQL_ROOT_PASSWORD valueFrom:map[secretKeyRef:map[key:db-pass name:db-creds]]] map[name:MYSQL_ROOT_USER valueFrom:map[secretKeyRef:map[key:db-user name:db-creds]]]]
So you can see it correctly navigates down and changes $map within the if statement, but when it exits the loop, it goes back to what it was before the loop.
How do I change the "global" value?

Try using the operator = instead of assignment operator := in the inner if-block. When you assigned the $map with :=, the scope is limited only to the if-block.
{{- define "myapp.getSubKey" -}}
{{- $map := .source }}
{{ "Before: " }}{{ $map }}
{{- range $key, $value := .keys }}
{{- if kindIs "int" $value }}
{{- $map = index $map (int $value) }} // <------- here
{{ "After: " }}{{ $map }}
{{- end }}
{{- end }}
{{ $map }}
{{- end }}
The operator = is supported since helm v2.13.0.

Related

Use defined variables from parent scope in a named template

I'm trying to provide idiomatic access to a value defined in a template to a named template being included. This much works:
# values.yml
versions:
- 1.2.3.4
- 5.6.7.8
# _helpers.tmpl
{{/*
Get the service path from the version string (passed as ".")
*/}}
{{- define "sample.servicePath" -}}
{{- $pat := "^[0-9]+\\.[0-9]+\\.([0-9]+)\\.[0-9]+$" }}
{{- $version := . | trimAll "\"" }}
{{- if regexMatch $pat $version }}
{{- regexReplaceAll $pat $version "${1}" }}
{{- else }}
{{- printf "Can't extract service number from version string %s" . | fail -}}
{{- end }}
{{- end }}
# sample.yml
{{- range $version := .Values.versions }}
{{- $servicePath := (include "sample.servicePath" $version) }}
# Here is the servicePath: {{ $servicePath }} which works
{{- end }}
But I imagined that this would also work:
# _helpers.tmpl (further down in the file)
{{- define "sample.labels" -}}
servicePath: {{ $servicePath }}
{{- end }}
# sample.yml
{{- range $version := .Values.versions }}
{{- $servicePath := (include "sample.servicePath" $version) }}
# Here is the servicePath: {{ $servicePath }} which still works
# but these labels: {{ include "sample.labels" . }} do not
{{- end }}
Ultimately I don't mind the implementation, I just need to be able to take a single list of values, range over them (to create multiple resources), and parse their value.
The Go text/template language, and by extension Helm charts, doesn't have global variables or dynamic scopes; you can't do this in the way you're suggesting. Each template invocation has its own scope, and the only things it can see are its single parameter . and any variables it declares locally.
However, Helm includes the Sprig extension library, which includes list and dict functions to create composite values. You can use these to pass multiple values into a template:
{{- define "sample.labels" -}}
{{- $top := index . 0 -}}
{{- $servicePath := index . 1 -}}
app.kubernetes.io/instance: {{ $top.Release.Name }}
servicePath: {{ $servicePath }}
{{ end -}}
{{- range $version := .Values.versions }}
{{- $servicePath := (include "sample.servicePath" $version) }}
{{- include "sample.labels" (list . $servicePath) }}
In the last line, we are invoking the template with a list of two parameters, the top-level Helm object (here as .) and the $servicePath local variable. The top of the sample.labels template then unpacks that back into two local variables within its own template, and then uses those to generate a reference set of labels.

helm template check for empty string

I have been asked to modify a Helm template to accommodate a few changes to check if a value is empty or not as in the code snippet below. I need to check $var.alias inside the printf in the code snippet and write custom logic to print a custom value. Any pointers around the same would be great.
{{- range $key, $value := .Values.testsConfig.keyVaults -}}
{{- range $secret, $var := $value.secrets -}}
{{- if nil $var.alias}}
{{- end -}}
{{ $args = append $args (printf "%s=/mnt/secrets/%s/%s" $var.alias $key $var.name | quote) }}
{{- end -}}
{{- end -}}
I decided to test what madniel wrote in his comment. Here are my files:
values.yaml
someString: abcdef
emptyString: ""
# nilString:
templates/test.yaml
{{ printf "someEmptyString=%q)" .Values.someString }}
{{ printf "emptyString=%q)" .Values.emptyString }}
{{ printf "nilString=%q)" .Values.nilString }}
{{- if .Values.someString }}
{{ printf "someString evaluates to true" }}
{{- end -}}
{{- if .Values.emptyString }}
{{ printf "emptyString evaluates to true" }}
{{- end -}}
{{- if .Values.nilString }}
{{ printf "nilString evaluates to true" }}
{{- end -}}
{{- if not .Values.emptyString }}
{{ printf "not emptyString evaluates to true" }}
{{- end -}}
{{- if not .Values.nilString }}
{{ printf "not nilString evaluates to true" }}
{{- end -}}
Helm template output:
➜ helm template . --debug
install.go:173: [debug] Original chart version: ""
install.go:190: [debug] CHART PATH: <REDACTED>
---
# Source: asd/templates/test.yaml
someEmptyString="abcdef")
emptyString="")
nilString=%!q(<nil>))
someString evaluates to true
not emptyString evaluates to true
not nilString evaluates to true
So yes, it should work if you use {{ if $var.alias }}

Use variable value with .Values in Helm

I've below parameters in values.yaml. I need to fetch the value of the schema but the database name may change
preparedDatabases:
a3s:
schemas:
a3s:
I'm using below functions in tpl to return the databasename and the schema name.
{{/* Return Databasename
*/}}
{{- define "databasename" }}
{{- range $key, $val := .Values.spec.preparedDatabases -}}
{{- $key | toYaml | trim }}
{{- end -}}
{{- end -}}
{{/* Return Schemaname
*/}}
{{- define "schema" }}
{{- $dbname := include "databasename" . }}
{{- range $key, $val := ( .Values.spec.preparedDatabases.{{ $dbname }}.schemas) -}}
{{- $key | toYaml | trim }}
{{- end -}}
{{- end -}}
But it returns the error bad character U+0024 '$'
Could you please help on how can i interpret the variable dbname to be used with .Values
Thanks in advance

helm double quote annotations value

I am trying to quote my annotation values. I am trying like this
annotations:
{{- range $key, $value := .Values.ingress.annotations }}
{{ $key }}: {{ printf "%s" $value | quote }}
{{- end }}
and this
annotations:
{{- range $key, $value := .Values.ingress.annotations }}
{{ $key }}: "{{ $value }}"
{{- end }}
this is my values.yaml
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/force-ssl-redirect: false
but it is not working. Even if I double quote the annotation value in values.yaml helm is removing the quote. Can somebody tell me how can I get helm with double quote values in annotation?
I am using Helm version 3.
You could try this:
annotations:
{{- range $key, $value := .Values.ingress.annotations }}
{{ $key }}: {{ $value | quote }}
{{- end }}

Helm: generate comma separated list

Using Helm templates, I'm trying to generate a list of server names based on a number in values.yaml. The dot for this template is set to the number (its a float64).
{{- define "zkservers" -}}
{{- $zkservers := list -}}
{{- range int . | until -}}
{{- $zkservers := print "zk-" . ".zookeeper" | append $zkservers -}}
{{- end -}}
{{- join "," $zkservers -}}
{{- end -}}
For an input of, say, 3 I'm expecting this to produce:
zk-0.zookeeper,zk-1.zookeeper,zk-2.zookeeper
It produces nothing.
I understand that the line within the range block is a no-op since the variable $zkservers is a new variable each time the loop iterates. It is not the same variable as the $zkservers in the outer scope.
I hope the intention is clear of what I want to do. I am at a loss how to do it.
Anyone know how to do this with Helm templates?
For those from 2020+ it now can be achieved as simple as that:
{{ join "," .Values.some.array }}
It produces the correct output:
value: "test1,test2,test3"
At least it works with (more or less) recent versions of helm-cli:
Client: &version.Version{SemVer:"v2.16.2", GitCommit:"bbdfe5e7803a12bbdf97e94cd847859890cf4050", GitTreeState:"clean"}
Another quick way of doing it:
{{- define "helm-toolkit.utils.joinListWithComma" -}}
{{- $local := dict "first" true -}}
{{- range $k, $v := . -}}{{- if not $local.first -}},{{- end -}}{{- $v -}}{{- $_ := set $local "first" false -}}{{- end -}}
{{- end -}}
If you give this input like:
test:
- foo
- bar
And call with:
{{ include "helm-toolkit.utils.joinListWithComma" .Values.test }}
You'll get the following rendered:
foo,bar
This is from OpenStack-Helm's Helm-toolkit chart, which is a collection of utilities for similar purposes.
I faced with the same problem and your solution with dictionary saved my day. It's a good workaround and it can be just a little simpler:
{{- define "zkservers" -}}
{{- $zk := dict "servers" (list) -}}
{{- range int . | until -}}
{{- $noop := printf "zk-%d.zookeeper" . | append $zk.servers | set $zk "servers" -}}
{{- end -}}
{{- join "," $zk.servers -}}
{{- end -}}
You're currently defining a new varialbe in the scope of the list while you want to alter the existing value.
In order to fix your bug, you only need to change the way you assign the value to $zkservers:
- {{- $zkservers := print "zk-" . ".zookeeper" | append $zkservers -}}
+ {{- $zkservers = print "zk-" . ".zookeeper" | append $zkservers -}}
which gives you the following script:
{{- define "zkservers" -}}
{{- $zkservers := list -}}
{{- range int . | until -}}
{{- $zkservers = print "zk-" . ".zookeeper" | append $zkservers -}}
{{- end -}}
{{- join "," $zkservers -}}
{{- end -}}
With this slight modification, {{ include "zkservers" 3 }}gives you the output zk-0.zookeeper,zk-1.zookeeper,zk-2.zookeeper
I was doing the same but on elasticsearch helm chart. Please find my example
values.yml:
...
replicaCount: 3
...
StatefulSets.yaml:
...
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.registry_address }}/{{ .Values.image.name }}:{{ .Chart.AppVersion }}"
env:
- name: ELASTICSEARCH_CLUSTER_NAME
value: "{{ .Values.env.ELASTICSEARCH_CLUSTER_NAME }}"
- name: ELASTICSEARCH_DISCOVERY_ZEN_PING_UNICAST_HOSTS
value: "{{ template "nodes" .Values }}"
...
_helpers.tpl:
{{/* vim: set filetype=mustache: */}}
{{- define "nodes" -}}
{{- $nodeCount := .replicaCount | int }}
{{- range $index0 := until $nodeCount -}}
{{- $index1 := $index0 | add1 -}}
elasticsearch-{{ $index0 }}.elasticsearch{{ if ne $index1 $nodeCount }},{{ end }}
{{- end -}}
{{- end -}}
This produces a comma separated list:
...
- name: ELASTICSEARCH_DISCOVERY_ZEN_PING_UNICAST_HOSTS
value: "elasticsearch-0.elasticsearch,elasticsearch-1.elasticsearch,elasticsearch-2.elasticsearch"
...
I got it working using:
{{- define "zkservers" -}}
{{- $dot := dict "nodes" (int .) "servers" (list) -}}
{{- template "genservers" $dot -}}
{{- join "," $dot.servers -}}
{{- end -}}
{{- define "genservers" -}}
{{- range until .nodes -}}
{{- $noop := print "zk-" . ".zookeeper" | append $.servers | set $ "servers" -}}
{{- end -}}
{{- end -}}
Seems a little bit verbose for what should normally be a simple one/two liner :)
How about just rendering and then trimming?
{{- include "template" . | trimSuffix "," -}}