Helm is somehow the most popular method of distributing customizable kubernetes manifests right now. If like $work you use it in a gitops environment, it's "just" a fancy templating system. Anyway, here are some notes on authoring helm chart (dependencies).
Helm passed data to its templates through its values system:
arbitrary nested key/values in a tree.
To access the data,
use {{ $.Values.key }}
in templates,
where nested objects have their keys separated by dots like {{ $.Values.some.sub.sytem.key }}
.
A problem arises when authoring templates:
in the previous example, if either of the some
, sub
, system
keys wasn't present,
the call would be a nil dereference error.
This leads to either very verbose templates like the next example,
or Helm's best pracitices recommendation to use a flat namespace
like {{ $.Values.someSubSystemKey }}
1{{ if $.Values.some }}
2{{ if $.Values.some.sub }}
3{{ if $.Values.some.sub.system }}
4{{ $.Values.some.sub.system.key }}
5{{ end }}
6{{ end }}
7{{ end }}
Instead, we can work with a quirk of Go's templating engine and instead use parenthesis to guard each level of access, allowing us to keep a nicely structured value tree:
1{{ ((($.Values.some).sub).system).key }}
If for some reason,
you have keys in values in kebab-case
,
you'll realize accessing them is difficult since -
is an invalud character for identifiers.
get
will get you one level of key access,
while dig
will go through multiple levels
(but still won't handle type mismatches):
1foo: {{ get (get $.Values "my-key") "sub-key" }}
2bar: {{ dig "my-key" "sub-key" "default-value" $.Values.AsMap }}
If you've ever wondered why the default templates are all prefixed with $name.
,
it's because all templates are shared everywhere when rendering.
The main charts templates are available to its dependencies,
the dependencies' are available to the main chart and each other.
"Available" is one way to put it,
the other is that it may inadvertently override a template another chart was expecting.
1{{ define "myapp.name" }}
2...
3{{ end }}
In helm, if you add a dependency,
then it gets passed all the values in the top level key matching its name
(or alias if one is defined)
hoisted in to its root.
But there's a magical global
namespace too,
which remains constant through all levels of dependencies,
which you can use to pass the same data to all your charts without duplicate specification.
So given the following values:
1# values.yaml
2global:
3 key: a
4mydep:
5 hello: world
6 foo:
7 bar: fizz
The mydep
chart will see it as:
1global:
2 key: a
3hello: world
4foo:
5 bar: fizz
Suppose you provide a template in a subchart to your users to render something complicated.
You need values from both global
and those passed to the chart.
From the previous example,
we can see that depending on where the template is invoked,
it'll have an inconsistent view of values.
This is where the $.Subcharts
built in object comes in:
under the name/alias of each subchart is the scope of each subchart,
so templates can be invoked with the view of values as if they were in their originating chart.
1# in parent chatt
2{{ include "mydep.template" $.Subcharts.mydep }}