How to conditionally render helm templates based on existence of nested values - kubernetes

I have a template that renders a secret containing the credentials to log in to my server. There is a deployment configuration in which this is done insecurely, in which case I don't want to render the secret. Normally my values.yaml contains auth.myapp.username, and auth.myapp.password, however when the deployment lacks credentials, the whole auth section no longer exists.
My secret template looks like this:
{{ if .Values.auth.myapp.username }}
apiVersion: v1
kind: Secret
metadata:
name: myapp-credentials-secret
type: Opaque
data:
USERNAME: {{ .Values.auth.myapp.username | b64enc }}
PASSWORD: {{ .Values.auth.myapp.password | b64enc }}
{{ end }}
However I get an error when running helm install on this because the 'auth' value doesn't exist, it fails to lookup myapp from it.
I know I can get around this by not using the --strict flag, but I don't have control over that, is there a proper way of predicating on nested values?

Use haskey function:
{{ if haskey .Values "auth" }}
apiVersion: v1
kind: Secret
metadata:
name: myapp-credentials-secret
type: Opaque
data:
USERNAME: {{ .Values.auth.myapp.username | b64enc }}
PASSWORD: {{ .Values.auth.myapp.password | b64enc }}
{{ end }}
The hasKey function returns true if the given dict contains the given key.
hasKey $myDict "name1"
If the key is not found, this returns false.

You can use the standard default function to fill in layers of the hierarchy that don't exist, combined with the Sprig dict function to create an empty dictionary:
{{- $auth := .Values.auth | default dict -}}
{{/* $auth is the "auth" object from the top-level values, or an empty
dictionary if there was no such value */}}
That will let you step through this structure one level at a time:
{{- $auth := .Values.auth | default dict -}}
{{- $myapp := $auth.myapp | default dict -}}
{{- if and $myapp.username $myapp.password -}}
apiVersion: v1
kind: Secret
...
data:
USERNAME: {{ $myapp.username | b64enc }}
PASSWORD: {{ $myapp.password | b64enc }}
{{ end }}

Related

Create kubernetes docker-registry secret from yaml file for each lookup namespaces?

I am trying to dynamic lookup available namespaces and able to create secrets in the namespaces using below helm chart.
templates/secrets.yaml
{{ range $index, $namespace := (lookup "v1" "Namespace" "" "").items }}
apiVersion: v1
kind: Secret
metadata:
name: myregcred
namespace: {{ $namespace.metadata.name }}
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: {{ template "imagePullSecret" . }}
{{- end}}
values.yaml
imageCredentials:
registry: quay.io
username: someone
password: sillyness
email: someone#host.com
_helpers.tpl
{{- define "imagePullSecret" }}
{{- with .Values.imageCredentials }}
{{- printf "{\"auths\":{\"%s\":{\"username\":\"%s\",\"password\":\"%s\",\"email\":\"%s\",\"auth\":\"%s\"}}}" .registry .username .password .email (printf "%s:%s" .username .password | b64enc) | b64enc }}
{{- end }}
{{- end }}
When i run this helm chart, i get below error
Error: INSTALLATION FAILED: template: secrets/templates/_helpers.tpl:2:16: executing "imagePullSecret" at <.Values.imageCredentials>: nil pointer evaluating interface {}.imageCredentials
I dont know what I am doing wrong here.
When you reference the named template "imagePullSecret" inside the range, the context "." you are providing refers to the body of the loop, which does not have the "Values" attribute.
Try providing the root context instead:
{{ range $index, $namespace := (lookup "v1" "Namespace" "" "").items }}
apiVersion: v1
kind: Secret
metadata:
name: myregcred
namespace: {{ $namespace.metadata.name }}
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: {{ template "imagePullSecret" $ }}
---
{{- end}}

Helm template converting to json

I would like to be able from Helm template file as below to insert template with toJson helm function as explained in the documentation :
value: {{ include "mytpl" . | lower | quote }}
https://helm.sh/docs/howto/charts_tips_and_tricks/#know-your-template-functions
My configuration :
_helper.tpl
{{- define "my_tpl" -}}
key1: value1
key2: value2
{{- end -}}
dep.yaml
template:
metadata:
annotations:
test: >-
{{ include "my_tpl" . | toJson }}
This should return
template:
metadata:
annotations:
test: >-
{"key1":"value1","key2":"value2"}
but it return
template:
metadata:
annotations:
test: >-
"key1:value1\nkey2:value2"
I'm using Helm v3.
Anyone have an idea please ?
A defined template always produces a string; the Helm-specific include function always returns a string.
In your example, you have a string that happens to be valid YAML. Helm has an undocumented fromYaml function that converts the string to object form, and then you can serialize that again with toJson.
{{ include "my_tpl" . | fromYaml | toJson }}
You may find it easier to have the template itself produce the correct JSON serialization. That could look something like
{{- define "my_tpl" -}}
{{- $dict := dict "key1" "value1" "key2" "value2" -}}
{{- toJson $dict -}}
{{- end -}}
{{ include "my_tpl" . }}
where the "key1", "value1", etc. can be any valid template expression (you do not need nested {{ ... }}).

How to create a k8s configmap by looping over nested helm values

The helm values look like in the following example. Here appdata can scale to any number but will contain the same set of keys.
data:
appdata:
config0:
url: 'https://example1.com'
port: '80'
config1:
url: 'https://example2.com'
port: '8673'
someotherconfig:
url: 'https://example3.com'
port: '9887'
...
...
This is what I have so far. This keeps updating the last config's data from someotherconfig key and also I want to have the config map name to contain the config name for each iteration, like {{ template "app" $ }}-config0, {{ template "app" $ }}-config1 and so on based on iteration.
{{- range $Mydata := $.Values.data.appdata }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "app" $ }}
labels:
data:
url: {{ $Mydata.url }}
port: {{ $Mydata.port }}
{{- end }}
You're almost there. You need to use the key/value notation to get the key name. Try the following.
{{- range $configuration, $Mydata := $.Values.data.appdata }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "app" $ }}-{{ $configuration }}
data:
url: {{ $Mydata.url }}
port: {{ $Mydata.port }}
{{- end }}

Helm: overloading global variables on {{ include }} call?

I have a shared metadata block, based on the commons library that I would like to override a name for one specific instance. Is it possible?
metadata:
{{ include "common.metadata" (merge (dict ".Values.fullnameSuffix" "-redirect") .) }} # Doesn't work - How do I add a `-redirect` suffix?
name: {{ include "common.fullname" . }}-redirect # Causes two `name:` attributes
Within common.metadata there is a call to "fullname" as well:
{{ define "common.metadata" -}}
name: {{ template "common.fullname" . }}
namespace: {{ .Release.Namespace }}
{{- end -}}
Is there a way to pass-down a variable override from the first include so that I can override the name:? It's specific only to this chart.
Use set to add a new key/value pair to a dictionary and pass it to slightly modified common.metadata helper function.
values.yaml
fullnameSuffix: redirect
_helpers.tpl
{{- define "common.metadata" -}}
{{- if .suffix }}
name: {{ template "common.fullname" . }}-{{ .suffix }}
{{- else }}
name: {{ template "common.fullname" . }}
{{- end }}
namespace: {{ .Release.Namespace }}
{{- end -}}
manifest.yaml
metadata:
{{- include "common.metadata" (set . "suffix" .Values.fullnameSuffix ) }}
If your fullnameSuffix is empty, name without suffix will be used.

Helm - Templating variables in values.yaml

I'm trying to template variables from a map inside the values.yaml into my final Kubernetes ConfigMap YAML.
I've read through https://github.com/helm/helm/issues/2492 and https://helm.sh/docs/chart_template_guide/ but can't seem to find an answer.
For some context, this is roughly what I'm trying to do:
values.yaml
config:
key1: value
key2: value-{{ .Release.Name }}
configmap.yaml
kind: ConfigMap
data:
config-file: |
{{- range $key, $value := .Values.config }}
{{ $key }} = {{ $value }}
{{- end }}
Where the desired output with would be:
helm template --name v1 mychart/
kind: ConfigMap
data:
config-file: |
key1 = value
key2 = value-v1
I've tried a few variations using template functions and pipelining, but to no avail:
{{ $key }} = {{ tpl $value . }}
{{ $key }} = {{ $value | tpl . }}
{{ $key }} = {{ tpl $value $ }}
The above would also have worked in this way
values.yaml
config:
key1: "value"
key2: "value-{{ .Release.Name }}"
configmap.yaml
kind: ConfigMap
data:
config-file: |
{{- range $key, $value := .Values.config }}
{{ $key }} = {{ tpl $value $ }}
{{- end }}
What I changed was : I put value in quotes in value.yaml and used template tpl in the config map.
I'll refer to the question's title regarding templating variables in helm and suggest another option to use on values.yaml which is YAML Anchors.
Docs reference
As written in here:
The YAML spec provides a way to store a reference to a value, and
later refer to that value by reference. YAML refers to this as
"anchoring":
coffee: "yes, please"
favorite: &favoriteCoffee "Cappucino"
coffees:
- Latte
- *favoriteCoffee
- Espresso
In the above, &favoriteCoffee sets a reference to Cappuccino.
Later, that reference is used as *favoriteCoffee.
So coffees becomes Latte, Cappuccino, Espresso.
A more practical example
Referring to a common image setup (Registry and PullPolicy) in all values.yaml.
Notice how the default values are being set at Global.Image next to the reference definition which starts with &:
Global:
Image:
Registry: &global-docker-registry "12345678910.dkr.ecr.us-west-2.amazonaws.com" # <--- Default value
PullPolicy: &global-pull-policy "IfNotPresent" # <--- Default value
Nginx:
Image:
Registry: *global-docker-registry
PullPolicy: *global-pull-policy
Version: 1.21.4
Port: 80
MySql:
Image:
Registry: *global-docker-registry
PullPolicy: *global-pull-policy
Name: mysql
Version: 8.0.27
Port: 3306
Managed to solve this using the following syntax:
configmap.yaml
kind: ConfigMap
data:
config-file: |
{{- range $key, $value := .Values.config }}
{{ $key }} = {{ tpl ($value | toString) $ }}
{{- end }}
there is fight in this PR here about this topic.
I know that it's possible now, but this require maintenance of the chart to be in-house (e.g. answer of Amrut ).
Let's summarize :
To have templating in values.yaml , these are the available options:
helm may support that in future ( watch this thread about this topic.)
use tpl function inside the chart
use another tool on top of helm : terraform or helmfile.