Map a ServiceDefinition `update` block to ImageRepository/ImagePolicy + the $imagepolicy setter marker: stable-only (semver range + bare-X.Y.Z filterTags, pre-release excluded, R-UPD-01), pin -> fixed image with no marker / suspended policy (R-UPD-03). Worked pihole example under per-service/ (reference only, not deployed; WS-GITOPS emits the equivalent into apps/<svc>/). Add renovate.json for Helm chart / dependency / operator-manifest bumps: auto-merge minor/patch/digest on green CI, major needs review, image managers disabled so Renovate never fights Flux over a tag. Co-Authored-By: Claude Opus 4.8 <[email protected]>
3.7 KiB
Per-service image-automation — the render contract (update block → Flux CRs)
Owner: WS-UPDATE (this contract) + WS-GITOPS (emits these next to each app's Deployment/HelmRelease
under apps/). Serves: R-UPD-01 (stable-only), R-UPD-02 (no human/AI authoring), R-UPD-03
(pin / auto-rollback). Source of truth: renderer-contract.md §update,
ADR-0009,
docs/11.
These files are reference examples, deliberately not wired into any
kustomization.yaml, so they never deploy. WS-GITOPS renders the equivalent per real container service intoapps/<svc>/. The cluster-wide write-back engine that consumes the setter markers is../image-update-automation.yaml.
What a container kind ServiceDefinition update block renders
For every kind: container service whose update.pin is unset, render THREE things:
update field |
Renders to | Notes |
|---|---|---|
(implicit — source.image) |
ImageRepository |
scans the registry repo on interval. |
channel: stable + range |
ImagePolicy .spec.policy.semver.range |
stable-only: the range plus a filter that excludes pre-release tags (-alpha/-rc/…). |
cadence |
ImageRepository.spec.interval (scan schedule) |
the "on a schedule" of R-UPD-02. Cron cadence → nearest Flux duration (Flux scans on a duration, not cron). |
| (image ref in the Deployment) | a setter marker comment on the image: field |
# {"$imagepolicy": "<ns>:<name>"} — where image-automation writes the advanced tag. |
autoRollbackOnFailedHealth: true |
delivery-side health gate + rollback | see below — HelmRelease remediation, or Deployment progressDeadline + revert. |
pin: <version> |
freeze | render the image image: pinned to <version> with NO setter marker, and suspend (or omit) the ImagePolicy. Automation cannot advance a pinned service (R-UPD-03). |
kind: vm services do not render these — they update in-guest (dnf-automatic/unattended-upgrades on
cadence, cloud-init), per docs/11 §3. See catalog/vm-demo.yaml.
Stable-only (R-UPD-01) — the two guardrails
- semver range (
.spec.policy.semver.range) — only versions matching the operator'srange. - pre-release exclusion — either a
filterTagspattern that only admitsX.Y.Z(no-suffix), or rely on semver's rule that pre-releases (1.2.3-rc1) sort below their release and are only selected if the range explicitly asks for them. We do BOTH (belt-and-braces) so an upstreamlatestor-rctag can never be picked.
Auto-rollback (R-UPD-03) — where the health gate lives
The setter write only changes a tag in git; the rollback is enforced at delivery:
- HelmRelease-delivered services (preferred): Flux
HelmRelease.spec.upgrade.remediation.retries.spec.rollback— a failed, health-gated upgrade auto-rolls-back to the last-good Helm revision. The image tag lives in the HelmReleasevalueswith the setter marker, so a bad advance is caught and reverted by Flux with no human step. This is the mechanism the conformance test exercises.
- Plain-Deployment services: the Deployment
progressDeadlineSeconds+ readiness probe make a bad image a stalled (never-Available) rollout; the old ReplicaSet keeps serving, and theUpdateHealthGateFailedalert fires (seeplatform/monitoring/alerts.yaml). Recovery is the next good advance or the operatorpinescape hatch.
Either way a failed/rolled-back update alerts by email (R-UPD-02 + R-OBS) via the
automated-updates PrometheusRule group.