Composing TemplateInstances#
A Template can render TemplateInstances — a parent that fans out to one child instance per item, where each child renders the real resources. This is the pattern platform integrations are built on: it gives every item its own status, isolates failures, and keeps the watch count flat.
When to reach for it#
Rendering to many namespaces emits the target resources directly from one instance. Prefer composition instead when you want, per item:
- its own status — each child
TemplateInstancereports its own conditions, so you can see which item failed; - failure isolation — a malformed render for one item fails only that child, not the whole fan-out;
- a bounded, shared watch — the parent holds one cluster-wide watch regardless of how many children it emits.
The two Templates#
The child renders the actual resource for one item, from parameters:
apiVersion: templates.v2.stakater.com/v2alpha1
kind: Template
metadata:
name: namespace-quota-child
namespace: provider
spec:
parameters:
- name: namespace
required: true
gotemplate: |
apiVersion: v1
kind: ResourceQuota
metadata:
name: default-quota
namespace: {{ .parameters.namespace }}
spec:
hard:
pods: "20"
sync:
mode: strict
The parent lists namespaces and emits one child TemplateInstance per match. Child instances live in the provider namespace; the child Template's default targetNamespaces (its own namespace) already allows that.
apiVersion: templates.v2.stakater.com/v2alpha1
kind: Template
metadata:
name: namespace-quota-parent
namespace: provider
spec:
parameters:
- name: namespaces
required: true
gotemplate: |
{{- range $ns := .parameters.namespaces }}
{{- if and (hasKey (default dict (index $ns.metadata "labels")) "team") (not (hasKey $ns.metadata "deletionTimestamp")) }}
---
apiVersion: templates.v2.stakater.com/v2alpha1
kind: TemplateInstance
metadata:
name: quota-{{ $ns.metadata.name }}
namespace: provider
spec:
templateRef:
name: namespace-quota-child
namespace: provider
parameters:
- name: namespace
value: {{ $ns.metadata.name | quote }}
{{- end }}
{{- end }}
outputs:
- name: children
fromAll:
apiVersion: templates.v2.stakater.com/v2alpha1
kind: TemplateInstance
namespace: provider
keyTemplate: "{.metadata.name}"
fields:
- name: ready
jsonPath: "{.status.conditions[?(@.type=='Ready')].status}"
sync:
mode: revert
Bind the parent with a LIST of namespaces, exactly as in the fan-out guide. The operator creates a child TemplateInstance per team namespace, each of which renders its ResourceQuota. The deletionTimestamp check drops the child when its namespace is being deleted; without it, the strict child would block the namespace deletion.
One place to read health#
The parent's outputs aggregate every child's Ready condition, so a single object answers "is every item reconciled?":
kubectl get templateinstance namespace-quota-parent -n provider \
-o jsonpath='{.status.outputs.ready}' | jq .
# { "quota-team-a": "[\"True\"]", "quota-team-b": "[\"True\"]" }
The [?(...)] filter is a list query, so each value is a JSON-encoded list.
Naming#
A child's name is composed from the item (quota-<namespace>). Choose a prefix the parent's own name can never take, so a crafted item value cannot produce a child that collides with the parent instance.
See also#
- Rendering to many namespaces — the simpler, direct fan-out.
- Outputs — aggregating child status with
fromAll. - Lifecycle — how the operator cleans up children when the parent stops emitting them.