26

Immutable Promotion with ApplicationSet

Promote one proven artifact through isolated clusters by changing only reviewed digest references.

Run it in the public monorepo

This course is built around the public Zheta Kubernetes Lab monorepo. The excerpt below is runnable source, not pseudocode.

Source: scripts/promote-release.py

if environment not in {"dev", "staging", "prod"} or len(digests) != 4:
    raise SystemExit("usage: promote-release.py dev|staging|prod ECR_REGISTRY CONTROL GENERATOR RUNTIME EVIDENCE")
if not re.fullmatch(r"[0-9]{12}\.dkr\.ecr\.[a-z0-9-]+\.amazonaws\.com", registry):
    raise SystemExit("registry must be a real account-scoped ECR registry")
if any(not re.fullmatch(r"sha256:[0-9a-f]{64}", digest) for digest in digests):
    raise SystemExit("every service must have a real sha256 digest")

Code to reality

Declared intent
Promote exactly one environment only when all four service images have real account-scoped digests.
Interpreter
Python validates the environment, ECR registry, and immutable digest syntax before rewriting one overlay.
Software effect
Promotion changes one environment repository artifact; each environment-local Argo instance remains independent.
Hardware effect
Only the selected cluster may later replace Pods with the approved image bytes.
Observable evidence
One-overlay diff, four ECR digests, review approval, and the matching local Argo revision prove promotion.

Start with the people and the result they need

The source tables below remain the detailed contract. Begin with these customer paths:

  • D26-UC-01
    • Person: Release manager
    • Job: Promote one dev-proven digest through staging to production
    • Observable result: Each overlay references the same digest after its own approval and probes
  • D26-UC-02
    • Person: On-call engineer
    • Job: Roll back a harmful release without losing audit history
    • Observable result: Previous proven digest reconciles and customer flow recovers

Turn each customer job into a testable story

Now turn each customer job into a story with a result that an engineer can check:

  • D26-US-01
    • Story: As a release manager, I want digest-only promotion with per-environment approval, so that evidence follows identical bytes
    • Observable acceptance: Chain contains source, digest, dev/staging proofs, overlay diffs, approvers, Argo revisions, responses, times, and run IDs
  • D26-US-02
    • Story: As an on-call engineer, I want rollback to a previously proven digest, so that service recovers without deleting the failed release record
    • Observable acceptance: Old/new digest, reason, approval, reconciliation, affected replay, positive control, time, run, and traces are recorded

Add real state and observable proof

Finally trace each story through the system that owns its state and the evidence that proves the outcome:

  • D26-FLOW-01
    • Trigger: Release manager promotes a dev-proven digest
    • Responsible systems: Git review, registry, ApplicationSet, Argo application controller, Kubernetes rollout, Forge probe
    • Authoritative state: Git and registry for artifact intent; Kubernetes APIs for rollout; release/evidence stores for approval and result
    • Owned record: ArtifactAttestation
    • Observable evidence: Source, digest, diffs, approvals, cluster/Application revisions, customer responses, environment, time, run, and traces
    • Failure signal: Mutable tag, digest mismatch, wrong overlay/cluster, missing approval, degraded rollout, or failed response
  • D26-FLOW-02
    • Trigger: Production probe breaches release acceptance
    • Responsible systems: Release controller, Argo CD, Kubernetes Deployment, probe runner
    • Authoritative state: Immutable release history plus Git overlay revisions and Application status
    • Owned record: PromotionDecision
    • Observable evidence: Failed and restored digests, commits, sync/health, recovered response, positive control, actor, time, and incident ID
    • Failure signal: Unproven target, direct live mutation self-healed, rollback timeout, or positive-control failure

The enterprise problem and today’s slice

Enterprise problem: Rebuilding an image or copying credentials during promotion makes staging evidence irrelevant and can smuggle unreviewed behavior into production. Whole-course context: The incoming bootstrap is valid only when real controller evidence exists; today gives it a fail-closed promotion contract. Today’s slice: scripts/promote-release.py accepts four real SHA-256 image digests and rewrites exactly one environment overlay; each environment-local forge-{env}.yaml ApplicationSet then reconciles only that overlay. Promotion does not add autoscaler or machine-capacity manifests. End-of-day evidence: Source SHA, four digests, one-overlay diff, approvals, Application revision, explicit FORGE_URL and FORGE_TOKEN probe, denial, time, run, and traces form one chain. Still unsolved: No promotion or AWS outcome is claimed until registries, Secrets, clusters, and approvals exist.

Customer use cases

An environment name is not a release, so promotion must identify the exact artifact and evidence that justify it. Rollback selects a previous digest rather than rewriting history.

Use case IDActorCustomer jobSuccess outcomeDenial or recovery evidence
D26-UC-01Release managerPromote one dev-proven digest through staging to productionEach overlay references the same digest after its own approval and probesTag-only image, rebuilt digest, skipped staging, or copied credential is rejected
D26-UC-02On-call engineerRoll back a harmful release without losing audit historyPrevious proven digest reconciles and customer flow recoversRollback to an unproven digest is denied; unaffected application remains healthy

Actor-centred user stories

Promotion policy fails when the artifact and environment configuration are mixed. These stories keep the reusable base stable and overlays small.

Story IDUse case IDsUser storyObservable acceptance conditions
D26-US-01D26-UC-01As a release manager, I want digest-only promotion with per-environment approval, so that evidence follows identical bytesChain contains source, digest, dev/staging proofs, overlay diffs, approvers, Argo revisions, responses, times, and run IDs
D26-US-02D26-UC-02As an on-call engineer, I want rollback to a previously proven digest, so that service recovers without deleting the failed release recordOld/new digest, reason, approval, reconciliation, affected replay, positive control, time, run, and traces are recorded

End-to-end product flows

The customer-visible trigger is a release decision and the terminal event is a verified product response. Argo CD is one controller in that path, not the evidence itself.

Flow IDUse case IDsPathTriggerNumbered stepsTerminal evidence
D26-FLOW-01D26-UC-01HappyRelease manager promotes a dev-proven digest1. Resolve source and digest. 2. Verify dev proof. 3. Update staging overlay only. 4. Reconcile and probe. 5. Approve production. 6. Update production overlay only. 7. Reconcile and probe.Source, digest, diffs, approvals, cluster/Application revisions, customer responses, environment, time, run, and traces
D26-FLOW-02D26-UC-02RecoveryProduction probe breaches release acceptance1. Freeze promotion. 2. Select previous proven digest. 3. Commit reviewed rollback reference. 4. Argo reconciles. 5. Replay affected flow. 6. Probe unaffected app. 7. Seal incident.Failed and restored digests, commits, sync/health, recovered response, positive control, actor, time, and incident ID

The stable path begins with accountable release authority and ends at the customer.

System design derived from the flows

ApplicationSet removes repeated Application fields, but it must not collapse environment approval or credentials. Each environment-local Argo instance renders its own manifest and targets the fixed https://kubernetes.default.svc destination with its in-cluster identity.

Use case IDEntry pointResponsible servicesAuthoritative storeFailure evidence
D26-UC-01Digest change in environment overlayGit review, registry, ApplicationSet, Argo application controller, Kubernetes rollout, Forge probeGit and registry for artifact intent; Kubernetes APIs for rollout; release/evidence stores for approval and resultMutable tag, digest mismatch, wrong overlay/cluster, missing approval, degraded rollout, or failed response
D26-UC-02Reviewed rollback commitRelease controller, Argo CD, Kubernetes Deployment, probe runnerImmutable release history plus Git overlay revisions and Application statusUnproven target, direct live mutation self-healed, rollback timeout, or positive-control failure

The new boxes separate artifact reference, destination generation, runtime rollout, and recovery responsibility.

DRY keeps one Forge base and three intentionally small per-cluster ApplicationSets; SRP assigns promotion writing, render verification, reconciliation, and runtime rollout to different owners. Argo CD’s automated sync semantics are controller-specific, and official guidance notes that rollback cannot be performed while automated sync is enabled; Zheta Forge therefore models rollback as a new reviewed digest commit rather than argocd app rollback (Argo CD automated sync).

Data model and ownership

A release record must survive rollback, or recovery destroys the audit trail. Existing generated-app data remains with each application and is not copied during deployment.

Generated-application database: Required in this slice — each generated application keeps its tenant data through release and rollback; promotion changes runtime artifacts, not data ownership.

Record or entityStore and ownerPrimary keyForeign key or opaque referenceTenant keyMaterial constraintLifecycle and deletionUse case IDs
ArtifactAttestationECR/evidence store, owned by Build Platformimage digestOpaque source, build, and registry scan refsorganization_idRepository is immutable, scan-on-push is enabled, and the digest matches the approved build; signature admission remains a separate launch prerequisiteCreate once, retain through releases, expire only after retention and no referencesD26-UC-01, D26-UC-02
PromotionDecisionProvider release store, owned by Release Controlpromotion_idOpaque digest, environment, approval, and probe refsorganization_idOrdered dev then staging then production evidencePropose, approve/reject, deploy, supersede; never rewriteD26-UC-01, D26-UC-02
ApplicationTenantStateGenerated-app database, owned by generated applicationapp_tenant_idOpaque release ref onlyapp_tenant_idData predicates independent of provider/runtime rolesPersist through rollout/rollback; export and delete by app lifecycleD26-UC-01, D26-UC-02

Attestation, decision, and tenant state now remain authoritative in their own planes throughout rollback.

Render before reconciling

Overlay mistakes are cheaper to reject before a controller changes a cluster. The real promotion command refuses placeholder registries or malformed digests and changes one exact overlay.

: "${AWS_PROFILE:?set the approved AWS profile}" "${AWS_REGION:?set the intended AWS region}" "${EXPECTED_ACCOUNT_ID:?set the intended 12-digit AWS account}"
actual_account="$(aws --profile "$AWS_PROFILE" --region "$AWS_REGION" sts get-caller-identity --query Account --output text)"
[ "$actual_account" = "$EXPECTED_ACCOUNT_ID" ] || { echo "AWS account mismatch" >&2; exit 1; }
for environment in local dev staging prod; do
  kubectl kustomize "gitops/apps/forge/overlays/${environment}" >/tmp/"forge-${environment}.yaml"
done
./scripts/promote-release.py staging "$ECR_REGISTRY" "$CONTROL_DIGEST" "$GENERATOR_DIGEST" "$RUNTIME_DIGEST" "$EVIDENCE_DIGEST"
aws --profile "$AWS_PROFILE" --region "$AWS_REGION" eks update-kubeconfig --name zheta-forge-staging --alias zheta-forge-staging
./scripts/verify-release.sh staging
kubectl --context zheta-forge-staging -n argocd get application forge-staging -o wide
Effect fieldWhat happens
Declared intentRender one base with small environment-specific digest and policy changes
InterpreterKustomize renders locally; Argo CD later compares Git render with live Kubernetes state
Software effectLocal render files and read-only Application observations change; clusters do not
Hardware effectLocal CPU/disk render YAML; Argo/Kubernetes APIs use network and controller capacity for reads
EvidenceRender digests, image digests, Application revisions, sync/health, approvals, and probes

Three authorization planes and lifecycle rule

Provider release approval does not grant cluster shell access or generated-app roles. Hosted runtime owns artifact deployment; the generated application owns data compatibility and authorization during rollback.

Decision rule: promote immutable bytes and environment-specific evidence; retire traffic before artifact deletion, and retain release decisions after generated-app data is erased.

Key takeaways

Promotion is an evidence chain around immutable bytes.

  • ApplicationSet removes repeated wiring, not environment authority.
  • Git changes desired workload state; Argo reconciles; Kubernetes runs it.
  • Rollback adds a new reviewed decision and preserves history.

Checklist

The chain is complete only when bytes and evidence match.

  • [ ] All cloud Applications use the approved overlays and scoped destinations
  • [ ] Promotion changes digest references only
  • [ ] Production approval consumes staging evidence
  • [ ] Rollback proves affected recovery and unaffected control