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:

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

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.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/