Helm Flow Control with Conditional Control Structures
Helm uses templates to generate Kubernetes manifests from chart content. To customize the templates, you can use Helm's built-in functions and operators or create your own. One of Helm’s most powerful features is flow control during a template's generation using control structures.
In this blog, you’ll see how to use conditional blocks (if/else) for flow control in Helm.
To learn more about template customization, read this blog: What are Template Function and Pipeline in Helm?
What are Conditional Control Structures?
Helm's conditional flow control is a feature that allows for the execution of certain code blocks only if certain conditions are met. This is an important concept, as it allows for the creation of more complex and flexible templates. By using conditional flow control, developers can create code that is capable of responding to changing conditions and adapting to new situations.
It makes use of the if/else condition blocks to determine the text that should be generated. Below is an example that uses this control structure.
{{ 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 }}
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 printing the replicas:
.
In an if
statement, we can use either a simple value or an entire pipeline. For instance, we can extract a value from somewhere, pass it through a pipeline of functions, transform the data in some ways, and then evaluate if the end result is true or false. But how does it decide if it is true or false?
If the evaluated pipeline 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. If it's a non-zero number, it is interpreted as true. If it’s a string, an empty one is false, while if it contains even a single letter, it is true. 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.
Conditional Flow Control Example
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 was 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
We can see no more replicas line.
Controlling Whitespace/Newlines Output
We noticed how our conditional block generates some rather weird output (empty lines) around the replicas
line. Remember that 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 need to add a -
sign next to the curly brackets.
This will remove whitespace/newlines to the left:
{{-
This will 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
To learn more Helm concepts, check out KodeKloud's Helm for Beginners course:
Conclusion
Conditional flow control is particularly useful in applications that need to handle a wide range of user inputs or environmental factors. Overall, it is an essential tool for any Helm user looking to create powerful, dynamic templates.
Empower Yourself with KodeKloud!
Over One Million students have taken KodeKloud DevOps Courses!
Supercharge your DevOps Journey with us. Our 65+ expertly crafted courses cover all aspects of DevOps, ensuring you gain a solid understanding of the most sought-after skills in the industry. Our highly experienced, dedicated, and hard-working trainers go to great lengths to ensure that DevOps is made simple to understand.
More on Helm: