Exercises

Exercise 1: Creating and Using Helper Functions

In this exercise, you will create a helper function (named template) and use it to generate a Kubernetes resource.
Helper functions allow you to encapsulate common patterns and reuse them throughout your chart.

Step 1: Create a New Chart

Create a new Helm chart for this exercise:

helm create helper-chart

Step 2: Create a Helper Function

Navigate to the templates directory and create a new file for your helper function:

cd helper-chart\templates
New-Item -Path "_helpers.tpl" -ItemType File

Open _helpers.tpl and add a helper function that generates standard labels.
Add the following content:

{{- define "helper-chart.labels" -}}
helm.sh/chart: {{ include "helper-chart.chart" . }}
{{ include "helper-chart.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

This helper function creates a set of standard labels that can be reused across multiple resources.

Step 3: Create a Resource Using the Helper

Create a new template file that uses your helper function to create a ConfigMap:

New-Item -Path "configmap.yaml" -ItemType File

Open configmap.yaml and add the following content:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "helper-chart.fullname" . }}-config
  labels:
{{ include "helper-chart.labels" . | indent 4 }}
data:
  app.properties: |
    environment={{ .Values.environment | default "development" }}
    logLevel={{ .Values.logLevel | default "info" }}

This ConfigMap uses the helper-chart.labels helper function to apply consistent labels.

Step 4: Test Your Template

Use helm template to render your chart and verify that the helper function works correctly:

cd ..\..
helm template test-release ./helper-chart

Review the output to confirm that:
* The ConfigMap is created with the correct name
* The labels from your helper function are properly applied
* The labels are correctly indented in the YAML output

Step 5: Verify the Helper Function Reusability

Create another resource (for example, a Secret) that also uses the same helper function:

cd helper-chart\templates
New-Item -Path "secret.yaml" -ItemType File

Add content to secret.yaml:

apiVersion: v1
kind: Secret
metadata:
  name: {{ include "helper-chart.fullname" . }}-secret
  labels:
{{ include "helper-chart.labels" . | indent 4 }}
type: Opaque
data:
  password: {{ .Values.password | b64enc }}

Render the chart again to see both resources using the same helper function:

cd ..\..
helm template test-release ./helper-chart

Notice how both the ConfigMap and Secret now have identical labels, demonstrating how helper functions enable consistency across resources while eliminating duplication.

Exercise 2: Using Helper Functions from Dependencies

In this exercise, you will create a new chart and add the helper chart from Exercise 1 as a dependency.
You will then use the helper function from the dependency in your new chart, demonstrating how library charts enable code reuse across multiple charts.

Step 1: Create a New Chart

Create a new Helm chart that will use the helper chart as a dependency:

cd ..\..
helm create app-chart

Step 2: Package the Helper Chart

First, package the helper chart so it can be used as a dependency.
If you are using a local file path, you can reference it directly, or package it for distribution:

helm package ./helper-chart

This creates a helper-chart-0.1.0.tgz file (assuming version 0.1.0 in Chart.yaml).

Step 3: Add the Helper Chart as a Dependency

Edit the Chart.yaml file in your app-chart directory to add the helper chart as a dependency:

cd app-chart
notepad Chart.yaml

Add a dependencies section to your Chart.yaml:

dependencies:
  - name: helper-chart
    version: "0.1.0"
    repository: "file://../helper-chart"

If you packaged the chart, you can also reference it from a local directory or OCI registry.

Step 4: Update Dependencies

Download the dependency into your chart:

helm dependency update

This downloads the helper chart into the charts/ directory and makes its template helpers available to your chart.

Step 5: Use the Helper Function from the Dependency

Create a new resource in your app-chart that uses the helper function from the dependency:

cd templates
New-Item -Path "deployment.yaml" -ItemType File

Add content to deployment.yaml that uses the helper function from the dependency:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "app-chart.fullname" . }}
  labels:
{{ include "helper-chart.labels" . | indent 4 }}
spec:
  replicas: {{ .Values.replicaCount | default 1 }}
  selector:
    matchLabels:
      app.kubernetes.io/name: {{ include "app-chart.name" . }}
      app.kubernetes.io/instance: {{ .Release.Name }}
  template:
    metadata:
      labels:
{{ include "helper-chart.labels" . | indent 8 }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}

Notice that this Deployment uses helper-chart.labels from the dependency, not from the current chart.
The helper function is accessible because it is defined in a dependency.

Step 6: Test the Chart with Dependency

Render your chart to verify that the helper function from the dependency works correctly:

cd ..\..
helm template test-app ./app-chart

Review the output to confirm that:
* The Deployment is created with the correct name
* The labels from the helper-chart dependency are properly applied
* The helper function from the dependency is accessible and working

This demonstrates how library charts enable you to share helper functions across multiple charts, creating consistency and eliminating duplication at an organizational level.

Exercise 3: Type Checking and Validation

In this exercise, you will implement type checking to ensure that required values are defined and are of the correct type.
You will use Helm template functions to validate that a value is defined as a string before using it in your templates.

Step 1: Create a New Chart

Create a new Helm chart for this exercise:

cd ..\..
helm create type-check-chart

Step 2: Add Type Checking to a Template

Navigate to the templates directory and edit the deployment.yaml file:

cd type-check-chart\templates
notepad deployment.yaml

Modify the deployment to include type checking for a required string value.
Add a section that requires an environment value to be defined as a string:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "type-check-chart.fullname" . }}
  labels:
    {{- include "type-check-chart.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount | default 1 }}
  selector:
    matchLabels:
      app.kubernetes.io/name: {{ include "type-check-chart.name" . }}
      app.kubernetes.io/instance: {{ .Release.Name }}
  template:
    metadata:
      labels:
        {{- include "type-check-chart.labels" . | nindent 8 }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        env:
        - name: ENVIRONMENT
          value: {{ required "environment must be defined as a string" .Values.environment | typeOf | eq "string" | ternary .Values.environment (fail "environment must be a string, not " (typeOf .Values.environment)) }}

This example uses required to ensure the value exists, but we need a better approach for type checking.

Step 3: Create a Helper Function for String Type Checking

Create a helper function that validates a value is a string.
Edit or create _helpers.tpl:

notepad _helpers.tpl

Add a helper function that checks if a value is a string:

{{- define "type-check-chart.requireString" -}}
{{- if . -}}
  {{- if eq (typeOf .) "string" -}}
    {{- . -}}
  {{- else -}}
    {{- fail (printf "Value must be a string, got %s" (typeOf .)) -}}
  {{- end -}}
{{- else -}}
  {{- fail "Value is required and must be a string" -}}
{{- end -}}
{{- end -}}

Step 4: Use the Type Checking Helper

Update your deployment.yaml to use the helper function:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "type-check-chart.fullname" . }}
  labels:
    {{- include "type-check-chart.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount | default 1 }}
  selector:
    matchLabels:
      app.kubernetes.io/name: {{ include "type-check-chart.name" . }}
      app.kubernetes.io/instance: {{ .Release.Name }}
  template:
    metadata:
      labels:
        {{- include "type-check-chart.labels" . | nindent 8 }}
    spec:
      containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        env:
        - name: ENVIRONMENT
          value: {{ include "type-check-chart.requireString" .Values.environment | quote }}

Step 5: Test with Valid String Value

First, test the chart with a valid string value.
Edit values.yaml:

cd ..
notepad values.yaml

Add an environment value:

environment: "production"

Render the chart to verify it works:

helm template test-release ./type-check-chart

The chart should render successfully with the environment value set to "production".

Step 6: Test Type Validation

Now test that the type checking works by providing an invalid type.
Modify values.yaml to use a non-string value:

environment: 123

Attempt to render the chart:

helm template test-release ./type-check-chart

The template should fail with an error message indicating that the value must be a string, demonstrating that your type checking is working correctly.

This exercise demonstrates how to implement type checking in Helm templates, ensuring that values meet your requirements before they are used in your Kubernetes resources.

Exercise 4: Creating Resources from Array Combinations

In this exercise, you will use nested loops to create a ConfigMap for each possible combination of two arrays.
This demonstrates how to use range to iterate over multiple arrays and generate resources dynamically.

Step 1: Create a New Chart

Create a new Helm chart for this exercise:

cd ..\..
helm create combination-chart

Step 2: Define Arrays in Values

Edit the values.yaml file to define two arrays that will be combined:

cd combination-chart
notepad values.yaml

Add two arrays to your values file:

environments:
  - name: development
    region: us-east-1
  - name: staging
    region: us-west-2
  - name: production
    region: eu-central-1

configurations:
  - type: standard
    replicas: 2
  - type: high-availability
    replicas: 5

This defines two arrays: environments and configurations.
We will create a ConfigMap for each combination of environment and configuration.

Step 3: Create a Template with Nested Loops

Navigate to the templates directory and create a template that generates ConfigMaps for each combination:

cd templates
New-Item -Path "configmaps.yaml" -ItemType File

Add content that uses nested range loops to create a ConfigMap for each combination:

{{- range $env := .Values.environments }}
{{- range $config := .Values.configurations }}
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ $.Release.Name }}-{{ $env.name }}-{{ $config.type }}-config
  labels:
    environment: {{ $env.name }}
    region: {{ $env.region }}
    config-type: {{ $config.type }}
    app.kubernetes.io/name: {{ include "combination-chart.name" $ }}
    app.kubernetes.io/instance: {{ $.Release.Name }}
data:
  environment: {{ $env.name | quote }}
  region: {{ $env.region | quote }}
  config-type: {{ $config.type | quote }}
  replicas: {{ $config.replicas | quote }}
{{- end }}
{{- end }}

This template uses nested range loops:
* The outer loop iterates over environments
* The inner loop iterates over configurations
* For each combination, it creates a ConfigMap with a unique name and data

Note the use of $ to access the root context when inside nested loops.

Step 4: Test the Template

Render the chart to see all the ConfigMaps that are created:

cd ..\..
helm template test-release ./combination-chart

Review the output to verify that:
* A ConfigMap is created for each combination of environment and configuration
* Each ConfigMap has a unique name combining the environment name and configuration type
* The data in each ConfigMap reflects the values from both arrays
* You should see 6 ConfigMaps total (3 environments × 2 configurations)

Step 5: Verify the Combinations

Count the ConfigMaps in the output to confirm all combinations are created:

helm template test-release ./combination-chart | Select-String -Pattern "kind: ConfigMap" | Measure-Object

You should see 6 ConfigMaps, one for each combination:
* development-standard
* development-high-availability
* staging-standard
* staging-high-availability
* production-standard
* production-high-availability

This exercise demonstrates how to use nested loops in Helm templates to generate multiple resources from array combinations, enabling you to create complex deployment scenarios efficiently.