Helm, useful as it is, has a big crux: it renders templates to text. That is, when inserting a data-structure at a point, it must be stringified to Yaml. Additionally it must then be reindented to fit Yaml constraints, likewise when including a file. This burdens the user with such low-level technicalities, when it wants to be a high level Kubernetes companion.
Such raw text processing is reminiscent of C++ templates vs. Rust generics, or of C vs. Rust macros, both without vs. with code insight. The result being that you only find out at a later stage, whether the produced text is correct.
The thing is that Helm mixes two different templating aspects on the same level:
Flow control should happen early during parsing, as it can add or remove nodes or whole subtrees.
Value insertion should happen late during parsing, if it wants to smartly integrate data into the growing tree.
I am thinking of integrated SerDe templating. It would parse conditionally and with looping. And it would insert values and subtrees directly, optionally deserialised just in time from any source it can handle. I am using Tera to show what I’d like:
name: {{ name }}
some: "thing"
sub:
{#- cross-language include would work if deserialised first, plugging the resulting data structure into sub.tree #}
tree: {% include "tree.json" %}
list:
{% for product in products %}
{# this might be tricky, as it mixes the flow-control and value insertion stages #}
- {{product.name}}
{% endfor %}
Does any such thing exist? Or do SerDe and any templating engines have hooks, that they can be made to interact smartly?
I am not sure exactly what you're asking for. But let me start by claiming that Helm is the perfect representation of the Configuration Complexity Clock. Templates should be very simple string interpolations, and nothing more. Once they start including control flow, the language is entering Turing completeness territory.
If you are asking for a solution to generate YAML files, I would suggest using Rust as your template processing language. It could be as simple as a transformer that takes a list of configurable values in TOML and spits out YAML.
By that, I mean your Rust code has a full in-memory representation of the complete structure that can be serialized to YAML in one go, after populating it the way you like. It may be easiest with serde, since you can use a native data structure. But you could also create dynamic data structures with yaml-rust2.
Again, i'm not sure how far off I am on interpreting your question. But I cannot in good conscience recommend a templating language for any purpose.
The clock was a good read! Nice pun on the topic that the comments have only time, no date. “Ground Hog Day” might be another name for this.
K8s does have a place. The manifests are rather verbose and partly not thought through for consistency. But the manifest kinds do reoccur for many apps. And, as one comment points out, one app will be deployed in several incarnations. They need their Urls, DB-secrets etc.
Helm offers a (baddish) way of simplifying this. But many Helm charts that consist only of placeholders merely move the configuration from K8s’ cumbersome but documented formats to a wild-west values.yaml. I wonder whether human nature has an innate urge to take useful flexibility to such extremes.
Rust and Toml are on my wishlist for a better Helm or directly integrated into K8s. Not sure whether this will ever materialise…
But mulling this, I concluded that – independent of use case – data-tree templating, while reading Toml, Json, Yaml & (gasp!) Xml is glaringly missing.
For simple cases I have found that often it's enough to have a base document in its native format, parse it and then apply a few rewrites in rust code. This works well if most of the document is fixed and only a few values need to be replaced or a few list entries need to be duplicated.
Another simple approach is tree-merging a base file + override files. Like json patch or kustomize.
I have only used helm a few times and hated it for those reasons. I don't know if that templating mess is fixable.