akua / examples / 11-install-as-package
11-install-as-package
Composes a third-party Akua package (`./upstream/`) and applies a tenant overlay, drops a kind, and appends extras — the install-as-Package shape. Demonstrates the natural call form `upstream.render(u
Composes a third-party Akua package (./upstream/) and applies a tenant overlay, drops a kind, and appends extras — the install-as-Package shape. Demonstrates the natural call form upstream.render(upstream.Input{...}): typed inputs at the consumer call site, no path strings in user code.
Layout
| file | purpose |
|---|---|
upstream/package.k | Sibling Akua package — Deployment + Service + PodDisruptionBudget. Authored as a normal Package; nothing about it is install-aware. |
akua.toml | Dep alias upstream = { path = "./upstream" } for akua tree + lock-time validation. |
package.k | The install: upstream.render(...), overlay tenant label, drop PDB, append a ConfigMap. |
inputs.example.yaml | Per-install inputs (tenant, app, replicas). |
rendered/ | Reference output (3 files: Deployment, Service, ConfigMap). |
Render
akua render --out ./rendered
The install pattern
import pkgs.upstream as upstream
_up = upstream.render(upstream.Input { appName = ..., replicas = ... })
# Overlay
_patched = [r | {metadata.labels = {"install.cnap.tech/tenant" = input.tenant}} for r in _up]
# Filter
_filtered = [r for r in _patched if r.kind != "PodDisruptionBudget"]
# Extras
_extras = [{apiVersion = "v1", kind = "ConfigMap", ...}]
resources = _filtered + _extras
The import lands a synthesized stub that owns a render lambda and re-exports upstream's schemas — KCL type-checks upstream.Input{...} at the call site (typos surface as compile errors, not as runtime worker traps). The mechanism mirrors import charts.<name> for Helm charts; see docs/package-format.md for the full shape.
package.k
import akua.ctx
import pkgs.upstream as upstream
# An "install" Package: composes a third-party Akua package
# (`upstream`) and applies tenant-specific overlay + filter + extras.
# This is the install-as-Package shape — no separate install schema,
# no helm-values-only escape hatch. Just KCL composing KCL.
schema Input:
"""Inputs for the install."""
tenant: str
app: str
replicas: int = 2
input: Input = ctx.input()
# 1. Render the upstream Package. Mirrors helm.template + import
# charts.webapp: the synthesized `pkgs.upstream` stub owns a
# `render` lambda that dispatches to the host plugin with the
# alias hardcoded, so the call site is just the typed inputs.
# `upstream.Input { ... }` type-checks here — typos surface as
# KCL compile errors, not as runtime worker traps.
_up = upstream.render(upstream.Input {
appName = input.app
replicas = input.replicas
})
# 2. Overlay: apply a tenant label to every upstream resource.
_patched = [r | {
metadata.labels = {"install.cnap.tech/tenant" = input.tenant}
} for r in _up]
# 3. Filter: this install doesn't want PDBs (single-replica policy).
_filtered = [r for r in _patched if r.kind != "PodDisruptionBudget"]
# 4. Extras: append an install-meta ConfigMap.
_extras = [{
apiVersion = "v1"
kind = "ConfigMap"
metadata.name = input.app + "-install-meta"
metadata.labels = {"install.cnap.tech/tenant" = input.tenant}
data.tenant = input.tenant
data.upstreamApp = input.app
}]
resources = _filtered + _extras
Rendered output
000-deployment-webapp.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
labels:
install.cnap.tech/tenant: acme
spec:
replicas: 3
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- image: nginx:1.27
name: webapp
ports:
- containerPort: 80
001-service-webapp.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp
labels:
install.cnap.tech/tenant: acme
spec:
ports:
- port: 80
targetPort: 80
selector:
app: webapp
002-configmap-webapp-install-meta.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: webapp-install-meta
labels:
install.cnap.tech/tenant: acme
data:
tenant: acme
upstreamApp: webapp
Source: examples/11-install-as-package/