Install image-reflector-controller + image-automation-controller as part of the flux-system install (bootstrap.sh --components-extra) so the image.toolkit CRDs pre-exist before reconcile — no F-MON1 split needed for the ImageUpdateAutomation CR. Refine the cluster-wide ImageUpdateAutomation for machine-authored git write-back (setter strategy, path ./apps) reusing the flux-system GitRepository secretRef, and document the operator git-write-credential hand-off + SOPS secret template (gitea-image-automation-credentials). image-automation stays deferred in platform/kustomization.yaml until that credential is provisioned. Co-Authored-By: Claude Opus 4.8 <[email protected]>
77 lines
5.1 KiB
Markdown
77 lines
5.1 KiB
Markdown
# platform/image-automation
|
|
|
|
**Flux image-update-automation** — hands-off container image tag advances driven by declared policy.
|
|
|
|
- **Governing ADR:** [ADR-0009 — Automated updates: Flux image automation + Renovate](../../../home-network-k3s/decisions/adr-0009-automated-image-updates.md)
|
|
- **Spec:** [docs/11 — Automated Updates](../../../home-network-k3s/docs/11-automated-updates.md),
|
|
[docs/05 §Automated updates interplay](../../../home-network-k3s/docs/05-gitops-and-delivery.md)
|
|
- **Serves:** R-UPD-02 (with R-UPD-01/03 safety rails)
|
|
- **Owner:** WS-UPDATE (mechanism), WS-GITOPS (policy rendering from ServiceDefinition)
|
|
|
|
## What lives here
|
|
|
|
- **`image-update-automation.yaml`** — the **cluster-wide** `ImageUpdateAutomation` that scans
|
|
registries per each service's `ImagePolicy` and **commits the resolved stable version back to git**
|
|
(machine-authored, auditable — ADR-0009). No human or AI authors a tag bump (R-UPD-02).
|
|
- **`per-service/`** — the **render contract** (`update` block → `ImageRepository`/`ImagePolicy`/setter
|
|
marker) plus a worked example (`pihole.example.yaml`). **Reference only, not deployed** — WS-GITOPS
|
|
emits the equivalent next to each real app's Deployment/HelmRelease under `apps/`.
|
|
|
|
## Controllers (how the CRDs get here)
|
|
|
|
The two controllers — **image-reflector-controller** + **image-automation-controller** — are installed
|
|
as part of the `flux-system` install by **`bootstrap/flux/bootstrap.sh`** via
|
|
`flux install --components-extra=image-reflector-controller,image-automation-controller`, exactly like
|
|
the other four Flux controllers. Consequences:
|
|
|
|
- The `image.toolkit.fluxcd.io` CRDs (`ImageRepository`/`ImagePolicy`/`ImageUpdateAutomation`) **exist
|
|
before the first reconcile** — just like `GitRepository`/`Kustomization` do.
|
|
- So the `ImageUpdateAutomation` CR here needs **no** F-MON1 controllers/configs split: F-MON1 bites
|
|
only when a CR is applied in the *same* Flux pass that installs its CRD (via a HelmRelease). Here the
|
|
CRD is a bootstrap artifact, so the CR applies cleanly from the `platform` Kustomization. (The
|
|
per-service CRs likewise ride with `apps/`, which `dependsOn platform`.)
|
|
|
|
## Scope boundary (contract)
|
|
|
|
- **This dir:** the cluster-wide automation + git write-back settings.
|
|
- **`apps/<svc>/`:** per-service `ImageRepository` / `ImagePolicy` (semver stable range) + the Deployment
|
|
setter marker — rendered from each ServiceDefinition's `update` block (see `per-service/README.md`).
|
|
- **Renovate (`/renovate.json`):** Helm **chart** / dependency / operator-manifest bumps (opens PRs,
|
|
auto-merges allowed classes on green CI). Not this controller's job, and deliberately non-overlapping
|
|
(Renovate's docker/kubernetes image managers are disabled so it never fights Flux over a tag).
|
|
|
|
## Safety rails (acceptance gates — ADR-0009)
|
|
|
|
| Rail | Where enforced |
|
|
|---|---|
|
|
| **Stable-only** (R-UPD-01) | `ImagePolicy` semver range + a `filterTags` pattern that admits only bare `X.Y.Z` (excludes `-rc`/`latest`). |
|
|
| **No human/AI authoring** (R-UPD-02) | image-automation writes the tag at the `$imagepolicy` setter; the commit is machine-authored (`fluxcdbot`). |
|
|
| **Health-gated auto-rollback** (R-UPD-03) | HelmRelease `upgrade.remediation` + `rollback` (preferred), or Deployment `progressDeadlineSeconds` + readiness probe. |
|
|
| **Pin escape hatch** (R-UPD-03) | `update.pin` renders a fixed image with **no** setter marker and no/suspended `ImagePolicy` — automation is frozen. |
|
|
| **Alert on failure** (R-UPD-02 + R-OBS) | `platform/monitoring/alerts.yaml` group `platform-health.automated-updates` (`ImageUpdateAutomationFailing`, `ImagePolicyStalled`, `UpdateHealthGateFailed`). |
|
|
|
|
Proven by the L3 conformance test
|
|
`conformance/tests/l3-fault-injection/hands-off-update/` (auto-advance stable, bad-update
|
|
auto-rollback + alert-wired, pin holds).
|
|
|
|
## Operator hand-off: git write credential (required before enabling)
|
|
|
|
image-automation-controller reuses the **`flux-system` GitRepository's `.spec.secretRef`** for push auth
|
|
(it has no independent secretRef). The Phase-1 source is anonymous/read-only, so before write-back works
|
|
the operator must:
|
|
|
|
1. Create a Gitea access token (scope `write:repository`) for the bot and put it in the SOPS secret
|
|
from the template `bootstrap/sops/secret-templates/gitea-image-automation-credentials.yaml.example`
|
|
→ `platform/image-automation/gitea-image-automation-credentials.sops.yaml` (name
|
|
`gitea-image-automation-credentials`, keys `username`/`password`), then `sops --encrypt --in-place`.
|
|
2. Add that file to this dir's `kustomization.yaml` `resources:`.
|
|
3. Attach it to the source: add `spec.secretRef.name: gitea-image-automation-credentials` to the
|
|
`flux-system` GitRepository via `bootstrap/flux/patches/flux-system-source-url.yaml`.
|
|
|
|
## Enabling (deferred — like velero/oauth2-proxy)
|
|
|
|
`image-automation` is intentionally **commented out** in `platform/kustomization.yaml`. Enable it only
|
|
after the write credential above is provisioned — otherwise the `ImageUpdateAutomation` reconcile errors
|
|
on push and, under the unified graph's `platform` wait, could stall the downstream graph. Uncomment the
|
|
`- image-automation` line once the hand-off is done.
|