Commit Graph
5128 Commits
Author SHA1 Message Date
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
Myasnikov DaniilandClaude Opus 4.8 bb113c59f4 fix(dashboards): join pod phase on (namespace,pod) instead of uid
The capacity-planning and namespaces dashboards joined request/limit
metrics to kube_pod_status_phase via `* on (uid) group_left(phase)`.
Some pods (e.g. static pods like ssh-proxy) carry no `uid` label, so
they all collapse to the empty match key and collide, producing:

  duplicate time series on the right side of `* on(uid) group_left(phase)`

and a 422 from the query backend.

Switch the join key to the always-unique `(namespace, pod)` and filter
`kube_pod_status_phase{phase="Running"} == 1`, matching the pattern
already used in dashboards/gpu/gpu-quotas.json. Sum results are
unchanged (the old multiply-by-0/1 is equivalent to filtering on == 1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-06-02 11:47:10 +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
Myasnikov Daniil 5c4e9277fc fix(foundationdb): regenerate types.go and README for split imageType default
The imageType default was switched to split in values.yaml/values.schema.json/
RD schema but make generate was not run, leaving the +kubebuilder:default marker
in api/apps/v1alpha1/foundationdb/types.go at "unified" and the README param
table sized for the old value — failing the pre-commit generate check (exit 123).
Regenerated with cozyvalues-gen v1.5.0.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-31 18:39:36 +05:00
Myasnikov Daniil 57c0ce66bc test: cover flux v2.8 chart fixes and wire missing test targets
- kafka: assert WorkloadMonitor reads .Values.kafka.replicas /
  .Values.zookeeper.replicas (regression for the phantom .Values.replicas),
  and that enableServiceLinks lives under entityOperator.template.pod.
- tenant: pin that no swept HelmRelease carries upgrade.force (would have
  caught the missed gateway.yaml).
- seaweedfs-db: baseline render cover for the new split-out chart.
- kafka and seaweedfs-db had no `test` Makefile target, so their suites never
  ran in CI; add one to each.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-31 15:02:33 +05:00
Myasnikov Daniil e44457aca9 docs(application): correct stale helm-install-timeout annotation comment
tenant-rd now carries release.cozystack.io/helm-install-timeout alongside
kubernetes-rd (its parent chart bootstraps the seaweedfs-db CNPG cluster, whose
first reconcile exceeds flux's default wait budget). Update the comment that
claimed only kubernetes-rd carries it.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-31 15:02:24 +05:00
Myasnikov Daniil 9cda93d710 fix(kubevirt-instancetypes): pin common-instancetypes and retain gn1 GPU types
Building from unpinned common-instancetypes HEAD on every `make update` silently
churns the catalog. Worse, the v1.3.0 upstream removal dropped the gn1.* (GPU
NVIDIA) instancetypes that existing GPU VMs and tenant Kubernetes GPU workers
still reference — vm-instance/templates/vm.yaml hard-fails the render when a
referenced instancetype is absent.

Pin the source to a commit SHA (not a tag: the multi-arch preferredArchitecture
fields exist only on main past v1.6.0, so a tag would regress them) and
re-append the gn1.* objects from a Cozystack-local extra-instancetypes.yaml the
regen copies into templates/. The EOL centos.7*/centos.stream8* preferences
stay dropped (release-noted). Regenerate the vm-instance and kubernetes README
instancetype/preference tables to match the shipped catalog.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-31 15:02:24 +05:00
Myasnikov Daniil 2a6112bdc7 fix(foundationdb): default imageType to split to keep upgrades non-disruptive
Pre-SSA the chart nested imageType under automationOptions, where Flux v2.7
silently dropped it, so the operator always used its own default (split).
Flux v2.8 SSA now delivers the field, so the chart's previous "unified" default
would trigger an in-place image-type migration on the first upgrade of every
existing cluster. Default to "split" to match the value clusters effectively
ran; operators can set "unified" to migrate deliberately.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-31 15:02:12 +05:00
Myasnikov Daniil e3f4e34a0b fix(tenant): drop deprecated upgrade.force from gateway HelmRelease
The flux-v2.8 sweep removed `upgrade.force: true` from the sibling tenant
HelmReleases (etcd, info, ingress, monitoring, seaweedfs) for helm-controller
v1.5 SSA compatibility, but gateway.yaml was missed. Complete the sweep so no
tenant HR keeps the flag that conflicts with Server-Side Apply.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-31 15:02:11 +05:00
Myasnikov Daniil f880b32495 fix(platform): bump migrations targetVersion to 44 so seaweedfs-db adoption runs
run-migrations.sh loops `seq $CURRENT $((TARGET-1))`, so with targetVersion: 43
a cluster at version 42 runs only migration 42 — migration 43 (the seaweedfs-db
Cluster adoption introduced by the split) is never iterated, and the
missing-file guard never fires because 43 is out of range. The migration is
written as "43 -> 44" and stamps version=44. Without it the new seaweedfs-db
HelmRelease hits a Helm ownership conflict on the existing Cluster and the
seaweedfs-system upgrade can prune it (filer metadata loss). Set targetVersion
to 44 so migration 43 runs, matching the migration-42 precedent.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-31 15:02:01 +05:00
Arsolitt 94f463d61a feat(postgres): add TLS support via CNPG operator-managed certificates (#2686)
## What this PR does

Adds TLS support to the Postgres managed service via the CNPG operator's
built-in cert management.

- Introduces a nullable `tls.enabled` field with tri-state semantics:
when unset, defaults to the value of `external` (auto-on for externally
published services, off for cluster-internal). Explicit `tls.enabled`
always wins.
- Uses CNPG's operator-managed TLS chain — the chart no longer renders
its own cert-manager chain. The operator auto-generates a self-signed CA
and signs server, client, and replication certs from it. The operator's
default SAN list already covers the three built-in services (rw/r/ro) in
four DNS forms each (bare, `.ns`, `.ns.svc`,
`.ns.svc.<cluster-domain>`).
- When `external: true` and TLS is on, the chart sets
`spec.certificates.serverAltDNSNames` so the operator appends the
external hostname `<release>.<tenant-host>` to the auto-generated leaf
cert. In all other cases the chart leaves `spec.certificates` absent and
the operator uses its default SAN set.
- Trust anchor for clients: the operator-managed `<release>-ca` Opaque
Secret (key `ca.crt`). Mount that key into client pods to verify TLS.

Why operator-managed instead of a chart-side cert-manager chain: an
earlier iteration that supplied `serverCASecret` and `serverTLSSecret`
from a chart-rendered cert-manager chain failed the CNPG admission
webhook with `missing ca.key secret data`. CNPG expects a `ca.key` field
on `serverCASecret`, while cert-manager writes the private key under
`tls.key`. Operator-managed mode avoids the schema fight entirely.

Verified end-to-end on a sandbox cluster: cert chain is created by the
operator within ~60 seconds, leaf cert's SAN list includes both the
standard service forms and the external hostname, TLSv1.3 handshake via
`openssl s_client -starttls postgres` against the rw service with the
operator CA returns verify code 0.

### Release note

```release-note
feat(postgres): add TLS support via CNPG operator-managed certificates with tri-state tls.enabled
```


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

* **New Features**
* TLS configuration added for PostgreSQL with a tri-state tls.enabled to
control operator-managed certificate behavior and optional injection of
the external hostname into server certificate SANs.

* **Documentation**
* Added TLS configuration guide, client CA retrieval and psql
verification examples, and updated parameters reference.

* **Tests**
* Added integration tests covering tri-state TLS behavior and
external-hostname injection scenarios.

<!-- 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/2686?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-05-29 15:47:42 +03:00
Arsolitt 41bab0c462 feat(qdrant): add TLS support via cert-manager (#2685)
## What this PR does

Adds TLS support to the Qdrant managed service via cert-manager.

- Introduces a nullable `tls.enabled` field with tri-state semantics:
when unset, defaults to the value of `external` (auto-on for externally
published services, off for cluster-internal). Explicit `tls.enabled`
always wins.
- Renders a self-contained cert-manager chain in the tenant namespace:
self-signed Issuer, CA Certificate, CA Issuer, leaf Certificate.
- SAN policy covers the three DNS forms per service (bare, `.ns.svc`,
`.ns.svc.<cluster-domain>`) plus the external hostname when `external:
true`.
- Wires the leaf cert into the Qdrant HelmRelease so both the REST and
gRPC endpoints use the chart-managed CA chain.

Verified end-to-end on a sandbox cluster: cert chain reaches Ready, TLS
handshake against the tenant service succeeds with the chart CA bundle.

### Release note

```release-note
feat(qdrant): add TLS support via cert-manager (tri-state tls.enabled)
```


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

* **New Features**
* TLS config for Qdrant with tri-state behavior (inherit from external,
or explicitly enable/disable); optional cert-manager provisioning and
automatic TLS secret mounting.

* **Documentation**
* README and chart values/schema updated to document TLS parameters,
defaults, and precedence.

* **Tests**
* Expanded Helm tests covering TLS rendering, certificates (SANs/ECDSA),
reload annotations, and volume/secret injection.

* **Chores**
  * Added a Helm test target to the Makefile.

<!-- 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/2685?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-05-29 13:26:49 +03:00
Arsolitt e76bde7676 feat(nats): add TLS support via cert-manager (#2684)
## What this PR does

Adds TLS support to the NATS managed service via cert-manager.

- Introduces a nullable `tls.enabled` field with tri-state semantics:
when unset, defaults to the value of `external` (auto-on for externally
published services, off for cluster-internal). Explicit `tls.enabled`
always wins.
- Renders a self-contained cert-manager chain in the tenant namespace:
self-signed Issuer, CA Certificate, CA Issuer, leaf Certificate.
- SAN policy covers the three DNS forms per service (bare, `.ns.svc`,
`.ns.svc.<cluster-domain>`) plus the external hostname when `external:
true`.
- Wires the leaf cert into the NATS HelmRelease so client connections
and cluster routes both use the chart-managed CA chain.

Verified end-to-end on a sandbox cluster: cert chain reaches Ready, TLS
handshake against the tenant service succeeds with the chart CA bundle.

### Release note

```release-note
feat(nats): add TLS support via cert-manager (tri-state tls.enabled)
```


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

* **New Features**
* Added optional TLS support for NATS with a nullable/tri-state
tls.enabled (true/false/null) that defaults to external access when
unset
* Conditional cert-manager integration to provision CA and TLS
certificates and enable cluster TLS, and toggle cluster routing to FQDN
when TLS is enabled

* **Documentation**
* Updated configuration docs and schema defaults to include new TLS
settings and fallback behavior

* **Tests**
* Added tests validating tri-state TLS behavior, cert-chain generation,
SANs, and conditional rendering

<!-- 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/2684?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-05-29 13:13:05 +03:00
Arsolitt 17b2fb70a3 feat(kafka): add TLS support via Strimzi listener configuration (#2681)
## What this PR does

Adds TLS support to the Kafka managed service via Strimzi listener
configuration.

- Introduces a `tls.enabled` field that controls the external listener
(port 9094). When unset, defaults to the value of `external` (auto-on
when external access is enabled). Explicit `tls.enabled` always wins.
- Strimzi manages the cluster PKI automatically; the operator publishes
`<release>-cluster-ca-cert` and `<release>-clients-ca-cert` for client
trust setup. No cert-manager chain is rendered by this chart — the
operator owns the chain end-to-end.
- The internal TLS listener on 9093 is always on (Strimzi default); this
field only governs the external listener on 9094.
- External LoadBalancer listener is decoupled from `tls.enabled`: it is
gated on `external: true` only. Previously, an explicit `tls.enabled:
true` with `external: false` would force the external listener to be
rendered.

Verified end-to-end on a sandbox cluster: external listener exposes a
TLS endpoint signed by the Strimzi-managed cluster CA, client
connections with the published trust bundle succeed.

Note: this chart does not configure listener authentication. With TLS on
the external listener, clients can connect without credentials unless
`KafkaUser` resources with `tls`/`scram-sha-512`/`oauth` authentication
are configured outside this chart.

### Release note

```release-note
feat(kafka): add TLS support on the external listener via Strimzi (tri-state tls.enabled)
```


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

* **New Features**
* Tri-state TLS for the external Kafka listener: explicit on, explicit
off, or inherit from the external toggle; chart values now include a
top-level tls object.

* **Documentation**
* Expanded chart docs/README clarifying tls.enabled inheritance,
plaintext risk when disabled with external access, and authentication
guidance.

* **Tests**
* Added/updated schema, rendering, RBAC/secret-naming, helm unit, and
end-to-end tests covering TLS tri-state and external-listener behavior.

* **Bug Fixes**
* RBAC and secret-name expectations updated to use the `-cert` suffixed
secret names.

* **Chores**
  * Added a Makefile test target to run helm unit 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/2681?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-05-29 13:12:42 +03:00
Arsolitt d052227dc4 Merge branch 'main' into feat/tls-postgres
Resolve the conflict in the generated packages/system/postgres-rd/cozyrds/postgres.yaml by regenerating openAPISchema and keysOrder from the merged values. The CRD now carries both the chart's tls.enabled field and main's backup useSystemBucket / BackupClass-driven fields.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-05-29 11:29:58 +03:00
Aleksei Sviridkin a9c8f206e8 chore(metallb): bump to v0.16.1 and rotate host-network port denylist
Upgrade vendored MetalLB chart from 0.15.2 to 0.16.1 and rebuild the
cozystack-built controller / speaker images at the new version (pinned to
linux/amd64 in the Makefile to match the prior baseline and the e2e Talos
sandbox arch — without the pin, a contributor building on an arm64 host
like Apple Silicon ships single-arch arm64 images that crashloop on amd64
nodes with "exec format error"). 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 new metallb endpoints.
The Makefile now vendors the frr-k8s subchart instead of dropping it, and
pins the chart version so future make update invocations are deterministic.

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: new probe contract — metallb controller and speaker serve
  /healthz and /readyz on a dedicated host-local health port 17472
  (distinct from the metrics port). The frr-k8s controller uses its own
  host-local probe port 7572. The cozystack-built image must move in
  lockstep with the chart so probes resolve against the new endpoints.
- 0.16.0: kube-rbac-proxy is replaced by native TLS; metrics endpoints are
  HTTPS-only, served 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 defaults).
- 0.16.1: chart-only patch on top of 0.16.0 — the liveness/readiness probe
  port is now a configurable value (default unchanged at 17472) and the
  controller probes drop the hardcoded host: 127.0.0.1; pods, ServiceMonitor
  and PodMonitor gain prometheus.io/scheme: https for the HTTPS metrics
  endpoint; the frr-k8s subchart gains an opt-in ServiceMonitor toggle
  (default off); one CRD numeric field widens from int32 to int64.
- 0.15.3: ConfigurationState CRD exposes reconcile errors per component;
  Helm chart gained NetworkPolicy support and tightened pod SecurityContext.

Adaptations:
- packages/system/metallb/Makefile: pin helm pull --version 0.16.1; remove
  the rm -rf charts/metallb/charts/frr-k8s line so the new default BGP
  backend ships with the chart; add a test: target running helm unittest;
  hardcode --platform=linux/amd64 on the buildx invocations so the image
  arch does not depend on the contributor's host.
- packages/system/metallb/charts/metallb/: regenerated via make update.
- packages/system/metallb/values.yaml: cozystack-built controller and
  speaker image tags moved to v0.16.1 with fresh linux/amd64 digests
  pushed to ghcr.io/cozystack/cozystack. The chart's new probe contract
  requires the v0.16.x binary; leaving the digests on v0.15.2 would
  CrashLoopBackOff every pod on main between merge and the next
  release-prep commit. A header comment documents the backend default
  switch, the metrics-port move (7472 HTTP → 9120 HTTPS), the new TLS 1.3
  default, the new host-network ports (9140, 9141, 19443), and the partial
  nature of the FRR-mode escape hatch — flipping
  metallb.frrk8s.enabled=false plus metallb.speaker.frr.enabled=true
  switches the speaker but does not suppress the idle frr-k8s subchart,
  because Helm evaluates a dep's `condition:` against the declaring
  chart's own values context and a grandparent override from the
  umbrella cannot reach that resolution.
- packages/system/metallb/tests/metallb_test.yaml: new helm-unittest suite
  pinning post-bump invariants — speaker is the single-container daemonset,
  BGP_TYPE is frr-k8s, frr-k8s daemonset and CRDs are installed,
  NetworkPolicies stay off by default, controller/speaker image tags
  exactly match the chart appVersion (the test that would have caught the
  chart / binary skew up-front), and the documented FRR-mode escape hatch
  is pinned in both halves (speaker switches to four-container FRR layout,
  frr-k8s subchart still renders idle — the latter test is the contract
  that a future cleanup must update together with the values.yaml note).
- packages/system/cilium-networkpolicy/templates/networkpolicy.yaml: drop
  the stale FRR-mode metrics port 7473; add every metallb v0.16
  host-network listener that this policy file is responsible for —
  7946 (speaker memberlist gossip, TCP/UDP; cluster peers still reach it
  via the same policy's ingress fromEntities[cluster,host] clause),
  9120 (speaker metrics), 9121 (legacy FRR metrics, kept defensively so
  the documented escape hatch stays safe), 9140 (frr-k8s controller
  metrics), 9141 (frr-k8s FRR metrics), 19443 (frr-k8s statuscleaner
  webhook). Port 9443 stays in the deny list with a tightened comment:
  the host-network bind belongs to the etcd kube-rbac-proxy DaemonSet in
  cozy-monitoring; the metallb controller webhook collides on the same
  port number but on pod-IP, out of scope for a world-deny.
- packages/system/cilium-networkpolicy/tests/networkpolicy_test.yaml: new
  helm-unittest suite that enumerates every metallb v0.16 host-network
  port in the deny list (including 7946 memberlist and the escape-hatch
  9121), pins port 9443 to its etcd kube-rbac-proxy owner with the
  pod-IP collision note, and asserts the legacy 7473 entry is gone.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-29 11:07:19 +03:00
Myasnikov Daniil a3a7a6d516 test(e2e): add HR existence backstop for external-dns ${name}-system
The two external-dns bats tests already poll for the user-facing ${name}
HelmRelease before calling kubectl wait, but they immediately wait on the
${name}-system HR without the same backstop. The system HR is materialised
asynchronously by the controller, so kubectl wait can race and exit non-zero
when the object does not exist yet. Apply the same existence poll pattern
to both occurrences.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-28 21:25:25 +05:00
Myasnikov Daniil d0480f4561 fix(fluxcd): suppress upstream web NetworkPolicy in flux-operator
The vendored flux-operator chart ships a NetworkPolicy on port 9080 gated
by web.enabled + web.networkPolicy.create (default true). Cozystack already
restricts ports 8080/8081/9080 via a CiliumClusterwideNetworkPolicy patched
into the same chart, so the upstream policy is redundant if a user opts in
to web.enabled: true. Pin networkPolicy.create to false to keep the Cilium
policy as the single source of truth.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-28 21:24:14 +05:00
myasnikovdaniil 0e8b46d78e refactor(seaweedfs): split seaweedfs-system into seaweedfs-db + seaweedfs-system (#2601)
## What this PR does

Splits the `seaweedfs-system` HelmRelease into two: the CNPG
`Cluster/seaweedfs-db` moves to its own HR (`seaweedfs-db`), and the
application HR (`seaweedfs-system`) `dependsOn` it. The new HR uses Flux
v2 `HelmRelease.spec.healthCheckExprs` with a CEL expression on
`Cluster.status.conditions[type=Ready]` plus `waitStrategy.name:
poller`, so its `Ready=True` only flips after the postgres primary is
actually serving connections — not just after `helm install` applied the
`Cluster` CR.

This eliminates the `seaweedfs-filer` CrashLoopBackOff race on a fresh
tenant install. With Cilium `kubeProxyReplacement: true`, socket-LB
returns `EPERM` from `connect(2)` to ClusterIPs with no Ready endpoints.
Pre-split, the filer StatefulSet scheduled concurrently with the CNPG
bootstrap (~55–70s of unavailable postgres), each `connect()` failed
`EPERM`, kubelet exponential restart backoff pushed past the e2e bats
`kubectl wait hr/seaweedfs-system --timeout=2m` window, and the
"Configure Tenant and wait for applications" test failed.

## Stacked on #2602

This PR depends on **#2602** (Flux v2.8.0 upgrade) — `healthCheckExprs`
is a helm-controller v1.5.0 feature. GitHub base set to `flux-bump-v048`
so the diff cleanly shows only this PR's changes; will auto-rebase onto
`main` once #2602 merges.

## Commits

1. **`refactor(seaweedfs)`** — the split itself. Squash of the three
iterative commits from the consolidated branch (#2619): the initial
split, a `dependsOn` merge-collision fix (collapses unconditional
`seaweedfs-db` dependency with the namespace-guarded ingress dependency
into one list), and the `healthCheckExprs` three-predicate form (route
CNPG's `ClusterIsNotReady` bootstrap condition to `inProgress`, reserve
`failed` for other `Ready=False` reasons, lean on the HR `timeout: 10m`
as the real backstop).
2. **`fix(tenant)`** — bump parent HR `spec.timeout` from 5m → 15m so
the serialized `seaweedfs-db → seaweedfs-system → seaweedfs` chain has
enough wall-clock budget. Two complementary changes: the static
`tenant-root` HR (created by `cozystack-basics`) gets its `spec.timeout`
bumped directly; sub-tenants (Tenant CRs whose HR is built by
`cozystack-api` from the `tenant` ApplicationDefinition) get the
`release.cozystack.io/helm-install-timeout=15m` annotation that
`kubernetes-rd` already uses to opt into the longer budget.
3. **`test(e2e)`** — consolidate two redundant child-HR waits in
`hack/e2e-install-cozystack.bats` into one budgeted assert. The second
`kubectl wait` (`hr/monitoring hr/seaweedfs-system --timeout=2m`) was a
residue of a `flux-reconcile-force` workaround removed in #2509;
`monitoring`'s Ready is already implied by `hr/tenant-root` Ready and
`seaweedfs-system`'s by `hr/seaweedfs` Ready, so the line was
tautologically true by the time it ran.

## Migration

`packages/core/platform/images/migrations/migrations/42` adopts existing
`Cluster/seaweedfs-db` resources into the new release on upgrade —
rewrites `meta.helm.sh/release-name: seaweedfs-system → seaweedfs-db`
and stamps `helm.sh/resource-policy: keep` so the `seaweedfs-system`
upgrade (which no longer renders the Cluster) does not delete it during
the transition. Renumbered from the consolidated branch's slot 40 to fit
fresh main (last migration is 41). `targetVersion` bumped 42 → 43.

The new HR's `upgrade.force: true` was dropped to match #2602's
platform-wide cleanup (Helm v4 in helm-controller v1.5.0 rejects
combining `--force-replace` with SSA's `--force-conflicts`).

## Verification

- `helm template` renders `seaweedfs-db` HR cleanly with
`healthCheckExprs`, `waitStrategy: poller`, and `upgrade: { remediation:
{ retries: -1 } }` (no `force: true`).
- Migration 42 syntax-checks under `sh -n`.
- All four PR commits GPG-signed + DCO-signed-off.

### Release note

```release-note
NONE
```


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

* **New Features**
* SeaweedFS database is now available as a separate, independently
configurable component with customizable replicas, storage size, and
storage class options.

* **Chores**
* Restructured database configuration from the main application into a
dedicated component for improved modularity and initialization order.

<!-- 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/2601?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-05-28 19:06:29 +05:00
Andrey Kolkov 67ba252e9f fix(backups): CI was broken after merge (#2762)
<!-- 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
Fix CI break root cause:
internal/backupcontroller/jobstrategy_controller.go:138, :422 calls
renderJobTemplate which no longer exists — the rebased commit cefbaadc0
renamed it to renderAltinityTemplate in altinitystrategy_controller.go
(per the codebase's per-strategy convention: renderCNPGTemplate,
renderEtcdTemplate, renderFoundationDBTemplate) but didn't add the
corresponding renderJobTemplate to the Job strategy file. PR #2716 was
merged with this break — main is currently broken.


### 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. -->

### 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
```

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

## Summary by CodeRabbit

* **New Features**
* Enhanced backup and restore operations to support templated Pod
configurations, allowing dynamic configuration rendering during Job
reconciliation with application context and backup metadata.

<!-- 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/2762?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-05-28 17:33:45 +04:00
Myasnikov Daniil 3fe74ba68e fix(seaweedfs): drop deprecated upgrade.force from seaweedfs-system HR
The flux-bump-v048 base branch removed upgrade.force from every other
HelmRelease in 7f94182a87 because helm-controller v0.48 refuses to
combine Helm's --force replace with server-side apply's --force-conflicts
("cannot use force conflicts and force replace together"). This refactor
re-introduced the field on the seaweedfs-system HR by accident; CI E2E
fails with the same error in a loop until the seaweedfs subtree times
out the tenant install. Drop the line so the upgrade path matches every
other HR on the base branch.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-28 16:46:15 +05:00
Aleksei Sviridkin 2869f1554d fix(dashboard): unblock token-proxy startup when JWKS is briefly unreachable (#2745)
<!-- 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

Closes #2741.

token-proxy panicked on every cold start where the in-cluster JWKS
endpoint at `kubernetes.default.svc` was briefly unreachable — exactly
the window where CoreDNS, the CNI, or the apiserver endpoint is still
warming up. The pod entered CrashLoopBackOff, the dashboard HelmRelease
never installed, and every release that depended on dashboard stalled
behind it.

Two startup paths touched the network synchronously:
`jwk.Cache.Register` with the default `WaitReady(true)` and an explicit
follow-up `Refresh`, both wrapped in `panic`. `WaitReady(true)` had no
built-in timeout, so a permanently unreachable JWKS would have turned
the panic into an unbounded startup wait rather than fixing anything.

This PR:

- Moves JWK cache construction out of `init()` into a testable function.
- Passes `WithWaitReady(false)` on `Register` and drops the redundant
`Refresh`. Cache setup now returns immediately; the background worker
keeps retrying.
- Lowers `MinInterval` from 5 minutes to 5 seconds. A single bad
response from the apiserver no longer holds the pod NotReady for several
minutes.
- Wires `httprc.WithErrorSink` so background fetch failures show up in
the container log instead of being invisible behind a 503.
- Adds `/healthz/live` (process up) and `/healthz/ready` (JWK cache
populated) plus a `startupProbe` so a genuinely misconfigured JWKS
eventually surfaces as `RestartCount`/`CrashLoopBackOff` instead of a
silently NotReady pod.
- Disambiguates the cold-start window from a real bad token at the
sign-in handler via an `errJWKSNotReady` sentinel.

The auth-proxy (`oidc-enabled=true`) branch keeps its existing `/ping`
probes unchanged.

### Testing

- Go test coverage: WaitReady(false) contract, background-worker
recovery from transient HTTP 5xx (no explicit Refresh — the real
production path), lazy population, catastrophic-misconfig surface
(missing/malformed CA still errors hard), errJWKSNotReady
disambiguation, SA token injection, and the /healthz/ready 503→200
transition.
- helm-unittest pins the gatekeeper template structure for both
`oidc-enabled` branches so a future refactor that collapses the if/else
differently fails the suite.
- `make test` now runs both `helm unittest` and `go test`, picked up
automatically by `hack/helm-unit-tests.sh`.

### Screenshots

N/A — no UI changes.

### Release note

```release-note
fix(dashboard): token-proxy no longer crash-loops when the in-cluster JWKS endpoint is briefly unreachable during a cold install; readiness now reflects JWK cache state and a startupProbe surfaces permanent JWKS misconfig as CrashLoopBackOff instead of a silently NotReady pod.
```


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

## Summary by CodeRabbit

* **New Features**
* Added dedicated health check endpoints (`/healthz/live` and
`/healthz/ready`) for better container orchestration integration
* Enhanced health probes with separate startup, liveness, and readiness
checks

* **Tests**
  * Added comprehensive test suite for token-proxy functionality

* **Chores**
  * Updated build system with improved test targets
  * Refined Docker image build configuration for better reliability
  * Updated runtime user ID for cluster parity

<!-- 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/2745?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-05-28 14:18:26 +03:00
Andrey Kolkov 1319fb78bf fix(backups): CI was broken on unittests
Signed-off-by: Andrey Kolkov <androndo@gmail.com>
2026-05-28 14:46:22 +04:00
Andrey Kolkov cefbaadc08 feat(platform): add default backupclass (#2716)
<!-- 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
align backups in charts, add CR for default strategies and backupclass.

### 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. -->

### 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

```

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

* **New Features**
* Platform-managed default BackupClass `cozy-default`, system bucket
`cozy-backups`, opt-in chart flag `useSystemBucket`, and chart behavior
to skip per-release credential Secrets when enabled; Velero
BackupStorageLocation support and ClickHouse/Postgres tuning for
system-bucket paths.

* **Behavior / Security**
* Controllers project platform credentials into tenant namespaces,
expose projection metrics (success/failure), skip reconciling terminal
jobs, and enforce RBAC isolation for projected Secrets with clear
Pending/Failed conditions on errors.

* **Documentation**
* Docs and READMEs updated with default-flow guidance and deprecation
notes for legacy per-tenant S3 fields.

* **Tests**
* New unit and e2e tests validating credential projection, default flow,
and strategy-specific backup/restore scenarios.

<!-- 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/2716?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-05-28 13:53:20 +04:00
Andrey Kolkov f17ead3d26 feat(platform): add default backupclass
Signed-off-by: Andrey Kolkov <androndo@gmail.com>
2026-05-28 13:49:59 +04:00
Myasnikov Daniil 7157158c93 fix(seaweedfs-db): make db resources configurable
values.yaml declared an unused Resources typedef while
templates/database.yaml hardcoded cpu/memory limits and requests. The
wrapper chart at packages/extra/seaweedfs/templates/seaweedfs-db.yaml
already passes db.resources through cozy-lib.resources.defaultingSanitize
into the sub-chart values, so the user-facing db.resources and
db.resourcesPreset on the outer chart silently dropped on the way in.

Drop the unused typedef, accept .Values.db.resources as a free-form
object (the wrapper already sanitizes it into the K8s nested
limits/requests shape that CNPG Cluster expects), and wire it through
to the Cluster manifest. Defaults are preserved verbatim so helm
template output is byte-identical for a no-override install.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-28 12:46:57 +05:00
Myasnikov Daniil dccdeb5229 fix(tenant): bump parent HR timeout to 15m to cover seaweedfs split
PR #2619 split seaweedfs into a serialized chain
(seaweedfs-db -> seaweedfs-system -> seaweedfs) that needs ~5 min of
wall-clock to install: CNPG bootstrap, master quorum, then the wrapper.
With the prior 5m wait budget on the parent tenant HR, helm-controller
would time out before the children reach Ready, rollback would delete
the child HRs, and the upgrade would retry in a loop. The e2e bats step
that patches the Tenant CR and waits 60s for child HRs to exist landed
in the rollback gap and timed out.

Two complementary changes:

- tenant-root is created statically by cozystack-basics, so its
  HelmRelease spec.timeout is bumped 5m -> 15m directly.
- Sub-tenants (Tenant CRs) get their HR built by cozystack-api from
  the tenant ApplicationDefinition. Add the
  release.cozystack.io/helm-install-timeout=15m annotation that
  kubernetes-rd already uses to opt the kind into the longer budget.

Keeps both values in lockstep and documents the coupling inline.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit cc9da3a80a)
2026-05-28 12:46:57 +05:00
Myasnikov DaniilandClaude Opus 4.7 29c6afc84d refactor(seaweedfs): split seaweedfs-system into seaweedfs-db + seaweedfs-system
Splits the seaweedfs-system HelmRelease in two so the CNPG
Cluster/seaweedfs-db lives in its own HR (seaweedfs-db), and the
application HR (seaweedfs-system) dependsOn it. The new HR uses Flux v2
HelmRelease.spec.healthCheckExprs with a CEL expression on
Cluster.status.conditions[type=Ready] plus waitStrategy.name: poller, so
its Ready=True only flips after the postgres primary is actually serving
connections — not just after helm install applied the Cluster CR.

This eliminates the seaweedfs-filer CrashLoopBackOff race on a fresh
tenant install. With Cilium kubeProxyReplacement: true, socket-LB
returns EPERM from connect(2) to ClusterIPs with no Ready endpoints.
Pre-split, the filer StatefulSet scheduled concurrently with the CNPG
bootstrap (~55–70s of unavailable postgres), each connect() failed
EPERM, kubelet exponential restart backoff pushed past the e2e bats
'kubectl wait hr/seaweedfs-system --timeout=2m' window, and the
'Configure Tenant and wait for applications' test failed.

healthCheckExprs uses Flux's three-predicate form: route
ClusterIsNotReady (the CNPG bootstrap condition) to `inProgress` so the
HR keeps polling, keep `failed` for other Ready=False reasons, and rely
on the HR `timeout: 10m` as the real backstop for a genuinely stuck
cluster. A plain Ready=False predicate would flag the HR failed within
seconds of creating the Cluster CR.

Migration 42 adopts existing Cluster/seaweedfs-db resources into the new
release on upgrade by rewriting meta.helm.sh/release-name and stamping
helm.sh/resource-policy: keep so the seaweedfs-system upgrade (which no
longer renders the Cluster) does not delete it during the transition.

The application HR's dependsOn collapses the db dependency (unconditional)
with the existing ingress dependency (guarded by `if eq $ingress
.Release.Namespace` so sub-tenants that inherit ingress from a parent
namespace don't deadlock on a non-existent local HR) into one list.

Requires Flux v2.8.x (helm-controller v1.5.0+) for healthCheckExprs.
Provided by the parent PR #2602 in this chain.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-28 12:46:56 +05:00
Myasnikov Daniil 401993ac62 test(e2e): wait for parent HR Ready before downstream asserts
Two timing bugs unmasked by Flux v2.8 kstatus, with the same root shape:
the bats steps assumed a side-effect ordering ("by the time X is true,
the parent HR's helm install is done") that held under v2.7's faster
dispatch but breaks under v2.8 where helm-controller walks kstatus on
every applied resource before flipping the HR Ready.

run-kubernetes.sh:
  After every addon HR (cilium, coredns, csi, vsnap-crd, ingress-nginx)
  reaches Ready, the remediation guard reads `.status.history` on the
  parent kubernetes-${test_name} HR. The parent's helm install can still
  be Reconciling at that point because kstatus is finishing its full
  sweep; `.status.history` is empty until install completes, so the
  guard exits 1 with "empty .status.history on a Ready HR". kubernetes-
  latest masked this because the ouroboros assertions add ~10m of
  buffer; kubernetes-previous (no ouroboros) hit the guard with the
  parent still installing. Add an explicit
    kubectl wait hr/kubernetes-${test_name} --for=condition=ready
  before the guard.

vminstance.bats:
  The test waited 120s for `kubectl get vmi vm-instance-$name` to
  return a VMI with an assigned IP, then waited for the parent HR Ready
  separately afterwards. Under v2.7 the parent HR's helm install
  applied the VirtualMachine CR fast enough that kubevirt had created
  the VMI before the 120s budget expired. Under v2.8 the dispatch is
  slower and the VMI poll returned NotFound for its entire budget.
  Reorder: wait for HR Ready first (which implies the VirtualMachine
  CR is applied + kstatus-ready), then poll for the VMI with IP.
  Loosen the VMI poll budget from 120s to 5m for nested-virt slow paths.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit c664b868bc)
2026-05-28 12:43:50 +05:00
Myasnikov Daniil 7b9f286faa test(e2e): bump app HR-Ready waits for Flux v2.8 kstatus
Flux helm-controller v1.5.0 (shipped in Flux v2.8) replaced the legacy
Helm readiness logic with kstatus-based health checking, which polls
Deployment/StatefulSet/Service status and waits for kstatus to assess
rollout-ready. This typically adds 10-30s of latency between resource
apply and HR condition Ready=True compared to v2.7.

Our per-app bats tests had `kubectl wait hr/... --for=condition=ready
--timeout=` budgets of 20s, 30s, 60s, 100s — sized for v2.7's faster
readiness flip and previously masked by the 3x retry on Run E2E tests
(dropped in #2558). Under v2.8 with no retry, every app that runs after
the first few hit its HR wait timeout exactly, surfacing as 13 of 16
test failures on PR #2619.

Standardise on 5m for HR Ready across all app tests. Tight enough to
catch genuine bring-up failures within the per-app job budget, loose
enough to absorb kstatus dispatch jitter on a management cluster that
is concurrently reconciling multiple tenant-test HRs.

Post-HR-Ready checks for app-specific resources (StatefulSet/Service/
PVC/operator CRs) remain — for CR-based apps kstatus only observes
that the chart applied, not that the operator finished. Audit of
which of those are actually redundant under v2.8 kstatus is queued
as a follow-up.

Also bumps the 10s "kamajicontrolplane appears" wait in
run-kubernetes.sh to 2m, and the kubernetes-addon HR Ready loop from
1m to 5m for the same reason.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 5ef02ee7b3)
2026-05-28 12:43:50 +05:00
Myasnikov Daniil f6c7379835 fix(kubernetes): drop lookup-guarded parent-HR dependsOn on tenant addon child HRs
Each of the 19 child HR templates in packages/apps/kubernetes/templates/
helmreleases/ added the parent kubernetes-application HR as a dependsOn
entry, guarded by a `lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease"`
on the parent so it only stamped after the parent already existed.

Under Flux 2.7 (helm-controller v1.4.x), the parent HR's Ready=True flips
after helm install returns, regardless of any child HRs it just applied,
so the guard was a no-op for first install and a soft ordering hint for
subsequent reconciles.

Under Flux 2.8 (helm-controller v1.5.0), the default waitStrategy is
`poller` -- the parent HR now waits via kstatus for every applied
resource, including the 19 child HRs, to be Ready=True before flipping
its own Ready. The child HRs in turn cannot become Ready until they have
a working `super-admin.svc` kubeConfig and the lookup-stamped dependsOn
back-edge resolves -- which only happens after the parent HR is Ready.
Deadlock: parent waits on child, child waits on parent.

Removing the parent dependsOn from each child breaks the cycle. The
children start reconciling immediately; helm-controller's existing retry
loop covers the brief window before the parent's helm install lands the
admin-kubeconfig secret. The cross-child ordering (cilium -> cert-manager,
prometheus-operator-crds -> metrics-server, etc.) stays intact via the
unconditional sibling entries.

Five files (the *-crds and vsnap-crd HRs) had only the parent reference
under dependsOn; remove \`dependsOn:\` entirely in those. Cilium had one
conditional gateway-api-crds entry under it; pull the \`dependsOn:\` key
inside that conditional so it disappears when gateway-API is off rather
than rendering as a null array.

This is option B from issue #2642 task 1.

Verified:
- \`helm template\` clean with default values-ci.yaml and with every addon
  enabled (cert-manager, ingress-nginx, monitoring-agents, velero, fluxcd,
  gateway-api, gpu-operator, hami, ouroboros).
- No remaining \`- name: {{ .Release.Name }}\` dependsOn entries in
  packages/apps/kubernetes/templates/helmreleases/.
- \`helm unittest\` passes (123/123).

Refs: #2642

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 6119aafcb5)
2026-05-28 12:43:50 +05:00
Myasnikov Daniil 435b3e6275 fix(vpa): break circular wait between parent install and nested vpa-for-vpa HR
The packages/system/vertical-pod-autoscaler chart's vpa-for-vpa.yaml template
emits an HR in cozy-vpa-for-vpa that the parent helm install applies and waits
on. That nested HR had `dependsOn: monitoring-agents`, which never resolves:

  parent VPA helm install
    waits for nested vpa-for-vpa HR Ready
      waits for monitoring-agents HR Ready
        waits for cozystack.vertical-pod-autoscaler PackageSource Ready
          waits for parent VPA HR Ready  <-- back to start

Symptom observed on PR #2619 e2e (cozyreport):

  Helm upgrade failed for release cozy-vertical-pod-autoscaler/
  vertical-pod-autoscaler with chart cozy-vertical-pod-autoscaler@...:
  timeout waiting for: [HelmRelease/cozy-vpa-for-vpa/vpa-for-vpa
  status: 'InProgress']

The recommender's /health-check readiness probe is served by the binary
itself and doesn't require Prometheus. Removing the dependsOn lets the
nested HR become Ready quickly; the recommender will log connection
errors against vmselect until monitoring-agents installs later in the
boot sequence, then start producing recommendations normally.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 08433dc02e)
2026-05-28 12:43:50 +05:00
Myasnikov Daniil 2e3e7bdb39 fix(vm-instance): emit empty disk object so KubeVirt accepts disks without bus
When a disk entry has no bus set (the schema default), the previous
template rendered `disk:` with no value — YAML parses that as
`disk: null` and KubeVirt's virtualmachines-mutator webhook denies the
request with: "spec.template.spec.domain.devices.disks.disk in body
must be of type object: null".

Restructure the disk loop so the disk/cdrom value is always a valid
object: `disk: {}` when bus is empty, `disk: {bus: <x>}` when set.
Mirrors how `cloudinitdisk` already does it in the same template.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 0f3d2fa3a6)
(cherry picked from commit e2c6b7b60b)
2026-05-28 12:43:50 +05:00
Myasnikov Daniil 7f94182a87 fix(platform): drop deprecated upgrade.force from HelmReleases; fix kafka WorkloadMonitor replicas paths
Flux v2.8 / helm-controller v1.5.0 embeds Helm v4, which renamed --force
to --force-replace and rejects combining it with SSA's --force-conflicts.
Existing HelmReleases with spec.upgrade.force: true fail every upgrade with
"cannot use force conflicts and force replace together", looping
upgrade -> rollback -> upgrade and blocking dependants. Observed on PR #2619
CI for tenant-root/{ingress-nginx-system,seaweedfs-db}, tenant-test/info,
and others.

Per Flux docs, upgrade.force has been a no-op since v2.8 anyway, so removing
it is safe on green clusters and unblocks broken ones.

Also fix packages/apps/kafka/templates/workloadmonitor.yaml: it referenced
.Values.replicas (no such field; values.yaml has kafka.replicas and
zookeeper.replicas). Flux v2.7 silently dropped the null; v2.8 SSA forwards
it, and the WorkloadMonitor CRD rejects "spec.replicas: null" as not
type:integer, which blocks every kafka-test install.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 7e286cf8e1)
2026-05-28 12:43:50 +05:00
Myasnikov Daniil 7cb7be8124 fix(kafka): place enableServiceLinks under template.pod, not a phantom .spec
Same class of bug as the foundationdb misplaced-field fixes. The kafka
chart had:

  entityOperator:
    template:
      pod:
        metadata: { labels: { ... } }
      spec:                          ← child of template, not of pod
        enableServiceLinks: false

The Strimzi Kafka CRD's `entityOperator.template` has no `spec` key.
`enableServiceLinks` is a direct field of `template.pod` (alongside
metadata, affinity, tolerations, etc.). Flux v2.7's permissive
client-side patch silently dropped the misplaced field; v2.8's strict
Server-Side Apply rejects:

  failed to create typed patch object (Kind=Kafka):
  .spec.entityOperator.template.spec: field not declared in schema

Move `enableServiceLinks` under `template.pod` where the operator
actually reads it, and fix the inconsistent metadata.labels indentation
(was 9/11 spaces instead of 10/12) while we're here.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 447f1dcbf1)
2026-05-28 12:43:50 +05:00
Myasnikov DaniilandClaude Opus 4.7 5121c7b91b fix(foundationdb): relocate misplaced fields from automationOptions to spec
The chart nested `faultDomain`, `imageType`, `labels`, and
`minimumUptimeSecondsForBounce` inside `automationOptions`, but the
FoundationDBCluster CRD schema defines all four as direct children of
`spec`. Flux v2.7 (helm-controller v1.4.3) used permissive client-side
patch and silently dropped the unknown fields; v2.8 (helm-controller
v1.5.0) does strict Server-Side Apply and rejects:

  failed to create typed patch object: .spec.automationOptions.<field>:
  field not declared in schema

This was a latent bug from day one — none of those fields ever actually
configured anything. Moving them one level up applies the values to
where the operator looks for them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-28 12:43:50 +05:00
Myasnikov DaniilandClaude Opus 4.7 4cc60e3ab5 fix(kubevirt-instancetypes): drop persistent strip that produced null TPM
`make update` stripped `persistent: true` from upstream's
`preferredTPM: { persistent: true }`, leaving the parent key with a null value.
KubeVirt v1.6's CRD tolerated it; v1.8 (the operator version we bumped to in
6af0bf71) enforces structural schema and rejects null:

  VirtualMachineClusterPreference "windows.11" is invalid:
  spec.devices.preferredTPM: Invalid value: "null": spec.devices.preferredTPM
  in body must be of type object: "null"

Stop stripping the line and regenerate the templates from upstream
common-instancetypes. Persistent TPM state falls back to the cluster default
StorageClass with RWO access mode when `vmStateStorageClass` is unset on the
KubeVirt CR, per pkg/storage/backend-storage/backend-storage.go.

Drive-by from the regen:
- Removed (upstream EOL): centos.7*, centos.stream8*
- Added: debian, oraclelinux, legacy, linux*, fedora.s390x, rhel.{9,10}.s390x,
  windows.{xp,7,7.virtio,2k3,2k8,2k8.virtio,2k12,2k12.virtio}, plus new
  cx1.*1gi and d1 instancetype variants.

Also regenerates packages/apps/vm-instance instanceProfile enum
(values.schema.json / README.md) from the refreshed preferences.yaml.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-28 12:43:49 +05:00