Helm Flow Control and Conditionals

In this blog, we will see what is Flow control in Helm.
Flow Control controls the flow of text generated. The flow of text can go in one direction or another direction, instead of the same content being generated every time. In a way, it’s like we can almost program our charts to generate their contents based on dynamic conditions.
Conditional Flow Control
Let’s explore one kind of flow control type: if/else conditional blocks.
A complex if/else conditional block can look like this:
{{ if VALUE or PIPELINE passed here is TRUE }}
# Print out this text
{{ else if OTHER VALUE or OTHER PIPELINE passed here is TRUE}}
# Print out this other text
{{ else }}
# If none of the conditions above are met, then print out this default text
{{ end }}
Of course, an if/else conditional block does not have to be this complex (with so many else and else ifs). In practice, a simple conditional flow control block can look something like this:
{{ if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{ end }}
which is what we’ll actually use. This block can be interpreted like this: if there is no autoscaling.enabled
value that the user-defined in his values.yaml file (or it is defined as false), then print out a replicas: line in the manifest sent out to Kubernetes. If there is an autoscaling.enabled
value defined as true, then just skip this, the replicas: line will not be printed.
Notice how in an IF statement we can use either a simple value or an entire pipeline. So in an IF statement, we can do something like, extract a value from somewhere, pass it through a pipeline of functions, transform the data in some ways, then evaluate if the end result is true or false. But how does it decide if it is true or false?
If the pipeline being evaluated returns a boolean value like true or false, it’s easy to understand how that is interpreted. Otherwise, if it’s a number, 0 is interpreted as false, non-zero numbers as true. If it’s a string, an empty one is false, while if it contains even a single letter, it is true. And the same can be said for other kinds of objects, like arrays and so on: if they’re empty, this is equivalent to being false, if they’re non-empty, the condition is evaluated as true.
Let’s use this in our deployments.yaml file. Our final content should look like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-nginx
labels:
app: nginx
spec:
{{ if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{ end }}
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository | default "nginx" }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
So all we did is basically wrap around our pre-existing line
replicas: {{ .Values.replicaCount }}
between the newly added lines:
{{ if not .Values.autoscaling.enabled }}
and
{{ end }}
We’ll also need to edit our values.yaml file and define this new autoscaling value that our if statement will verify.
Scroll to the end and add these lines:
autoscaling:
enabled: false
Make sure the text is properly indented (there should be two empty spaces before the enabled: false
line). Also, change replicaCount to 3. The final content should look like this:
replicaCount: 3
image:
repository:
pullPolicy: IfNotPresent
tag: "1.16.0"
autoscaling:
enabled: false
So, with autoscaling disabled (set to false) we expect our if statement to print out a replicas line in the generated manifest.
helm template ./nginx
And we see that indeed it does (also notice the weird empty lines added before and after the replicas line, we’ll see how to fix that too):
---
# Source: nginx/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: RELEASE-NAME-nginx
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: "nginx:1.16.0"
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
protocol: TCP
But this is the same as what happened before. Let’s see if our if statement does its job when autoscaling is enabled.
Let’s see the generated manifest this time by running helm template ./nginx
---
# Source: nginx/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: RELEASE-NAME-nginx
labels:
app: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: "nginx:1.16.0"
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
protocol: TCP
We can see no more replicas line.
Controlling Whitespace/Newlines Output
We noticed how our if conditional block generates some rather weird output (empty lines) around the replicas
line. If we remember, our if
and end
statement is wrapped around that line.
{{ if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{ end }}
So when seeing the new empty lines added in the generated manifest, we can imagine that those have something to do with that if
and end
line above and below the replicas
line.
spec:
replicas: 3
selector:
matchLabels:
The templating language we use in our template files has a simple method to deal with this. We just add a -
sign next to the curly brackets.
This means remove whitespace/newlines to the left:
{{-
And this means remove whitespace/newlines to the right:
-}}
Let’s edit our deployment template file again and add the “-
” signs to their proper places, at the beginning of the if statement and the end statement. The final content will look like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-nginx
labels:
app: nginx
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository | default "nginx" }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
Let’s see how our generated manifest changes using helm template ./nginx
---
# Source: nginx/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: RELEASE-NAME-nginx
labels:
app: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: "nginx:1.16.0"
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
protocol: TCP
Read the official documentation here
Checkout the Helm for the Absolute Beginners course here
Checkout the Complete Kubernetes learning path here