Search code examples
kubernetes-helmgo-templates

How to use default if index does not exists


I have the following input:

alerts:
  api_http_500:
    default_config:
      enabled: true
    override:
      user_1:
        enabled: false

I wanna check if the enabled field exists for a user. If it not so I use the default one. I'm using index and default like follow:

{{ (index $alerts "alerts" "api_http_500" "override" "user_2" "enabled") | default (index $alerts "alerts" "api_http_500" "default_config" "enabled") }}

But I have this error <index $alerts "alerts" "user_2" "enabled">: error calling index: index of nil pointer.

I don't know how to use the default value without a if/else structure.


Solution

  • The fundamental error is that there is no user_2 field in the data, so when you try to fetch it, you get nil; you can't do deeper lookups in that. default can still come to the rescue!

    The Go text/template language supports local variables. So we can set a local variable to what should be the user_2 value. If that's not present, the Sprig support library includes a dict function that can produce an empty dictionary.

    {{- $overrides := index $alerts "user_2" | default dict -}}
    

    Now $overrides is always a dictionary, so we can look up in that, or if there's nothing there, fall back to the defaults.

    {{- $a := index $alerts.alerts "api_http_500" -}}
    {{- $defaults := $a.default_config -}}
    {{- $overrides := index $a.override "user_2" | default dict -}}
    {{- $enabled := $overrides.enabled | default $defaults.enabled -}}
    

    This looks good, but the one other problem is that default doesn't distinguish present-but-false from absent; both are "falsey" and will get replaced with the default value. The standard template eq requires values to have the same type, but Sprig's deepEqual doesn't have this requirement.

    This lets you write the detailed logical statement that the option should be enabled if the override is true, or if the override is not false (I don't think there's a way to spell out "nil" or "absent") and the default is true.

    {{- $enabled := or $overrides.enabled (and (not (deepEqual $overrides.enabled false)) $defaults.enabled) -}}
    

    (It is worth considering whether you want this much Go template logic, and whether you can either restructure your values file to simplify this, or use something like a Kubernetes operator that's in a more normal language and is more testable.)