Commit Graph
4005 Commits
Author SHA1 Message Date
Aleksei Sviridkin 083811a40e chore(seaweedfs): bump seaweedfs-cosi-driver to v0.3.1 (#2791)
## What this PR does

Bumps the `seaweedfs-cosi-driver` image from `v0.3.0` to `v0.3.1`.

`v0.3.1` ships the stale-socket self-heal fix. After a non-graceful exit
(SIGKILL / OOM / panic) Go does not unlink the COSI unix socket, and
because it lives on an `emptyDir` that survives container restarts, the
next start failed with `bind: address already in use` and the
`objectstorage-provisioner` wedged in `CrashLoopBackOff`. While it was
down, `BucketClaim` reconciliation stopped and buckets were never
provisioned — the COSI `Bucket` could even report `bucketReady: true`
while no bucket existed. This is the CrashLoopBackOff reported in #2429.

The driver now removes a leftover socket before binding (only when it
exists, is a socket, and has no live listener), so the provisioner
self-heals on restart.

Upstream: seaweedfs/seaweedfs-cosi-driver#8 (merged), released in
v0.3.1. Points at the upstream published multi-arch image (amd64 +
arm64) — no vendoring needed.

### Release note

```release-note
fix(seaweedfs): bump seaweedfs-cosi-driver to v0.3.1, which self-heals the object storage COSI provisioner from a CrashLoopBackOff caused by a stale unix socket
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
  * Updated SeaweedFS cosi driver container image to a newer version.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-05 11:09:41 +03:00
Aleksei Sviridkin 43fc969a10 fix(backups): carry dropdown option sources in CRD annotations, not schema (#2823)
## What this PR does

Two changes that together fix the v1.5.0 release blocker where the
`cozy-backup-controller` HelmRelease failed on every reconcile.

**Root cause** — the four `backups.cozystack.io` CRDs (`backupjobs`,
`backups`, `plans`, `restorejobs`) embedded the `x-cozystack-options`
vendor extension directly in their OpenAPI validation schema.
`apiextensions` `JSONSchemaProps` only preserves the documented
`x-kubernetes-*` vendor extensions — every other `x-` key is rejected by
helm-controller's server-side apply (`field not declared in schema`) and
dropped by the apiserver on decode. So the CRDs never applied and the
controller crash-looped.

This moves the field→source mapping the dashboard needs out of the
schema and into CRD `metadata.annotations` (which the apiserver
preserves), emitted declaratively via
`+kubebuilder:metadata:annotations` markers on the Go types, and removes
the post-processing awk injector (`hack/inject-cozystack-options.awk`).
A helm-unittest suite pins the contract: each dropdown field carries its
source in annotations and must not carry `x-cozystack-options` in the
schema. The dashboard reattaches the extension client-side from these
annotations.

Annotation contract: `options.cozystack.io/source.<spec-relative-path>:
<option-source>`, e.g. `options.cozystack.io/source.applicationRef.kind:
appkind`.

**Detection gap** — the install e2e let this ship green: the fanned-out
`kubectl wait` discarded child exit codes (a bare POSIX `wait` returns
0) and the readiness check printed offending HelmReleases without ever
exiting non-zero. The gate is now restored — a single `kubectl wait hr
--all -A` for trace visibility, an outcome-based re-list that also gates
late-created HelmReleases (with a short retry to absorb momentary
drift-reconcile flaps) and `exit 1`s, plus a full Ready-condition dump
per non-ready HelmRelease so the real failure reason is visible. This
commit is by @myasnikovdaniil, consolidated here from #2824 so the root
cause and the CI gap land in one PR. With the gate restored, CI on
`main` stays red until the backups fix in this PR merges — intended.

Closes #2822 · Dashboard counterpart: cozystack/cozystack-ui#40

### Release note

```release-note
fix(backups): fix backup-controller CRDs failing to apply — the dashboard dropdown hint was placed in the CRD schema where apiextensions rejects it; it now lives in CRD annotations
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a CRD option-source test suite and a package test target to
validate annotation-based option sources.

* **Bug Fixes**
* Improved end-to-end install checks with consolidated readiness waits
and richer failure diagnostics for releases.

* **Refactor**
* Migrated backup-controller CRD option metadata from legacy vendor
extensions to annotation-based keys for cleaner schema metadata.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-04 15:31:45 +03:00
Myasnikov Daniil 67b1a960dd test(e2e): fail install test when HelmReleases are not Ready
The HR readiness gate in e2e-install-cozystack.bats was toothless in two
layers: the backgrounded kubectl-wait fan-out discarded child exit codes
(POSIX `wait` without args returns 0), and the final check echoed
"Some HelmReleases failed to reconcile" without exiting non-zero — a
regression from 1f240387f, which replaced the unavailable bats `fail`
helper with a bare echo. A permanently-failing platform HelmRelease
(e.g. the backup-controller CRD rejection, #2822) shipped through green
CI for weeks.

Replace the fan-out with a single `kubectl wait hr --all -A` for trace
visibility, gate on an outcome-based re-list (which also covers HRs
created after the snapshot, with a 120s retry to absorb momentary
drift-reconcile flaps), and dump the full Ready condition message per
non-ready HR so the real error is visible in the test output.

Refs #2822

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-06-04 15:19:28 +03:00
Aleksei Sviridkin 2363f2d9be fix(backups): carry dropdown option sources in CRD annotations, not schema
The four backups.cozystack.io CRDs injected the x-cozystack-options vendor
extension into their OpenAPI validation schema. apiextensions JSONSchemaProps
is a closed struct that only preserves x-kubernetes-* vendor extensions, so
helm-controller's server-side apply rejected the unknown key ("field not
declared in schema") and the CRDs never applied — the controller crash-looped
and backups were non-functional.

Move the field-to-source mapping the dashboard needs into CRD
metadata.annotations (preserved by the apiserver), emitted via
+kubebuilder:metadata:annotations markers, and drop the post-processing awk
injector. The dashboard reattaches the extension client-side.

A helm-unittest suite pins the contract: each dropdown field carries its source
in metadata.annotations and must not carry x-cozystack-options in the schema,
guarding against re-introducing the injector.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-04 12:45:14 +03:00
Aleksei Sviridkin adb9989b3c docs(harbor): document S3 object-storage (SeaweedFS) prerequisite (#2818) 2026-06-03 21:13:20 +03:00
Aleksei SviridkinandMyasnikov Daniil 6cfa1a6fb4 docs(harbor): document S3 object-storage (SeaweedFS) prerequisite
Harbor stores registry data only in S3-compatible object storage and
has no local-filesystem backend, but the app docs never stated this.
Without SeaweedFS available in the tenant chain the registry
BucketClaim never yields its credentials secret and the HelmRelease
silently stays unreconciled — a common and confusing first-deploy
failure.

Add a Prerequisites section to the chart README (propagated to the
published app docs) explaining the requirement, how the bucket is
provisioned via COSI from SeaweedFS, how the class is inherited down
the tenant tree, and how to enable it.

Co-authored-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-03 20:17:03 +03:00
Aleksei Sviridkin c3a50d632a test(tests): exclude loop devices from host LVM scanning (#2798)
## What this PR does

Adds an `/etc/lvm/lvm.conf` overwrite to the e2e Talos machine config so
the host LVM ignores `/dev/loop` devices, alongside the existing
`drbd`/`dm`/`zd` filters. This prevents the host from scanning and
activating volume groups located inside loop-mounted disk images used
during e2e.

### Release note

```release-note
test(e2e): exclude loop devices from host LVM scanning
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Updated cluster preparation to adjust LVM behavior in test
environments: LVM backup/archive is disabled and device filtering was
tightened to ignore ephemeral/virtual block devices.
* This reduces interference from transient devices during end-to-end
cluster tests.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2798?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-03 16:03:57 +03:00
Andrei KvapilandClaude 4dc2367fe4 test(tests): exclude loop devices from host LVM scanning
Add an /etc/lvm/lvm.conf overwrite to the e2e Talos machine config so the
host LVM ignores /dev/loop devices (alongside drbd/dm/zd). This prevents the
host from scanning and activating volume groups inside loop-mounted disk
images used during e2e.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
2026-06-03 14:00:16 +03:00
myasnikovdaniil 367ca3ab65 docs: add changelog for v1.1.7 (#2447)
This PR adds the changelog for release `v1.1.7`.

✅ Changelog has been automatically generated in
`docs/changelogs/v1.1.7.md`.
v1.5.0-alpha.1 api/apps/v1alpha1/v1.5.0-alpha.1
2026-06-03 12:25:25 +05:00
cozystack-ci[bot] 0a0541e62a docs: add changelog for v1.1.7
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com>
2026-06-03 02:11:20 +00:00
Aleksei Sviridkin 588c9f2d62 feat(api): add Option resource and x-cozystack-options for dynamic form dropdowns (#2778)
## What this PR does

Adds a generic mechanism for **runtime-populated dropdowns** in
dashboard create/edit forms. Many fields are references to live cluster
resources (GPU device names, KubeVirt instancetypes/preferences, Multus
networks, VM images, bucket storage pools, storage classes, backup
classes/plans) but were free-text or static enums that drift from
reality.

- **`Option` resource** — `core.cozystack.io/v1alpha1`, namespaced,
read-only (list/get/watch). Items are computed on read by an in-process
provider registry using a privileged client, so tenants get curated
option lists without broad cluster reads. Providers: `gpu`,
`instancetype`, `instanceprofile`, `network`, `image`, `storagepool`,
`storageclass`, and backup-related `backupclass`, `plan`, `backup`,
`appkind`.
- **`x-cozystack-options` schema keyword** — app `values.yaml` declare
it via the new cozyvalues-gen `@x-<keyword>` directive (e.g. `##
@x-cozystack-options {source: instancetype}`). Schemas, Go types, and
the `-rd` ApplicationDefinitions are regenerated from source so the
extension is carried into the served OpenAPI. The dashboard renders
annotated fields as dropdowns sourced from the live cluster (UI widget
ships in a separate cozystack-ui PR).
- **RBAC** — the apiserver ServiceAccount gets the privileged reads;
tenants get read-only access to `options` in their namespace via the
aggregated tenant ClusterRoles (granted at `cozy:tenant:view:base`, so
every access level from view upward can populate dropdowns).
- Drops the redundant manual `instanceProfile` enum injection from
vm-instance's Makefile (now driven dynamically).

## Dependency on cozyvalues-gen

The `x-cozystack-options` annotations require the new vendor-extension
directive from cozystack/cozyvalues-gen#27. This PR bumps the pinned
cozyvalues-gen to **v1.6.0**. **A cozyvalues-gen v1.6.0 release must be
cut before CI can pass** — until then the pre-commit schema-generation
job cannot download the binary and will fail.

### Screenshots

N/A — no UI changes in this repository. The consuming dashboard widget
ships in a separate cozystack-ui PR.

### Release note
```release-note
feat(api): add a namespaced read-only Option resource (core.cozystack.io) and the x-cozystack-options schema keyword, enabling dashboard form fields (GPU, instancetype/profile, network, image, storage pool/class, backup class/plan) to render as dropdowns populated from live cluster resources
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Expanded dynamic dropdowns across many apps (storageClass,
storagePool, instanceType/instanceProfile, GPU, image, vmdisk, network,
backup/plan/backupclass, appkind) so chart fields can be populated from
live cluster resources.

* **Chores**
* Updated API/RBAC manifests to grant read access needed for option
providers.

* **Tests**
* Added provider and REST tests to validate option sources, scoping,
ordering, and failure handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
v1.5.0-rc.1 api/apps/v1alpha1/v1.5.0-rc.1
2026-06-03 01:52:12 +03:00
Aleksei Sviridkin 26da871c00 feat(rbac): allow tenants to start/stop/restart their VMs (#2777)
<img width="2770" height="1970" alt="image"
src="https://github.com/user-attachments/assets/774c7c71-0e6a-496a-9898-8ad1ed848b0a"
/>


## Summary

Grants tenant users permission to call the KubeVirt VM power-control
subresources (`subresources.kubevirt.io` →
`virtualmachines/{name}/start|stop|restart`, verb `update`) by adding
them to the `cozy:tenant:use:base` ClusterRole, which aggregates into
`tenant-use`, `tenant-admin`, and `tenant-super-admin`.

Without this, the dashboard VM power buttons return 403 even for tenant
admins: `kubectl auth can-i update virtualmachines --subresource=start`
is `no` for every tenant role today, while
`virtualmachineinstances/console|vnc|portforward` are already granted at
the same `use` level — so power control belongs there too.

## Companion PR

UI that consumes this permission (VM power controls + VNC fixes):
cozystack/cozystack-ui#27

## Test

- `helm template` renders `cozy:tenant:use:base` with the new
`virtualmachines/start|stop|restart` rule.
- After apply, `kubectl auth can-i update virtualmachines
--subresource=start|stop|restart` flips from `no` to `yes` for the
`tenant-*-use/admin/super-admin` groups.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Virtual Machine Power Control: Users with tenant-level access can now
start, stop, and restart virtual machines, providing enhanced management
capabilities for VM operations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-03 00:12:53 +03:00
Aleksei Sviridkin 7857282d66 chore(seaweedfs): bump seaweedfs-cosi-driver to v0.3.1
v0.3.1 ships the stale-socket self-heal: the driver removes a leftover
COSI unix socket before binding, so the objectstorage provisioner
recovers from CrashLoopBackOff after a non-graceful exit (SIGKILL / OOM /
panic) instead of wedging on "bind: address already in use".

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-03 00:11:27 +03:00
Aleksei Sviridkin 86ae5a5f02 fix(kubernetes): stamp application lineage labels on worker node VMs (#2779)
## What this PR does

Worker-node VMs of a tenant Kubernetes cluster are created by Cluster
API / the KubeVirt provider, so their virt-launcher pods were never
stamped with the `apps.cozystack.io/application.{group,kind,name}`
lineage labels that the chart already sets on its other resources
(`ingress.yaml`, `cloud-config.yaml`). As a result the dashboard could
not attribute those pods to the owning `kubernetes` application instance
— e.g. cluster resource-consumer views showed the raw `virt-launcher-…`
pod name with no link to the cluster.

This adds the application lineage labels to the worker VM template
(`KubevirtMachineTemplate` →
`virtualMachineTemplate.spec.template.metadata.labels`), mirroring the
existing stamping in `ingress.yaml`/`cloud-config.yaml`. KubeVirt
propagates labels from this block to the VMI and the virt-launcher pods
(the same path the existing `cluster.x-k8s.io/deployment-name` label
already takes), so the dashboard now attributes worker VMs to their
Kubernetes application.

### Screenshots

N/A — no UI changes; this only affects generated pod labels.

### Release note
```release-note
fix(kubernetes): stamp tenant Kubernetes worker-node VMs with apps.cozystack.io/application.{group,kind,name} so the dashboard attributes their pods to the owning Kubernetes application
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Added application lineage labels to Kubernetes VM templates so cluster
VMs include application group, kind, and name (release-name prefix
trimmed).
* **Tests**
* Added template tests validating those labels and the name-trimming
behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-03 00:06:46 +03:00
Aleksei Sviridkin 7d1096b8da fix(kubernetes): quote worker-VM application.name label and cover it with tests
Pin the apps.cozystack.io/application.{group,kind,name} labels stamped on the
worker VM template, including a case that trims the kubernetes- release prefix.
Also quote application.name so a purely-numeric cluster name renders a YAML
string rather than an integer that a label value would reject.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-02 19:46:01 +03:00
Aleksei Sviridkin c9655e23d5 test(cozystack-api): guard instancetype/instanceprofile provider GVRs
instancetype/instanceprofile replaced the static vm-instance enum with live
dropdowns sourced from instancetype.kubevirt.io. A wrong group/version would
make those dropdowns silently empty in every cluster (List tolerates the
error) — the failure mode the appkind GVR guard already catches. Pin both
GVRs to the served API.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-02 18:27:09 +03:00
Aleksei Sviridkin f92b49e1c9 fix(cozystack-api): log when the option watch's initial list fails
The one-shot option watch emitted the current state once then blocked on
ctx.Done(). If the initial List failed (e.g. every provider forbidden by a
broken RBAC config) it blocked silently, leaving the client with an empty
stream indistinguishable from "no options". Log the error so an operator can
tell the two apart.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-02 18:27:09 +03:00
Aleksei Sviridkin e59f93605a test(cozystack-basics): pin tenant view grant on core.cozystack.io options
The tenant view aggregation grants read on the core.cozystack.io/options
virtual resource so dashboard dropdowns can be populated. Add a helm-unittest
on the cozy:tenant:view:base ClusterRole so a silent RBAC regression on the
tenant side is caught, mirroring the apiserver-side rbac test.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-02 18:20:51 +03:00
Aleksei Sviridkin d563912c0d test(cozystack-api): cover the parsing-heavy Option providers
The gpu, image, storagepool, storageclass and vmdisk providers carry the real
parsing logic (whitelist-intersect-allocatable, prefix strip, nested pool
walk, default-class marking, size label) but had no tests — and List tolerates
a provider error by skipping it, so a logic bug ships as a silently empty or
wrong dropdown rather than a failure. Add table tests against the fake dynamic
client for each, raising option-package coverage from 39% to 77%.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-02 18:19:09 +03:00
Aleksei Sviridkin 6c7f851156 fix(cozystack-api): migrate apps fuzzer to randfill so the roundtrip test passes
The core fuzzer was migrated from gofuzz to randfill, but the apps fuzzer was
left on gofuzz. TestRoundTripTypes runs both fuzzers (apps first), so
registering a gofuzz.Continue func against the randfill-based roundtrip harness
panics ("customFuncs' second argument must be a randfill.Continue").

Migrate the apps fuzzer the same way (randfill.Continue / FillNoCustom). With
no gofuzz consumers left, go mod tidy drops the dependency. TestRoundTripTypes
now passes.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-02 18:06:26 +03:00
Aleksei Sviridkin f0f1431546 feat(kubernetes): enable GPU passthrough out-of-the-box (NvLinkDisable + gpu=on) (#2780)
## What this PR does

Makes GPU passthrough work out-of-the-box for tenant Kubernetes clusters
(the `kubernetes` app), so operators no longer have to hand-fix it. Two
defaults:

1. **Tenant gpu-operator loads the nvidia driver with
`NVreg_NvLinkDisable=1`.**
2. **Nodes in GPU node-groups are labeled `gpu=on` automatically.**

### Root cause (validated empirically on a live cluster)

cozystack passes through individual SXM GPUs (e.g. H200) into tenant
worker VMs **without** the NVSwitches. Such a GPU's driver then waits
for an NVLink fabric that can never come up — there is no NVSwitch and
fabricmanager has nothing to manage — so `nvidia-smi` reports `Fabric
State: In Progress` indefinitely. As a result every CUDA call fails with
`system not yet initialized`, `nvidia-operator-validator`
(cuda-validation) crashloops, and no GPUs are advertised.

Loading the kernel module with `NVreg_NvLinkDisable=1` flips `Fabric
State` to `N/A`, CUDA initializes, and the validators pass. This is the
correct setting for per-GPU passthrough because there is no in-VM
NVLink/NVSwitch peering — the GPU is alone in the VM, so there is
nothing to disable that the workload needs.

The second issue: HAMi's `hami-device-plugin` DaemonSet has
`nodeSelector: gpu=on` and stays at `DESIRED=0` because GPU worker nodes
lack that label, so `nvidia.com/gpu` is never advertised. Labeling the
GPU nodes `gpu=on` lets the device plugin schedule and advertise the
GPUs.

### How it works

- **`gpu=on` label:** node-groups in
`packages/apps/kubernetes/templates/cluster.yaml` that declare `gpus`
get `--node-labels=gpu=on` appended to their kubelet args (in the
`KubeadmConfigTemplate` `joinConfiguration`). Non-GPU node-groups are
untouched.
- **`NVreg_NvLinkDisable=1`:** the upstream gpu-operator chart only
references an existing ConfigMap by name
(`driver.kernelModuleConfig.name`) and never creates it. The
gpu-operator wrapper chart (`packages/system/gpu-operator`) now renders
that ConfigMap (opt-in via `kernelModuleConfig.create`) so it deploys
into the tenant `cozy-gpu-operator` namespace alongside the operator.
The tenant gpu-operator HelmRelease defaults enable it and point
`driver.kernelModuleConfig.name` at it.

Both defaults remain overridable: a user setting
`addons.gpuOperator.valuesOverride` still wins via the existing
`mergeOverwrite(defaults, overrides)` in the HelmRelease template.

### Screenshots

Not applicable — no UI changes.

### Release note

```release-note
feat(kubernetes): enable GPU passthrough out-of-the-box for tenant clusters. GPU node-groups are now labeled `gpu=on` so HAMi's device plugin schedules and advertises `nvidia.com/gpu`, and the tenant gpu-operator loads the nvidia driver with `NVreg_NvLinkDisable=1` so single-SXM-GPU passthrough (no NVSwitch in the VM) no longer hangs on `Fabric State: In Progress` / `system not yet initialized`. Both defaults are overridable via `addons.gpuOperator.valuesOverride`.
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* GPU-enabled node groups now get the expected GPU label during node
registration.
* GPU operator supports optional kernel-module parameter configuration
with a default parameter set.

* **Tests**
* Added tests verifying GPU node labeling is applied only for
GPU-enabled node groups.
* Added tests validating kernel-module config creation, naming, and
content behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-02 17:53:05 +03:00
Aleksei Sviridkin 35a2970907 build(deps): mark sigs.k8s.io/randfill as a direct dependency
fuzzer.go imports sigs.k8s.io/randfill directly after the gofuzz-to-randfill
migration, but go.mod still listed it under the indirect block. go mod tidy
moves it into the direct require block; commit the result so the metadata
matches the import and a contributor running go mod tidy gets no diff.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-02 17:49:08 +03:00
Aleksei Sviridkin 4c34e0f427 build(codegen): reproduce x-cozystack-options on backup CRDs after controller-gen
controller-gen cannot emit the x-cozystack-options vendor extension, so the
annotations on the backup CRDs (backupjobs/backups/plans/restorejobs) were
hand-edited into generated output — the next make generate would strip them,
breaking the appkind/plan/backup form dropdowns and failing the codegen-drift
check.

Add a deterministic awk pass that re-applies the extension after
controller-gen, driven by per-CRD field targets. It inserts lines without
reserializing the YAML, so it reproduces the committed CRDs byte-for-byte and
is independent of the host yq version.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-02 17:38:27 +03:00
Aleksei Sviridkin 9e19c7dd0b test(cozystack-api): cover Option List tolerate-and-skip vs Get 500 on provider error
The Option REST deliberately makes List tolerate a single failing provider
(it logs and skips it, so a partial RBAC config drops one dropdown instead
of breaking the whole list) while Get surfaces the error as a 500. Neither
path was covered. Add tests that inject a failing provider and assert List
keeps the healthy sources and omits the failing one, and that Get returns an
InternalError.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-02 17:13:19 +03:00
Aleksei Sviridkin b30eb4602d fix(cozystack-api): drop unused objectstorage bucketclasses RBAC grant
The Option providers RBAC block granted read on
objectstorage.k8s.io/bucketclasses, but no provider in the registry lists
BucketClasses and no schema field references a bucketclass source — the
bucket app's storage pool field uses the storagepool provider, which reads
seaweedfs HelmReleases. Drop the unused grant and pin its absence in the
chart test.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-02 17:13:18 +03:00
Aleksei Sviridkin 4cbe5b62d1 fix(cozystack-api): grant the option apiserver read on plans and backups
The plan and backup option providers list namespaced plans and backups in
the backups.cozystack.io group, but the cozystack-api ClusterRole only
granted backupclasses there. The apiserver's privileged client runs as this
ServiceAccount, so both providers got forbidden — List silently dropped the
sources and Get returned 500, leaving the plan/backup dropdowns in the
backup forms empty.

Add plans and backups to the backups.cozystack.io rule and cover the grant
with a chart test.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-02 16:42:59 +03:00
Aleksei Sviridkin 6babd3324c fix(cozystack-api): list ApplicationDefinitions from the cozystack.io group
The appkind option provider queried applicationdefinitions in the
apps.cozystack.io group, but the ApplicationDefinition CRD is served from
cozystack.io. The provider listed a resource that does not exist, so the
appkind source resolved empty on every cluster (tolerated and dropped by
List, 500 on Get), and the backup form fields that reference it never
populated.

Point the provider at cozystack.io, which the existing cozystack.io read
grant already covers. Add a guard test pinning the CRD group, since the
existing fake-client test is built from the same GVR and cannot catch a
wrong group.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-02 16:42:58 +03:00
Andrei KvapilandClaude 34e370e5c5 chore(apps): regenerate schemas and ApplicationDefinitions with x-cozystack-options
Regenerate values.schema.json and embedded openAPISchema in the -rd
ApplicationDefinitions so the x-cozystack-options dynamic-source
directives declared in each app values.yaml are carried into the
generated artifacts. Brings the -rd schemas in sync with the
hand-edited values.schema.json files (CI runs make generate +
git diff --exit-code).

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
2026-06-02 16:42:58 +03:00
Andrei KvapilandClaude 49976c02cd fix(rbac): grant tenant view role read access to options
Options were granted on cozy:tenant:use:base, leaving view-only tenant
users (cozy:tenant:view) without read access — so their create/edit
forms could not populate dynamic dropdowns. Move the grant down to
cozy:tenant:view:base, which aggregates up into use/admin/super-admin,
covering every human tenant access level. The tenant ServiceAccount role
keeps its grant via cozy:tenant:base.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
2026-06-02 16:35:23 +03:00
Andrei KvapilandClaude 518040d4b8 chore(vm-instance): drop redundant instanceProfile enum injection
The instanceProfile field is now driven by an x-cozystack-options dynamic
dropdown (source: instanceprofile), so the Makefile generate step no longer
needs to post-inject a static enum into values.schema.json via yq. Remove
that injection along with the already-commented-out instanceType enum
injection so the dynamic dropdown is not fought by a stale static enum.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
2026-06-02 16:35:23 +03:00
Andrei KvapilandClaude 972a1b3973 feat(apps): declare storageClass x-cozystack-options in data app values.yaml
Add the storageclass dynamic-dropdown x-cozystack-options annotation to the
storageClass field of the data apps (openbao, clickhouse, mariadb, redis,
harbor, opensearch, rabbitmq, mongodb, postgres, nats, kafka, qdrant,
foundationdb, http-cache) at the values.yaml source, so the extension is
emitted by cozyvalues-gen instead of hand-edited into values.schema.json.
kafka carries two (Kafka and ZooKeeper structs); foundationdb is nested
under the Storage struct.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
2026-06-02 16:33:57 +03:00
Andrei KvapilandClaude 0d1e5aa2f3 feat(apps): declare x-cozystack-options in vm/storage app values.yaml
Move the dynamic-dropdown x-cozystack-options vendor extensions from the
generated values.schema.json into the values.yaml source for vm-instance,
kubernetes, vm-disk and bucket so they survive schema regeneration. The new
cozyvalues-gen @x-<keyword> directive emits these extensions from the source
annotations. Annotations on typedef @field entries (Disk, Network, GPU,
NodeGroup) are declared once and cover every place the typedef is reused.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
2026-06-02 16:33:57 +03:00
Andrei KvapilandClaude d0c74f9b89 feat(cozystack-api): add plan, backup and appkind Option sources
Add three dropdown sources to the Option provider registry so the
dashboard's backup forms can use the unified x-cozystack-options
mechanism instead of injecting client-side enum arrays:

- plan / backup: namespaced, list Plans / Backups in the tenant namespace
- appkind: cluster-scoped, lists application kinds from ApplicationDefinitions

Annotate the backups.cozystack.io CRD schemas (Plan, BackupJob, Backup,
RestoreJob) with x-cozystack-options on backupClassName (backupclass),
applicationRef.kind / targetApplicationRef.kind (appkind), planRef.name
(plan) and backupRef.name (backup). This also gives the previously
unused backupclass source its first consumer.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
2026-06-02 16:33:57 +03:00
Andrei KvapilandClaude c2f3e1f379 feat(apps): annotate schema fields with x-cozystack-options
Mark fields that reference cluster resources with the x-cozystack-options
vendor keyword so the dashboard renders them as dynamic dropdowns:
GPU, instanceType, instanceProfile, network (vm-instance), GPU/
instanceType (kubernetes nodeGroups), image (vm-disk), storagePool
(bucket), storageClass (all apps) and disks[].name (vm-instance). Drop
the stale static instanceProfile enum.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
2026-06-02 16:33:57 +03:00
Andrei KvapilandClaude ec6ec76703 feat(cozystack-api): add Option resource for dynamic form dropdowns
Introduce a read-only, namespaced virtual resource
core.cozystack.io/v1alpha1 Option that serves named lists of dropdown
options for the dashboard. Each object (one per source: gpu,
instancetype, instanceprofile, network, image, storagepool,
storageclass, vmdisk, backupclass) is computed on read by an in-process
provider using a privileged dynamic client, so tenants need no direct
access to Nodes, the KubeVirt CR, cluster instancetypes, etc.

Tenants get read access via the aggregated cozy:tenant roles; the
apiserver ServiceAccount gains read on the underlying sources.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
2026-06-02 16:32:38 +03:00
Andrei KvapilandClaude a17234d12e fix(cozystack-api): migrate core fuzzer to randfill
The roundtrip test for the core API group panicked (Filler.Funcs:
customFuncs' second argument must be a randfill.Continue) because the
fuzzer still used github.com/google/gofuzz while apimachinery switched
to sigs.k8s.io/randfill. Port the fuzz functions to randfill.Continue
/ FillNoCustom.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
2026-06-02 16:32:38 +03:00
Aleksei Sviridkin 86c3de292c ci: bump cozyvalues-gen to v1.6.0 (#2784)
## What this PR does

Bumps the `cozyvalues-gen` version installed in the pre-commit CI
workflow from v1.5.0 to v1.6.0.

v1.6.0 adds two additive, opt-in annotation features: the `date-time`
string-format alias (mirroring the existing `date` alias) and the
`@example` annotation, which accumulates JSON-encoded values into an
`examples` array on the matching schema property.

Both are opt-in, so charts that do not use the new directives regenerate
byte-identical `values.schema.json`, `README.md` and Go type output.
Verified locally by regenerating all 31 packages (`apps/*`, `extra/*`,
`library/cozy-lib`) with both v1.5.0 and v1.6.0 — every produced
artifact is identical, so the pre-commit regeneration check stays green
without touching any committed file.

### Release note

```release-note
ci: bump cozyvalues-gen to v1.6.0
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Updated build tool to the latest version for improved code generation.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-02 16:29:00 +03:00
Aleksei Sviridkin bac670d07b ci: bump cozyvalues-gen to v1.6.0 in pre-commit workflow
v1.6.0 adds the date-time string-format alias and the @example
annotation. Both are additive, opt-in directives: charts that do not
use them regenerate byte-identical schema, README and Go type output,
so the pre-commit check stays green without touching any committed
artifact.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-02 15:20:43 +03:00
Aleksei Sviridkin a60c2e6e68 fix(gpu-operator): name kernel-module ConfigMap after the driver reference
The wrapper chart named the kernel-module ConfigMap from its own
kernelModuleConfig.name key, while the NVIDIA driver mounts the ConfigMap
referenced by gpu-operator.driver.kernelModuleConfig.name. The two keys
defaulted to the same value but were never linked, so overriding only the
driver reference left the driver pointing at a ConfigMap that is never
rendered and the NVreg_NvLinkDisable=1 parameters were silently dropped.

Derive the ConfigMap name from the driver's effective reference, falling
back to the wrapper key when unset, so the rendered ConfigMap and the
driver reference can no longer diverge. Add helm-unittest coverage for the
rendered name, namespace, data key, and the create=false no-op.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-06-02 14:34:02 +03:00
myasnikovdaniil c435efa199 feat(flux): upgrade to v2.8.0 + chart fixes for strict SSA & kstatus (folds #2612) (#2602)
## What this PR does

Upgrades Flux v2.7.3 → v2.8.0 across both the vendored tenant chart and
the embedded management-cluster manifests, and folds in the chart fixes
that become hard errors under the new strict helm-controller v1.5.

Flux v2.8's helm-controller v1.5.0 ships:
- **Server-Side Apply with `--force-conflicts`** — strict CRD schema
enforcement; misplaced fields (silently dropped on v2.7) now fail the
apply.
- **kstatus-based health checking** by default — parent HR waits for
every applied resource (including child HRs) to be Ready before flipping
its own Ready, surfacing latent ordering deadlocks.
- **Helm v4 embedded** — `upgrade.force: true` is deprecated and now
conflicts with SSA.
- **`HelmRelease.spec.healthCheckExprs`** — prerequisite for proper
readiness gating (used in PR #2601 split).

Folds in PR #2612 (kubevirt-instancetypes null TPM fix) since the same
Flux upgrade triggers it.

## Commits

**Flux upgrade itself:**
- `feat(fluxcd)`: bump `flux-operator` / `flux-instance` vendored charts
to v0.48.0; web UI opt-in.
- `feat(flux)`: regenerate embedded management-cluster manifests via
`make update` in `packages/core/flux-aio` (timoni bundle build).

**Chart fixes for strict SSA — fields the chart sent that v2.7 silently
dropped, v2.8 rejects:**
- `fix(kubevirt-instancetypes)`: drop persistent strip that produced
null `preferredTPM` (folds #2612).
- `fix(foundationdb)`: relocate `faultDomain`, `imageType`, `labels`,
`minimumUptimeSecondsForBounce` from inside `automationOptions` to
direct children of `spec`.
- `fix(kafka)`: place `enableServiceLinks` under `template.pod`, not a
phantom `template.spec`.
- `fix(vm-instance)`: emit `disk: {}` (not `disk:`/null) when no bus is
set.
- `fix(platform)`: drop deprecated `upgrade.force: true` from
HelmReleases; fix `kafka` WorkloadMonitor `replicas` paths.

**Ordering / deadlock fixes under v2.8 kstatus:**
- `fix(vpa)`: break circular wait between parent install and nested
`vpa-for-vpa` HR.
- `fix(kubernetes)`: drop lookup-guarded parent-HR `dependsOn` on tenant
addon child HRs (parent waits on child via kstatus, child waited on
parent — deadlock).

**E2E waits for v2.8 kstatus timing:**
- `test(e2e)`: bump app HR-Ready waits to 5m (was 20s–100s under v2.7's
faster dispatch).
- `test(e2e)`: wait for parent HR Ready before downstream asserts in
`run-kubernetes.sh` and `vminstance.bats`.

## Scope discipline

This PR is part of the split of #2619 (the consolidated CI fixes branch)
into review-friendly pieces. Companion PRs:
- **PR #2601** (seaweedfs split) — folded into this PR (commits
`29c6afc8`, `0e8b46d7`, `7157158c`, `dccdeb52`, `f880b324`): the
seaweedfs-system → seaweedfs-db + seaweedfs-system split, its adoption
migration 43 (targetVersion 44), and the configurable db resources all
land here, because the strict-SSA `upgrade.force` removal and the
kstatus parent-HR timeout bump only make sense together with the split.
#2601 is superseded.
- **PR #2558** (drop 3× retry on `Run E2E` + `Install Cozystack`) —
independent, lands separately.
- Several smaller standalone fixes lifted out of #2619 (startup probes,
cert-manager `dependsOn`, prepull machinery, CSI HR timeout, NFS/OIDC
test improvements) — opened as separate PRs.

## Verification

- `helm template` renders cleanly for both `fluxcd` and
`fluxcd-operator` packages with `web.enabled=false` (default) and
`web.enabled=true`.
- Embedded `cmd/cozystack-operator` binary contains the v1.5.0 / v1.8.0
/ v2.1.0 controller image strings.
- No references to the v0.39-removed `--disable-wait-interruption` flag
anywhere in `packages/` or `internal/`.

### Release note

```release-note
Flux upgraded to v2.8.0 (helm-controller v1.5 — Helm v4 Server-Side Apply with --force-conflicts, kstatus health checking).

When upgrading existing clusters:
- Kubernetes 1.33+ is now required for the platform (management) cluster, and for any tenant cluster that enables the optional (default-off) Flux addon — that addon ships the bumped Flux too.
- HelmReleases no longer set `upgrade.force: true`. Helm v4 SSA resolves field-ownership conflicts automatically (`--force-conflicts`), but that is not the old client-side replace: immutable-field changes (e.g. StatefulSet volumeClaimTemplates/serviceName) no longer self-heal and require manual recreation — delete the object (e.g. `kubectl delete sts <name> --cascade=orphan`) and let Flux recreate it.
- KubeVirt: persistent TPM/EFI is re-enabled for the Windows 11/2k22/2k25 preferences (KubeVirt 1.8 VMPersistentState); each affected VM provisions an extra RWO backend-storage PVC from the default StorageClass.
- KubeVirt: the EOL centos.7*/centos.stream8* preferences are retained as deprecated, hidden aliases (`tags: hidden`, `instancetype.kubevirt.io/deprecated: "true"`) — existing VMInstances on these profiles keep rendering and need no action on upgrade, but the profiles are no longer offered for new VMs; repoint to centos.stream9/10 when convenient. The gn1.* GPU instancetypes are likewise retained.
- FoundationDB: imageType now reaches the operator (silently dropped pre-SSA); it is pinned to `split` to match the value existing clusters effectively ran, so upgrades stay non-disruptive. Set `imageType: unified` to migrate deliberately.
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Flux Status web UI: optional web server, config secret, service port,
HTTPRoute/Ingress, network policy, and RBAC roles.
  * New SeaweedFS DB Helm chart and optional managed DB release.

* **Improvements**
* CRD/schema enhancements: new provider kinds, validations, variant
option, and external checksum refs.
* Raised Kubernetes prerequisite to 1.30+; extended e2e timeouts for
reliability.

* **Chores**
* Bumped Flux Operator and Flux versions; documentation links updated to
fluxoperator.dev.
* **Bug Fixes**
  * Removed aggressive HelmRelease force-upgrade/install flags.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2602?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-02 14:08:39 +05:00
Aleksei Sviridkin 2d58479f06 feat(platform): add kuberture as optional system package (#2647)
<!-- Thank you for making a contribution! Here are some tips for you:
- Use Conventional Commits for the PR title: `type(scope): description`
- Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore
- Scopes are not an exhaustive list — pick the most specific scope for
the change and extend the list when a genuinely new area appears.
Examples:
- System components: dashboard, platform, operator, cilium, kube-ovn,
linstor, fluxcd, cluster-api
- Managed apps: postgres, mariadb, redis, kafka, clickhouse,
virtual-machine, kubernetes
- Development and maintenance: api, hack, tests, ci, docs, maintenance
- Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or
add a `BREAKING CHANGE:` footer
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does

Adds [kuberture](https://github.com/lexfrei/kuberture) as an optional
system package. Kuberture is a small controller that translates the
`default/kubernetes` EndpointSlice into headless Services with
external-dns-style annotations, closing the gap that external-dns cannot
use EndpointSlice as a source directly:

```text
EndpointSlice (kubernetes) → kuberture → headless Service(s) with annotations → external-dns → DNS
```

The package is opt-in via `bundles.enabledPackages:
[cozystack.kuberture]`. Operators populate `config.outputs` per
HelmRelease — each output targets a specific external-dns instance by
its `annotationPrefix`, so a single kuberture install can serve multiple
external-dns instances (e.g. one publishing to the public zone, another
to an internal corporate prefix) simultaneously.

The vendored chart at `packages/system/kuberture/charts/kuberture/` is
pulled from `oci://ghcr.io/lexfrei/kuberture/charts/kuberture` and
pinned by both version AND OCI manifest digest in the Makefile
(mirroring the ouroboros lockstep pattern). The controller image at
`ghcr.io/lexfrei/kuberture` is pinned by tag+sha256 in the shim
values.yaml. Both pins advance in lockstep with each upstream release;
the procedure is documented inline.

Install-order dependencies declared in `PackageSource`:
`cozystack.networking` (CNI before pod scheduling) and
`cozystack.prometheus-operator-crds` (ServiceMonitor CRD before the
chart's enabled-by-default ServiceMonitor manifest). External-dns is
deliberately NOT a dependency — kuberture creates annotated Services
regardless of whether external-dns is installed; external-dns is a
downstream consumer, not a runtime prerequisite.

Coverage:

- **helm-unittest**
(`packages/system/kuberture/tests/kuberture_test.yaml`, 10 cases):
fail-fast on empty outputs, ServiceMonitor enabled by default, source
pinned to `default/kubernetes`, multi-output prefix isolation,
empty-string `annotationPrefix` schema rejection, omitted-prefix
fallback to controller default, ConfigMap → Deployment volume mount,
controller image pinned by tag+sha256 digest, Role permits full Service
lifecycle, metrics containerPort derived from `metricsBindAddress`.
- **bats e2e** (`hack/e2e-apps/kuberture.bats`): applies a `Package
cozystack.kuberture` with two outputs (different `annotationPrefix`
values), waits for HelmRelease and controller readiness, asserts both
output Services are headless and carry the right
`hostname`/`target`/`ttl` annotations under their configured prefix,
cross-checks per-output isolation (each Service only carries its own
prefix's annotations).

### Screenshots

<!-- REQUIRED for UI changes: attach screenshots or screen recordings
demonstrating
the visual impact of your changes. PRs with UI changes without
screenshots will not be merged. -->

Not applicable — no UI changes.

### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same `type(scope):` prefix as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
feat(platform): add kuberture as an optional system package — translates the default/kubernetes EndpointSlice into annotated headless Services so external-dns can publish the Kubernetes API endpoint to DNS. Opt-in via bundles.enabledPackages: [cozystack.kuberture]; populate config.outputs to target one or more external-dns instances by annotationPrefix.
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Adds kuberture as an optional system package to publish the Kubernetes
API endpoint to DNS via configurable external-dns outputs; includes Helm
chart, RBAC, NetworkPolicy, PDB, ServiceMonitor, and validated chart
values/schema.

* **Tests**
* Adds Helm unit tests and an end-to-end smoke test validating
controller readiness, headless Service annotations, and external-dns
routing/probe behavior.

* **Documentation**
* Adds a full README with usage, configuration examples, and
vendoring/pinning guidance.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2647)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---

Documentation companion:
[cozystack/website#539](https://github.com/cozystack/website/pull/539).
2026-06-02 09:40:26 +03:00
Myasnikov Daniil 910b505dc3 fix(fluxcd): sync app.kubernetes.io/version on the tenant flux Deployment
Address non-blocking review feedback from lexfrei: the flux-tenants
sharded-controller Deployment carried a stale app.kubernetes.io/version: v2.7.3
while running helm-controller v1.5.0 (Flux 2.8.0). The flux-aio Makefile update
target only synced the container image, so the version label drifted on every
bump and make update never self-corrected. Bump the label to v2.8.0 and extend
the yq patch to carry app.kubernetes.io/version from the source flux Deployment.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-06-02 11:00:45 +05:00
Andrei KvapilandClaude 238a3b665c feat(kubernetes): default NVreg_NvLinkDisable=1 for tenant gpu-operator
When a single SXM GPU (e.g. H200) is passed through into a tenant worker
VM without the NVSwitches, the nvidia driver waits forever for an NVLink
fabric that can never come up (Fabric State: In Progress). Every CUDA
call then fails with 'system not yet initialized' and the
nvidia-operator-validator crashloops. Loading the kernel module with
NVreg_NvLinkDisable=1 flips Fabric State to N/A so CUDA initializes —
correct for per-GPU passthrough, which has no in-VM NVLink/NVSwitch
peering anyway.

The upstream gpu-operator chart only references an existing ConfigMap by
name via driver.kernelModuleConfig.name and never creates it. Render
that ConfigMap from the gpu-operator wrapper chart (opt-in via
kernelModuleConfig.create) so it lands in the tenant cozy-gpu-operator
namespace alongside the operator, and wire the tenant HelmRelease
defaults to enable it and point the driver at it. Remains overridable
via addons.gpuOperator.valuesOverride.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
2026-06-02 02:56:11 +02:00
Andrei KvapilandClaude 9c0e76dc05 feat(kubernetes): label GPU node-groups with gpu=on
cozystack passes individual GPUs into tenant worker VMs, but HAMi's
hami-device-plugin DaemonSet carries nodeSelector gpu=on and stays at
DESIRED=0 until the GPU nodes are labeled, so nvidia.com/gpu is never
advertised. Append --node-labels=gpu=on to the kubelet args of every
node-group that declares gpus, so GPU nodes register with the label out
of the box. Non-GPU node-groups are unaffected.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
2026-06-02 02:56:01 +02:00
Andrei KvapilandClaude 99360dab29 fix(kubernetes): stamp application lineage labels on worker node VMs
Worker-node VMs of a tenant Kubernetes cluster are created by Cluster API
and the KubeVirt provider, so their virt-launcher pods were never stamped
with the apps.cozystack.io/application.{group,kind,name} lineage labels the
chart already sets on its other resources (ingress, cloud-config). Without
them the dashboard cannot attribute those pods to the owning Kubernetes
application instance.

Add the labels to the worker VM template (KubevirtMachineTemplate ->
virtualMachineTemplate.spec.template.metadata.labels), mirroring the
existing stamping in ingress.yaml/cloud-config.yaml. KubeVirt propagates
them to the VMI and virt-launcher pods (the same path as the existing
cluster.x-k8s.io/deployment-name label).

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
2026-06-02 01:58:20 +02:00
Andrei KvapilandClaude 8e78add8ea feat(rbac): allow tenants to start/stop/restart their VMs
Grant the subresources.kubevirt.io virtualmachines/start|stop|restart
"update" verb in cozy:tenant:use:base so the dashboard VM power
controls work for tenant users (aggregates into admin/super-admin).
Without it the actions return 403 even for tenant admins.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io>
2026-06-01 21:07:06 +02:00
Aleksei Sviridkin d2bc261897 chore(metallb): bump to v0.16.1 and rotate host-network port denylist (#2699)
## What this PR does

Bump vendored MetalLB chart from v0.15.2 to v0.16.1 and rebuild the
cozystack-built `metallb-controller` / `metallb-speaker` images at the
new version. Adopt the upstream-default BGP backend: FRR-K8s replaces
the deprecated FRR mode. Rotate the cilium-networkpolicy clusterwide
host-network port denylist to match the v0.16 listener set.

The chart introduces a new probe contract (`/healthz` and `/readyz` on a
dedicated host-local port 17472, distinct from the metrics port), so the
cozystack-built controller and speaker images must move in lockstep with
the chart. Leaving the digests on v0.15.2 would CrashLoopBackOff every
pod on main between merge and the next release-prep commit.

Upstream highlights between 0.15.2 and 0.16.1:

- 0.16.0: FRR-K8s is the new default BGP backend; legacy FRR is
deprecated.
- 0.16.0: kube-rbac-proxy replaced by native TLS; metrics are HTTPS-only
on the formerly-rbac-proxy port 9120. Default TLS minimum is now 1.3
(was 1.2); operators with TLS 1.2 scrapers override via
`metallb.tls.minVersion: VersionTLS12`.
- 0.16.0: new `tls.*` and `networkpolicies.*` value blocks (opt-in).
- 0.16.1: chart-only patch — liveness/readiness probe port is now a
configurable value (default unchanged at 17472), controller probes drop
the hardcoded `host: 127.0.0.1`, pods/ServiceMonitor/PodMonitor gain
`prometheus.io/scheme: https`, the frr-k8s subchart gains an opt-in
ServiceMonitor toggle (default off), and one CRD numeric field widens
from int32 to int64.
- 0.15.3: `ConfigurationState` CRD exposes reconcile errors per
component; chart gained NetworkPolicy support and tightened pod
SecurityContext.

The `packages/system/metallb/values.yaml` header documents the changes
operators see on upgrade, including the partial nature of the legacy-FRR
escape hatch (Helm evaluates dep `condition:` against the declaring
chart's values context, so a grandparent override from the umbrella
cannot suppress the frr-k8s subchart through that condition; the
subchart stays installed but idle when the speaker is flipped to FRR
mode).

New helm-unittest coverage pins the post-bump invariants and would have
caught the original chart/binary skew before merge: controller/speaker
image tags exactly match the chart `appVersion`, speaker is the
single-container DaemonSet with `BGP_TYPE=frr-k8s`, frr-k8s DaemonSet
and CRDs are installed, NetworkPolicies stay off by default. The
cilium-networkpolicy chart gains its first unittest suite, enumerating
every metallb v0.16 host-network port plus the unrelated etcd
kube-rbac-proxy port that shares port number 9443 (pod-IP vs
host-network).

### Release note

```release-note
chore(metallb): bump MetalLB to v0.16.1; adopt upstream-default FRR-K8s BGP backend in place of the deprecated FRR mode. Metrics endpoints move from HTTP on 7472 to HTTPS on 9120 with native TLS 1.3 by default (operators on TLS 1.2 scrapers must set `metallb.tls.minVersion: VersionTLS12`). The cilium-networkpolicy clusterwide host-network port denylist is rotated to cover the new metallb listeners (7946 memberlist, 9120, 9121, 9140, 9141, 19443). The legacy FRR mode remains available via `metallb.frrk8s.enabled=false` + `metallb.speaker.frr.enabled=true`, with the caveat that the frr-k8s subchart stays installed (idle) under that escape hatch.
```
2026-06-01 18:34:12 +03:00
Myasnikov Daniil 27edd73bca fix(kubevirt-instancetypes): retain EOL centos preferences as deprecated aliases
Address review feedback from lexfrei: dropping the centos.7/centos.7.desktop/
centos.stream8{,.desktop,.dpdk} VirtualMachineClusterPreference objects hard-fails
the vm-instance Helm render (vm.yaml fails when a referenced preference is absent)
and makes the dynamic CRD enum reject the stored value for every existing VM on
those profiles on upgrade. Re-add them as deprecated aliases in
extra-instancetypes.yaml, mirroring the gn1.* GPU instancetype treatment, and
extend the vm-instance enum generation to union preference names from
extra-instancetypes.yaml so the retained profiles stay in values.schema.json and
the RD schema.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-06-01 18:51:29 +05:00
Myasnikov Daniil 797284ab4f fix(platform): preserve no-delete label when migration 43 stamps version
Address review feedback from lexfrei on migrations/43: the version stamp
did a label-less `kubectl create configmap | kubectl apply` under the same
field manager migration 42 used, stripping the platform.cozystack.io/no-delete
label and dropping cozystack-version out of the deletion-protection guardrail on
every upgrade through this migration. Replace it with a labeled heredoc manifest
mirroring migration 42 so the label survives the stamp.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-06-01 18:47:07 +05:00
Myasnikov Daniil fd59dea234 feat(fluxcd): bump flux-operator/flux-instance charts to v0.50.0
Re-pull the vendored flux-operator and flux-instance charts from 0.48.0 to the
latest v0.50.0 (operator-version bumps only: OpenShift v4.21 compat, OLM base
image, Kubernetes client deps — no Flux distribution change). The kubernetesEnvs
and networkPolicy patches re-apply cleanly. distribution.version stays 2.8.x, so
the tenant Flux toolkit auto-tracks the latest 2.8 patch (currently 2.8.8) at
reconcile. The management flux-aio module is already at its latest published
build (2.8.0); no newer 2.8.x module exists upstream yet.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-31 19:04:20 +05:00