Validating Helm Chart Content

I am developing a chart and I had an error in it—incorrectly placed imagePullSecrets. When I tried to install it via

helm install ./mychart

the misplaced element was simply ignored and I wondered what is wrong.

When I did

helm template ./mychart | kubectl apply --dry-run -f -

it instead printed:

error: error validating "STDIN": error validating data: ValidationError(Deployment.spec.template.spec.containers[0]): unknown field "imagePullSecrets" in io.k8s.api.core.v1.Container

which clearly shows what is wrong. I am not sure whether it matches what the tiller actually does with the expanded templates.

But if I just do a

helm install --dry-run --debug ./mychart

it just shows the expanded template and looks OK.

So how do I correctly verify all my templates match corresponding schemata with helm?

1

4 Answers

You can lint the chart by going helm lint ./mychart which should print the following if an issue is found:

$ helm lint ./mychart
==> Linting ./mychart
[ERROR] Chart.yaml: version is required
[INFO] Chart.yaml: icon is recommended

Error: 1 chart(s) linted, 1 chart(s) failed

See helm lint.

2

Use kubeconform.

helm template ./mychart | kubeconform -strict

If you have CRDs you may need to use kubeconform -ignore-missing-schemas. I would recommend supplying the schema version: kubeconform -kubernetes-version 1.18.

A recommendation: specialise your charts and validate them. Examples follow.

Simple:

helm template --set some.key="val" | kubeconform -strict

Complex:

VALUES_FILE=$(cat << EOF
some:
  key: "val"

another:
  key:
    another: "val"
EOF
)
# It is important to quote "$VALUES_FILE" to ensure line breaks and indentation are preserved
echo "$VALUES_FILE" | helm template -f - | kubeconform -strict

To avoid helm chart installation failure I suggest to follow the following sequence locally or/and in CI pipelines:

If the linter encounters things that will cause the chart to fail installation, it will emit [ERROR] messages.

  • run helm template for testing whether the chart can be rendered locally successfully. While the installation can still fail once helm->kubernetes api server interaction happens, it still gives good quality check.

  • validate that chart's Kubernetes manifests conform to Kubernetes schema using kubeval (doesn't support CRDs) or kubeconform (supports CRDs) after rendering the chart:

helm template [chart_name] . | kubeval

  • run helm unit tests for validating that the chart values are as expected (e.g. num of replicas is 1, volume is of type hostPath, etc..):

helm unittest [chart_name] [chart_name]

I summarized how to test helm charts in my blog.

0

I would high recommend using a combination of the 2 solutions below.

Solution 1

Using a values.schema.json for imposing a structure on the values.yaml file.

Example:

{
  "$schema": "",
  "properties": {
    "image": {
      "description": "Container Image",
      "properties": {
        "repo": {
          "type": "string"
        },
        "tag": {
          "type": "string"
        }
      },
      "type": "object"
    },
    "name": {
      "description": "Service name",
      "type": "string"
    },
    "port": {
      "description": "Port",
      "minimum": 0,
      "type": "integer"
    },
    "protocol": {
      "type": "string"
    }
  },
  "required": [
    "protocol",
    "port"
  ],
  "title": "Values",
  "type": "object"
}

(!) This schema will be applied to the values to validate it. Validation occurs when any of the following commands are invoked:

helm install
helm upgrade
helm lint
helm template

In order to add conditions to the validation - read in here.

(*) Further reading: Nice article.

Solution 2

There are cases when the code in values.schema.json might be less readable when using conditions or we want to use a more dynamic logic into our validation.

In this case, we can create an validations.yaml file (some vendors prefer using a .tpl file) and add the validation logic using go templates.

Example:

If a specific feature (when enabled) needs an ip or a dns:

  some_feature:
    enabled: true
    ip:
    dns:

The validation logic can be written explicitly:

{{- if .Values.some_feature.enabled -}}
    {{- if and (not .Values.some_feature.ip ) (not .Values.some_feature.dns  ) -}}
        When some_feature is enabled, ip or dns must be given.
    {{- end -}}
{{- end -}}

(*) This logic could also be writen using json-schema if-else statement, but it might be less readable.

(**) Consider placing all validations under a /tests or /validations folder where all tests are devided into seperated files (like unit tests).

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Maya Lin-Takahashi

Maya Lin-Takahashi

Consumer Tech & Gadget Reviewer

Maya is a hardware enthusiast who tests and reviews smart home devices, smartphones, wearables, and audio gear. She focuses on practical consumer value and build quality.

Share this article
Twitter Facebook Pinterest