akua / examples / 06-multi-engine
Example 06 — multi-engine
Four source engines composed in one Package, with per-source output routing. Demonstrates:
Four source engines composed in one Package, with per-source output routing. Demonstrates:
webapp.template(...)— a Helm chart consumed unchanged via the synthesizedcharts.<name>stubkustomize.build(...)— a Kustomize overlay on a base (engine-direct: bases are within-Package, not deps)rgd.instantiate(...)— a kro RGD rendered offline (no controller)- Inline KCL resources — just dicts, composed with the rest
- Named outputs — static resources routed to raw manifests, runtime-late-binding resources routed to an RGD for kro to reconcile
This is the "realistic monorepo Package" shape. Most production Packages have 2–5 sources; 4 is a reasonable teaching number.
Layout
06-multi-engine/
├── akua.toml
├── akua.lock
├── package.k the Package; mixes Helm + Kustomize + kro + inline KCL
├── overlays/ Kustomize overlay (local source)
│ ├── kustomization.yaml
│ └── servicemonitor-patch.yaml
├── inputs.yaml
└── README.md
The Package, in pieces
Imports
import akua.kustomize
import akua.rgd
import charts.webapp as webapp
import rgds.platform as platform
charts.webapp resolves to an OCI-published Helm chart via akua.toml; the synthesized stub owns the template method, so import akua.helm doesn't appear at the call site. rgds.platform resolves to an OCI-published kro ResourceGraphDefinition. akua.kustomize / akua.rgd stay engine-direct because their inputs aren't typed deps — the kustomize base is a within-Package directory, the RGD is instantiated against an OCI artifact.
Four sources
- Helm — the webapp chart, values mapped from the public schema.
- Kustomize — a local overlay under
./overlays/, building aServiceMonitor+ a patched ConfigMap. - kro RGD — the platform glue (e.g. app-scoped DNS record + cert) instantiated with the current App's metadata.
kro.rgd(...)produces the ResourceGraphDefinition as a regular K8s manifest; kro's controller picks it up from the rendered YAML. - Inline KCL — a
NetworkPolicyauthored directly in KCL. No external engine needed.
Aggregation
_app = webapp.template(webapp.TemplateOpts { values = webapp.Values { ... } })
_monitor = kustomize.build(kustomize.Build { path = "./overlays" })
_netpol = NetworkPolicy { ... }
_glue = kro.rgd(kro.Rgd { definition = platform.RGD, instance = { ... } })
resources = [*_app, *_monitor, _netpol, _glue]
One Package, one flat resource list, one render output. ArgoCD/Flux applies the rendered YAML as it would any other manifest set; kro's controller sees the RGD and reconciles its instances.
Render
akua add # resolve deps
akua render --inputs inputs.yaml --out ./deploy
Result:
deploy/
├── 000-deployment-webapp.yaml # from helm
├── 001-service-webapp.yaml # from helm
├── 002-ingress-webapp.yaml # from helm
├── 003-configmap-webapp.yaml # from kustomize
├── 004-servicemonitor-webapp.yaml # from kustomize
├── 005-networkpolicy-webapp.yaml # from inline KCL
└── 006-resourcegraphdefinition-glue.yaml # from kro.rgd — kro reconciles it
See also
- package-format.md — the Package spec
- embedded-engines.md — which engines are embedded; escape-hatch via
--engine=shell - architecture.md#what-akua-is-not — why we compose reconcilers instead of replacing them
package.k
# Example 06 — multi-engine
#
# One Package, four sources, one render output (raw YAML):
# - Helm (upstream webapp chart)
# - Kustomize (./overlays base → ServiceMonitor + ConfigMap patch)
# - kro RGD (platform glue as a transformation producing an RGD manifest)
# - Inline KCL (NetworkPolicy authored directly in KCL)
#
# Render:
# akua add
# akua render --inputs inputs.yaml --out ./deploy
import akua.ctx
import akua.kustomize
import akua.kro
import charts.webapp as webapp
import rgds.platform as platform
schema Input:
"""Public inputs for the multi-engine example."""
appName: str
hostname: str
replicas: int = 3
team: str = "platform"
check:
replicas >= 1, "replicas must be at least 1"
input: Input = ctx.input()
# --- 1. Helm source ---
# `charts.<name>` synthesizes a `template` lambda + typed `Values`
# schema per dep — alias-method form, mirrors `pkgs.<name>.render` for
# Akua packages. No `import akua.helm` needed at the call site.
_app = webapp.template(webapp.TemplateOpts {
values = webapp.Values {
replicaCount = input.replicas
ingress.hostname = input.hostname
ingress.tls.enabled = True
fullnameOverride = input.appName
}
})
# --- 2. Kustomize source ---
# Engine-direct: kustomize bases are within-Package directories, not
# typed deps, so they don't get a `bases.<name>` alias. Same form
# every Package uses for kro RGDs and OCI manifest fetches.
_monitor = kustomize.build(kustomize.Build {
path = "./overlays"
})
# --- 3. Inline KCL-native resource ---
# NetworkPolicy denying all ingress except from the app's own namespace.
# No engine required — just a typed dict that ends up in resources[].
_netpol = {
apiVersion: "networking.k8s.io/v1"
kind: "NetworkPolicy"
metadata = {
name: "${input.appName}-default-deny"
namespace: input.appName
labels.team: input.team
}
spec = {
podSelector = {}
policyTypes = ["Ingress"]
ingress = [{
from = [{
namespaceSelector.matchLabels = {
"kubernetes.io/metadata.name": input.appName
}
}]
}]
}
}
# --- 4. kro RGD transformation ---
# Genuine runtime late-binding: DNS records and cert references whose status
# fields are observed post-apply. `kro.rgd` produces the ResourceGraphDefinition
# as a regular K8s manifest that kro's controller picks up from the rendered
# YAML — not a separate emit kind.
_glue = kro.rgd(kro.Rgd {
definition = platform.RGD
instance = {
metadata.name = input.appName
spec.domain = input.hostname
spec.ingress_class = "traefik"
}
})
# --- aggregate ---
resources = [*_app, *_monitor, _netpol, _glue]
Source: examples/06-multi-engine/