Helm chart fails with "nil pointer evaluating interface {}" when trying to evaluate a missing nested key
Asked Answered
B

3

11

I am writing a Helm 3 library chart and would like to create a YAML with default values. However, when trying to set a default value for a nested key that does not exist, Helm fails with the following error message:

nil pointer evaluating interface {}

Say I have this snippet in my Kubernetes object:

{{- if eq (.Values.deployment.scale.type | default "static") "static" }}
  replicas: {{ default "3" .Values.deployment.scale.replicas }}
{{- end }}

If .Values.deployment.scale is defined, the template will render fine and the value of replicas will be 3 even if .Values.deployment.scale.replicas is not defined.

However, if one of the parent keys is not defined, Helm will fail with the error message above. For example if values.yaml is the following:

# values.yaml
deployment:
  not_scale: {}

The render will fail with: nil pointer evaluating interface {}.scale

How is it possible to set a default value for a nested key, even if its parent keys are undefined?

Bodyguard answered 11/4, 2020 at 9:40 Comment(0)
B
1

I've decided to solve this using step-by-step evaluation of the hierarchy and using default dict to assign an empty map if the key does not exist. It both works and looks better.

Example:

{{ $deployment := default dict .Values.deployment }}
{{ $scale      := default dict $deployment.scale }}
{{- if eq ($scale.type | default "static") "static" }}
  replicas: {{ default "3" $scale.replicas }}
{{- end }}
Bodyguard answered 13/4, 2020 at 10:40 Comment(1)
Use ((.Values.deployment).scale).type | default "static" for brevity.Dahlgren
C
6

I don't think, you can set default for that case. You need to pre-check if scale field exists. For that, you can use hasKey function from sprig:

{{- if hasKey .Values.deployment "scale" }}
{{- if eq (.Values.deployment.scale.type | default "static") "static" }}
replicas: {{ default "3" .Values.deployment.scale.replicas }}
{{- end }}
{{- else }}
replicas: 3
{{- end }}
Cabin answered 11/4, 2020 at 10:46 Comment(0)
D
4

In my case, the code accessing .Values was wrapped inside a with ... end block. In this case the current context no longer has a variable called .Values. The solution is to reference the global Values object with $.Values.

From https://helm.sh/docs/chart_template_guide/variables/ :

However, there is one variable that is always global - $ - this variable will always point to the root context. This can be very useful when you are looping in a range ...

Dahlgren answered 28/7, 2023 at 20:45 Comment(1)
Side note: $ is not always global. E.g. you won't have it in define blocks unless you explicitly pass it as an argument with the include.Dahlgren
B
1

I've decided to solve this using step-by-step evaluation of the hierarchy and using default dict to assign an empty map if the key does not exist. It both works and looks better.

Example:

{{ $deployment := default dict .Values.deployment }}
{{ $scale      := default dict $deployment.scale }}
{{- if eq ($scale.type | default "static") "static" }}
  replicas: {{ default "3" $scale.replicas }}
{{- end }}
Bodyguard answered 13/4, 2020 at 10:40 Comment(1)
Use ((.Values.deployment).scale).type | default "static" for brevity.Dahlgren

© 2022 - 2024 — McMap. All rights reserved.