31

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?

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
  • This is an error from Kubernetes, not from Helm. Maybe [this doc entry](https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod) could help you – Mauro Baraldi Jan 23 '19 at 16:43

4 Answers4

22

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.

GHETTO.CHiLD
  • 3,267
  • 1
  • 22
  • 34
  • 26
    helm lint doesn't seem to do enough. For example: `helm lint chart/myProject` gives `1 chart(s) linted, no failures`, but `helm template chart/myProject/ | kubectl apply --dry-run -f -` gives `error: error validating "STDIN": error validating data: ValidationError(Deployment.spec.template.spec): unknown field "env" in io.k8s.api.core.v1.PodSpec; if you choose to ignore these errors, turn validation off with --validate=false` – Steve Onorato Nov 03 '18 at 03:13
  • Kubeval understands k8s manifest specs, see my answer below: https://stackoverflow.com/a/64895342 – mkingston Mar 25 '21 at 12:03
18

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
Kalle Richter
  • 8,008
  • 26
  • 77
  • 177
mkingston
  • 2,678
  • 16
  • 26
5

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.

rok
  • 9,403
  • 17
  • 70
  • 126
4

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": "https://json-schema.org/draft-07/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).

Rot-man
  • 18,045
  • 12
  • 118
  • 124
  • The question was primarily aimed at validating that the chart itself is implemented correctly, which this won't help with, but it still sure is a useful addition. – Jan Hudec Nov 12 '21 at 08:04