mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-05 19:47:37 +09:00
v1.6.0
5099
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
764e30418d |
chore(release): promote v1.6.0-rc.4 -> v1.6.0 (#3425)
Promotes `v1.6.0-rc.4` to stable `v1.6.0` — no rebuild. The tree pins the rc's images by digest. On merge, pull-requests-release.yaml creates the write-once `v1.6.0` tag at the merge commit, retags those digests to `v1.6.0` (+`:latest` when this is the newest stable), publishes the stable cozy-installer chart, and publishes the release — so the stable artifacts are bit-for-bit the rc that passed e2e. The `full-e2e` label forces the full suite on this PR. Do NOT squash-merge (decision B): the stable tag must attach to a real merge commit.v1.6.0 api/apps/v1alpha1/v1.6.0 |
||
|
|
d3815a4ce6 |
Merge branch 'main' into release-1.6.0
Signed-off-by: myasnikovdaniil <60174387+myasnikovdaniil@users.noreply.github.com> |
||
|
|
235d38dae1 |
Prepare release v1.6.0 (promoted from v1.6.0-rc.4)
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com> |
||
|
|
706c3f06fc |
fix(release): reuse only an OPEN promote PR, not one in any state (#3424)
## What this PR does The promote step guarded PR creation with `gh pr view <branch>`, which resolves a pull request by head branch **in any state**. A closed one satisfied the guard, so creation was skipped while the step still exited 0 — a promotion that reports success and leaves nothing to merge. Same silent-skip shape as the enumeration bug fixed in #3404: the run is green, the artifact is missing, and nothing says so. `STABLE_BRANCH` is derived from the stable version (`release-${version}`), not from the rc number, so every rc promoted to that version shares it. Once any promote PR for a version has been closed, both re-dispatch paths this workflow documents as supported are wedged: re-dispatching the same rc, and promoting a newer rc to the same version. That second one is named in the workflow's own header as the reason a leftover draft is tolerated — the tolerance was implemented for the release draft and missed for the PR. Neither path recovers by reopening the old PR, because the step's own `git checkout -B` plus force-push makes that PR's head unreachable, and GitHub then refuses: ``` 422 Validation Failed state cannot be changed. The release-1.6.0 branch was force-pushed or recreated. ``` The fix asks the question the guard meant to ask — is there an **open** PR for this head — and creates one otherwise. **Hit live.** Promoting `v1.6.0-rc.4` after #3397 had been closed: [run 29917852639](https://github.com/cozystack/cozystack/actions/runs/29917852639) went green, logged `PR already open for release-1.6.0`, and opened no PR. This currently blocks the v1.6.0 release. **Verified in both directions** against live data, so the change is not vacuous: | head branch | PR state | old guard | new guard | | --- | --- | --- | --- | | `release-1.6.0` | #3397 closed | `TRUE` → skip (bug) | `FALSE` → create ✅ | | `chore/gitignore` | #3412 open | `TRUE` → skip | `TRUE` → skip ✅ | `actionlint` exits 0 and `zizmor` reports no findings. The guard reads `${STABLE_BRANCH}` as a shell variable from the step's `env:` block rather than as a `${{ }}` expansion inside `run:`, so no expression-injection surface is added. ### Screenshots N/A — no UI change. ### Downstream repositories Walked the trigger map in `docs/agents/contributing.md` against the diff, which is one file: `.github/workflows/promote-rc.yaml`. The only workflow-related trigger in the map is cozystack/ccp on "change release-prep behaviour in `.github/workflows/tags.yaml`", which this does not touch. This change also restores the documented behaviour of `promote-rc.yaml` rather than altering its contract, so nothing downstream sees a result different from what the docs already promise. - [x] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: ### Release note ```release-note fix(release): promoting a release candidate now opens the promotion pull request even when an earlier promotion attempt for the same version was abandoned. Previously the workflow mistook a closed pull request for an open one, skipped creating a new one, and reported success with nothing left to merge. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved release PR creation checks so closed or previously merged pull requests no longer prevent new release PRs from being opened. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
55195ded0c |
test(e2e): capture previous container logs and stop killing the snapshot (#3346)
## What this PR does Root-causing a recent E2E failure was nearly blocked because the diagnostics captured on failure had collected almost nothing. Two independent mechanisms were responsible, and both had been silently in place for a long time. **The failing container's previous logs were never captured.** The pod had crash-looped, so the decisive evidence lived in the first container instance, reachable only through `kubectl logs --previous`. Chainsaw's `podLogs` action has no `previous` field, so this could not be expressed per-suite; the conclusion had to be reconstructed from indirect evidence instead. A new capture runs from the global catch and from the BATS failure hook, selecting containers at runtime by `restartCount > 0` across both init and regular containers, so a healthy run stays quiet and future suites are covered without per-suite wiring. **The catch was structurally guaranteed to be killed.** A Chainsaw `script` op is bounded by `timeouts.exec`, which is 5m, while the catch's own inner budgets summed to roughly 960s. It reported `SCRIPT | ERROR | signal: killed` and produced nothing — and its second leg, the data-plane capture, had therefore never run at all. The kill signature was itself misleading: `timeout` exits 124 and cannot produce that message, which comes from Go's `exec.Cmd` killing the process group. Each catch op now carries an explicit timeout above the budgets it contains, so the inner one fires and leaves a partial, usable artifact instead of losing everything. **The snapshot was truncated at 60 seconds.** `crust-gather --duration` defaults to 60s and was never passed, so every snapshot ever taken was cut short, with nothing in the artifact saying so. The API discovery it performs first is not covered by that budget at all, which is where the outer wall-clock was going on a cluster with a degraded aggregated APIService — precisely the state a failed run is in. Both budgets are now set explicitly, and the collector's output goes to a log inside the snapshot with a completeness verdict, so "is this snapshot complete?" is answerable from the artifact. Failure paths are reported rather than swallowed: an unreachable API says so instead of implying nothing had restarted, a truncated capture is kept and marked, and every dropped item is logged, since silent truncation reads as full coverage. ### What is not verified here The mechanism claims are read from the pinned versions of Chainsaw and crust-gather. Everything cluster-facing is not verified by this PR: the go-template's output shape against a live API server, `--previous` returning the first instance for a real crash-looper, and whether the new collection budgets suffice on a degraded cluster. The bats suite covers the selection helpers only, and those cases are mutation-tested. ### Release note ```release-note NONE ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Diagnostics** * Improved end-to-end failure evidence by capturing “previous instance” logs for crash-looping pods (including restarted init containers) and prioritizing relevant namespaces. * Added time-bounded diagnostics collection with explicit outer timeouts; collector output is saved to per-snapshot log files. * Classifies collector outcomes as complete, timeout-truncated, or failed, while preserving the original test failure signal. * **Documentation** * Expanded E2E failure-diagnostics guidance with new timeout and evidence-capture conventions. * **Tests** * Added unit-style tests for previous-log selection, prioritization, row limiting, and deterministic log artifact naming. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
baf0158d81 |
fix(release): reuse only an OPEN promote PR, not one in any state
The promote step guarded PR creation with `gh pr view <branch>`, which resolves a pull request by head branch in any state. A closed one satisfied the guard, so creation was skipped while the step still exited 0 — a promotion that reports success and leaves nothing to merge, the same silent-skip shape as the enumeration bug fixed in #3404. STABLE_BRANCH is derived from the stable version, so every rc promoted to that version shares it. Both re-dispatch paths this workflow documents as supported are therefore wedged once any promote PR for that version has been closed: re-dispatching the same rc, and promoting a newer rc to the same version. Neither recovers by reopening the old PR, because the step's own force-push makes its head unreachable and GitHub refuses with "state cannot be changed. The <branch> branch was force-pushed or recreated." Hit live while promoting v1.6.0-rc.4 after #3397 had been closed: the run went green and opened no PR. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
d5806eb1a0 |
feat(kubernetes-nodes): split worker node pool into its own app (Phase 2, additive) (#3314)
## What this PR does Phase 2 of the Kubernetes-app split ([cozystack/community#8](https://github.com/cozystack/community/pull/8)) — the **additive** half. Introduces `kubernetes-nodes`: a per-pool application that renders the worker node-pool objects (`KubevirtMachineTemplate`, `MachineDeployment`, `MachineHealthCheck`, worker `WorkloadMonitor`, and the `talos-reconcile` Job + RBAC) extracted from the monolithic `kubernetes` chart. A `KubernetesNodes` CR named `<cluster>-<pool>` attaches to a parent `Kubernetes` cluster in the same namespace by name convention (`kubernetes-<cluster>`), mirroring the `vm-instance`/`vm-disk` precedent. This PR is additive: the parent `kubernetes` chart gets exactly one scoping fix (its old-KMT preservation block is limited to KMTs owned by its own Helm release) so that attaching a `kubernetes-nodes` pool to a live cluster cannot break the parent's next upgrade. Existing clusters keep provisioning workers via `nodeGroups`. Removing `nodeGroups` from the parent and adopting existing pools into `kubernetes-nodes` releases is a follow-up **breaking** PR (Phase 2b). Key properties: - Pool objects render **byte-identical** to the parent for the same group (golden-parity check in `tests/render-parity.sh`), so the follow-up adoption never churns live worker VMs — the content-hashed `KubevirtMachineTemplate` name is preserved. - cluster-autoscaler (kept in the parent chart) discovers the pool's `MachineDeployment`s cross-release via the unchanged `cluster.x-k8s.io/cluster-name` label, namespace, and min/max annotations. - Registered as a dedicated `cozystack.kubernetes-nodes-application` PackageSource + `kubernetes-nodes-rd` ApplicationDefinition, wired into the iaas bundle (mirrors `vm-instance`/`vm-disk`). ### Downstream repositories Left unticked deliberately — this is a draft. A new user-facing `KubernetesNodes` API is added, so cozystack/website (docs) and cozystack/terraform-provider-cozystack likely need follow-ups; the trigger-map walk will be completed before this leaves draft. - [ ] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: ```release-note Add the `kubernetes-nodes` application: manage the worker node pools of a managed Kubernetes cluster as independent resources, one HelmRelease per pool. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a new Kubernetes worker node pool offering with configurable sizing (CPU/memory/instance, disk), roles, storage class, GPUs, and Talos worker image settings. * Introduced a dedicated Helm package, dashboard/resource-map access, and Flux application wiring for installing/managing pools. * **Bug Fixes** * Improved safeguards against pool naming/ownership conflicts and tightened upgrade behavior for reusing previously created templates. * **Documentation** * Added full node pool configuration docs and operational notes (including supported Kubernetes versions and reconciliation expectations). * **Tests** * Added node pool rendering/validation tests plus golden parity checks between chart layouts. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
284a17ba1f |
fix(release): unblock the v1.6.0 promote on the rc.4 staging tree
Promoting this branch fails in two independent ways, both landing after finalize has already created the write-once stable tag, published the GitHub release and moved :latest. chBackupClientImage had no producer and froze at v1.4.0-rc.2 while .migrations.image advanced to v1.6.0-rc.4, leaving platform-migrations pinned at two digests. promote-retag.sh copies every collected <repo>@<digest> to <repo>:<stable>, so both compete for one destination tag: the lower-sorting v1.4.0-rc.2 digest wins it and the second copy trips the write-once guard, aborting the retag with five repositories still untagged and the installer chart unpublished. Align it to the digest this branch already carries for the migrations image. Both come from the same tree, so the runner keeps the curl, jq and ca-certificates the Altinity strategy Pod needs, and that Pod overrides the ENTRYPOINT with its own command. promote-rc.yaml on main now calls hack/promote-rewrite-tags.sh, which does not exist here, so the dispatch would fail outright. Vendor it and hack/lib/image-refs.sh verbatim from main (identical blobs). That also fixes the rewrite's coverage: the old inline glob enumerated 155 files and missed 10 rc-bearing ones, which is why the earlier promote left 11 references reading v1.6.0-rc.4. The shared enumeration covers all 168 and fails the promotion if any rc reference survives. Both fixes are on main via #3405 and #3404; this carries them onto the rc.4 staging tree so v1.6.0 ships the bytes rc.4 tested. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
d0c0f399f8 |
fix(ci): cover every image-ref storage shape in promote, retag and mirror (#3404)
## The bug Promotion rewrites the rc version substring to the stable version across the vendored image references. That rewrite lived inline in `promote-rc.yaml` and globbed the depth-2 package `values.yaml` plus `packages/apps/kubernetes/images/*.tag` **only**, on the premise that the kubernetes app was the only one whose `.tag` files carry the cozystack version. Nine other `.tag` files do, and `system/multus` seds its reference straight into a vendored upstream daemonset manifest. #3397 (promote `v1.6.0-rc.4` → `v1.6.0`) is therefore staged with **33 of 55 first-party references still reading `v1.6.0-rc.4`**. `hack/promote-retag.sh` and `hack/nightly-mirror.sh` had the same blind spot from the other direction — both scanned the depth-2 `values.yaml` alone, so those images were never retagged to the stable version (**30 references selected before, 42 after**) and never mirrored to the public registry for a nightly. A retag miss leaves an image short of a tag, since the digest still resolves inside one registry. A mirror miss is worse: the host rewrite walks the same file list, so a reference that is neither mirrored nor rewritten leaves the published tree pointing at the private build registry. `v1.6.0` is the first release to go through promotion — every earlier release was a full rebuild from the tag, which stamped every file uniformly. The glob has always been wrong; the old path just made it unobservable. ## Why it could not be caught before The rewrite was workflow-inline, so there was nothing to unit test. The only way to observe the miss was to cut a release. It is a script now — `hack/promote-rewrite-tags.sh` — and `hack/promote-rewrite-tags_test.bats` round-trips it against the real tree under `make unit-tests`, with no cluster, registry or release involved. ## The fix The three call sites each carried their own idea of where a reference can live, which is how they drifted apart. That knowledge moves into `hack/lib/image-refs.sh` as the single enumeration — `image_ref_files()` for the files, `collect_image_refs()` for the references inside them — and all three source it, so a new storage location is declared once and reaches every consumer. The rewrite also asserts its own postcondition: it scans **wider** than it rewrites and fails the promotion when a reference survives in a location the enumeration does not know about, rather than shipping it quietly. The wide scan is shape-filtered to image-reference position, because `X.Y.Z-rc.N` is an ordinary version string that other things legitimately contain — `kubevirt-csi-driver/go.sum` pins `github.com/golang/protobuf v1.4.0-rc.2`, and `v1.4.0-rc.2` is a cozystack rc that was actually cut. Narrowing it to `${RC}@sha256:` instead would have been wrong: `system/cilium/values.yaml` keeps `tag:` and `digest:` under separate keys. `docs/agents/image-refs.md` records the contract — three tag classes, three storage shapes, the invariants, and which consumer does what. ## Not a bug: kamaji `cluster-api-control-plane-provider-kamaji` stays at `v0.19.0-cozystack.0` across the promotion. It is a first-party rebuild of an upstream component, versioned by that component rather than by the cozystack version line, so no rewrite is owed. A test pins this so a future fix here cannot over-reach. ## Documented known gaps, deliberately not fixed here Both are pre-existing and in the **host** dimension; the version dimension this PR owns is complete, because the version always lives in a `tag` key regardless of where the host sits. - **The nightly host rewrite cannot reach a split host.** It is a literal `<src-registry>/` substring replace, so `keycloak-operator` (host in a sibling `registry:` key) and `kubeovn` (`global.registry.address`, no trailing slash) are mirrored but keep pointing at the build registry in the published tree. Fixing it needs structure-aware rewriting. - **A reference inside a gzip is invisible.** `capi-providers-cpprovider` ships `files/components.gz`, and the promote postcondition cannot catch this class at all since `grep -rIl` skips binaries. Nothing is owed today — kamaji is component-versioned and the image is still mirrored and retagged via its `.tag` — so the residual is that a nightly's kamaji ConfigMap keeps the build-registry host. ## Testing - 17 tests in the new suite; `make bats-unit-tests` green (312 tests, exit 0); `shellcheck -x` clean; `zizmor` clean on the workflow. - Verified against four real rc versions (`1.4.0-rc.2`, `1.4.0-rc.4`, `1.5.0-rc.2`, `1.6.0-rc.4`) in both directions — no false positives, no false negatives. - Every test is mutation-proven. Reverting the enumeration to the old glob, deleting any of the five YAML shapes, corrupting emitted digests, reverting extras to parse-only, dropping the unreadable-file guard, broadening shape 4 beyond `global.images`, or planting an unenumerated first-party reference in any storage shape each fail a named test. ## Reviewer note, out of scope `platform-migrations` is pinned at two different digests — `core/platform/values.yaml` at `v1.5.0` and `backupstrategy-controller/values.yaml` at `v1.4.0-rc.2` (an unowned pin; no Makefile writes that key, tracked in #3143). A `promote-retag.sh` dry-run emits **two copies to the same destination tag**, so the write-once guard will fail the promotion. This is independent of this PR and needs a decision about the backup-client image version, so it is left alone here — but it blocks the 1.6.0 promote and wants fixing before the re-cut. ## Release note ```release-note Promotion, retagging and nightly mirroring now cover every image reference the build stamps, including those stored in `images/*.tag` files and in vendored manifests. Previously a promoted release could ship images still tagged with the release-candidate version, and those images received no stable tag in the registry. ``` Fixes the promote half of #3143. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Image promotion and nightly mirroring now detect digest-pinned image references across values files, standalone tag files, and supported templates. - Stable release preparation supports RC→stable tag rewriting via a dedicated promotion script, with broader verification to ensure no RC references remain. - **Bug Fixes** - Improved completeness of ref discovery and scanning, including previously missed template/tag locations. - **Documentation** - Added and linked guidance documenting image-reference invariants and how tooling enumerates supported storage shapes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
bbfe9f9a9d |
fix(backupstrategy-controller): give chBackupClientImage a producer (#3405)
## This is a workaround `backupstrategy-controller` should not be running the platform **migrations** image. This PR does not fix that — it makes the existing reuse *safe* so the v1.6.0 promote can proceed, and documents in the tree exactly what is wrong and what should replace it. Treat it as a stopgap with a TODO, not as an endorsement of the design. ## What is actually wrong The Altinity strategy Pod drives clickhouse-backup's HTTP API. Its script uses exactly four binaries — `curl`, `jq`, `sleep`, `date`. For that it currently pulls `platform-migrations`: | | | | --- | --- | | Size | **349 MB** (a dedicated `alpine + curl + jq + ca-certificates` measures **23 MB**) | | Also contains | `kubectl`, `helm`, `git`, `cozyhr`, the `etcd-migrate` binary, every migration script, vendored etcd CRDs | | `ENTRYPOINT` | `run-migrations.sh` | Two consequences worth naming. A tenant-adjacent backup Pod carries cluster tooling it has no use for. And because the image's own entrypoint is the migration runner, the `command:` override in the strategy template is load-bearing — drop that one line and the Pod runs platform migrations with `kubectl` and `helm` already on the PATH. It also creates the coupling that caused the bug this PR fixes: bumping `ETCD_OPERATOR_VERSION` or editing any migration script re-digests the image, forcing the backup client's pin to move for reasons that have nothing to do with backups. ## The release blocker `platform-migrations` was pinned at two different digests in the committed tree: | Location | Pin | Producer | | --- | --- | --- | | `packages/core/platform/values.yaml` `.migrations.image` | `v1.5.0@sha256:8bf61f17…` | `packages/core/platform/Makefile` | | `backupstrategy-controller/values.yaml` `.chBackupClientImage` | `v1.4.0-rc.2@sha256:17390197…` | **none** | Promotion retags **by digest**: `hack/promote-retag.sh` copies every collected `<repo>@<digest>` to `<repo>:<stable-version>`. Two digests under one repository produce two copies competing for the same destination tag — the first wins, the second hits the write-once guard, and the promotion fails. A dry run against `main` emits exactly that pair: ``` ▸ ghcr.io/cozystack/cozystack/platform-migrations sha256:17390197… → docker://ghcr.io/cozystack/cozystack/platform-migrations:v9.9.9 ▸ ghcr.io/cozystack/cozystack/platform-migrations sha256:8bf61f17… → docker://ghcr.io/cozystack/cozystack/platform-migrations:v9.9.9 ``` `v1.6.0` is the first release cut through promotion rather than a full rebuild. Every earlier release ran `make build`, which restamped both copies as a side effect and hid the missing producer. ## What this PR does `packages/core/platform/Makefile` now stamps both keys with the same ref, so they move in lockstep by construction rather than by instruction, and the committed value is aligned to the digest `core/platform` already carried. Moving the runner forward two minors is safe: it is used only as a curl + jq container, and the image built at `v1.5.0` carries them (`git show v1.5.0:…/migrations/Dockerfile` → `apk add … jq ca-certificates bash curl`). ## What should replace it A dedicated `ch-backup-client` image built by **this package's own Makefile** — measured at 23 MB, 15× smaller, with none of the cluster tooling and none of the entrypoint hazard. That also deletes the cross-package stamp this PR adds, because the pin would be produced by the package that owns it. The objection that has kept this reuse in place — "a second tag release CI would have to re-digest each cut" — does not survive contact with the actual defect. Every other first-party image already pays that cost, automatically. What broke here was not an extra tag; it was **a pin with no producer**, which a package-owned image cannot reproduce. I am deliberately not doing that here: it means a new image, a new build target and a new digest introduced during a release cut. It belongs in a follow-up. ## The guard `hack/image-pin-consistency.bats` asserts the general invariant — no repository pinned at more than one digest — driven through the real promotion selector so it tracks whatever set promotion actually acts on, rather than a reimplementation that could drift from it. Plus a specific lockstep assertion so a recurrence names its cause. This check could not have been added before now: it fails on the very duplicate this PR removes. ## Testing - `hack/image-pin-consistency.bats` — both tests pass; verified they fail when the drifted pin is restored. - `make bats-unit-tests` green (294 tests, exit 0). - `make -C packages/system/backupstrategy-controller test` — 11 tests, 3 suites, green. - `helm template … --set backupStorage.bucketNameOverride=test-bucket` renders the strategy Pod with the new ref. - The Makefile's `yq` step was simulated directly to confirm the relative path and key path resolve and that comments survive the in-place edit. - Merged locally with #3404 onto `main` and re-run: the expanded ref collector in that PR surfaces no additional duplicate pins (314 tests, exit 0). ## Relationship to other work Relates to #3143 — this is one instance of its unowned-pin class, the one its discussion flagged as having no producer. The four never-built packages there remain untouched. Independent of #3404 (promote/retag/mirror storage-shape coverage); the two touch no common files and can merge in either order. Both are needed before the `v1.6.0` promote is re-cut. ## Release note ```release-note Fixed a release-blocking duplicate pin: the ClickHouse backup client image is now stamped from the same source as the platform migrations image it reuses, so both carry one digest. Previously they could drift, which made promotion attempt to retag two digests to the same stable tag and fail. Reusing the migrations image here remains a documented workaround pending a dedicated backup-client image. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Ensured the platform migrations image digest is pinned consistently between the platform and backup strategy components. * Updated the backup strategy component to reference the new pinned `platform-migrations` digest. * **Tests** * Added promotion/retag consistency checks to fail on conflicting source digests for the same stable tag. * Added drift detection to ensure `platform-migrations` image references match across consumer configuration files. * **Documentation** * Expanded in-chart comments and values documentation clarifying the coupling and workaround behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
8ea589230f |
test(mariadb): add MariaDb backup/restore e2e test (#3384)
## What Reworks the never-merged [`backup-mariadb-e2e`](https://github.com/cozystack/cozystack/tree/backup-mariadb-e2e) branch onto the modern Chainsaw e2e approach ([`docs/agents/e2e-testing.md`](https://github.com/cozystack/cozystack/blob/main/docs/agents/e2e-testing.md)), rebuilt on fresh `main`. That branch bundled the whole MariaDB backup-strategy controller **plus** a `hack/e2e-apps/backup-mariadb.bats` test. The controller and the `examples/backups/mariadb/` manifests have since landed on `main` independently (`feat(platform): implement backup strategy for mariadb`), and the `hack/e2e-apps/*.bats` app suite has been replaced by `hack/e2e-chainsaw/`. So the only piece still missing was the e2e coverage — this PR delivers it the modern way. ## Changes - **`examples/backups/mariadb/` → executable harness.** Converts the demo from static numbered YAML into the same script harness the other backup demos use (`00-helpers.sh`, `run-all.sh`, `cleanup.sh`), keeping the numbered manifests as the human-readable flow that `run-all.sh` drives — so the documented flow and the automated test can never drift. `run-all.sh` provisions a `Bucket`, derives the two per-app Secrets the strategy references from `BucketInfo` + the seaweedfs CA, deploys a source MariaDB with a sentinel row, fires an ad-hoc `BackupJob`, restores to a copy with a to-copy `RestoreJob`, and asserts the sentinel round-tripped through S3 into the copy while the source is left untouched. - **`hack/e2e-chainsaw/mariadb/` → second Chainsaw Test** (`mariadb-2-backup-roundtrip`) that drives `run-all.sh` + `cleanup.sh`, following the merged `etcd-2-backup-roundtrip` harness-driving shape: runs in `tenant-root` (the isolated e2e tenant cannot reach `tenant-root`'s seaweedfs across the Cilium egress policy), `skipDelete` since the harness owns the resources, an idempotent pre-clean step so a run killed before `finally` cannot leave a stale `Succeeded` BackupJob that false-passes, fail-fast on terminal `BackupJob`/`RestoreJob` phases, and scoped `catch` diagnostics. Cleanup also prunes the operator-side `k8s.mariadb.com` `Backup`/`Restore` CRs the Cozystack jobs own only by label. - **`hack/select-e2e.sh` → TIA rule for `examples/backups/<app>/`.** The harness lives outside the suite dir, so without this a harness-only diff produced an empty selection, which `pull-requests.yaml` turns into "skip the whole E2E stage" — a future bug-fix to `run-all.sh` would merge with its own round-trip test never run. The new rule maps `examples/backups/<app>/` to the `<app>` suite that drives it (dir name == suite name for every backup demo except `vmi` → `vminstance`), with three cases added to `hack/select-e2e_test.bats`. ## Conventions followed - No retries on deterministic steps; the single `script` step carries a justified 30m budget (2 MariaDB instances × 310s first-boot startup probe + dump/restore) and fails fast rather than polling out. - Self-contained trap-based temp-file cleanup only inside a single step; no test-level EXIT trap. - Controller-created artifacts Chainsaw cannot reclaim are pruned explicitly by `cleanup.sh`. - TIA verified: `examples/backups/mariadb/run-all.sh` now selects the `mariadb` suite (`hack/select-e2e.sh`), and editing `select-e2e.sh` itself escalates this PR to the full suite. ## Downstream Repositories Walked the trigger map in `docs/agents/contributing.md` against the diff. The change touches only e2e test material, backup examples, and CI suite-selection — no package added/renamed/removed, no `values.schema.json` / `values.yaml` / platform-values / variant / `ApplicationDefinition` / namespace / release-asset changes, and no `hack/` file moved/renamed. No downstream repository is reached. - [x] `cozystack/website` — not affected (no package or platform/docs-source change) - [x] `cozystack/terraform-provider-cozystack` — not affected (no app/schema/enum/default/prefix change) - [x] `cozystack/ansible-cozystack` — not affected (no installer values / namespace / variant / node-prereq change) - [x] `cozystack/ccp` — not affected (no `hack/` move-or-rename, no make-target behavior change) ## Notes No `make generate` needed — the change touches only `examples/`, `hack/`, and a Chainsaw suite (the edited README is under `examples/`, not a package). ## Release note ```release-note Add an end-to-end Chainsaw test for the MariaDB backup/restore round-trip, and convert the `examples/backups/mariadb/` demo into an executable `run-all.sh`/`cleanup.sh` harness. ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an end-to-end MariaDB backup and restore demonstration workflow. * Added automated backup round-trip validation, including data integrity checks. * Added reusable scripts for running the demo and cleaning up created resources. * Added automatic handling of backup credentials, endpoints, and trusted certificates. * **Documentation** * Expanded setup, configuration, execution, cleanup, and troubleshooting guidance. * Documented same-namespace restore support and cross-tenant limitations. * **Tests** * Added automated Chainsaw coverage for the MariaDB backup and restore workflow. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
efa4e41e9c |
feat(etcd-operator): bump to v0.5.3 (S3 checksum fix for non-AWS backends) (#3403)
## What this PR does Bumps the etcd-operator to **v0.5.3**, which carries the S3 request-checksum fix ([cozystack/etcd-operator#342](https://github.com/cozystack/etcd-operator/pull/342)). `git compare v0.5.2...v0.5.3` is exactly #340, #341, #342. **Why:** scheduled etcd snapshots to non-AWS S3-compatible backends (**Ceph RGW** confirmed) fail at upload. Since early 2025 `aws-sdk-go-v2` defaults `RequestChecksumCalculation` to `WhenSupported`, which stamps a CRC32 on every upload; over HTTPS a multipart part rides it as `x-amz-content-sha256: STREAMING-UNSIGNED-PAYLOAD-TRAILER`, and RGW rejects it with `400 InvalidArgument`. v0.5.3 sets `WhenRequired` on both the S3 client and the transfer manager, so multipart uploads (>5 MiB — every real etcd snapshot) carry no checksum trailer. **Changes:** - `system/etcd-operator` — `appVersion` v0.5.2 → v0.5.3 (image tag follows AppVersion; `values.yaml` keeps `tag: ""`), bump `ETCD_OPERATOR_REF`, update deployment unittest expectations. - `system/etcd-operator-crds` — re-vendor CRDs from v0.5.3. `etcdmembers` gains the additive #340 `status.version` field + **Running** printer column and picks up the `peerAutoTLS` spec field the vendored copy was already missing (all additive, backward-compatible; `etcdclusters`/`etcdsnapshots` unchanged). Fixes the stale `make update` (upstream has no `config/crd` kustomization — CRDs live in `charts/etcd-operator/crd-bases`) and makes it fail loudly on a fetch error (temp file under `set -e`, not a pipe). **Verification:** `helm unittest` 18/18 and `helm template` green on both packages; regenerated CRDs are byte-identical to upstream `crd-bases` at v0.5.3. End-to-end on a live Ceph RGW backend (freedom-portal-stage): the v0.5.2 (client-only) agent fails a multipart snapshot upload with the 400; v0.5.3 uploads an 11 MiB multipart snapshot successfully, and the operator-driven CronJob→EtcdSnapshot path reaches `Complete`. ### Downstream repositories Walked the trigger map in `docs/agents/contributing.md` file-by-file against the diff (`packages/system/etcd-operator{,-crds}/**` only — a component version bump + additive CRD re-vendor). Nothing matches: not an `apps/`/`extra/` package add/rename/remove, no `core/platform` values, no variant/bundle, no platform component add/remove, no `hack/` layout or shared-tooling change, and the provider does not type the `etcd-operator.cozystack.io` CRDs (and the change is additive/opaque regardless). - [x] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: ### Release note ```release-note fix(etcd-operator): bump to v0.5.3 — etcd snapshot uploads to non-AWS S3-compatible backends (Ceph RGW, some MinIO/R2) no longer fail with `400 InvalidArgument: x-amz-content-sha256 ...`; the snapshot agent now requests a checksum only when required, on both the S3 client and the multipart transfer manager. Also brings observed EtcdMember versions (#340) and `--watch-namespace` (#341). ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added runtime etcd version visibility to `EtcdMember` (including a “Running” status column). * Added `peerAutoTLS` support to `EtcdMember` for operator-managed peer TLS behavior. * **Updates** * Updated the etcd-operator to version 0.5.3. * Refreshed bundled CRDs and ensured CRDs are preserved during Helm lifecycle operations. * **Tests** * Updated deployment test expectations to use the 0.5.3 operator image. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
a302bd0ddc |
docs(backupstrategy-controller): reframe the migrations-image reuse as a workaround
Running the platform migrations image as the ClickHouse backup client is not a design decision worth defending, and the in-tree comments read as though it were. Reframe them. The strategy script uses exactly curl, jq, sleep and date. For that it pulls a 349 MB image carrying kubectl, helm, git, cozyhr, the etcd-migrate binary and every migration script into a tenant-adjacent Pod, and that image's ENTRYPOINT is run-migrations.sh -- so the `command:` override in the strategy template is load-bearing rather than incidental. A dedicated alpine + curl + jq + ca-certificates image measures 23 MB. The standing objection to a dedicated image -- "a second tag release CI would have to re-digest each cut" -- does not survive contact with the actual defect. Every other first-party image already pays that cost automatically. What broke was a pin with NO producer, which an image owned by its own package cannot reproduce; that fix would also delete the cross-package stamp introduced here. Comment-only. No rendered output changes. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
1f302d0d78 |
docs(agents): correct issue ref, mirror claim and example in image-refs
The build-list violations bullet pointed at #3143, which tracks the adjacent gap (packages in the list going stale between releases) and never covers packages absent from the list; they are now tracked in the newly filed #3416. The nightly split-host paragraph claimed both affected images are mirrored while the published tree points at the build registry — today neither ref carries the source-registry host (keycloak-operator is never rebuilt by CI, kubeovn is built externally by cozystack/kubeovn-chart), so the ownership filter skips them and the gap is latent, not live; reworded to say exactly that. The opening example paired a v1.6.0 tag with the real v1.5.0 grafana digest — a ref that will never exist — and grafana is slated to stop being built; replaced with the literal committed cozystack-api ref. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
4605bbf3db |
test(mariadb): pre-clean the backup roundtrip against stale tenant-root state
Address review feedback on #3384. The TIA rule for
examples/backups/<app>/ that an earlier revision of this commit added
has since landed on main independently (
|
||
|
|
2829f7a300 |
docs(mariadb): address review notes on backup example
- 00-bucket.yaml: correct the Secret-name comment to include the "bucket-" release prefix (bucket-<bucket>-<user>), matching the README and 00-helpers.sh. - README: note that S3_CA_NAMESPACE defaults to tenant-root independently of NAMESPACE, so a non-root tenant run must override it (and S3_CA_SECRET) too. Assisted-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Andrey Kolkov <androndo@gmail.com> |
||
|
|
d1f2822018 |
test(mariadb): add backup/restore round-trip e2e (chainsaw) via example harness
Reworks the never-merged backup-mariadb-e2e branch onto the modern Chainsaw approach (docs/agents/e2e-testing.md). The bats port that branch carried (hack/e2e-apps/backup-mariadb.bats) is dropped — the whole hack/e2e-apps/*.bats app suite has been replaced by hack/e2e-chainsaw/, and the mariadb strategy controller + example manifests it depended on have since landed on main independently. Only the e2e coverage was still missing. Convert examples/backups/mariadb/ from static numbered YAML into the same executable harness the other backup demos use (00-helpers.sh, run-all.sh, cleanup.sh), keeping the numbered manifests as the human-readable flow that run-all.sh drives — so the documented flow and the automated test can never drift. run-all.sh provisions a Bucket, derives the two per-app Secrets the strategy references from BucketInfo + the seaweedfs CA, deploys a source MariaDB with a sentinel row, fires an ad-hoc BackupJob, restores to a copy with a to-copy RestoreJob, and asserts the sentinel round-tripped through S3 into the copy while the source is left untouched. Add a second Chainsaw Test (mariadb-2-backup-roundtrip) to hack/e2e-chainsaw/mariadb/ that drives run-all.sh + cleanup.sh, mirroring postgres-2-backup-roundtrip: runs in tenant-root (the isolated e2e tenant cannot reach tenant-root's seaweedfs across the Cilium egress policy), skipDelete since the harness (not Chainsaw) owns the resources, fail-fast on terminal BackupJob/RestoreJob phases, and scoped catch diagnostics. Cleanup also prunes the operator-side k8s.mariadb.com Backup/Restore CRs the Cozystack jobs own only by label. Assisted-By: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Andrey Kolkov <androndo@gmail.com> |
||
|
|
ae946c7143 |
fix(etcd-operator): make CRD vendoring fail loudly on fetch error
Review of #3403 noted the `curl … | awk > file` recipe swallowed curl failures: the pipe's exit status is awk's, and neither the Makefile nor hack/package.mk sets pipefail, so a 404 / network error / upstream path rename on a future bump would write an empty/truncated CRD template and still exit 0. Fetch to a temp file under `set -e` (curl -f already exits non-zero on HTTP error), then awk from it — shell-agnostic (no pipefail, which dash lacks). Verified: the regenerated templates are byte-identical to the committed ones, and a bad ref now exits non-zero leaving no template files instead of silently writing empties. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Andrey Kolkov <andrey.kolkov@aenix.io> |
||
|
|
b2e1707632 |
fix(kubernetes-nodes): drop dead ephemeralStorage guard, clarify parity scope
The ephemeralStorage guard could never fire (the pool group is built from a fixed set of chart values that never includes ephemeralStorage), so it read as protection that is not there — remove it. Note in the parity script header that only the four pool objects are compared, not the talos-reconcile Job. Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com> |
||
|
|
326dbe0a5b |
test(kubernetes-nodes): cover GPU and kubelet pool shapes in render parity
Extend the golden-parity matrix beyond the resources-sized pool to also diff a GPU pool and a kubelet-override pool against the parent render, so future drift in those branches (which would roll the content-hash-named KubevirtMachineTemplate) is caught in CI. Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com> |
||
|
|
f21365c4f0 |
fix(kubernetes-nodes): address review nits in guard comment and NOTES
Reference the adoption migration by role (Phase 2b) instead of a number that does not exist on this branch, and correct the NOTES operational-note count. Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com> |
||
|
|
9e2f7cc953 |
test(kubernetes-nodes): run render parity in make test
Wire tests/render-parity.sh into the chart's test target so CI verifies the pool objects stay byte-identical to the parent render. Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com> |
||
|
|
3d10a1c431 |
fix(kubernetes-nodes): keep applied TalosConfigTemplate byte-identical to the parent
Restore the inline machineconfig comments (nameservers, GPU node labels) inside the reconcile Job's data block so the applied TalosConfigTemplate matches the parent's, avoiding drift on adoption. Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com> |
||
|
|
73be7032a3 |
fix(kubernetes-nodes): drop unbacked cluster option source
The cluster field referenced x-cozystack-options source 'kubernetes', which has no provider (cozystack-api returns NotFound), so the dashboard dropdown 404s. Drop the annotation so the field is a plain text input; a real provider listing Kubernetes CRs is a follow-up. Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com> |
||
|
|
fe32fd8e58 |
fix(kubernetes-nodes): scope KMT preservation by release and guard foreign pool names
Replace the collision-prone name-prefix match (pool 'gpu' matched sibling 'gpu-large-<hash>') with a release-name annotation scope, and add a render-time guard that fails cleanly when a pool's MachineDeployment already exists under a different release (e.g. naming a pool after a parent nodeGroup such as the default md0). Document the naming caveat in NOTES.txt. Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com> |
||
|
|
1e8d44995c |
fix(kubernetes): scope worker KMT preservation to the release
The preserve-old-KubevirtMachineTemplate block re-emitted any KMT owned by the CAPI Cluster and referenced by a MachineSet. A kubernetes-nodes pool's KMT lives in the same namespace under the same Cluster, so the parent would re-emit it with a foreign meta.helm.sh/release-name and its next upgrade would fail Helm ownership validation. Restrict the block to KMTs whose release-name annotation matches the parent release. Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com> |
||
|
|
e7947dd0e7 |
feat(platform): register kubernetes-nodes application
Add the kubernetes-nodes-rd package (ApplicationDefinition), the cozystack.kubernetes-nodes-application PackageSource (kubevirt variant, kubernetes-nodes + kubernetes-nodes-rd components), and wire it into the iaas bundle. Mirrors the vm-instance/vm-disk registration precedent. Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com> |
||
|
|
277b32306c |
test(kubernetes-nodes): add unit tests and golden-parity check
Unit tests cover naming/selectors, clusterName wiring, autoscaler annotations, omitted replicas, bootstrap TCT reference, KMT hash name, worker WorkloadMonitor, and the empty-cluster / mis-prefixed-release render guards. render-parity.sh diffs the pool objects against the parent kubernetes chart and asserts byte-identity. Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com> |
||
|
|
f842c0a17e |
feat(kubernetes-nodes): generate API types for KubernetesNodes
Generated by cozyvalues-gen + controller-gen from values.yaml. Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com> |
||
|
|
fd14126bea |
feat(kubernetes-nodes): add worker node pool chart
Introduce the kubernetes-nodes application chart: one HelmRelease per worker node pool, attached to a parent Kubernetes cluster by name convention (kubernetes-<cluster>). Renders the KubevirtMachineTemplate, MachineDeployment, MachineHealthCheck, worker WorkloadMonitor and the talos-reconcile Job with its RBAC, extracted from the monolithic kubernetes chart. Pool objects are byte-identical to the parent's render for the same group, so a later adoption never churns live worker VMs. This is the additive half of the Phase 2 split (community#8); the parent kubernetes chart is left unchanged. Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com> |
||
|
|
427ed68761 |
fix(backupstrategy-controller): give chBackupClientImage a producer
platform-migrations was pinned at two different digests: v1.5.0 in packages/core/platform/values.yaml, stamped by its Makefile, and v1.4.0-rc.2 as .backupStrategyController.chBackupClientImage, stamped by nothing at all. The second is the Altinity strategy Pod's curl+jq runner, deliberately reusing the migrations image rather than shipping a second one-binary tag that release CI would have to re-digest every cut. That reuse only holds while the two move together, and the values.yaml comment said so -- but a comment was the only thing enforcing it, so the copy with no producer froze two minors back. This is a release blocker, not just staleness. Promotion retags by digest, copying every collected <repo>@<digest> to <repo>:<stable>. Two digests under one repository produce two copies competing for one destination tag: the first wins and the second hits the write-once guard in hack/promote-retag.sh, which fails the promotion. A dry run against the tree emitted exactly that pair. v1.6.0 is the first release cut through promotion rather than a full rebuild, so it would have been the first to hit it -- every earlier release ran `make build`, which restamped both copies as a side effect and hid the missing producer. packages/core/platform/Makefile now stamps both keys with the same ref, so they are in lockstep by construction rather than by instruction, and the committed value is aligned to the digest core/platform already carried. The image is used only as a curl + jq + ca-certificates runner, so moving it forward two minors changes no behaviour the strategy depends on; it renders and the chart's helm-unittest suite passes. hack/image-pin-consistency.bats adds the general guard -- no repository pinned at more than one digest, driven through the real promotion selector so it tracks whatever set promotion acts on -- plus a specific lockstep assertion so a recurrence names its cause. The general check could not be added before now: it fails on the very duplicate this commit removes. Relates to #3143 (the unowned-pin class) and unblocks the v1.6.0 promote. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
91c076137f |
test(ci): pin shape 3's per-map digest binding
The shape-3 fixture held a single repository/digest pair, so a rule that binds one digest per file and reuses it for every repository satisfied it -- and passed all three suites. That is not a theoretical mutation: against the real linstor values it hands piraeus-server's digest to linstor-csi, which retags and mirrors the wrong image under the right name. Silently shipping wrong bytes is worse than the missing-tag bug this branch started from. The fixture now carries two shape-3 maps with distinct digests in one file and asserts both exact canonical refs, so the digest must be bound per map rather than per file. Assisted-By: Claude <noreply@anthropic.com> Assisted-By: GPT-5 <noreply@openai.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
a16f218875 |
test(ci): correct a stale comment and isolate the fail-open fixtures
Two review nits, both comment- or fixture-level. The completeness oracle's comment still said the per-shape test "matches on the repository substring, so a shape that loses its registry rejoin still satisfies it". That stopped being true in the same commit that introduced it -- the substring match was replaced with an exact canonical-ref assertion, which kills both the shape-2 and shape-3 rejoin mutations. The comment understated coverage. The fail-open test copied the tree once and looped three rc versions over it, so the second and third iterations ran against a tree the first rewrite had already mutated. Each now gets a fresh copy, so each tests the input it names. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
539a7b8e0b |
test(ci): catch shape-4 stapling inside global, and spaced marker keys
Round-5 review. The shape-4 negative fixture placed its decoy outside `global` entirely, so it only caught a document-wide broadening. Mutating the rule to `.global | ..` instead passed the per-shape test and all three suites while still stapling the document-level registry onto unrelated maps inside `global` -- manufacturing first-party refs that promotion would then retag and mirror to destinations nothing ever built. The fixture now carries two decoys at different depths, one inside `global` but outside `global.images` and one outside `global`, and the mutation is caught. Markers also required the key to be immediately followed by its colon, so `registry : ghcr.io/...` was missed. No producer writes that spelling and it is valid YAML rather than anything in the tree, so this is theoretical -- but tolerating whitespace costs one character class and keeps the comment's exhaustiveness claim true lexically as well as structurally. Marker count is unchanged at 43 files with the same two allowlist entries. Assisted-By: Claude <noreply@anthropic.com> Assisted-By: GPT-5 <noreply@openai.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
c13a102274 |
feat(etcd-operator): bump to v0.5.3 (S3 checksum fix for non-AWS backends)
v0.5.3 carries the S3 request-checksum fix (cozystack/etcd-operator#342): the snapshot agent sets RequestChecksumCalculation=WhenRequired on BOTH the S3 client and the transfer manager, so multipart snapshot uploads (>5 MiB — every real etcd snapshot) no longer carry the CRC32 STREAMING-UNSIGNED-PAYLOAD-TRAILER that Ceph RGW rejects with 400 InvalidArgument. Also pulls in #340 (observed EtcdMember versions) and #341 (--watch-namespace). - system/etcd-operator: appVersion v0.5.2 -> v0.5.3 (image tag follows AppVersion), bump ETCD_OPERATOR_REF, update deployment unittest expectations. - system/etcd-operator-crds: re-vendor CRDs from v0.5.3. etcdmembers gains the additive #340 status.version field + "Running" printer column and picks up the peerAutoTLS spec field the vendored copy was already missing (all additive, backward-compatible; etcdclusters/etcdsnapshots unchanged). Fix the stale `make update`: upstream has no config/crd kustomization — CRDs live in charts/etcd-operator/crd-bases — so vendor those directly and stamp helm.sh/resource-policy: keep. Verified end-to-end on a Ceph RGW backend: the v0.5.2 (client-only) agent fails a multipart snapshot upload with the 400 above; v0.5.3 uploads an 11 MiB multipart snapshot successfully. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Andrey Kolkov <andrey.kolkov@aenix.io> |
||
|
|
fa4ae65d04 |
test(ci): assert usable refs, quoted keys, and shape-4 over-collection
Round-4 review. Both reviewers independently found the declared-extra test was passing while the thing it guards was broken, and each found one more. That test asserted `grep -q "@sha256:<digest>"`, which any token containing the digest satisfies. Its fixture was a split-host ref, which a textual scrape can never reconstruct -- only the contiguous tag value is recoverable, so ref_repo() reduces it to `latest` and every consumer drops it at the ownership filter. The fixture was therefore enumerated-but-uncollected, which is exactly the conflation the test was added to catch, and gutting the scrape to emit a bare digest with no repository left the whole suite green. The fixture is now a contiguous ref in an unparseable template (single-quoted, so the scrape's new quote-tolerance is exercised) and the assertion pins the exact canonical ref. The limitation that a split-host ref in an unparseable file is recoverable by neither route is recorded rather than implied away: the file-level marker check is what covers that case. The scrape also captured a leading quote, so any quoted ref in a .tag file or declared extra yielded a token every consumer silently discards. Nothing in the tree is affected today, but it meant the double-pass rescued only unquoted refs. Markers missed quoted YAML keys: an unenumerated template using "registry": / "repository": passed the entire suite. They now tolerate quoting on the key as well as the value, still matching 43 files with no new allowlist entries. Shape 4 could be broadened from .global.images[] to every map in the document and pass everything, because asserting that the expected ref appears cannot see an unexpected one. That scoping is load-bearing -- the host is a document-level key, so a global binding staples kubeovn's registry onto unrelated repositories and manufactures owned refs that were never built, which promotion would then retag and mirror. The shape-4 fixture now carries a digest-pinned sibling outside global.images and the test asserts its absence. Two per-shape fixture gaps closed while there: shape 2 had no sibling `registry:`, so it losing its rejoin was invisible here and caught only by one nightly-mirror fixture; and shape 1 had no tagless case, so narrowing it to require a :tag before the digest went undetected -- a digest-only pin is an ordinary Helm spelling. Also corrected the residual-limit comment on the markers. Coverage is exhaustive, not merely broad: the collector joins registry + "/" + repository, so a split can only fall on a path-separator boundary and markers 1-3 cover all four possibilities. A split elsewhere does not need covering because it does not produce a first-party ref. Assisted-By: Claude <noreply@anthropic.com> Assisted-By: GPT-5 <noreply@openai.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
aef9e201c7 |
fix(ci): make workflow cancellation effective (#3401)
## What this PR does - allows superseded PR workflow runs to cancel `finalize` and `E2E Tests` instead of continuing because of job-level `always()` - makes `update-website-docs` cancellation-responsive while preserving its behavior when changelog generation fails or is skipped - retains step-level `always()` conditions for reports and sandbox cleanup ### Screenshots Not applicable. ### Release note ```release-note fix(ci): ensure superseded and manually canceled workflows stop long-running jobs ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Prevented release and validation-related jobs from running when a workflow is cancelled. * Updated pull request check, end-to-end testing, and stable documentation update conditions to avoid executing after cancellation. * Kept existing success-gating so follow-up tasks still require prerequisite steps to complete successfully. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
8ad25b970f |
test(ci): pin digests exactly and generalize the first-party markers
Round-3 review, both reviewers again converging on the same defects. The file-level marker check was keyed to the exact ref spellings present in the tree rather than to the join semantics _collect_yaml_shapes implements, and three realistic first-party refs evaded it: `registry: ghcr.io/cozystack/cozystack` + `repository: <name>` (kubeovn's layout under the key name keycloak-operator and ingress-nginx use, which the old marker missed because it required a trailing slash a complete-value host does not have), a host+org split with precedent at fluxcd's values.yaml, and a single-quoted `repository:` where only double quotes were admitted. All three carried a version-free latest@sha256: tag -- exactly the profile the check exists for -- and passed the whole suite. The markers now follow the collector's join semantics; this matches 43 files and needs no new allowlist entries. The residual limit (a host split at an arbitrary point inside cozystack/cozystack) is stated rather than implied, along with why widening further is not worth it: a bare cozystack/cozystack marker matches 65 files and would need 23 allowlist entries for Makefiles and test fixtures. More seriously, making the completeness oracle share shape logic introduced a regression the previous independent oracle would have caught: corrupting the last hex digit of every emitted digest left the rewrite, retag and mirror suites entirely green, because both sides of the comparison move together and the per-shape test matched only a repository substring. The digest is the only part deciding which bytes get retagged, so the per-shape test now asserts the exact canonical repo@sha256:digest for every shape. Declared extras were also narrowed by the refactor: parsing alone means a ref inside a block scalar yields the enclosing block (which the ownership filter then discards) and an extra that stops parsing after `make update` yields nothing at all. Both are silent skips, in the one storage shape defined as a manifest vendored verbatim from upstream. Extras get the textual scrape again alongside parsing, with the callers' dedup absorbing the overlap. Finally, enumerating a path only proves the path is visited: a declared extra whose ref the parser cannot reach stayed enumerated but uncollected while both completeness checks passed. Added a test asserting a declared extra yields its ref, using an unparseable Helm template with a single-quoted split-host ref -- the shape that defeated both guards. Also corrected the claim that the per-shape test compensates for the shared oracle: it catches a shape being DELETED, not narrowed. A shape losing its registry rejoin is caught by promote-retag and nightly-mirror, which assert named packages, and the comment now says so. Assisted-By: Claude <noreply@anthropic.com> Assisted-By: GPT-5 <noreply@openai.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
f3a0f2d323 |
test(e2e): avoid head -n 0 in prevlog_cap for BSD portability
A zero container cap reached head -n 0, which GNU head treats as an empty
result but BSD/macOS head rejects ("illegal line count -- 0"), aborting a
local run. Special-case the zero cap to consume stdin instead, so the
helper behaves the same on both coreutils flavours.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
|
||
|
|
648eb42cae |
test(ci): make the image-ref guards see split-host and unparseable files
Both round-2 reviewers independently landed on the same two test-side
holes, and both are the guard being blind in exactly the way the bug it
guards was.
The completeness oracle derived its expected set from a grep for a
contiguous ghcr.io/cozystack/cozystack/...@sha256: string, so it could
not see any split-host shape: cilium, keycloak-operator, kubeovn, the OCI
artifact and seven shape-3 refs, 12 of 43 first-party refs in all. That
includes keycloak-operator, which the test's own rationale names as the
motivating profile. The fixture-discovery grep in the same file had
already been widened for this exact reason; the fix was never carried to
the oracle.
The oracle now applies the same shape knowledge to an independently
discovered file list, which is the axis that actually drifts -- what each
consumer disagreed about historically is which FILES it visits, not how a
ref is spelled inside one. To keep that sharing safe, shape coverage is
now pinned by its own test, so deleting a shape still fails something
even though both sides of the comparison would move together.
Sharing required splitting collect_refs_from_file out of
collect_image_refs. Behaviour is unchanged: same 67 refs collected before
and after, multus and the packages artifact both still present.
A YAML oracle cannot cover everything, though. A reviewer planted a
split-host ref in a Helm template, which yq cannot parse at all, and it
survived every test. A file-level marker check now backstops that: any
file carrying a first-party marker must be enumerated or explicitly
allowlisted. Two files are allowlisted today, each with its reason -- the
talos-csr-signer Dockerfile placeholder, and the documented capi gzip
gap. This also closes the secondary weakness that comparing on repo@digest
masks an unenumerated occurrence when the same image sits at an
enumerated path, which is live for that capi file.
Second hole: the postcondition's key-position branch had no coverage.
Replacing the whole pattern with the naive "${RC}@sha256:" -- the
narrowing rejected because cilium keeps tag: and digest: under separate
keys -- left all 22 tests green, so the branch that makes the split shape
visible was asserted by nothing. Added a cilium-shaped fixture at an
unenumerated path.
Both previously-surviving mutations are now caught. The oracle was also
pre-filtered by registry marker before parsing, cutting it from 32s to 5s.
Assisted-By: Claude <noreply@anthropic.com>
Assisted-By: GPT-5 <noreply@openai.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
|
||
|
|
76f7267ff2 |
test(e2e): describe --previous as the preceding instance, not the first
kubectl logs --previous returns the immediately preceding terminated container instance, not the original first one — after multiple restarts the first instance is already gone. Reword the helper, the BATS and Chainsaw failure handlers, and the diagnostics doc to match the actual --previous semantics. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
1ff83f5c06 |
test(e2e): fix false claim that a job timeout preserves the report
The catch-timeout comment justified the raised ceiling by asserting that if: always() upload survives a job timeout. It does not: a 180-minute cap ends the job cancelled, and the post-cancellation grace window is consumed by the earlier report-collection step, so both cozyreport.tgz and the job log are lost. Correct the comment to describe the real failure mode. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
9bd8c17971 |
fix(ci): shape-filter the promote postcondition and close test gaps
Review of the previous commit found the postcondition itself could abort
a release. It grepped the bare "X.Y.Z-rc.N" substring across the tree, and
three live files carry such strings without being image refs:
kubevirt-csi-driver's go.sum pins github.com/golang/protobuf v1.4.0-rc.2
-- a version cozystack actually cut, so promoting v1.4.0 would have
failed on a Go checksum line -- metallb_test.yaml names v1.5.0-rc.2 in a
comment, and the console pnpm-lock.yaml carries rolldown@1.0.0-rc.15. The
remediation the error message gave would have made things worse: declaring
go.sum as a ref source would sed a checksum file.
Match the version only in image-reference position now: after an
image/repository/tag key with no intervening '#', or immediately before an
@sha256 digest. Narrowing it to "${RC}@sha256:" alone would have been
wrong -- cilium keeps `tag: v1.5.0` and `digest:` under separate keys, so
the version is not always adjacent to the digest. Verified against four
real rc versions that previously aborted.
The suite also only tested the fail-closed direction, which is why that
defect survived, and the collector had no completeness guard at all: a
first-party ref added at an unenumerated path on a `latest@` tag was
mirrored by nothing and passed all eighteen tests. Since the mirror's
failure mode is the worst of the three -- the published nightly points at
the private build registry -- that gap mattered most where coverage was
thinnest.
Added: a fail-open test using the three real rc versions above; a
collector completeness diff that discovers refs by content and fails on
any the enumeration misses; coverage for the OCI-artifact shape, whose
deletion previously left every suite green; and an unreadable-file test,
since a read error was being treated as "no match" and silently skipped.
The component-versioned test was vacuous -- its rc version shared no
substring with the kamaji tag it claimed to protect, so it passed by
construction. It now uses an rc version sharing kamaji's 0.19.0 prefix,
which a loosely-anchored implementation would corrupt.
Round-trip fixture discovery matched a combined 'ghcr.io/cozystack/
cozystack/' prefix, which skips keycloak-operator (host in a sibling
registry: key) and kubeovn (host in global.registry.address). It now
matches 'cozystack/cozystack' and stamps split `tag:` keys too.
Also documented two pre-existing gaps rather than half-fixing them: the
nightly host rewrite is a contiguous-substring replace and cannot reach a
split host, and a ref inside a gzip is invisible to every tool including
the postcondition, which skips binaries. Both are host-dimension defects;
the version dimension this change owns is unaffected, because the version
always lives in a tag key regardless of where the host sits.
Assisted-By: Claude <noreply@anthropic.com>
Assisted-By: GPT-5 <noreply@openai.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
|
||
|
|
07c67a8913 |
test(e2e): capture previous container logs and stop killing the snapshot
Root-causing a crash-loop failure was blocked twice over by the diagnostics collected on failure. `kubectl logs` shows only the current container instance, so for a crash-looping pod the suites' `podLogs` collector captured the Nth restart -- a fresh replay of the same startup path -- while the evidence that explained the failure sat in the first, interrupted instance. Chainsaw's podLogs collector has no `previous` option, so add e2e-capture-previous-logs.sh and drive it from the global catch and from the BATS failure hook. It discovers containers with restartCount > 0 at runtime (init containers included, where an interrupted bootstrap lives), orders the app suites' namespace first, and dumps each to the job log and the snapshot tree. Containers that never restarted are skipped, so a healthy run emits one line rather than a page of "previous terminated container not found". Every way that capture can come up short is named rather than inferred, because kubectl exits 1 for a garbage-collected container, an RBAC denial and an unreachable node alike: the container cap, a pod list that never returned (which is not the same as nothing having restarted), a read that timed out versus one kubectl refused and the reason it gave, and a previous instance that produced no output. A partial capture is kept and marked truncated rather than deleted -- it is the only copy of a log already gone from the cluster. The crust-gather snapshot was killed by the Chainsaw op containing it. A script op is bounded by timeouts.exec (5m), and the catch's own wall-clock wrappers summed to 960s, so the script was SIGKILLed partway through -- taking out the process group mid-write and skipping the data-plane capture entirely. Give each catch op an explicit timeout above the budgets it contains, so an inner timeout fires instead and leaves a partial, still-servable snapshot behind. The data-plane leg drops from 600s to 300s at that call site so both legs fit one envelope; the BATS hook keeps the wider budget, having no containing op. Set crust-gather --duration explicitly as well. It is the collection phase's own budget, it defaults to 60s, and on elapse it abandons the collection and skips its finish step, leaving a truncated tree with no indication anything is missing. The outer wall-clock has to exceed it because the API discovery that runs first is not covered by that budget at all -- with an unhealthy aggregated APIService, the state a failed run is usually in, discovery alone can consume the whole timeout before a single object is written. Send each collector's output to a log beside its snapshot instead of /dev/null, with a complete/truncated/failed verdict, so that question is answerable from the artifact. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
64097dc455 |
fix(platform): forward backupStorage overrides to the backupstrategy-controller Package (#3333)
## What this PR does Fixes #3245. The documented admin override for the `cozy-default` backup S3 coordinates — `spec.components.backupstrategy-controller.values` on the `Package cozystack.cozystack-platform` (`docs/operations/backup-classes.md`, "Admin overrides for `cozy-default`") — had no effect: the `cozystack-platform` PackageSource exposes exactly one component (`platform`), so the operator silently ignored the block, and the platform chart rendered the child `Package cozystack.backupstrategy-controller` via the `.default` helper with no components argument, so nothing forwarded `backupStorage` either. Admins running against an external or TLS S3 had no working supported path to set `backupStorage.endpoint` / `region` / `systemSecretName`. This PR wires the override through the platform chart, the same way `bundles/iaas.yaml` forwards kubevirt and gpu-operator values: - `packages/core/platform/values.yaml` gains a top-level `backupStorage: {}` value. - `templates/bundles/system.yaml` renders it into the emitted `Package cozystack.backupstrategy-controller` as `components.backupstrategy-controller.values.backupStorage`, from where the cozystack operator merges it into the `backupstrategy-controller` HelmRelease over the chart defaults. When `backupStorage` is unset the components block is omitted and the emitted Package is byte-identical to the historical render. - `docs/operations/backup-classes.md` now documents the working path (`spec.components.platform.values.backupStorage`) and explicitly calls out the two plausible-looking paths that do not work (the old documented one, and patching the child Package directly, which the platform helm-reconcile reverts). - A new helm-unittest suite (`packages/core/platform/tests/bundles_backupstorage_wiring_test.yaml`) is the behavioural regression test: it proves the unset case emits no components block and the set case lands the values on the child Package, on both `isp-full` and `isp-full-generic`. Verified with `make test` in `packages/core/platform` (22 suites, 91 tests, all green). `make show` requires a live cluster (`cozyhr` + `lookup` in `templates/repository.yaml`), so the unittest render of `templates/bundles/system.yaml` is the offline verification path. ### Screenshots Not a UI change. ### Downstream repositories - [ ] No downstream repository is affected by this change - [x] [cozystack/website](https://github.com/cozystack/website) - follow-up: cozystack/website#622 - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: Walking the trigger map against the diff: the change to `packages/core/platform/values.yaml` reaches **cozystack/website** twice — `content/en/docs/next/operations/configuration/platform-package.md` (hand-written table of `spec.components.platform.values.*` keys gains `backupStorage`) and `content/en/docs/next/operations/services/backup-classes.md` (mirrors the broken override example this PR corrects). The follow-up is cozystack/website#622, which also carries the superseded website#547 content and covers both next/ and v1.4/ docs, marked not to merge before this PR lands; its v1.4 copy assumes this fix is backported to release-1.4. No other downstream repository restates the touched files: the templates change is chart-internal, no app `values.schema.json`, CRD, variant list, `hack/` file, or namespace changed. ### Release note ```release-note fix(platform): the documented admin override for the cozy-default backup S3 coordinates now works — set spec.components.platform.values.backupStorage on the cozystack.cozystack-platform Package and the platform chart forwards it to the backupstrategy-controller component (previously the documented spec.components.backupstrategy-controller path was silently ignored) ``` |
||
|
|
09323fff13 |
fix(ci): cover every image-ref storage shape in promote, retag and mirror
Promotion rewrote the rc version substring across the depth-2 package values.yaml plus packages/apps/kubernetes/images/*.tag alone, on the premise that the kubernetes app was the only one whose .tag files carry the cozystack version. Nine other .tag files do, and system/multus seds its ref straight into a vendored upstream manifest. v1.6.0 was therefore staged with 33 of 55 first-party refs still reading v1.6.0-rc.4. promote-retag.sh and nightly-mirror.sh had the same blind spot from the other direction: both scanned only the depth-2 values.yaml, so those images were never retagged to the stable version (30 refs selected before, 42 after) and never mirrored to the public registry for a nightly. A retag miss merely leaves an image short of a tag, since the digest still resolves inside one registry; a mirror miss is worse, because the host rewrite walks the same file list and a ref that is neither mirrored nor rewritten leaves the published tree pointing at the private build registry. The three call sites each carried their own idea of where a ref can live, which is how they drifted apart. Move that knowledge into hack/lib/image-refs.sh as the single enumeration -- image_ref_files() for the files, collect_image_refs() for the refs inside them -- and have all three source it, so a new storage location is declared once and reaches every consumer. The rewrite itself moves out of promote-rc.yaml into hack/promote-rewrite-tags.sh. Being workflow-inline is why this was only observable by cutting a release: there was nothing to unit test. It now also asserts its own postcondition, scanning wider than it rewrites and failing the promotion when any ref survives in a location the enumeration does not know about, rather than shipping it quietly. Component-versioned images (kamaji's v0.19.0-cozystack.N) and third-party pass-through images do not ride the cozystack version line and are deliberately left alone; a test pins that so a future fix here cannot over-reach. capi-providers-cpprovider is a documented known gap: its ref is also embedded in a gzipped copy that the chart actually ships, and rewriting only the readable copy would diverge the two. docs/agents/image-refs.md records the contract -- three tag classes, three storage shapes, the invariants, and which consumer does what. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
3ad3c2e611 |
fix(kubernetes): move kubevirt-csi-driver base off quay.io/centos to almalinux (#3400)
## What this PR does `packages/apps/kubernetes/images/kubevirt-csi-driver/Dockerfile` pinned its runtime stage to `quay.io/centos/centos:stream9` by digest. Quay serves **only currently-tagged manifests** — when a rolling tag moves, the superseded digest immediately returns `MANIFEST_UNKNOWN`, with no grace period. `stream9` rolls roughly every six days, so the pin has already died twice: | Date | Fix | Digest pinned | Status today | |---|---|---|---| | Jul 15 | #3303 | `0c9db821…` → `a93e3316…` | `0c9db821` gone | | Jul 21 | #3386 | `a93e3316…` → `3714c89f…` | `a93e3316` gone | Each death broke `make build` on `main` and on every PR triggering a full rebuild. Because the root `build` target is serial, a failure in `packages/apps/kubernetes` (4th of 29) aborts the remaining packages and leaves the shared build cache unwritten. Repinning cannot fix this: the freshly pinned digest is always the current live tag, so it is next in line to be retired. This is not specific to CentOS — the same probe against `quay.io/fedora/fedora:latest` and `quay.io/centos/centos:stream10` found every historical digest unfetchable. This PR keeps the digest pin and changes the *source* to a registry that provably retains superseded manifests, making the pin durable rather than decorative. Renovate policy is unchanged — `pinDigests` stays on, and `almalinux:9` is an ordinary rolling tag on a retaining registry exactly like the `golang`, `alpine` and `debian` bases already in the grouped digest PR. ### Verification Both bases were run locally with the Dockerfile's exact install line (`dnf install -y e2fsprogs xfsprogs nfs-utils`): | Binary | CentOS Stream 9 | AlmaLinux 9 | |---|---|---| | `mkfs.ext4` | `/usr/sbin/mkfs.ext4` | `/usr/sbin/mkfs.ext4` | | `mkfs.xfs` | `/usr/sbin/mkfs.xfs` | `/usr/sbin/mkfs.xfs` | | `blkid` | `/usr/sbin/blkid` | `/usr/sbin/blkid` | | `mount` | `/usr/bin/mount` | `/usr/bin/mount` | | `umount` | `/usr/bin/umount` | `/usr/bin/umount` | | `fsck.ext4` | `/usr/sbin/fsck.ext4` | `/usr/sbin/fsck.ext4` | | `fsck.xfs` | `/usr/sbin/fsck.xfs` | `/usr/sbin/fsck.xfs` | | `mount.nfs` | `/usr/sbin/mount.nfs` | `/usr/sbin/mount.nfs` | Every path `k8s.io/mount-utils` shells out to is identical. Package versions: | Package | CentOS Stream 9 | AlmaLinux 9 | |---|---|---| | `e2fsprogs` | 1.46.5-8.el9 | 1.46.5-8.el9 | | `xfsprogs` | 6.4.0-7.el9 | 6.4.0-7.el9 | | `nfs-utils` | 2.5.4-44.el9 | 2.5.4-42.el9_8 | | `util-linux` | 2.37.4-26.el9 | 2.37.4-25.el9 | | `glibc` | 2.34-274.el9 | 2.34-270.el9_8 | Same upstream versions throughout; the el9 release-number lag is expected since AlmaLinux is downstream of RHEL 9 while CentOS Stream 9 is upstream. `e2fsprogs` and `xfsprogs` — the two that govern `mkfs`/`fsck` behaviour — are identical NVRs. `glibc` is not linked at all: the driver is built `CGO_ENABLED=0`. The pinned digest is the OCI image index covering `amd64`, `arm64`, `ppc64le` and `s390x` — the same architecture set as the previous base. Retention was verified empirically rather than assumed: four superseded `docker.io/library` digests taken from this repo's own git history (`golang:1.26`, `golang:1.26-alpine`, `alpine:3.24`, `alpine:3.22`) all still resolve after their tags moved. ### Scope This addresses the immediate recurring breakage only. The broader concern in #3305 — that any retired digest blocks the entire platform build, with no advance warning and no fast path to repin — is not solved here and remains open. ### Release note ```release-note fix(kubernetes): move the kubevirt-csi-driver runtime base from quay.io/centos/centos:stream9 to docker.io/library/almalinux:9. Quay retires superseded manifests as soon as a rolling tag moves, which broke builds twice in six days; AlmaLinux 9 provides an equivalent RHEL 9 userspace on a registry that retains them, so the digest pin remains valid over time. No functional change to the driver. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated the container runtime environment to use AlmaLinux 9 instead of CentOS Stream 9. * Kubernetes CSI driver behavior and startup configuration remain unchanged. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
a4b5b9d57b |
docs(release): update workflow cancellation condition
Assisted-By: GPT-5 <noreply@openai.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
792951b9c6 |
fix(ci): make workflow cancellation effective
Assisted-By: GPT-5 <noreply@openai.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
14af87296f |
fix(kubernetes): move kubevirt-csi-driver base off quay.io/centos to almalinux
quay.io serves only currently-tagged manifests: when a rolling tag moves, the superseded digest immediately returns MANIFEST_UNKNOWN. The pinned `centos:stream9` digest has died twice in six days (#3303, #3386), each time breaking `make build` on main and on every PR that triggers a full rebuild. Repinning is not a fix -- the newly pinned digest is always the live tag, so it is next in line to be retired. Move the runtime stage to a registry that provably retains superseded manifests instead, which makes the digest pin durable rather than decorative. Pinning policy is unchanged. Verified equivalent: after `dnf install -y e2fsprogs xfsprogs nfs-utils`, both bases provide mkfs.ext4, mkfs.xfs, blkid, mount, umount, fsck.ext4, fsck.xfs and mount.nfs at identical paths. e2fsprogs and xfsprogs are identical NVRs. The driver is built CGO_ENABLED=0, so the base libc is not linked. The pinned digest is the OCI index covering amd64, arm64, ppc64le and s390x -- the same architecture set as the previous base. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |