Kubernetes Helm, combine two variables with a string in the middle - kubernetes

I’m trying to change the value of a variable if another variable it set by combining the two with a dash in the middle, I’m not sure of the syntax to do this, I’m thinking of somethings like:
{{- $serviceNamespace := .Values.serviceNamespace -}}
{{- $serviceTag := .Values.serviceTag -}}
{{- if $serviceTag}}
{{- $serviceNamespace := .Values.serviceNamespace "-" .Values.serviceTag -}}
{{- end}}
Is this correct? if serviceNamespace was hello and serviceTag was 1.0.0 would I end up with serviceNamespace being hello-1.0.0?

For concatenation just use printf:
{{- $serviceNamespace := printf "%s-%s" .Values.serviceNamespace .Values.serviceTag -}}

You can simply do it like this , with string ":" in middle
"{{ $values.image.repository }}:{{ $values.image.tag }}"

Update
It is now possible in the 1.11 version of golang, see commit:
{{- $serviceNamespace := .Values.serviceNamespace -}}
{{- $serviceTag := .Values.serviceTag -}}
{{- if $serviceTag}}
{{- $serviceNamespace = print .Values.serviceNamespace "-" .Values.serviceTag -}}
{{- end}}
Notice the new = operator in $serviceNamespace = print .Values.serviceNamespace "-" .Values.serviceTag
Older golang versions
You cannot currently (in golang 1.9, but available in 1.11, see update above) reassign template variables because if introduces a new scope. Until this is fixed (see issue and proposed fix), you can work around this by writing a function:
{{ define "makeServiceNamespace" }}
{{- if .Values.serviceTag }}
{{- printf "%s-%s" .Values.serviceNamespace .Values.serviceTag -}}
{{- else }}
{{- print .Values.serviceNamespace }}
{{- end }}
{{- end }}
Then use it like so:
serviceNamespace: {{ template makeServiceNamespace . }}

Related

helm function to transform yaml to property-like format

new to k8s, trying to remove stupid boilerplate and write better config-map.yaml generation. Expected format is:
...
data:
first.property: 1
second.property: 2
...
I don't want to refer there key by key to values.yaml, like:
...
data:
first.property: {{.Values.configuration.first.property}}
second.property: {{.Values.configuration.second.property}}
...
I want to include whole subtree into here, like:
{{ (toYaml .Values.configuration | indent 2) }}
That works, but (as expected) the yaml is inserted as is. I need to adapt it to property-like format. So the question is: is there a function/way in helm/go templates how to transform this yaml:
a:
b:
c: 1
d: 2
into following representation?
a.b.c: 1
a.b.d: 2
this based on answer How do you apply a recursive formatting with Go Templates (Helm)? in which I fixed some problems and the final solution could be:
{{- define "flattenYaml" -}}
{{- $dict := . -}}
{{- $prefix := $dict.prefix -}}
{{- $data := $dict.data -}}
{{- $knd := kindOf $data -}}
{{- if eq $knd "map" }}
{{- range (keys $data) }}
{{- $key := . }}
{{- $prefixedKey := (printf "%s.%s" $prefix $key) }}
{{- $value := get $data $key }}
{{- $valueKind := kindOf $value }}
{{- if eq $valueKind "map" }}
{{- include "flattenYaml" (dict "prefix" ($prefixedKey) "data" $value) }}
{{- else }}
{{- printf "%s=%s\n" $prefixedKey (toJson $value) }}
{{- end }}
{{- end }}
{{- else }}
{{ toJson . }}#k({{ $knd }})
{{- end }}
{{- end -}}
for more details see my answer there.

helm: how to check if a service with prefix already exists in namespace

I want to implement some k8s services only if there is a service with suffix "mysuffix" in the namespace. I tried the following but doesn't work
{{- $previousReleaseInstalled := false -}}
{{- range $index, $service := (lookup "v1" "Service" .Release.Namespace "").items }}
{{- if hasSuffix "mysuffix" "$service.name" }}
{{- $previousReleaseInstalled := true -}}
{{- end }}
{{- end }}
{{- if $previousReleaseInstalled -}}
implement some services
{{- end }}
Your lookup function is almost correct. The issue with the assignment of the $previousReleaseInstalled variable. You need to use = instead of := inside the range. Check this answer for the details Why doesn't this change the value of the variable in the range loop in Helm?
Another issue is the wrong key name. You should use $service.metadata.name instead of $service.name, and remove the quotes around the $service.metadata.name
{{- $previousReleaseInstalled := false -}}
{{- range $index, $service := (lookup "v1" "Service" .Release.Namespace
"").items }}
{{- if hasSuffix "mysuffix" $service.metadata.name }}
{{- $previousReleaseInstalled = true -}}
{{- end }}
{{- end }}
{{- if $previousReleaseInstalled -}}
implement some services
{{- end }}
Another important thing to note, the lookup function always returns empty response when used with the helm template command. https://helm.sh/docs/chart_template_guide/function_list/#lookup

How can I apply template function to a range result in Helm?

My goal is to convert values in Values.yaml into the following:
CUSTOM_VARIABLE: "TEST_ENV_1=devil,TEST_ENV_2=god,TEST_ENV_3=angel"
### Values.yaml
env:
TEST_ENV_1: devil
TEST_ENV_2: god
TEST_ENV_3: angel
The below template almost does this but I'm getting comma at the end: TEST_ENV_1=devil,TEST_ENV_2=god,TEST_ENV_3=angel,.
### _envVars.tpl
{{ define "envVars" }}
...
- name: CUSTOM_VARIABLE
value: "
{{- range $key, $value := .Values.env -}}
{{- printf "%s=%s," $key $value -}}
{{- end -}}
"
...
{{- end }}
Is there a way to apply template function (e.g. trunc to remove last symbol) to a range result in my case?
try something like
{{range $i, $e := $}}
{{if $i}},{{end}}
{{$e}}{{end}}
If actually look for the index and if it's zero it's wont to add the , at last. here is if is not behave like normal it checks the index also.
{{- range $i, $e := . -}}
{{if $i}}, {{end}}prefix_{{$e}}
{{- end}}
above loop will give output like : prefix_one, prefix_two, prefix_three
https://play.golang.org/p/KuRh55BHna8
Read more at : https://groups.google.com/g/golang-nuts/c/XBScetK-guk/m/Bh7ZFz6R3wQJ
If you write the range call into a helper template, Helm has an include extension function that calls a template and captures its output as a string.
{{/* Render the map-type template parameter to a key=value,key=value,
list, ending with a trailing comma. */}}
{{- define "custom.variable.format" -}}
{{- range $key, $value := . -}}
{{ $key }}={{ $value }},
{{- end -}}
{{- end -}}
- name: CUSTOM_VARIABLE
value: {{ include "custom.variable.format" .Values.env | trimSuffix "," | quote }}
(It is probably cleaner to not generate the comma at all, as #HarshManvar's answer proposes.)

How to check for a non-existent dictionary value within a helm template?

In my values config file, I have an array of dictionaries as follows:
connects_to
- name: myname
release: optional release
- name: another name
Note that name will always be provided but release may or may not be. In my template, I have:
{{- if .Values.connects_to }}
app.openshift.io/connects-to: '
{{- range .Values.connects_to -}}
{{- if .release -}}
{{- .release -}}-{{- .name -}},
{{- else -}}
{{- $.Release.Name -}}-{{- .name -}},
{{- end -}}
{{- end -}}
'
{{- end }}
which gives the error:
can't evaluate field release in type interface {}
I have also tried using "hasKey" as follows:
{{- if .Values.connects_to }}
app.openshift.io/connects-to: '
{{- range .Values.connects_to -}}
{{- if hasKey . "release" -}}
{{- .release -}}-{{- .name -}},
{{- else -}}
{{- $.Release.Name -}}-{{- .name -}},
{{- end -}}
{{- end -}}
'
{{- end }}
which gives the error:
wrong type for value; expected map[string]interface {}; got string
How would I accomplish this check without having to specify a value for "release" every time? My helm version:
version.BuildInfo{Version:"v3.2.3+4.el8", GitCommit:"2160a65177049990d1b76efc67cb1a9fd21909b1", GitTreeState:"clean", GoVersion:"go1.13.4"}
This actually works with the "hasKey" approach, there was an error in the values definition.
For people looking for a possible cause for the error (wrong type for value; expected map[string]interface {}; got string).
Given the template
{{- if hasKey .Values.somedict "valueX" -}}
{{- .Values.somedict.valueX -}}
{{- end -}}
And using 2 values.yaml (eg: helm template . --values Values1.yaml --values Values2.yaml).
Values1.yaml:
somedict:
valueA: 1
Values2.yaml:
# Causes error
somedict:
# Works
somedict: {}
# Leaving out "somedict" also work
Regarding #DieterDP 's response https://stackoverflow.com/a/72360797/17143221
I am getting the same issue when using this template.
This can be solved by using parentheses around the optional dict as a mean of null-safety.
template:
# Fails when somedict doesn't exist
{{- if hasKey .Values.somedict "valueX" -}}
{{- .Values.somedict.valueX -}}
{{- end -}}
# Works when somedict doesn't exist
{{- if hasKey (.Values.somedict) "valueX" -}}
{{- .Values.somedict.valueX -}}
{{- end -}}
values.yaml:
# without "somedict"

If condition checking value returned by helm template

I have a parent chart with 2 subcharts. The parent chart has global.myflag while the subcharts have myflag fields, in their respective values.yaml. I want the flexibility, where the sub-charts could be deployed independently. So, I have added a template function in the sub-chart _helper.tpl where I want to check
- if global.myflag exists, use that value
- else use value of myflag from the subchart
The template will return true/false. Something like this -
{{- define "isFlagEnabled" -}}
{{- $flag := false -}}
{{- if .Values.myflag -}}
{{- $flag := .Values.myflag -}}
{{- end -}}
{{- if .Values.global.myflag -}}
{{- $flag := .Values.global.myflag -}}
{{- end -}}
{{- printf "%s" $flag -}}
{{- end -}}
And using this value (true/false), I want to set some values in my config.yaml.
{{- if eq (value from template) true -}}
I am having two questions here -
1. Can we do 'if' condition on the template values? How?
2. Is there a better way to do this?
Definition of isFlagEnabled template
Retouched and cleaned your function
{{- define "isFlagEnabled" -}}
{{- if .Values.global -}} {{/* <-- check parent exists to avoid nil pointer evaluating interface {}.myflag */}}
{{- if .Values.global.myflag -}}
{{- .Values.global.myflag -}}
{{- end -}}
{{- else if .Values.myflag -}} {{/* <-- make sure its else if so you wont override if both defined */}}
{{- .Values.myflag -}}
{{- else -}}
{{- printf "false" }}
{{- end -}}
{{- end -}}
Using the template
Inside another template
When using template inside golang template syntax, you will need to escape them with round brackets:
{{- define "flagUsage" -}}
{{- if eq (include "isFlagEnabled" .) "true" -}}
{{- printf "%s" (include "isFlagEnabled" .) -}}
{{- end -}}
{{- end -}}
another example used inside a resource
Pay attention the template is being used twice in the example, once as an operand for the if operator and one as text for the label
{{- if eq (include "isFlagEnabled" .) "true" -}} {{/* <--- operand used in spring function surrounded by `{{ }}` */}}
apiVersion: v1
kind: Service
metadata:
name: {{ include "my-chart.fullname" . }}
labels:
my-meta-label: {{ include "isFlagEnabled" . }} {{/* <---- plain text */}}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "my-chart.selectorLabels" . | nindent 4 }}
{{- end }}
To build on #Totem's excellent and helpful answer, you can make the "isFlagEnabled" template reusable across multiple flags.
{{- define "isFlagEnabled" -}}
{{- $root := index . "root" -}}
{{- $flag := index . "flag" -}}
{{- if (index $root.Values $root.Chart.Name) -}}
{{- if hasKey (index $root.Values $root.Chart.Name) $flag -}}
{{- (index (index $root.Values $root.Chart.Name) $flag).enabled -}}
{{- end -}}
{{- else if hasKey $root.Values $flag -}}
{{- (index $root.Values $flag).enabled -}}
{{- else -}}
{{- printf "false" }}
{{- end -}}
{{- end -}}
Use it in the yaml file like so:
{{- if eq (include "isFlagEnabled" (dict "root" . "flag" "myflag")) "true" }}
Local values file:
myflag:
enabled: false
Overriding values file:
mychart:
myflag:
enabled: true
This worked for me in the situation where I had a subchart that needed to be overridden by a global chart, and I wanted to reference the chart by name in the global chart.