Using Jsonnet
While Tera is excellent for simple text substitution, Jsonnet is a data templating language designed for generating configuration data. It supports local variables, functions, conditionals, list comprehensions, and file imports — making it ideal for complex output generation where Tera’s string-based approach becomes hard to maintain.
For advanced Jsonnet techniques consult the official case studies.
When to prefer Jsonnet over Tera: Use Jsonnet when your output requires list comprehensions, helper functions, conditional object construction, or when you want to split reusable logic into
.libsonnetlibraries. For straightforward key-value serialisation, Teratemplateis simpler and sufficient.
Data Context
When using jsonnet, the data context is injected as External Variables accessed with
std.extVar().
| External variable | Description |
|---|---|
"origin_resource" |
The current resource and all its traversed graph relations |
"<header_key>" |
Any top-level variable defined in the TOML file header |
Example 1: Inline Jsonnet Logic
[[output]]
resource_type = "k8s_manifest"
name = "svc-{{ origin_resource.name }}" # Name generation still uses Tera
jsonnet = """
local origin = std.extVar("origin_resource");
{
apiVersion: "v1",
kind: "Service",
metadata: {
name: origin.name,
labels: {
// Use Jsonnet logic
app: if std.length(origin.application) > 0 then origin.application[0].name else "unknown"
}
}
}
"""
Note: The name, filename, and match_on fields always use Tera even when the
body uses
jsonnet. Only the output body (the JSON object that becomes the resource’s properties) is evaluated as Jsonnet.
Example 2: Modular
data/output/k8s.libsonnet:
{
service(name):: {
apiVersion: "v1",
kind: "Service",
metadata: { name: name }
}
}
data/output/services.toml:
[[output]]
resource_type = "k8s_manifest"
name = "svc-{{ origin_resource.name }}"
jsonnet = """
local k8s = import 'k8s.libsonnet';
local origin = std.extVar("origin_resource");
k8s.service(origin.name)
"""