Commit Graph
4231 Commits
Author SHA1 Message Date
Aleksei Sviridkin bded448d56 fix(objectstorage-controller): shorten COSI leader-election lease
The controller and the per-driver sidecar both run single-replica, so leader
election provides no high availability -- it only governs how quickly a freshly
recreated pod takes over. With the upstream 150s lease duration a recreated
provisioner pod stalls reconciliation for up to 150s while it waits out the
stale lease still held by the previous, now-deleted pod. Lower the lease timings
to the standard Kubernetes values (15s/10s/2s) so a recreated pod resumes
reconciliation in roughly 15s.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
(cherry picked from commit 250d92323d)
2026-08-04 13:18:15 +05:00
Aleksei Sviridkin d9c6f579de fix(objectstorage-controller): requeue BucketClaim until backend Bucket is ready
The dynamic-provisioning path re-reads the live Bucket after create and copies
its readiness onto the BucketClaim, but only during a reconcile. Nothing
re-drives that reconcile afterwards: the controller does not watch Buckets, and
the generic controller drops no-op resync deltas (reflect.DeepEqual), so once
the BucketClaim stops changing the listener is never re-invoked. The Bucket's
false->true transition, performed asynchronously by the sidecar on a different
object, is therefore reflected onto the claim only if the Bucket happened to be
ready during that single reconcile -- a race the sidecar usually loses, leaving
bucketReady frozen at false and BucketAccess never granted.

Return an error while the backend Bucket is not ready so the work queue
requeues the claim with exponential backoff; each retry re-reads the live
Bucket until its readiness has been propagated, converging deterministically
instead of depending on a resync that never fires.

Add a unit test pinning the contract (requeue while not ready, propagate true
once ready) and adjust the existing status-update conflict-retry test, which
pinned the old never-requeue behavior.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
(cherry picked from commit 4437cccaf0)
2026-08-04 13:18:15 +05:00
myasnikovdaniil ef72c63e6e ci: install flux in the release-1.5 cache warmer (#3480)
## What this PR does

Adds the flux CLI to the release-1.5 cache warmer, which #3469 moved
onto the ephemeral runner shape without carrying over the toolchain step
that main's warmer and this branch's `pull-requests.yaml` both have.

`make build` finishes at `packages/core/installer`, whose
`image-packages` target shells out to `flux push artifact`. The
self-hosted runner the warmer used to target had flux baked in; the
ephemeral shape does not.

Confirmed on the first warmer run after #3469 merged ([run
30430753152](https://github.com/cozystack/cozystack/actions/runs/30430753152)):
it failed at 49 minutes with `/bin/sh: 1: flux: not found` on
`Makefile:35`, the last of 28 packages.

The cache itself came out warm, because every package exports its cache
as it builds and installer is last:

| | |
| --- | --- |
| cache export operations | 87 |
| cache manifests written | 70 |
| repos created under `CACHE_REGISTRY` | 34 |
| `ubuntu-container-disk` refs (`v1.30`–`v1.35`, `mode=max`) | all 6 |
| real auth or export failures | 0 |

So the only loss was the `cozystack-packages:release-1.5` artifact push,
and nothing downstream depends on it — PR builds build the installer
themselves with flux present. This also settles the open question from
#3469: the registry credentials can create repositories under the new
`cozystack-cache-release-1.5/*` namespace, so no fallback to a cache-tag
suffix is needed.

Fixing it still matters. A permanently red warmer means a genuine
breakage is indistinguishable from this known-broken tail, and nothing
else reports on that workflow.

The alternative would be teaching the warmer to run the 28 image builds
without the artifact push, but no such target exists and it would
diverge from main's warmer for no gain. Installing the CLI keeps the two
warmers identical in shape, and matches main publishing
`cozystack-packages:main` from its own warmer.

### Release note

```release-note
NONE
```
2026-07-31 10:13:47 +05:00
Myasnikov Daniil 922a6c1c03 ci: install flux in the release-1.5 cache warmer
`make build` finishes at packages/core/installer, whose image-packages
target shells out to `flux push artifact`. The warmer used to target the
self-hosted runner, which had flux baked in; #3469 moved it to the
ephemeral shape without carrying over the toolchain step that main's
warmer and pull-requests.yaml both have for exactly this reason.

Confirmed on the first run after #3469 merged (run 30430753152): it failed
at 49 minutes with `/bin/sh: 1: flux: not found` on Makefile:35, the last
of 28 packages. Everything before it had already exported its cache -- 87
export operations, 70 cache manifests, 34 repos created under
CACHE_REGISTRY, including all six ubuntu-container-disk refs -- so the
cache came out warm and only the cozystack-packages artifact push was
lost. A red warmer is still a broken signal, and nothing else would have
reported it.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-07-29 14:53:44 +05:00
myasnikovdaniil 5f2bea1f51 ci: warm the build cache on release-1.5 pushes (#3469)
## What this PR does

Nothing has ever warmed `release-1.5`'s build cache, so every PR build
on this branch runs cold end to end and exceeds its 30-minute timeout.

This branch's copy of `build-main.yaml` is dead code. It triggers on
`push: branches: [main]`, which never happens here, and it targets
`[self-hosted]` — the decommissioned cozy-runner-1 (#2937) — so it could
not have run even if it fired. This PR points it at this branch instead.

Both halves of the cold build trace back to 39cdc8866
(`feat(kubernetes): bootstrap tenant workers with Talos via CABPT`)
landing on `main` on 2026-06-16, six days before `release-1.5` was cut
on 2026-06-22:

- That commit dropped `ubuntu-container-disk` from `make image`, so
`main` stopped writing its six `:<minor>-buildcache` refs. `release-1.5`
is now the only branch that still builds the image, and the CI log shows
all six importers failing with `not found`. Serially that was ~28min of
the 30min budget; #3457 parallelises the target down to ~10min.
- Every other cache ref carries `main`'s sources, which have diverged
from this branch, so most of the remaining 28 packages miss too.
Measured on the #3457 run: ~38min across the full build, and no single
hot spot left to attack — `apps/kubernetes` at 15m07s and
`system/linstor` at 5m39s, everything else 20-60s each.

### Changes

| | before | after |
| --- | --- | --- |
| trigger | `push: [main]` | `push: [release-1.5]` |
| runner | `[self-hosted]` (decommissioned) |
`oracle-vm-24cpu-96gb-x86-64` |
| cache namespace | shared `cozystack/` | `cozystack-cache-release-1.5/`
|
| `IMAGE_TAG` | `main` | `release-1.5` |
| warmer timeout | 90 | 120 |

`pull-requests.yaml` gets the same `CACHE_REGISTRY` so PR builds read
what the warmer writes, and its Build timeout goes to 75 minutes.

The cache namespace is branch-scoped on purpose. Writing the shared refs
from here would replace `main`'s layers with this branch's, so `main`'s
next PR build would miss on everything and blow its own 30-minute
timeout until the next `main` push re-warmed it. `CACHE_REGISTRY`
already exists as a knob in `hack/common-envs.mk`, so this is one env
var per job rather than new machinery. Image tags are unaffected — they
still resolve to `cozystack/<image>`, only cache refs move.

The timeout raise is not optional. The new namespace starts empty, so
this PR and the next one still build cold at ~45min, and anything that
later invalidates the cache broadly — a Dockerfile edit, a `go.mod` bump
— costs the same again. 30 minutes only ever fit a warm build, and when
it did not fit, the job reported a timeout rather than whatever actually
went wrong.

### Verification

- Both workflows are asserted to agree on the cache namespace, rather
than eyeballed.
- Confirmed the env split renders as intended: `--tag
iad.ocir.io/…/cozystack/ubuntu-container-disk:v1.35-release-1.5`
alongside `--cache-from`/`--cache-to` on
`…/cozystack-cache-release-1.5/ubuntu-container-disk:v1.35-buildcache`.
- `WRITE_CACHE` stays unset in PR builds, so they remain read-only
against the cache and never race on a manifest.

### Notes for the reviewer

- **Stacked on #3457.** Both PRs edit adjacent lines of
`pull-requests.yaml`, so keeping them independent guaranteed a conflict.
GitHub retargets this to `release-1.5` automatically once #3457 merges.
- **The filename stays `build-main.yaml`** even though it no longer
warms `main`. Renaming it would make any later backport touching
`main`'s copy arrive as a whole new file rather than a modification,
which is the failure mode the backport bot already hits on this branch.
- **One assumption I could not test locally:** the registry credentials
need repo-create permission for the new `cozystack-cache-release-1.5/*`
path. It is the same tenancy namespace that already auto-creates repos
as packages get added, so it should hold — but if it does not,
`--cache-to` fails the warmer, and the fallback is a cache-tag suffix in
`hack/common-envs.mk` instead of a separate namespace.
- **Sequencing:** the merge commit is itself a push to `release-1.5`, so
the warmer fires on merge and takes ~50-60min cold. PR builds after that
should be ~15min. The warmer cannot be triggered manually —
`workflow_dispatch` is only available for workflows present on the
default branch, and this one exists only here.
- **Out of scope, but adjacent:** `tags.yaml`, `release-e2e.yaml` and
`pull-requests-release.yaml` on this branch all still say `runs-on:
[self-hosted]`, so the release and release-e2e lanes are broken the same
way this warmer was.

### Release note

```release-note
NONE
```
2026-07-29 12:11:32 +05:00
Myasnikov Daniil 55d25aa551 ci: read the branch-scoped build cache in PR builds
Point the Build job at the namespace build-main.yaml now writes for this
branch. Without this the warmer would run and nothing would read it: the
cache-from refs would still resolve to the shared namespace main writes,
whose layers carry main's sources and miss on most of this branch's 28
packages. Builds only read the cache -- WRITE_CACHE defaults to 0, so
concurrent PR builds still never race on a cache manifest, and a miss
degrades to a cold build rather than an error.

Raise the timeout to 75 minutes at the same time. A warm build is ~15min,
but the namespace starts empty, so this PR and the next one run cold at
~45min: six ubuntu-container-disk images plus 28 packages from scratch.
Anything that later invalidates the cache broadly -- a Dockerfile edit, a
go.mod bump -- costs the same again. 30 minutes only ever fit a warm
build, and when it did not fit, the job reported a timeout rather than
whatever actually went wrong.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-07-28 12:32:43 +05:00
Myasnikov Daniil 428fe40e93 ci: warm the build cache on release-1.5 pushes
Nothing has ever warmed this branch's build cache. This workflow is a
copy of main's warmer that still triggers on `push: branches: [main]`, so
on release-1.5 it has never fired once -- and it targets [self-hosted],
the decommissioned cozy-runner-1 (#2937), so it could not have run even
if it did fire.

The consequence is a PR Build that runs cold end to end and exceeds its
30-minute timeout. Two separate reasons, both traceable to 39cdc8866
("bootstrap tenant workers with Talos via CABPT") landing on main six
days before this branch was cut:

  * that commit dropped ubuntu-container-disk from `make image`, so main
    stopped writing its six :<minor>-buildcache refs. release-1.5 is the
    only branch that still builds the image, and the CI log shows all six
    importers failing with "not found" -- ~28min serial, cut to ~10min by
    parallelising the target in #3457.
  * every other ref carries main's sources, which have diverged from this
    branch, so most of the remaining 28 packages miss too -- ~38min across
    the full build, with no single hot spot left to fix.

Point it at this branch, move it to the ephemeral shape main's warmer
already uses, and write to a branch-scoped CACHE_REGISTRY namespace.
The namespace matters: writing the shared refs from here would replace
main's layers with release-1.5's, so main's next PR build would miss on
everything and blow its own 30-min timeout until the next main push
re-warmed it. pull-requests.yaml has to read the same namespace, which
the following commit does.

The filename stays build-main.yaml even though it no longer warms main.
Renaming it would make any later backport touching main's copy arrive as
a whole new file rather than a modification, which is the failure mode
the backport bot already hits on this branch.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-07-28 12:31:09 +05:00
Myasnikov Daniil 9e11998ae9 fix(ci, kubernetes): build container disks concurrently to unblock Build
Every release-1.5 PR Build job dies at the 30-minute timeout, and all of
it is spent in one target. `image-ubuntu-container-disk` builds a disk
per Kubernetes minor -- six of them -- and each spends ~4-5min inside a
libguestfs appliance installing that minor's kubelet/kubeadm into its own
copy of the cloud image. Serially that is ~28min of the 30min budget:

  v1.35 6:00, v1.34 4:21, v1.33 4:27, v1.32 4:39, v1.31 4:42, v1.30 cancelled

None of it is cached, and no warmer will fix that on its own. Only
build-main.yaml sets WRITE_CACHE=1, and main dropped this image from
`make image` in 39cdc8866 six days before release-1.5 was cut, so the
:*-buildcache refs it reads have never been written -- the CI log shows
all six importers failing with "not found". release-1.5 is now the only
branch that builds this image at all.

The six builds share no per-version work, so run them concurrently
instead. A sub-make carries its own -j, keeping the root `make build`
serial, and buildkit dedups the shared guestfish and cloud-image stages
across the concurrent solves. --output-sync=target groups each version's
log under its target name; six interleaved buildx progress streams have
no per-solve identifier and are unreadable. Verified the per-version
buildx/echo/rm command set is byte-identical to the loop it replaces
across PUBLISH_VERSIONED, PUBLISH_FLOATING and WRITE_CACHE combinations.

Moving off the 4cpu/16gb shape because six concurrent libguestfs
appliances want ~9gb of RAM and a core apiece. The comment justifying
the small shape assumed a warm cache, which no longer holds here.

This also stops swallowing failures. The loop separated versions with
`;`, so make saw only the last version's exit status: a failed build for
any other version left its committed .tag file stale and the job green.
Under the sub-make a failure in any version fails the target.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-07-27 20:46:38 +05:00
myasnikovdaniil 2b2869fd9a [Backport release-1.5] fix(fluxcd): omit empty distribution.artifact in FluxInstance for guest clusters (#3292)
# Description
Backport of #3284 to `release-1.5`.
2026-07-15 14:41:37 +05:00
myasnikovdaniil 1cbc5af76c [Backport release-1.5] fix(dashboard): unbreak CORS on expired session for k8s API calls (#3291)
# Description
Backport of #2788 to `release-1.5`.
2026-07-15 14:39:28 +05:00
myasnikovdaniil 695823555e [Backport release-1.5] fix(apps/vpn): remove invalid foo field from urls Secret (#3290)
# Description
Backport of #3281 to `release-1.5`.
2026-07-15 14:39:15 +05:00
myasnikovdaniil 5e66d6f68c [Backport release-1.5] fix(kubevirt): update KubeVirt to v1.8.4 (#3285)
# Description
Backport of #2940 to `release-1.5`.
2026-07-15 14:39:03 +05:00
Ivan Okhotnikov aaf54886fe fix(fluxcd): omit empty distribution.artifact in FluxInstance
The flux-instance template rendered spec.distribution.artifact
unconditionally, unlike the sibling optional fields (artifactPullSecret,
imagePullSecret, variant). The umbrella default is empty to use the
operator's embedded manifests (the air-gapped default from #964), but an
empty value renders as YAML null. In the flux-operator v0.50.0 CRD the
field is optional yet typed 'string' with pattern ^oci://.*$ and is not
nullable, so the rendered null (like an empty string) fails schema
validation and the API rejects the FluxInstance.

Guest Kubernetes clusters with the fluxcd addon enabled could therefore
never install it: the sub-release looped install/uninstall, the parent
kubernetes release stayed InProgress, and the cluster never reached Ready.

Guard the field so an empty value is omitted (the operator falls back to
its embedded manifests, preserving the air-gapped default) while a
non-empty artifact still renders. The template lives in the vendored
subchart, so the change is carried as a patch applied by 'make update'.

Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com>
(cherry picked from commit da47b70e2c)
2026-07-14 10:18:48 +00:00
Ivan Okhotnikov 78709bba88 fix(dashboard): set webOrigins to "+" and add --api-route for k8s API paths
Two related issues caused the dashboard SPA to break with CORS errors
after the kc-access cookie expired:

1. KeycloakClient for dashboard had no webOrigins set, so the operator
   never touched the field in Keycloak after initial creation. After
   any rootUrl change the existing webOrigins value became stale and
   blocked CORS from the SPA. Setting "+" tells Keycloak to derive
   allowed origins from redirectUris.

2. oauth2-proxy returned 302 -> Keycloak /auth for unauthenticated
   XHR/fetch on /api and /apis. The login page does not emit
   Access-Control-Allow-Origin, so the SPA's fetch failed with CORS.
   --api-route makes oauth2-proxy answer 401 without redirect, so the
   SPA can handle the re-auth itself via top-level navigation.

Signed-off-by: Ivan Okhotnikov <ivan.okhotnikov@aenix.io>
(cherry picked from commit be8f6b4739)
2026-07-14 10:07:33 +00:00
Ivan Okhotnikov 1b5ccddb32 fix(apps/vpn): remove invalid foo field from urls Secret
A leftover debug field "foo" in the <release>-urls Secret template
violates the core/v1 Secret schema. It went unnoticed under client-side
apply but breaks Helm upgrades under server-side apply with
".foo: field not declared in schema", leaving the release in
UpgradeFailed state. Drop the debug block.

Signed-off-by: Ivan Okhotnikov <xorokhotnikov@gmail.com>
(cherry picked from commit 70a2284551)
2026-07-14 09:51:03 +00:00
Aleksei Sviridkin d1fc12609b chore(kubevirt-operator): update to v1.8.4
Refresh the vendored virt-operator manifest from v1.8.2 to v1.8.4. The
release backports validation of the VMI checksum status fields as a
uint32 range, required on Kubernetes 1.36 where strict CRD numeric
format validation otherwise leaves VMIs stuck in Scheduled. Remaining
changes are bug fixes and CVE remediations; the embedded CRDs and RBAC
are unchanged, so only the operator image tag moves.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
(cherry picked from commit 192246e8b6)
2026-07-14 08:57:55 +00:00
myasnikovdaniil 37cd6a797f [Backport release-1.5] fix(kubevirt-instancetypes): restore persistent EFI/TPM state (#3212)
# Description
Backport of #3154 to `release-1.5`.
api/apps/v1alpha1/v1.5.3 v1.5.3
2026-07-06 11:36:59 +05:00
Andrei Kvapil f52af1922c fix(kubevirt-instancetypes): restore persistent EFI/TPM state
Reverts #3006. Dropping persistent EFI/TPM was motivated by a live-migration
concern that rested on an outdated assumption — that an RWO Filesystem
backend-storage PVC pins the VM to its node. KubeVirt has migrated
RWO-Filesystem backend storage since v1.4 (kubevirt/kubevirt#12629): on
migration it creates a fresh target state PVC and copies the small state blob,
so persistent-EFI/TPM VMs on the default replicated storage live-migrate fine
(verified: the VM reports LiveMigratable=True and the copy-on-target PVC is
created on migration). Restore upstream's persistent firmware state for the
windows.* preferences.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
(cherry picked from commit 59f035ff53)
2026-07-06 06:07:52 +00:00
myasnikovdaniil a14fbf8d7b [Backport release-1.5] fix(seaweedfs): configure postgres2 connection pool for the filer (#3194)
# Description
Backport of #2906 to `release-1.5`.
2026-07-06 10:03:43 +05:00
mattia-eleuteri 20b053cbbe fix(seaweedfs): configure postgres2 connection pool for the filer
The vendored seaweedfs chart ships connection pool settings only for the
mysql store, which cozystack does not use. The postgres2 store, which is
enabled, has no pool settings, so the Go sql pool keeps zero idle
connections and every filer metadata lookup opens a fresh PostgreSQL
connection (TCP + TLS + SCRAM, ~300ms each). With ~5 path lookups per S3
operation this adds roughly 2 seconds of latency to every S3 request,
regardless of cluster load.

Measured on a production cluster (12 nodes, 2 filers): PUT of a 4KiB
object went from 10s (p50) to 0.28s, HEAD from 2.6s to 0.16s after
setting these values; pg_stat_activity confirmed connections were
previously recycled every ~200ms and are now reused.

max_open is capped at 40 so that two filer replicas stay below the CNPG
default max_connections of 100.

Signed-off-by: mattia-eleuteri <mattia@hidora.io>
(cherry picked from commit 49cbea8c73)
2026-07-03 16:31:59 +00:00
myasnikovdaniil ab0f9ed08f chore(release): cut v1.5.2 (#3161)
This PR prepares the release `v1.5.2`.
v1.5.2
2026-07-03 17:14:23 +05:00
cozystack-ci[bot] 7cc5df5139 Prepare release v1.5.2
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com>
2026-07-03 10:12:24 +00:00
myasnikovdaniil ccb325eaaa [Backport release-1.5] fix(kamaji): backport datastore unused-deletion deadlock fix (clastix/kamaji#1122) (#3187)
# Description
Backport of #3146 to `release-1.5`.
api/apps/v1alpha1/v1.5.2
2026-07-03 14:53:25 +05:00
Myasnikov Daniil a114736fd3 fix(kamaji): backport datastore unused-deletion deadlock fix (clastix/kamaji#1122)
Deleting a Tenant whose etcd DataStore has 0 referencing TenantControlPlanes left the DataStore stuck Terminating (finalizer kamaji.clastix.io/TenantControlPlane), hanging the etcd helm uninstall and wedging the tenant namespace. Backports clastix/kamaji#1122 (fixes clastix/kamaji#1115) as a source patch on the pinned 26.3.6-edge build. Refs #3018.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 47638f8569)
2026-07-03 09:52:31 +00:00
myasnikovdaniil 3e25f19223 [Backport release-1.5] fix(kubernetes): gate vmop cert-manager-crds dependency on certManager.enabled (#3185)
# Description
Backport of #3130 to `release-1.5`.
2026-07-03 14:08:54 +05:00
Myasnikov Daniil 5378a53157 fix(kubernetes): gate vmop cert-manager-crds dependency on certManager.enabled
The victoria-metrics-operator HelmRelease is gated on
monitoringAgents.enabled but listed cert-manager-crds in dependsOn
unconditionally, while the cert-manager-crds HelmRelease is only
created when certManager.enabled is true. The valid combination
monitoringAgents.enabled=true + certManager.enabled=false therefore
left vmop blocked on a non-existent dependency
("...cert-manager-crds ... not found"), cascading to monitoring-agents
and vertical-pod-autoscaler. Gate the dependency on the same condition
that creates it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 2b1d3e904d)
2026-07-03 09:07:07 +00:00
myasnikovdaniil 4da2f4201d [Backport release-1.5] fix(tenant): gate VM-resource HelmReleases on victoria-metrics-operator readiness (#3166)
# Description
Backport of #3141 to `release-1.5`.
2026-07-03 13:03:34 +05:00
myasnikovdaniil 943f2b02a9 [Backport release-1.5] fix(mariadb): re-guard replication so single-replica passes the operator webhook (#3183)
# Description
Backport of #3180 to `release-1.5`.
2026-07-03 12:59:29 +05:00
myasnikovdaniil e441b2d3a3 [Backport release-1.5] fix(kubernetes): make node group resources optional when instanceType is set (#3156)
## What this PR does

Backport of #3121 to `release-1.5`.

Makes node group `resources` optional, so a node group sized purely by
`instanceType` (with `resources` omitted) passes `values.schema.json`
validation and installs. When a group sets both `instanceType` and
explicit `resources` (both `cpu` and `memory`), the explicit resources
take precedence: the instancetype is omitted from the rendered VM and
the per-group instancetype lookup is skipped, so KubeVirt does not
reject a VM that references an instancetype while overriding
`domain.cpu`/`memory`. A partial spec (only one of `cpu`/`memory`)
alongside an `instanceType` fails fast at render time.

Cherry-picked from `a349c753`; conflicts resolved against the
`release-1.5` tree (the management-cluster Talos worker bootstrap stack
present on `main` is not on this branch, so the schema/template/test
layout differ). Generated files (`values.schema.json`, `README.md`,
cozyrds, API types) regenerated with `make generate` to match. `helm
unittest` passes (134 tests), including the four new node-group sizing
cases adapted to this branch's document layout.

### Release note

```release-note
fix(kubernetes): make node group resources optional when instanceType is set
```
2026-07-03 12:53:57 +05:00
myasnikovdaniil 746f873a4a [Backport release-1.5] fix(seaweedfs): enforce read-only bucket access by bumping cosi-driver to v0.3.1 (#3153)
# Description
Backport of #3136 to `release-1.5`.
2026-07-03 11:16:56 +05:00
myasnikovdaniil d490ebbd41 [Backport release-1.5] fix(platform): order backupstrategy-controller after cozystack-basics (#3079)
# Description
Backport of #3037 to `release-1.5`.
2026-07-03 11:15:03 +05:00
myasnikovdaniil 7799ec4923 [Backport release-1.5] fix(kafka): set entity-operator resources to avoid OOM under tenant LimitRange (#3054)
# Description
Backport of #2934 to `release-1.5`.
2026-07-03 11:11:08 +05:00
Алексей Артамонов e115e62fb1 test(mariadb): fix operator MariaDB CR name in single-replica failure dump
The failure-path dump queried mariadbs.k8s.mariadb.com with $name, but the
operator CR is named after the Helm release ({{ .Release.Name }} = mariadb-$name),
so the dump would hit NotFound instead of showing the rejected CR.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Алексей Артамонов <alexeyartamonov1987@gmail.com>
(cherry picked from commit f8e4222b7d)
2026-07-02 19:54:31 +00:00
Алексей Артамонов b5a09cd14b fix(mariadb): re-guard replication so single-replica passes the operator webhook
PR #2279 made `replication.enabled: true` unconditional so the operator would
always create -primary/-secondary services. But the mariadb-operator validating
webhook (vmariadb-v1alpha1.kb.io) rejects replication when spec.replicas == 1,
so single-replica MariaDB could no longer be created — the HelmRelease never
became Ready.

Re-guard replication on replicas>1 and teach the service-name consumers about
the bare <release> service the operator provisions in both topologies:

- mariadb.yaml: gate `replication` on replicas>1; restore single-replica
  external access via `service.type: LoadBalancer` and keep `primaryService`
  LoadBalancer for replicas>1 only.
- backup-cronjob.yaml: route single-replica dumps to the bare service
  (no -secondary exists without replication).
- dashboard-resourcemap.yaml: always grant RBAC on the bare service, plus
  -primary/-secondary when replicas>1, matching the ApplicationDefinition.
- mariadb-rd: list the bare service in services.include so single-replica
  installs are visible in the dashboard.

Adds a single-replica e2e test that reproduces the webhook rejection and
guards against regression.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Алексей Артамонов <alexeyartamonov1987@gmail.com>
(cherry picked from commit c7e8f4d975)
2026-07-02 19:54:31 +00:00
Aleksei Sviridkin 6cca847421 fix(tenant): gate VM-CR-creating HelmReleases on victoria-metrics-operator
The tenant etcd, ingress and monitoring HelmReleases create VM* custom
resources (VMPodScrape directly in etcd/ingress; VMCluster, VMAlert,
VLCluster, VMAlertmanager and VMServiceScrape via the monitoring-system
child of monitoring) guarded by the victoria-metrics-operator validating
webhook with failurePolicy: Fail. On a cold install the operator pod can
be briefly unavailable on :9443, so these releases reconcile before the
webhook backend is serving and are rejected with connection refused. They
carry install.remediation.retries: -1 and would eventually recover, but
the failed-install -> rollback -> retry churn can outlast a cold-install
readiness deadline and surfaces as spurious install failures.

Add a Flux spec.dependsOn from each of these releases to the
victoria-metrics-operator HelmRelease in cozy-victoria-metrics-operator,
so they wait for the operator to be Ready before creating VM* resources.
The system monitoring components gate on the operator the same way, via a
variant-level dependsOn. The cross-shard reference (tenant releases are
sharded, the operator release is not) resolves via helm-controller's
uncached dependency check (APIReader, v1.1.0+).

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
(cherry picked from commit 6db5ae9c6f)
2026-07-01 10:51:50 +00:00
Aleksei Sviridkin 0dbd0e1597 fix(kubernetes): make node group resources optional and prefer them over instanceType
A node group is sized either by instanceType or by explicit resources, but the
generated values schema required resources on every node group. A node group
with instanceType and no resources — or an empty resources dropped during
typed-CR to HelmRelease translation — failed schema validation with "missing
property 'resources'" even though sizing was fully specified.

Mark resources optional in the node group definition and regenerate the schema,
API types, README, and ApplicationDefinition. The chart template emits
domain.cpu/memory only when resources is present, so a node group sized purely
by instanceType now passes validation and installs.

When a node group sets both instanceType and explicit resources, explicit
resources take precedence: the instancetype is omitted from the rendered VM and
the per-group instancetype lookup is skipped, so KubeVirt accepts the explicit
domain.cpu/memory with no instancetype to conflict with. instanceType stays
required and defaulted (no schema change), so the console's required
instanceType dropdown keeps working — sending the default instanceType plus
explicit resources simply sizes the VM by resources. This replaces an earlier
render-time hard failure on the both-set case, which was unreachable to clear
from the dashboard (the required dropdown cannot be emptied).

Explicit resources are all-or-nothing alongside an instanceType: a partial spec
(only cpu, or only memory) on a group that carries a non-empty instanceType
would emit one domain override next to the instancetype — which KubeVirt rejects
— so it now fails at render time with a clear error. Partial resources on a
group without an instanceType (used to tune kubelet reservations) are unaffected.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
(cherry picked from commit a349c753ad)
2026-06-30 18:14:23 +05:00
Aleksei Sviridkin 46d5d9d1e9 fix(seaweedfs): enforce read-only bucket access by bumping cosi-driver to v0.3.1
The seaweedfs COSI driver shipped at v0.1.2 hardcodes read-write S3
actions (Read, Write, List, Tagging) for every BucketAccess and never
reads the accessPolicy parameter. The chart already defines a -readonly
BucketAccessClass carrying parameters.accessPolicy: readonly, but v0.1.2
ignores it, so a credential meant to be read-only can still write
(upload and delete) objects.

Bump the driver to v0.3.1, which honours accessPolicy: readonly by
issuing Read,List-only credentials (read-write and the empty default
stay Read,Write,List,Tagging). The override lives in the package
values.yaml rather than the vendored chart so it survives make update;
the sister extra/seaweedfs chart already runs v0.3.1.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
(cherry picked from commit 0c6426feaa)
2026-06-30 13:04:17 +00:00
myasnikovdaniil 76f296482e [Backport release-1.5] fix(harbor): preserve jobservice and trivy storageClass on upgrade (#3147)
# Description
Backport of #2930 to `release-1.5`.
2026-06-30 17:47:56 +05:00
myasnikovdaniil e676165d84 [Backport release-1.5] fix(seaweedfs): point S3 consumers at the reachable S3 service (#3127)
# Description
Backport of #3119 to `release-1.5`.
2026-06-30 17:47:24 +05:00
Myasnikov Daniil dad745874c fix(harbor): preserve jobservice and trivy storageClass on upgrade
The cozy-harbor wrapper never passed storageClass to the jobservice
jobLog PVC, so it was created without a storageClassName and the
default-StorageClass admission stamped the cluster default. Because PVC
storageClassName is immutable, this caused rendered-vs-live drift on
upgrade. Preserve an existing PVC's class via lookup and fall back to the
configured storageClass for fresh installs.

The trivy StatefulSet's volumeClaimTemplate had the same latent drift --
.Values.storageClass was forwarded blindly into a VCT that cannot be
patched at all -- so it gets the same lookup-preserve treatment, keyed
off data-<release>-trivy-0.

A live object pinned to an explicit "" (no dynamic provisioning) now
round-trips through upstream's "-" sentinel instead of collapsing to an
omitted storageClassName, and the spec lookup is guarded with
`| default dict` so a PVC carrying no storageClassName key stays omitted.

Adds a helm-unittest regression suite and wires a test target so
make unit-tests stops skipping packages/apps/harbor.

Fixes #2368

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit bdf520bf74)
2026-06-30 08:28:07 +00:00
Aleksei Sviridkin 71ed3be6e4 fix(seaweedfs): point S3 consumers at the reachable S3 service
The s3-service-name.patch renamed the S3 Service to <name>-s3
(seaweedfs-s3), but several consumers still resolved it through
componentName -> <fullname>-s3. name and fullname only coincide when the
release name equals the chart name; the shipped release is
seaweedfs-system, so they pointed at seaweedfs-system-s3 while the real
service stayed seaweedfs-s3:

- the S3 ingress backend and the iceberg ingress backend, which left
  ingress-nginx returning 503 on the external S3 endpoint.

The COSI provisioner in-cluster ENDPOINT was wrong on the same axis and
also on the port. Both the s3-gateway branch and the filer-S3 branch
rendered a portless URL that defaults to 443, while the s3 service only
listens on the S3 port (8333); the filer branch additionally targeted
the filer service, which serves no S3. Both in-cluster branches now
resolve seaweedfs-s3 on the configured S3 port.

The default path (s3.ingress.enabled=true) is repaired by the ingress
fix; the COSI in-cluster paths are latent until an operator disables the
ingress. Resolve every consumer through the same name helper the Service
uses, and pin the contract with unittests rendered under the production
release name so the mismatch cannot recur.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
(cherry picked from commit 8a14de9abf)
2026-06-29 08:25:09 +00:00
Aleksei Sviridkin 0240dc128f fix(platform): order backupstrategy-controller after cozystack-basics
backupstrategy-controller creates the cozy-backups Bucket into the
tenant-root namespace, which cozystack-basics provisions. On a fresh
install the Bucket patch can race namespace creation and fail with
`namespaces "tenant-root" not found`, which then races the install
wait deadline and fails the run — on changes that have nothing to do
with backup ordering. Add the cozystack-basics dependsOn edge so the
target namespace exists first, pinned with a helm-unittest regression
guard.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
(cherry picked from commit ee853b00bd)
2026-06-25 16:21:42 +00:00
Myasnikov Daniil 459ae5324b fix(kafka): set entity-operator resources to avoid OOM under tenant LimitRange
entityOperator.topicOperator/userOperator carried no resources, so in tenant
namespaces (the LimitRange defaults containers to 128Mi) the JVM topic-operator
and user-operator are OOMKilled on startup. The entity-operator stays in
CrashLoopBackOff, KafkaTopics/KafkaUsers never reconcile, and on teardown topics
get stuck on the strimzi.io/topic-operator finalizer. Set explicit requests/limits
(256Mi/512Mi) so they no longer inherit the namespace default; add a template test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 1ab5f17cac)
2026-06-25 05:30:42 +00:00
Andrei Kvapil 5ba0fee60c chore(release): cut v1.5.1 (#3023)
This PR prepares the release `v1.5.1`.
v1.5.1
2026-06-24 08:46:15 +02:00
cozystack-ci[bot] 22d9f224f9 Prepare release v1.5.1
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com>
2026-06-24 01:42:54 +00:00
myasnikovdaniil af5b724260 [Backport release-1.5] fix(kubevirt-instancetypes): drop persistent EFI/TPM to unblock live-migration (#3016)
# Description
Backport of #3006 to `release-1.5`.
api/apps/v1alpha1/v1.5.1
2026-06-23 13:32:38 +05:00
Andrei Kvapil 9ef3fe3722 fix(kubevirt-instancetypes): drop persistent EFI/TPM to unblock live-migration
TEMPORARY revert. Restoring persistent EFI/TPM state (4cc60e3) makes
windows.11/2k22/2k25 VMs get a backend-storage PVC that is ReadWriteOnce on the
default StorageClass, which pins the VM to its node and blocks live-migration and
node drains (with evictionStrategy: LiveMigrate this can stall cluster upgrades).
See #3005.

Strip persistent EFI/TPM from the preferences again, but with `yq del` so an
emptied preferredTPM stays `{}` (the v1.8 CRD rejects null) — the bug the original
sed strip caused. Secure Boot and the vTPM stay present, just non-persistent.

Stopgap until vm-state can request an RWX backend (RWX-Block via the upstream
KubeVirt change, or an RWX-Filesystem vmStateStorageClass).

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
(cherry picked from commit 6ac2601a68)
2026-06-23 08:30:29 +00:00
myasnikovdaniil 82b8c460c6 chore(release): cut v1.5.0 (#2970)
This PR prepares the release `v1.5.0`.
v1.5.0
2026-06-22 20:45:22 +05:00
myasnikovdaniil b8ce1c4e75 docs(release): add changelog for v1.5.0 (#2971)
This PR adds the changelog for release `v1.5.0`.

✅ Changelog has been automatically generated in
`docs/changelogs/v1.5.0.md`.
2026-06-22 20:40:14 +05:00
Myasnikov Daniil 62646bff15 docs: expand v1.5.0 changelog with platform-component and upgrade detail
Merge the AI-generated v1.5.0 changelog with a fuller pass:

- Add an "Upgrade Notes and Required Actions" section (K8s 1.33+, removal
  of upgrade.force, GPU permittedHostDevices ownership, MetalLB FRR-K8s /
  HTTPS metrics, automatic TLS on externally-published services).
- Expand "Platform Components" with per-component upstream highlights and
  CVE references (Flux v2.8 CVE-2026-23990, MetalLB v0.16 CVE-2025-22874),
  plus the new frr-k8s subchart and kuberture package.
- Scope the storageClass-immutability entry (#2639) to UI-only enforcement,
  with apiserver enforcement tracked in #2657.
- Broaden Development/Testing/CI coverage and add the v1.4.x website docs
  backports (#565, #566, #567, #580).
- Correct the loop-device LVM-filter attribution to talm#215 (the prior
  ansible-cozystack#51 reference was a dependency bump, not this change).

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-06-22 20:24:27 +05:00