Docs · Airframe
Airframe API versioning and deprecation
Every Airframe kind is catalog.hangar.io/v1alpha1. This page says what that promises, how a kind’s
schema may change, and how a breaking change ships. It exists because Airframe’s XRDs are consumed by
machines (agents, Glidepath, Tower, the tenants-repo validator) as well as by people (review C13).
What v1alpha1 promises today
- Additive changes ship without a version bump. A new optional field, a new enum value, a new status field, a new printer column, a looser constraint. Existing XR requests stay valid.
- Tightening is a breaking change, even inside v1alpha1: a new required field, a removed field or
enum value, a narrower pattern or length limit, a new CEL rule. Before one ships, every live XR request
on every cluster must already satisfy it (check with
tools/airframe-validate --xrover both tenants repos and the live XRs), because the API server applies the new schema to every later update of an existing object. - Field names never change meaning. Rename by adding the new field, reading both in the composition, migrating requests, then removing the old one as a tightening change.
- The composition is not the API. A composition may change freely as long as the sidecar contract
(
xrds/<kind>.meta.yaml: outputs, conditions, verify) still holds;tools/test_sidecars.pyenforces that in CI.
Shipping a new version (v1beta1, v1)
Crossplane XRDs support several versions at once but have no conversion webhook: every served version must share one schema shape. So a version bump is a rename-free promotion, never a reshape:
- Add the new version to the XRD with the same schema,
served: true,referenceable: false. - Release, pin, and let
tools/airframe-validate --xraccept both apiVersions. - Flip
referenceableto the new version (the storage version), and mark the old onedeprecated: truewith adeprecationWarningthat names the replacement. The API server then warns every client that still writes it. - Migrate every XR request in the tenants repos and every chart template to the new apiVersion.
- Stop serving the old version (
served: false) one release later, then remove it.
A real reshape (moving or retyping fields) is a new kind or a new group, migrated with the
delete-old/create-new recipe the Tier 2 domain rename used; see
hangar/docs/ and the RepositoryFile safety rules in each composition’s AGENTS.md before any prune.
Constraints every kind carries
defaultCompositionRefnames the kind’s one Composition, so an XR that names none is deterministic.metadata.nameis length-limited where a derived name would overflow a 63-character Kubernetes name or label (Helm release names,<name>-tektoncicd, CloudNativePG Jobs, operator StatefulSets). Each limit states its reason in the XRD; the ApplicationEnvironment and TektonCICD specs carry CEL rules for theapp-<app>-<env>andapp-<app>-cicdnamespaces they derive.- Printer columns show each kind’s custom condition (
kubectl get <kind>shows ComponentReady, TargetReady, CicdOnboarded, ClusterReady/AppResolved or the rollout phase). - Attach-mode kinds (RabbitMQ, Dex) compose a Crossplane
Usagethat blocks deleting the broker or server they attach to while they exist.
Checking a change before it ships
python3 tools/test_xr_validate.pylints every XRD for the structural-schema rules the API server enforces (a schema it refuses leaves the live CRD frozen while the XRD still reports Established).- After the pin syncs, compare the XRD with its CRD:
kubectl get events --field-selector involvedObject.name=<plural>.catalog.hangar.io,reason=EstablishComposite.