testingkubernetesautomated-testskubernetes-helm

Testing helm chart templating


Is there a way to test that the templating works fine for all the possible values?
(note: this is different from helm test which is used for testing the deployed chart through arbitrary code ran in a job).

What I would like to achieve is iterating over a set of values and checking the generated K8s resources for each.

Lets say we want to test whether our chart is correctly written:

The chart:
Values.yaml

app:
  port: 8081

pod2:
   enabled: true

AppPod.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: AppPod
  labels:
    app: nginx
spec:
...
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: {{ $.Values.app.port| default 8080  }}

Pod2.yaml

{{- if $.Values.pod2.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: Pod2
  labels:
    app: nginx2
spec:
...
{{- end}}

We want to run the following tests:

So basically to test the templating logic.

What I am doing right now:
Whenever I modify something in the chart I just run helm template for different Values.yaml and check the results by hand. Doing this by hand is error prone and it becomes more time consuming the more template the chart contains.

Is there any builtin helm feature or a separate framework for this?


Solution

  • Yes, we do that with Rego policy rules. The set-up is not complicated, this is how it looks as part of one of our pipelines (this is a very simplified example to get you started):

    # install conftest to be able to run helm unit tests
    wget https://github.com/open-policy-agent/conftest/releases/download/v0.28.1/conftest_0.28.1_Linux_x86_64.tar.gz
    tar xzf conftest_0.28.1_Linux_x86_64.tar.gz
    chmod +x conftest
        
    # you can call "helm template" with other override values of course, too
    helm template src/main/helm/my-service/ > all.yaml
    
    echo "running opa policies tests"
    if ! ./conftest test -p src/main/helm/my-service/ all.yaml; then
      echo "failure"
      exit 1
    fi
    

    Inside the my-service directory there is a policy folder that holds the "rules" for testing (though this can be passed as an argument). Here is an example of two rules I recently wrote:

    package main
    
    deny_app_version_must_be_present[msg] {
        input.kind == "Deployment"
        env := input.spec.template.spec.containers[_].env[_]
        msg := sprintf("env property with name '%v' must not be empty", [env.name])
        "APP_VERSION" == env.name ; "" == env.value
    }
        
    deny_app_version_env_variable_must_be_present[msg] {
        input.kind == "Deployment"
        app_version_names := { envs | envs := input.spec.template.spec.containers[_].env[_]; envs.name == "APP_VERSION"}
        count(app_version_names) != 1
        msg := sprintf("'%v' env variable must be preset once", ["APP_VERSION"])
    }
    

    This validates that the container in the Deployment has an env variable called APP_VERSION that must be unique and non-empty.