Commit Graph
407 Commits
Author SHA1 Message Date
Aleksei Sviridkin 55a98b8815 docs(apps): point seven app README preset tables at canonical docs
Address review feedback from coderabbitai across:
  - packages/apps/kubernetes/README.md
  - packages/apps/mariadb/README.md
  - packages/apps/nats/README.md
  - packages/apps/qdrant/README.md
  - packages/apps/redis/README.md
  - packages/apps/tcp-balancer/README.md
  - packages/apps/vpn/README.md

Each of these READMEs left an inline 7-row preset table listing only
the legacy flat names after the parameters block above already
defaulted to an instance-type value (e.g. `t1.nano`). cozyvalues-gen
only regenerates the `## Parameters` block, so the hand-written
section drifted.

Replace the inline tables with the same short paragraph already used
by postgres and kafka READMEs: name the five series, note that the
seven legacy names remain accepted as deprecated aliases, and link to
the canonical reference at docs/operations/resource-presets.md so the
full size matrix and legacy mapping live in exactly one place.

Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-12 03:08:52 +03:00
Aleksei SviridkinandMatthieu ROBIN fdffedbd4b feat(apps): instance-type resourcesPreset across apps and extras
Rewrite resourcesPreset values in every apps/* and extras/* chart from
the legacy flat names to the new instance-type form, using the 1:1
mapping:

  nano    -> t1.nano       micro   -> t1.micro
  small   -> t1.small      medium  -> c1.small
  large   -> c1.medium     xlarge  -> c1.large
  2xlarge -> c1.xlarge

Each affected values.yaml carries the canonical 47-value enum (40
instance-type names plus 7 legacy aliases) so the schema accepts both
forms during the deprecation window. cozyvalues-gen regenerates the
matching api/apps/v1alpha1/*/types.go kubebuilder enum and defaults,
the chart values.schema.json, the README parameter table, and the
ApplicationDefinition openAPISchema in packages/system/*-rd/cozyrds.

Three hardcoded legacy literals in chart templates migrated:
  - packages/apps/harbor/templates/harbor.yaml:121 (portal)
  - packages/apps/opensearch/templates/opensearch.yaml:43 (dashboards)
  - packages/system/keycloak/templates/sts.yaml:115 (heap fallback)

packages/apps/foundationdb/Makefile gains the missing hack/update-crd.sh
call. Without it, every regen left foundationdb-rd cozyrds stale and
new FoundationDB app CRs through cozystack-api were rejected at
validation for any non-legacy resourcesPreset value.

Every default-rendered chart produces identical CPU and memory to the
value applied before the rename, courtesy of the legacy alias table in
cozy-lib and the 1:1 default mapping.

Co-authored-by: Matthieu ROBIN <matthieu@hidora.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-11 21:08:41 +03:00
Aleksei Sviridkin 6f83d9a794 feat(ouroboros): bump to v0.7.2 with debug logging on tenants
The v0.7.2 ouroboros release adds --log-level / OUROBOROS_CONTROLLER_LOG_LEVEL,
plants Debug logs at every informer event handler (AddFunc/UpdateFunc/
DeleteFunc with kind+namespace/name via describeObj) and at reconcile
entry/exit, wires SetWatchErrorHandler on Ingress / Gateway / HTTPRoute
informers, and surfaces cache-sync elapsed at Info. The diagnostic
surface targets the kamaji-tenant-via-konnectivity flake where bats
sees a fresh Ingress create succeed against the tenant apiserver but
the controller's coredns-custom rewrite lands roughly 7-10 minutes
later — without per-event visibility the failure mode is
indistinguishable between 'watch never fires', 'reconcile silently
errors', and 'API list returns empty until reflector reconnect'.

Bumps the chart-manifest digest, the controller-image digest, and the
cozystack-side image-pin assertion in lockstep. The bats deadline for
the coredns-custom rewrite check rises from 180s to 900s — even on
the broken-watch path the reflector eventually reconnects within
that budget, so the test stops failing on the symptom while we keep
the diagnostic on to learn what the symptom actually is.

cozystack tenants force controller.logLevel: debug via the
defaultOuroborosValues helper; operators that want quieter logs can
override through addons.ouroboros.valuesOverride. Two helm-unittest
fixtures pin both the default and the override path.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-11 02:20:34 +03:00
Aleksei Sviridkin d6c24dd078 fix(ouroboros): pin tenant clusterDomain to cluster.local
The cozystack tenant ouroboros wrapper deliberately left
`controller.clusterDomain` empty so the upstream chart's per-pod
`/etc/resolv.conf` auto-detect could pick up whatever the tenant CoreDNS
actually serves. In a cozystack tenant that auto-detect resolves to the
host platform's `cozy.local` rather than the tenant's `cluster.local`:
the cozystack tenant kubelet injects the host's cluster-domain into pod
search lists, but Kamaji's TenantControlPlane is configured with
`networkProfile.clusterDomain: cluster.local` and the tenant CoreDNS
Corefile serves only that domain. The mismatch makes the proxy compose
its `BackendHost` as
`ingress-nginx-controller.cozy-ingress-nginx.svc.cozy.local`, which
NXDOMAINs against tenant CoreDNS — `/readyz` returns 503 forever, the
proxy Deployment never reaches Ready, helm install times out at 10
minutes, and the e2e job hits the 2h cancel without `ouroboros.bats`
ever progressing past the first kubectl wait.

Pin `controller.clusterDomain: cluster.local` in `defaultOuroborosValues`
so the chart bakes the resolved BackendHost against the domain tenant
CoreDNS actually serves. This is the contract Kamaji TCP defines for
every cozystack tenant; operators on a non-default tenant CoreDNS can
still override via
`addons.ouroboros.valuesOverride.ouroboros.controller.clusterDomain`.

The underlying tenant kubelet injecting the wrong cluster-domain into
pod resolv.conf is a separate bootstrap concern that should be addressed
on its own track; pinning the chart input here keeps ouroboros
operational on the default cozystack tenant topology in the meantime.

Update the corresponding helm-unittest to assert the new contract.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-08 10:49:51 +03:00
Aleksei Sviridkin 7ab940f309 feat(kubernetes): expose ouroboros as a per-tenant addon
Adds the OuroborosAddon typedef, addons.ouroboros knob, and tenant
HelmRelease that deploys the cozy-ouroboros chart inside a tenant
Kubernetes cluster via the host's flux-helm-controller and the tenant
admin-kubeconfig — same shape as the cert-manager addon.

The HelmRelease overrides controller.mode back to coredns-import
because tenants run the cozystack-coredns wrapper (which carries the
`import /etc/coredns/custom/*.override` directive); the parent chart's
default of mode=coredns is the right shape only for the Talos-managed
host CoreDNS. controller.gatewayApi.enabled is mirrored from the
per-tenant addons.gatewayAPI.enabled — forcing --gateway-api on a
tenant without Gateway-API CRDs CrashLoops the controller on
WaitForCacheSync against missing GVRs. dependsOn grows a conditional
gateway-api-crds entry on the same gate.

controller.clusterDomain is intentionally NOT pre-set by the wrapper.
The chart auto-detects it inside the controller pod by parsing
/etc/resolv.conf — the search list of which is `*.<ns>.svc.<domain>`
and reflects exactly the zone the tenant CoreDNS actually serves
(governed by KamajiControlPlane.spec.networkProfile.clusterDomain
defaulting to `cluster.local`, and the cozystack-coredns wrapper
kubernetes-plugin zone, also `cluster.local`). Pre-setting from a
host-side fact like `_cluster.cluster-domain` would override the
auto-detect with a value that does not match what the tenant kube-dns
serves, silently emitting NXDOMAIN-bound rewrites. Operators that run
a tenant on a non-default cluster-domain can still override via
addons.ouroboros.valuesOverride.ouroboros.controller.clusterDomain.

If addons.ouroboros.enabled is true but ingressNginx is off, the
HelmRelease fails the render with a clear error rather than silently
skipping — the hairpin fix has nothing to fix against without
ingress-nginx, and a no-op addon that quietly does nothing is harder
to debug than an explicit render-time error.

The valuesOverride gateway-api guard accepts every truthy spelling
Helm/YAML emit (bool true, string "true"/"True"/"TRUE", numeric 1,
YAML "yes"/"Yes"/"YES"/"on"/"On"/"ON"/"y"/"Y") via
toString-then-lower-then-string-compare; direct bool eq would error
on type mismatch when an operator passes the value as a string from
--set, and case-insensitive folding catches `--set ...=On` /
`--set ...=True` that pure-lowercase comparison would miss.

The acknowledgeUncleanRemoval gate is defence-in-depth for the rare
edge case where helm uninstall fails to land cleanly (tenant
unreachable, hook timeout, manual kubectl delete bypassing helm) —
in the normal path the upstream chart's pre-delete cleanup hook
(coredns-cleanup-hook.yaml, vendored at 0.6.0) nulls the
ouroboros.override key automatically when the HelmRelease is
uninstalled. The gate only blocks render when a leftover HelmRelease
on the host kube-apiserver shows the previous install is still
sitting on the cluster.

The component is also registered in the kubernetes-application
PackageSource (as kubernetes-ouroboros at path system/ouroboros) so
the tenant HelmRelease's ExternalArtifact resolves.

make generate re-emitted README.md, values.schema.json, the Go struct
+ deepcopy, and the kubernetes-rd ApplicationDefinition openAPISchema
in lockstep.

helm-unittest covers: render gating on enabled flag, hard-fail when
ingress-nginx is off, namespaces and labels, chartRef, dependsOn
cilium/coredns/ingress-nginx + conditional gateway-api-crds, default
coredns-import mode, valuesOverride precedence, gateway-api flag
mirroring in both directions, gateway-api truthy-spelling acceptance
(including case-insensitive on/On/True/TRUE), the lookup-driven
acknowledge gate (kubernetesProvider mock asserts the failedTemplate
path when a prior HelmRelease still exists on the host), the
lookup-returns-nil CI/dry-run path (render proceeds when the cluster
mock has no objects), the _namespace.etcd-empty render gate, and
that controller.clusterDomain is NOT pre-set in the rendered
HelmRelease (so chart auto-detect runs).

Signed-off-by: Aleksei Sviridkin <f@lex.la>
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-05-06 06:48:06 +03:00
Arsolitt 5786afe8bf feat(kubernetes): add kubelet resource reservations for worker nodes (#2420)
**What this PR does**

Adds configurable kubelet resource reservations and eviction thresholds
for tenant cluster worker nodes. Without explicit reservations and
cgroup enforcement, the scheduler treats nearly all node memory and CPU
as allocatable. Under pressure, the OOM killer can target kubelet itself
because no cgroup boundaries protect it, leading to "Kubelet stopped
posting node status" failures and unrecoverable NotReady nodes.

This PR auto-computes per-node-group kubelet reservations from the
effective node capacity (instanceType or explicit `resources`):

- **Memory**: `systemReservedMemory` and `kubeReservedMemory` each
default to 5% of effective memory, clamped 256Mi-1Gi
- **CPU**: `systemReservedCpu` and `kubeReservedCpu` each default to 5%
of effective CPU, clamped 50m-500m
- **Eviction**: configurable `evictionHardMemory` (default 7%) and
`evictionSoftMemory` (default 10%) with validation that hard < soft
- **Disk eviction**: `nodefs.available`, `imagefs.available`, and
`nodefs.inodesFree` thresholds are preserved alongside memory signals
- **Capacity annotations**: cluster-autoscaler annotations now report
allocatable memory and CPU (total minus all reservations and eviction
threshold) instead of raw totals
- **Cgroup enforcement**: `enforce-node-allocatable=pods` configures
advisory-only reservations — the scheduler accounts for reserved
resources when computing allocatable capacity, but no cgroup boundaries
are enforced for system or kube reserved resources
- **Validation**: format checks, zero-value rejection, eviction ordering
(hard < soft), same-unit-type requirement for eviction thresholds, and
total reservation sanity check against effective node capacity

All reservation fields are optional and overridable per node group via
`.nodeGroups[].kubelet`.

**Default rendered kubeletExtraArgs:**

```yaml
kubeletExtraArgs:
  system-reserved: "memory=256Mi,cpu=50m"
  kube-reserved: "memory=256Mi,cpu=50m"
  eviction-hard: "memory.available<7%,nodefs.available<10%,imagefs.available<15%,nodefs.inodesFree<5%"
  eviction-soft: "memory.available<10%,nodefs.available<15%,imagefs.available<20%"
  eviction-soft-grace-period: "memory.available=1m30s,nodefs.available=1m30s,imagefs.available=1m30s"
  eviction-minimum-reclaim: "memory.available=256Mi"
  enforce-node-allocatable: "pods"
```

**New values schema:**

```yaml
nodeGroups:
  md0:
    kubelet:
      systemReservedMemory: ""   # auto-computed (5% of effective memory, 256Mi-1Gi)
      kubeReservedMemory: ""     # auto-computed (5% of effective memory, 256Mi-1Gi)
      systemReservedCpu: ""      # auto-computed (5% of effective CPU, 50m-500m)
      kubeReservedCpu: ""        # auto-computed (5% of effective CPU, 50m-500m)
      evictionHardMemory: "7%"   # absolute (e.g. 200Mi) or percentage
      evictionSoftMemory: "10%"  # must be > evictionHardMemory, same unit type
```

**Release note:**

```
feat(kubernetes): add per-node-group kubelet resource reservations with auto-computed system/kube reserved memory (5% of effective memory, clamped 256Mi-1Gi) and CPU (5% of effective CPU, clamped 50m-500m), configurable memory eviction thresholds, preserved disk eviction signals (nodefs, imagefs, inodesFree), pods-only cgroup enforcement, and reservation-aware allocatable capacity annotations for cluster-autoscaler.
```
2026-05-01 14:45:30 +03:00
Andrei Kvapil 70e507ee5c fix(security): bump Go dependencies to clear Dependabot CVE alerts (#2494)
## What this PR does

Closes the 25 open Dependabot Go-ecosystem alerts on this repository (2
critical, 11 high, 12 moderate) by bumping the affected dependencies in
five Go modules to their latest stable versions. Splits the work into
six signed commits, one per logical block, so the diff in each commit
can be reviewed in isolation.

### Full advisory coverage

| # | Module | Severity | Package | Advisory | Title | Vulnerable |
Shipped |
|---|---|---|---|---|---|---|---|
| 29 | `go.mod` | critical | `google.golang.org/grpc` |
[GHSA-p77j-4mvh-x3m3](https://github.com/advisories/GHSA-p77j-4mvh-x3m3)
| gRPC-Go authorization bypass via missing leading slash in `:path` | `<
1.79.3` | `1.80.0` |
| 28 | `kubevirt-csi-driver/go.mod` | critical |
`google.golang.org/grpc` |
[GHSA-p77j-4mvh-x3m3](https://github.com/advisories/GHSA-p77j-4mvh-x3m3)
| gRPC-Go authorization bypass via missing leading slash in `:path` | `<
1.79.3` | `1.80.0` |
| 32 | `go.mod` | high | `go.opentelemetry.io/otel` |
[GHSA-mh2q-q3fh-2475](https://github.com/advisories/GHSA-mh2q-q3fh-2475)
| Multi-value `baggage` header extraction causes excessive allocations
(remote DoS amplification) | `>= 1.36.0, <= 1.40.0` | `1.43.0` |
| 31 | `go.mod` | high | `github.com/moby/spdystream` |
[GHSA-pc3f-x583-g7j2](https://github.com/advisories/GHSA-pc3f-x583-g7j2)
| SpdyStream: DoS on CRI | `<= 0.5.0` | `0.5.1` |
| 30 | `go.mod` | high | `go.opentelemetry.io/otel/sdk` |
[GHSA-hfvc-g4fc-pqhx](https://github.com/advisories/GHSA-hfvc-g4fc-pqhx)
| BSD `kenv` command not using absolute path enables PATH hijacking |
`>= 1.15.0, <= 1.42.0` | `1.43.0` |
| 27 | `go.mod` | high | `go.opentelemetry.io/otel/sdk` |
[GHSA-9h8m-3fm2-qjrq](https://github.com/advisories/GHSA-9h8m-3fm2-qjrq)
| OpenTelemetry Go SDK vulnerable to arbitrary code execution via PATH
hijacking | `>= 1.21.0, < 1.40.0` | `1.43.0` |
| 26 | `kubevirt-csi-driver/go.mod` | high |
`github.com/opencontainers/runc` |
[GHSA-cgrx-mc8f-2prm](https://github.com/advisories/GHSA-cgrx-mc8f-2prm)
| runc container escape and DoS due to arbitrary write gadgets and
procfs write redirects | `<= 1.2.7` | `1.4.2` |
| 25 | `kubevirt-csi-driver/go.mod` | high |
`github.com/opencontainers/runc` |
[GHSA-qw9x-cqr3-wc7r](https://github.com/advisories/GHSA-qw9x-cqr3-wc7r)
| runc container escape with malicious config due to `/dev/console`
mount and related races | `>= 1.0.0-rc3, <= 1.2.7` | `1.4.2` |
| 24 | `kubevirt-csi-driver/go.mod` | high |
`github.com/opencontainers/runc` |
[GHSA-9493-h29p-rfm2](https://github.com/advisories/GHSA-9493-h29p-rfm2)
| runc container escape via "masked path" abuse due to mount race
conditions | `<= 1.2.7` | `1.4.2` |
| 23 | `kubevirt-csi-driver/go.mod` | high | `golang.org/x/oauth2` |
[GHSA-6v2p-p543-phr9](https://github.com/advisories/GHSA-6v2p-p543-phr9)
| Improper Validation of Syntactic Correctness of Input | `< 0.27.0` |
`0.36.0` |
| 16 | `kubeovn-webhook/go.mod` | high | `golang.org/x/oauth2` |
[GHSA-6v2p-p543-phr9](https://github.com/advisories/GHSA-6v2p-p543-phr9)
| Improper Validation of Syntactic Correctness of Input | `< 0.27.0` |
`0.36.0` |
| 4 | `kubeovn-webhook/go.mod` | high | `golang.org/x/net` |
[GHSA-4374-p667-p6c8](https://github.com/advisories/GHSA-4374-p667-p6c8)
| HTTP/2 rapid reset can cause excessive work in `net/http` | `< 0.17.0`
| `0.53.0` |
| 3 | `kubeovn-webhook/go.mod` | high | `golang.org/x/net` |
[GHSA-vvpx-j8f3-3w6h](https://github.com/advisories/GHSA-vvpx-j8f3-3w6h)
| `golang.org/x/net` vulnerable to Uncontrolled Resource Consumption |
`< 0.7.0` | `0.53.0` |
| 22 | `kubevirt-csi-driver/go.mod` | medium | `golang.org/x/net` |
[GHSA-vvgc-356p-c3xw](https://github.com/advisories/GHSA-vvgc-356p-c3xw)
| `golang.org/x/net` vulnerable to Cross-site Scripting | `< 0.38.0` |
`0.53.0` |
| 21 | `kubevirt-csi-driver/go.mod` | medium | `golang.org/x/net` |
[GHSA-qxp5-gwg8-xv66](https://github.com/advisories/GHSA-qxp5-gwg8-xv66)
| HTTP Proxy bypass using IPv6 Zone IDs | `< 0.36.0` | `0.53.0` |
| 20 | `kubevirt-csi-driver/go.mod` | medium |
`github.com/opencontainers/runc` |
[GHSA-jfvp-7x6p-h2pv](https://github.com/advisories/GHSA-jfvp-7x6p-h2pv)
| runc can be confused to create empty files/directories on the host |
`< 1.1.14` | `1.4.2` |
| 19 | `go.mod` | medium | `golang.org/x/crypto` |
[GHSA-f6x5-jh6r-wrfv](https://github.com/advisories/GHSA-f6x5-jh6r-wrfv)
| `golang.org/x/crypto/ssh/agent` vulnerable to panic if message is
malformed (OOB read) | `< 0.45.0` | `0.50.0` |
| 18 | `go.mod` | medium | `golang.org/x/crypto` |
[GHSA-j5w8-q4qc-rx2x](https://github.com/advisories/GHSA-j5w8-q4qc-rx2x)
| `golang.org/x/crypto/ssh` allows an attacker to cause unbounded memory
consumption | `< 0.45.0` | `0.50.0` |
| 14 | `kubeovn-webhook/go.mod` | medium | `golang.org/x/net` |
[GHSA-vvgc-356p-c3xw](https://github.com/advisories/GHSA-vvgc-356p-c3xw)
| `golang.org/x/net` vulnerable to Cross-site Scripting | `< 0.38.0` |
`0.53.0` |
| 11 | `kubeovn-webhook/go.mod` | medium | `golang.org/x/net` |
[GHSA-qxp5-gwg8-xv66](https://github.com/advisories/GHSA-qxp5-gwg8-xv66)
| HTTP Proxy bypass using IPv6 Zone IDs | `< 0.36.0` | `0.53.0` |
| 9 | `kubeovn-webhook/go.mod` | medium | `golang.org/x/net` |
[GHSA-xrjj-mj9h-534m](https://github.com/advisories/GHSA-xrjj-mj9h-534m)
| `golang.org/x/net/http2` vulnerable to possible excessive memory
growth | `< 0.4.0` | `0.53.0` |
| 8 | `kubeovn-webhook/go.mod` | medium | `golang.org/x/net` |
[GHSA-4v7x-pqxf-cx7m](https://github.com/advisories/GHSA-4v7x-pqxf-cx7m)
| `net/http`, `x/net/http2`: close connections when receiving too many
headers | `< 0.23.0` | `0.53.0` |
| 7 | `kubeovn-webhook/go.mod` | medium | `google.golang.org/protobuf` |
[GHSA-8r3f-844c-mc37](https://github.com/advisories/GHSA-8r3f-844c-mc37)
| `protojson.Unmarshal` infinite loop on certain forms of invalid JSON |
`< 1.33.0` | `1.36.11` |
| 6 | `kubeovn-webhook/go.mod` | medium | `golang.org/x/net` |
[GHSA-qppj-fm5r-hxr3](https://github.com/advisories/GHSA-qppj-fm5r-hxr3)
| HTTP/2 Stream Cancellation Attack | `< 0.17.0` | `0.53.0` |
| 5 | `kubeovn-webhook/go.mod` | medium | `golang.org/x/net` |
[GHSA-2wrh-6pvc-2jm9](https://github.com/advisories/GHSA-2wrh-6pvc-2jm9)
| Improper rendering of text nodes in `golang.org/x/net/html` | `<
0.13.0` | `0.53.0` |

Sorted by severity (critical → high → medium), then by alert number. The
`Module` column lists the manifest path inside the repo (`go.mod` is the
root); `Vulnerable` is what GitHub flagged in the original advisory;
`Shipped` is the version this PR pins to.

### Modules bumped

1. Root `go.mod` (`github.com/cozystack/cozystack`) — purely transitive
bumps for `grpc`, `otel`, `otel/sdk`, `x/crypto`, `spdystream`.
2. `api/apps/v1alpha1/go.mod` — bumps `k8s.io/apimachinery` and
`golang.org/x/net`/`x/text` for consistency with the root module's `go
1.26.2` directive.
3. `packages/apps/kubernetes/images/kubevirt-csi-driver/go.mod` — bumps
the upstream `kubevirt.io/csi-driver` pin to its latest commit, raises
the `k8s.io/*` replace block from `v0.31.4` to `v0.36.0`, bumps `runc`
to `v1.4.2`, and adapts `main.go` to the upstream's new builder-style
API for `service.NewKubevirtCSIDriver` (the constructor was split into
`WithIdentityService` / `WithControllerService` / `WithNodeService`).
4. `packages/system/kubeovn-webhook/images/kubeovn-webhook/go.mod` —
direct bumps to `golang.org/x/net`, `golang.org/x/oauth2`,
`google.golang.org/protobuf`.
5. `packages/system/dashboard/images/token-proxy/go.mod` — bumps `jwx`,
`httprc`, `x/crypto`, `x/sys` for consistency with the root module's
directive.

All five modules now declare `go 1.26.2`. The three vendored-image
Dockerfiles (`kubevirt-csi-driver`, `kubeovn-webhook`, `token-proxy`)
move to `golang:1.26{,-alpine}`.

### First-party Dockerfile bumps

The root `go.mod` directive jump to `1.26.2` means every Dockerfile that
copies the root `go.mod` and builds a cozystack-owned binary needs a
matching base image, otherwise the in-container toolchain has to pull
`go 1.26` on the fly via `GOTOOLCHAIN=auto` at every build. Eight images
are bumped from `golang:1.25-alpine` to `golang:1.26-alpine`:
`cozystack-operator`, `cozystack-api`, `cozystack-controller`,
`backup-controller`, `backupstrategy-controller`, `flux-plunger`,
`kubeovn-plunger`, `lineage-controller-webhook`.

Vendored Dockerfiles (`kubevirt-cloud-provider`, `redis-operator`,
`keycloak-operator`, `linstor-csi`, `multus-cni`, `objectstorage`,
`s3manager`, `kamaji`) are intentionally untouched: each one fetches its
source from upstream at build time and pins its `golang:` base image to
whatever the upstream commit was tested against.

### Why bump past the advisory minimums

Bumping each affected dependency to its latest stable rather than the
lowest patched version means we pick up subsequent (non-CVE) bug fixes
for free and reduce the number of follow-up Dependabot bumps the next
reviewer has to triage. Where this required a cascading bump (e.g.
`runc` v1.4.2 only compiles together with `mount-utils` v0.32+, which
dropped the deprecated `libcontainer/userns` import path), the cascade
was followed through.

### Build verification

`go build ./...` and `go vet ./...` are clean across all five modules
locally. Two `go vet` warnings in the root module
(`pkg/lineage/lineage_test.go:76` unkeyed struct fields,
`cmd/cozypkg/cmd/add.go:521` non-constant format string) and three
`randfill.Continue` test panics in `pkg/apis/{apps,core}/install` and
`pkg/apiserver` also reproduce on `main` and are out of scope for this
PR.

### Out of scope

The `k8s.io/*` libraries in `kubeovn-webhook/go.mod` are still pinned at
`v0.26.0`; they are not flagged by Dependabot and bumping them would be
a separate, much larger maintenance change.

### Release note

```release-note
fix(security): bump Go dependencies in root, api/apps/v1alpha1, kubevirt-csi-driver, kubeovn-webhook, and token-proxy modules to clear all open Dependabot CVE alerts
```


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

* **Chores**
  * Upgraded Go toolchain across components and build stages to 1.26.x.
* Updated numerous dependencies for compatibility, stability and
security (Kubernetes APIs, gRPC, OpenTelemetry, golang.org/x/*, and
related libs).
* Several containers now run as a non-root user (UID/GID 65532) to
improve runtime security.

* **Refactor**
* CSI driver initialization refactored to a builder-style pattern
enabling conditional service wiring.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-01 13:16:03 +02:00
Arsolitt 22d2a224bb test(kubernetes): rename ephemeralStorage to diskSize in kubelet tests
PR #2454 renamed the nodeGroups field ephemeralStorage to diskSize and
added a migration guard that fails template rendering when the old name
is used. Update kubelet reservation tests to use the new field name.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-30 14:14:23 +03:00
Arsolitt d4808b9674 merge: resolve conflicts with main after PR #2454
Merge origin/main into feat/kubernetes-kubelet-reserved-resources.

PR #2454 (persistent storage for worker nodes) landed in main and
touched the same nodeGroups schema. Conflicts resolved by keeping
both feature sets (kubelet reservations + persistent storage):

- api/apps/v1alpha1/kubernetes/types.go: merged kubebuilder default
  marker with all keys from both features
- packages/apps/kubernetes/README.md: merged Parameters table with
  diskSize/storageClass + kubelet* rows
- packages/system/kubernetes-rd/cozyrds/kubernetes.yaml: merged
  openAPISchema and keysOrder, dropped stale ephemeralStorage path

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-30 13:51:28 +03:00
Arsolitt 998cc77635 chore(kubernetes): merge main to resolve CRD schema conflict
Integrate the HAMi GPU virtualization addon from main while
keeping the persistent-storage diskSize and storageClass fields
introduced by this branch.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-29 16:54:58 +03:00
Arsolitt d5caffcc8d fix(kubernetes): gate HAMi HelmRelease on _namespace.etcd
Match the pattern used by every other tenant-cluster addon HelmRelease
(gpu-operator, cilium, fluxcd, ...): only render when the upstream etcd
namespace is ready, not just when the addon is enabled. Add values-ci.yaml
to the HAMi test suite so the new gate has the fixture it needs.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-29 11:13:42 +03:00
Arsolitt 805b3b17cb fix(kubernetes): add CI values to GPU Operator HAMi tests
Tests need _namespace.etcd from values-ci.yaml after the merge
introduced an etcd-namespace guard on the gpu-operator HelmRelease
condition.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-28 19:11:26 +03:00
Arsolitt f6ee54e4ea Merge remote-tracking branch 'origin/main' into feat/hami-integration
Signed-off-by: Arsolitt <arsolitt@gmail.com>

# Conflicts:
#	api/apps/v1alpha1/kubernetes/types.go
#	packages/apps/kubernetes/templates/helmreleases/gpu-operator.yaml
#	packages/system/kubernetes-rd/cozyrds/kubernetes.yaml
2026-04-28 18:58:57 +03:00
Arsolitt 633ff1a617 chore(kubernetes): merge main to resolve CRD schema conflict
Incorporate images.waitForKubeconfig support from main while
keeping the persistent-storage diskSize and storageClass fields
introduced by this branch.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-27 16:14:07 +03:00
Arsolitt 34ec220a9c merge: resolve conflicts with main
Merge origin/main into feat/kubernetes-kubelet-reserved-resources.

Conflicts resolved:
- packages/apps/kubernetes/Makefile: kept .PHONY declaration from PR
- packages/system/kubernetes-rd/cozyrds/kubernetes.yaml: kept both
  kubelet schema from PR and images property from main

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-27 16:13:29 +03:00
Aleksei Sviridkin bdf23e66d1 fix(kubernetes): close admin-kubeconfig race on tenant cluster bootstrap (#2413)
## What this PR does

Closes #2412. On a cold tenant-Kubernetes bootstrap, the parent
HelmRelease raced the admin-kubeconfig Secret that Kamaji provisions
asynchronously. Three CP-side Deployments (cluster-autoscaler, kccm,
kcsi-controller) mounted that Secret as a hard volume, flux
helm-controller's default wait budget was too short for Kamaji cold
start, and `install.remediation { retries: -1 }` then uninstalled the
Cluster CR and restarted the cycle forever.

Implements a defense-in-depth fix:

- `optional: true` on the admin-kubeconfig Secret volume in all three
Deployments so kubelet no longer FailedMounts while Kamaji is still
bootstrapping.
- A shared `wait-for-kubeconfig` init container (in
`templates/_helpers.tpl`) that polls for `super-admin.svc` with a 10m
deadline, strictly below the HelmRelease Install.Timeout so a broken
tenant falls into CrashLoopBackOff visibly instead of hanging forever.
- Per-Application HelmRelease Install/Upgrade timeout, driven by a new
`release.cozystack.io/helm-install-timeout` annotation on
ApplicationDefinition. Kubernetes-rd sets it to `15m`; other kinds leave
it unset and keep flux defaults, so their failed installs remediate on
the normal cadence. Parser rejects ns/us/µs (accepted by
`time.ParseDuration`, rejected by Flux's CRD pattern) at startup.
- Soft-skip when `_namespace.etcd` is empty: the CP-side Deployments,
the Cluster/KamajiControlPlane/KubevirtCluster/WorkloadMonitor CRs, and
every child HelmRelease that references admin-kubeconfig now render only
when an etcd DataStore exists for this tenant. An `awaiting-etcd`
ConfigMap is emitted as a user-visible status beacon so `helm install`
still succeeds and flux retries on its 5m interval until the Tenant
chart catches up.
- e2e remediation guard built on `.status.history[].status` (the
Snapshot shape), not on `.status.installFailures` - `ClearFailures()`
zeroes the latter on every successful reconciliation, which made the
previous guard vacuous.

Tests:

- Go unit tests for the annotation parser (accepted/rejected units) and
the HR builder (table-driven across kinds).
- helm unittest for the per-template structure (optional volume, init
container, dataStoreName, awaiting-etcd beacon).
- bats unit tests for the shell guard (every combination of
empty/zero/positive history entries, plus pinned HR v2 shape).
- Chart-wide bats invariants: every Deployment mounting admin-kubeconfig
has the guards; zero such Deployments and zero HelmReleases render when
etcd is empty.

All wired into the existing `make unit-tests` target (`go-unit-tests`
added alongside `helm-unit-tests` and `bats-unit-tests`).

Option 2 from the ticket (separate HelmRelease with `dependsOn`) was
intentionally not taken: the combination above closes the same race
without restructuring the chart's HelmRelease topology.

### Release note

```release-note
fix(kubernetes): close admin-kubeconfig race on tenant Kubernetes bootstrap. The parent HelmRelease no longer enters an uninstall/retry cycle when Kamaji control-plane cold start exceeds flux's default wait budget. A Kubernetes tenant created before the parent Tenant application has etcd enabled now renders only an awaiting-etcd beacon ConfigMap and waits quietly for the DataStore to appear, instead of producing half-installed Deployments that CrashLoopBackOff forever.
```


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

* **New Features**
* Per-application Helm install/upgrade timeout via metadata annotation.
* Init-container guards that wait for admin kubeconfig before workloads
start.
  * Chart resources now render conditionally based on etcd presence.

* **Tests**
* Helm-template tests for admin-kubeconfig invariants and
remediation-cycle detection.
* New Go unit tests and CI Helm/unittest coverage plus test value files.

* **Chores**
* Added BusyBox image pin and new Makefile test targets (including Go
unit-tests).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-27 15:31:52 +03:00
Aleksei Sviridkin fb1ef59287 fix(kubernetes): drop undocumented status-beacon annotation from awaiting-etcd ConfigMap
The cozystack.io/status-beacon: "true" annotation had no consumer in
the chart, no documented contract, and no convention defined for other
charts to follow. It would have become accidental precedent for
contributors copying the pattern without understanding it.

The ConfigMap itself is self-explanatory: the name <release>-awaiting-etcd,
data.status: "awaiting-etcd", and the human-readable message in
data.message all surface the same operator signal via kubectl get cm.
Drop the annotation; keep the ConfigMap.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-27 13:50:18 +03:00
Aleksei Sviridkin 37ecd7b3af feat(kubernetes): make wait-for-kubeconfig image overridable for air-gapped registries
Operators in air-gapped or rate-limited environments cannot reach
docker.io and the bundled busybox digest pin gives them no escape
hatch. Add an optional images.waitForKubeconfig chart value that, when
set, replaces the helper's image reference with any registry path
kubelet can pull. Empty value falls back to images/busybox.tag, so the
prior digest-pinned default is preserved.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-27 13:50:08 +03:00
Arsolitt 9ba5e58781 fix(kubernetes): avoid rendering empty values key in gpu-operator HelmRelease
Wrap the values: block in a conditional so the key is omitted entirely
when no defaults or overrides produce content. Previously the template
always emitted values: null, triggering unnecessary FluxCD reconciliation.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-27 13:44:17 +03:00
Arsolitt f0e033ebcb style(kubernetes): use with instead of if for hami valuesOverride
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-27 13:44:08 +03:00
Aleksei Sviridkin 0fa1ca5f03 fix(security): bump kubevirt-csi-driver to latest deps
Bumps all CVE-flagged dependencies in the kubevirt-csi-driver wrapper
to their latest stable versions and adapts the local main.go to the
new upstream service.NewKubevirtCSIDriver builder API.

CVE-flagged bumps:
- google.golang.org/grpc 1.65.0 -> 1.80.0 (GHSA-p77j-4mvh-x3m3, critical)
- github.com/opencontainers/runc -> 1.4.2 (GHSA-cgrx-mc8f-2prm,
  GHSA-qw9x-cqr3-wc7r, GHSA-9493-h29p-rfm2 high, GHSA-jfvp-7x6p-h2pv
  medium)
- golang.org/x/oauth2 -> 0.36.0 (GHSA-6v2p-p543-phr9, high)
- golang.org/x/net -> 0.53.0 (GHSA-vvgc-356p-c3xw, GHSA-qxp5-gwg8-xv66
  medium)

Cascading bumps required to consume runc 1.4.2 (which removed
libcontainer/userns) and the latest x/net (which requires Go 1.25+):
- k8s.io/* replace block 0.31.4 -> 0.36.0
- kubevirt.io/csi-driver upstream pin -> 20260424143118-bee6c348c004
- go directive 1.22.0 -> 1.26.2
- builder image golang:1.22.5 -> golang:1.26

The upstream csi-driver split NewKubevirtCSIDriver into a builder
chain (WithIdentityService / WithControllerService / WithNodeService);
main.go is rewritten to call the new entry points and gate them on
the runControllerService and runNodeService flags as before.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-26 01:46:14 +03:00
Arsolitt 6a9c310a4b fix(hami): conditional values emission and cosmetic template cleanup
Only emit values key in hami.yaml when valuesOverride has content,
matching gpu-operator pattern. Add test verifying empty valuesOverride
does not produce spec.values. Fix trailing whitespace and missing
newlines in vendored chart templates.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-24 15:31:24 +03:00
Arsolitt 37d5ff0c6f fix(hami): align templates with project patterns and clean dead code
Unconditionally emit values in hami.yaml matching the project pattern.
Remove duplicate test case and add coverage for omitted valuesOverride key.
Delete dead PSP template and RBAC rules (policy/v1beta1 removed in K8s
1.25). Override kube-scheduler image registry to registry.k8s.io to avoid
Chinese registry for international users.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-24 14:07:17 +03:00
Arsolitt cd4590b426 docs(kubernetes): clarify storageClass default behavior
Specify that empty storageClass uses the management cluster default
StorageClass (annotated with is-default-class: true), not the
top-level chart storageClass value.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-24 14:01:48 +03:00
Arsolitt 2734dc0bcb fix(hami): clean up leftover hami-dra references and harden defaults
Remove broken hami-dra subchart dependency from vendored chart
(Chart.yaml, Chart.lock, values.yaml) and strip DRA condition guards
from all templates since the subchart was already deleted. Override
devicePlugin updateStrategy to OnDelete to prevent destructive rolling
updates of GPU workloads. Align gpu-operator template with project
pattern (unconditional values emission). Add nodeConfiguration format
documentation and test for conflicting valuesOverride scenario.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-24 14:00:48 +03:00
Arsolitt 3afcaa5d26 docs(kubernetes): clarify persistence scope in breaking changes note
Add explicit scope: persistence covers same-VM reboots only.
VM replacement by CAPI still provisions a fresh PVC.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-24 14:00:34 +03:00
Arsolitt e2b10d42c8 docs(kubernetes): correct overwrite: false comment reasoning
DataVolumes are tied to VM lifecycle — PVC does not survive VM
recreation. The real reason for overwrite: false is the same-VM
reboot path: XFS on /dev/vdb already exists and cloud-init must
skip reformatting.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-24 14:00:26 +03:00
Arsolitt 56054710e9 fix(kubernetes): use floor instead of ceil for memory capacity annotation
cluster-autoscaler relies on the capacity annotation to decide whether a
node can fit a pending pod. ceil rounds the allocatable value up by <1Mi,
which can advertise capacity that kubelet will never actually schedule.
floor produces a safe under-approximation instead.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-24 13:47:29 +03:00
Arsolitt ab7deb2b05 chore(kubernetes): regenerate after HAMi addon integration
Run make generate to update generated types, schema, README,
and CRD definitions for the new HAMi addon.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-24 13:03:49 +03:00
Arsolitt 89aeb4cb83 test(kubernetes): add migration guard test for both fields present
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 19:12:46 +03:00
Arsolitt cac38eeca1 docs(kubernetes): add inline comment explaining overwrite false behavior
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 19:12:45 +03:00
Arsolitt a3393d4d14 fix(kubernetes): merge duplicate files blocks in KubeadmConfigTemplate
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 19:12:45 +03:00
Arsolitt 5b994b2cca docs(kubernetes): document migration failure consequences
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 19:12:45 +03:00
Arsolitt c5b3a93d52 fix(kubernetes): use hasKey for ephemeralStorage migration guard
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 19:12:45 +03:00
Arsolitt a1046b5ef0 fix(kubernetes): make storageClass optional with default in schema
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 19:12:45 +03:00
Arsolitt 8a441d6232 test(kubernetes): document omitted defaults in test fixture
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 19:12:45 +03:00
Arsolitt 0fb0de7062 feat(kubernetes): add migration guard for ephemeralStorage field rename
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 19:12:44 +03:00
Arsolitt c2eb6c0b3d docs(kubernetes): fix storageClass default value in README parameter table
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 19:12:44 +03:00
Arsolitt 70dbb12450 fix(kubernetes): add kubebuilder default annotation to StorageClass field
The NodeGroup.StorageClass field lacked a +kubebuilder:default annotation,
causing a semantic inconsistency: the map-level default in ConfigSpec
included "storageClass": "" but CRD defaulting would not inject it at the
leaf level when a user specified a nodeGroup without storageClass.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 19:12:44 +03:00
Arsolitt a451a898d6 docs(kubernetes): document ephemeralStorage to diskSize breaking change
Add a Breaking Changes section noting that ephemeralStorage has been
renamed to diskSize with no backward-compatibility fallback.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 18:40:04 +03:00
Arsolitt fccea96730 fix(kubernetes): remove empty default from storageClass in schema
Drop the explicit `"default": ""` from storageClass so the field is
simply absent when not set, matching how other optional string fields
behave in the schema.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 18:40:04 +03:00
Arsolitt 5560162934 test(kubernetes): add KubeadmConfigTemplate persistent storage tests
Verify that the KubeadmConfigTemplate correctly configures XFS
filesystem, disk partitioning, mount points, bind-mounts for kubelet
and containerd, and preKubeadmCommands guards.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 18:40:04 +03:00
Arsolitt ca12f28734 test(kubernetes): add multi-nodegroup dataVolumeTemplate test
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 18:40:04 +03:00
Arsolitt a336f1812c fix(kubernetes): add default value for storageClass in schema
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 18:40:03 +03:00
Arsolitt d1dfb75581 fix(kubernetes): add explicit overwrite false to disk_setup for persistent storage safety
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 18:40:03 +03:00
Arsolitt 6403982039 test(kubernetes): add helm test for diskSize default fallback
Verify that KubevirtMachineTemplate renders 20Gi storage when diskSize
is not explicitly set, ensuring the default value propagates correctly.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 18:40:03 +03:00
Arsolitt c434fbb1a7 fix(kubernetes): include per-nodeGroup storageClass in values default
Add storageClass: "" to nodeGroups.md0 in values.yaml so that the
code generator picks it up and places it into keysOrder for the
dashboard field ordering.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 18:40:03 +03:00
Arsolitt 2de4a81623 refactor(kubernetes): deduplicate test fixtures in cluster_test.yaml
Extract repeated values (_cluster, _namespace, version, controlPlane,
nodeGroups base config) into a shared values/common.yaml fixture loaded
at suite level, so each test case only overrides the fields it varies.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 18:40:03 +03:00
Arsolitt ad3fbc1e55 fix(kubernetes): quote storage values and guard bind-mount commands
Quote diskSize and storageClassName Helm template outputs to prevent
bare numeric or special-char values from causing YAML parse errors.

Also ensure mount-point directories exist before bind-mounting and skip
the mount when the target is already a mountpoint, avoiding failures
on node reboot or re-provisioning.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 18:40:02 +03:00
Arsolitt cf98381d48 docs(kubernetes): document automatic block size matching for worker node disks
Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-23 18:40:02 +03:00