mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-05 03:27:07 +09:00
daniil/admin-gitops
579
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d00ae9d14c |
feat(platform): add cozystack.customizer system package
Introduces a new opt-in system package that provisions an admin-facing GitOps surface: a Flux GitRepository + Kustomization in cozy-system reconciling an admin-owned git repo of cluster customizations via a curated ServiceAccount. The admin uses this to manage Package CR patches (e.g. OIDC enable, MetalLB option overrides) and bring their own HelmReleases / Keycloak realm imports / NetworkPolicies as code in a repo they own, rather than via in-cluster kubectl patch. Wired into the platform chart via a single `customizer.enabled` flag in values; off by default. When enabled, emits the cozystack.customizer Package CR through the existing bundles/system.yaml path and respects bundles.disabledPackages. Curated ClusterRole grants patch (not delete) on packages.cozystack.io, read-only on platform HelmReleases, full ownership of PackageSources + admin-owned namespaces (default: cozy-customizer), and Keycloak realm CRs. Explicitly does NOT grant delete on Packages, anything on CRDs, the cozystack-controller Deployment, or kube-system. NOTE: this MVP ships the GitOps surface only. The field-ownership contract documented in the package values is currently advisory; a validating admission webhook on packages.cozystack.io is the planned enforcement (kustomize-controller hardcodes client.ForceOwnership on Apply, so SSA conflict detection cannot block silent ownership transfer at the Flux layer). Risk callout is inline in the values block. Includes helm-unittest coverage for the bundle wiring (off, on with values, disabledPackages override). Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
ed1bb53d2c |
fix(platform): migrate ephemeralStorage to diskSize via pre-upgrade hook
Add pre-upgrade migration 41 that walks all kuberneteses.apps.cozystack.io Application CRs and renames nodeGroups[*].ephemeralStorage to diskSize, preserving the user's value. Without this migration, clusters upgraded after PR #2454 either fail to reconcile (hard fail blocks all Flux operations) or silently lose the user's disk size setting (default 20Gi replaces whatever was configured). The migration is idempotent and best-effort: a failed patch is logged and leaves the version stamp at 41 for retry on next upgrade. Update the chart-side guard error message to direct operators to the migration job logs when the field appears post-upgrade (regression detector). Bump migrations.targetVersion 41 -> 42. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
1c059b3ba3 |
chore(platform): drop dashboard.cozystack.io CRDs via migration 40
Existing clusters still have the legacy dashboard.cozystack.io CRDs installed even after the dashboard controller has been removed. Add a platform migration that deletes them and bump targetVersion to 41. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com> |
||
|
|
45937d6d13 |
fix(e2e-sandbox): use curl -fsSL when downloading mc
The minio client was downloaded without -L, so when dl.min.io served
a 302 redirect, curl wrote the HTML body ("<a href=...>Found</a>") to
/usr/local/bin/mc, chmod +x'd it, and shipped that as the binary. When
the bucket E2E test ran "mc alias set ...", the shell tried to interpret
the HTML as a script and errored with:
/usr/local/bin/mc: 1: cannot open a: No such file
(dash reading "<a" as an input redirect from a file named "a").
The image is rebuilt per CI run from cached layers; the broken mc layer
was cached and reused. Adding -fsSL fixes the download and the textual
change busts the cache so the next build pulls a real ELF binary.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
|
||
|
|
3c1c848cb3 |
fix(platform): add OpenSearch to PaaS bundle (#2648)
## What this PR does OpenSearch has shipped as a complete package set in the repo for several releases — `packages/apps/opensearch/`, `packages/system/opensearch-operator/`, `packages/system/opensearch-rd/`, and `PackageSource` files under `packages/core/platform/sources/` — but the PaaS bundle template was never updated to include the two `cozystack.opensearch-*` PackageSources. Result on any cluster with `bundles.paas.enabled=true`: - `opensearch-operator` is not deployed → no `OpenSearchCluster` CRD on cluster - `opensearch-rd` release is not deployed → no `ApplicationDefinition/opensearch` → the dashboard catalog has no OpenSearch entry and tenants cannot create `opensearches.apps.cozystack.io` This PR adds the missing includes alongside every other DB operator/application in `packages/core/platform/templates/bundles/paas.yaml`. ### Release note ```release-note Fix: OpenSearch is now installed by the PaaS bundle (`bundles.paas.enabled=true`); previously the operator and ApplicationDefinition were defined but never referenced from the bundle, so OpenSearch did not appear in the dashboard catalog. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * OpenSearch operator and application packages are now included in the PaaS bundle when enabled, providing additional search and analytics capabilities for supported deployments. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2648) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
e84677782c |
revert(ingress)!: remove broken publishing.exposure=loadBalancer mode (#2603)
## What this PR does Reverts the `publishing.exposure=loadBalancer` mode for ingress-nginx added in #2468. The mode rendered the ingress-nginx Service as `type: LoadBalancer` and a `CiliumLoadBalancerIPPool` over `publishing.externalIPs`, as a migration path off `Service.spec.externalIPs` (deprecated upstream in Kubernetes v1.36 per KEP-5707). In default Cozystack the implementation produces two failure modes: - `publishing.externalIPs` is the operator-supplied list of node public IPs (addresses the cluster nodes already hold and listen on, see [docs section "4b. Node Public IP Setup"](https://cozystack.io/docs/v1.3/install/cozystack/platform/#4b-node-public-ip-setup)), not a virtual-VIP pool. Treating it as one and DNATing those IPs via Cilium LB-IPAM collides with the existing ingress-nginx DNAT on the same IPs (`Service.spec.externalIPs` path), so traffic is forwarded non-deterministically. - Cilium in default Cozystack runs with `ipam.mode: kubernetes` and no L2/BGP announcer, so a pool over addresses outside the node range is allocated but never advertised — the Service stays unreachable. The KEP-5707 deprecation is real, but the right migration is a mechanism-agnostic tenant API knob translated by a Cozystack-side controller into whichever backend the cluster admin has configured (MetalLB pool, Cilium LB-IPAM, robotlb, externalIPs pin) — not a chart-side `IPPool` baked into the tenant chart. That follow-up belongs in its own PR. ### Removed - `publishing.exposure` value (and the inline comment) from `core/platform/values.yaml`. - `expose-mode` plumbing line in `core/platform/templates/apps.yaml`. - `CiliumLoadBalancerIPPool` template at `apps/tenant/templates/cilium-lb-pool.yaml`. - `type: LoadBalancer + externalTrafficPolicy: Local` branch in `extra/ingress/templates/nginx-ingress.yaml`. The Service now renders as `ClusterIP + spec.externalIPs` when externalIPs are set, falling through to `type: LoadBalancer` when they are not — pre-#2468 shape. - Exposure mode section from `extra/ingress/README.md`. - `exposure_test.yaml` fixtures in both charts. ### Compatibility Operators who explicitly set `publishing.exposure=loadBalancer` will see their ingress Service revert to `ClusterIP + spec.externalIPs` on upgrade. Anyone on the default value (`externalIPs`) is unaffected. ### Release note ```release-note revert(ingress): remove the publishing.exposure=loadBalancer mode added in #2468. The default externalIPs path is unchanged; operators who explicitly opted into loadBalancer will have their ingress Service revert to ClusterIP + spec.externalIPs on upgrade. Migration off Service.spec.externalIPs (deprecated in KEP-5707) will be reintroduced via a mechanism-agnostic API knob in a follow-up. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Refactor** * Removed conditional generation of the Cilium load‑balancer IP pool and simplified service exposure to rely on explicit external IP values. * Streamlined configuration for service exposure keys. * **Documentation** * Removed the exposure-mode guidance from ingress documentation. * **Tests** * Deleted the exposure-mode-related test suites covering various external IP and loadBalancer scenarios. [](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2603) <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
999202c004 |
fix(platform): add OpenSearch to PaaS bundle
The opensearch chart, operator chart, ApplicationDefinition and PackageSources have existed since the original OpenSearch series, but the PaaS bundle template never references the opensearch-operator or opensearch-application PackageSources. On isp-* variants with bundles.paas.enabled=true, neither the operator nor the ApplicationDefinition is ever installed, so the dashboard catalog has no OpenSearch entry and opensearches.apps.cozystack.io resources cannot be created. Add both includes to packages/core/platform/templates/bundles/paas.yaml, following the same pattern used for every other DB application. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
9f655e29ff |
Prepare release v1.4.0-rc.2
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com> |
||
|
|
252c18f3e8 |
Prepare release v1.4.0-rc.1
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com> |
||
|
|
f18ce1974e |
feat(platform): migration 39 converts legacy resourcesPreset values
Add a new pre-upgrade hook migration that walks every HelmRelease
spec.values and every app CR spec under apps.cozystack.io/v1alpha1, and
rewrites legacy flat resourcesPreset values (nano/micro/small/medium/
large/xlarge/2xlarge) to their instance-type equivalents (t1/c1).
Mapping is 1:1 by CPU and memory — the migration never bumps resource
usage. The same table lives in cozy-lib (legacy aliases) and in
pkg/apis/apps/presets/legacy.go.
Properties:
- Idempotent: re-running on a converted cluster is a no-op because
non-legacy values fall through the jq map_legacy else arm.
- Best-effort: a single failed patch logs a warning and continues;
cozy-lib legacy aliases keep charts rendering with identical CPU
and memory even if the migration is incomplete. kubectl patch
stderr is captured and surfaced in the WARN line so operators see
why a patch was rejected.
- Distinguishes "no items" from "kubectl errored" (RBAC stripped,
network down, CRD missing) so a broken cluster cannot look
identical to a clean no-op run and stamp the version
unconditionally.
- Dry-run: MIGRATION_DRY_RUN=1 prints the would-be patches and
leaves the version stamp at 39.
- Final summary echoed to both stdout and stderr.
- Version stamp only advances to 40 on a fully successful real run.
Bump migrations.targetVersion 39 -> 40 so the hook executes on the
next platform upgrade.
Co-authored-by: Matthieu ROBIN <matthieu@hidora.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
|
||
|
|
f919afe15f |
revert(ingress)!: remove broken publishing.exposure=loadBalancer mode
Reverts the ingress loadBalancer exposure mode added in #2468. The mode rendered the ingress-nginx Service as type: LoadBalancer and a CiliumLoadBalancerIPPool over publishing.externalIPs, as a migration path off Service.spec.externalIPs (deprecated upstream in Kubernetes v1.36 per KEP-5707). In default Cozystack the implementation produces two failure modes: - publishing.externalIPs is the operator-supplied list of node public IPs (addresses the cluster nodes already hold and listen on), not a virtual-VIP pool. Treating it as one and DNATing those IPs via Cilium LB-IPAM collides with the existing ingress-nginx DNAT on the same IPs (Service.spec.externalIPs path), so traffic is forwarded non-deterministically. - Cilium in default Cozystack runs with ipam.mode: kubernetes and no L2/BGP announcer, so a pool over addresses outside the node range is allocated but never advertised — the Service stays unreachable. The KEP-5707 deprecation is real, but the right migration is a mechanism-agnostic tenant API knob translated by a Cozystack-side controller into whichever backend the cluster admin has configured (MetalLB pool, Cilium LB-IPAM, robotlb, externalIPs pin) — not a chart-side IPPool baked into the tenant chart. That follow-up belongs in its own PR. Removed: - publishing.exposure value (and the inline comment) from core/platform/values.yaml - expose-mode plumbing line in core/platform/templates/apps.yaml - CiliumLoadBalancerIPPool template at apps/tenant/templates/cilium-lb-pool.yaml - type: LoadBalancer + externalTrafficPolicy: Local branch in extra/ingress/templates/nginx-ingress.yaml (Service renders back as ClusterIP + spec.externalIPs when externalIPs are set, falling through to type: LoadBalancer when they are not — pre-#2468 shape) - Exposure mode section from extra/ingress/README.md - exposure_test.yaml fixtures in both charts Operators who explicitly set publishing.exposure=loadBalancer will see their ingress Service revert to ClusterIP + spec.externalIPs on upgrade. Anyone on the default value (externalIPs) is unaffected. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com> |
||
|
|
45e21a330f |
feat(networking): add ouroboros for hairpin-NAT under PROXY-protocol (#2561)
## What this PR does Adds [`lexfrei/ouroboros@v0.7.0`](https://github.com/lexfrei/ouroboros/releases/tag/v0.7.0) — a Go reimplementation of [`compumike/hairpin-proxy`](https://github.com/compumike/hairpin-proxy) — and wires it into cozystack as a *conditional default*: a single `publishing.proxyProtocol: true` switch turns PROXY-protocol on at the host ingress-nginx **and** auto-deploys ouroboros to fix the resulting hairpin-NAT problem (intra-cluster traffic to the cluster's own public hostnames arriving at ingress-nginx without the PROXY header it now requires). Default stays `false` — clusters that do not run PROXY-protocol get zero new resources, zero RBAC delta, zero behaviour change. The chart is platform-agnostic at v0.7.0: the controller and proxy binaries auto-detect the cluster's DNS domain from `/etc/resolv.conf` inside the pod and compose the rewrite target FQDN and the backend host at runtime as `<svc>.<ns>.svc.<cluster-domain>`. Cozystack tenants on `cozy.local`, kubeadm-bootstrapped clusters on `cluster.local`, federations on `k8s.example.com`, custom-domain RKE2/k3s — all work without operator override. Verified end-to-end on a `cozy.local` cluster: controller logs `cluster-domain=cozy.local` at startup, chart emits `--proxy-service-name + --proxy-service-namespace` (no chart-baked `--proxy-fqdn`), Corefile picks up the rewrite block between BEGIN/END markers, in-pod `dig <ingress-host>` returns the ouroboros-proxy ClusterIP. Operators that prefer a chart-baked FQDN can still set `controller.clusterDomain` explicitly — the explicit-override path is preserved for non-Service backends and for split-horizon DNS topologies. The chart runs in `coredns` mode for the host (the host CoreDNS is Talos-managed and does not carry an `import` directive, so the alternative would silently no-op there) and in `coredns-import` mode for tenant deployments (tenants run cozystack-coredns, which carries the import directive and ships an empty `kube-system/coredns-custom` ConfigMap; ouroboros writes plugin-only `rewrite name` snippets into the `ouroboros.override` data key without ever touching the chart-rendered Corefile, so it does not race the chart that re-templates `data.Corefile` on every Flux reconcile). ### Layout * `packages/system/ouroboros/` — vendored chart from `oci://ghcr.io/lexfrei/charts/ouroboros@0.7.0`. Cozystack-side defaults pinned in `values.yaml`: `controller.mode=coredns` (host scenario), `proxy.target.namespace=cozy-ingress-nginx` (override the chart-default `ingress-nginx` namespace; chart-default `serviceName=ingress-nginx-controller` matches as-is), Gateway-API watching left at upstream's default of `false`. `proxy.target.host` is left empty so the proxy composes the backend host at runtime. Image is digest-pinned at `0.7.0@sha256:478665e05dd0ffc4c1f7764320ea24b52251781884ddfd668d93a03e39d9094c` and pulled directly from `ghcr.io/lexfrei/ouroboros` — this package is intentionally not mirrored under `ghcr.io/cozystack/*`. The chart manifest itself is also digest-pinned via `OUROBOROS_CHART_DIGEST=sha256:f28bfac9fd7070b1f3357983ff5aad28c16115e7de1cfc16ee568a3b4cfc9d7e` in the package Makefile, with a Chart.yaml-version vs. Makefile-version assert that aborts the update target on lockstep drift. Helm-unittest in `tests/` covers controller args, RBAC narrowing per mode, Gateway-API gating, runtime-composition flag emission in both `coredns` and `coredns-import` modes, image pin drift. * `packages/system/coredns/templates/coredns-custom.yaml` + `values.yaml` — empty `coredns-custom` ConfigMap as the chart-managed `import` prerequisite, plus the `import /etc/coredns/custom/*.override` plugin in the `.:53` server block + `extraVolumes`/`extraVolumeMounts` mount at `/etc/coredns/custom/`. Same package serves host and tenant CoreDNS, so this covers both layers without duplication. Helm-unittest pins the rendered Corefile via a snapshot so future `make update` does not silently drop the import directive. The empty rendered ConfigMap deliberately omits `data:` — Helm three-way merge has no template-side keys to compare against, so apiserver-side runtime writes (the `ouroboros.override` data key) survive every chart upgrade. The `notExists: data` assertion in `tests/coredns_custom_test.yaml` pins the invariant. * `packages/core/platform/sources/ouroboros.yaml` — new `cozystack.ouroboros` PackageSource for the host install (`dependsOn: cozystack.networking, cozystack.ingress-application`). * `packages/core/platform/sources/kubernetes-application.yaml` — `kubernetes-ouroboros` component registered against `system/ouroboros` so the tenant HelmRelease can reference it. * `packages/apps/kubernetes/templates/helmreleases/ouroboros.yaml` — tenant-side HelmRelease deployed via the tenant admin-kubeconfig, mirroring the cert-manager pattern. A `cozystack.defaultOuroborosValues` helper flips `mode` back to `coredns-import` for tenants and mirrors per-tenant `addons.gatewayAPI.enabled` into the controller (forcing `--gateway-api` on a tenant without Gateway-API CRDs CrashLoops the controller on `WaitForCacheSync`). `controller.clusterDomain` is intentionally NOT pre-set — chart 0.7.0 auto-detects per-pod from `/etc/resolv.conf` and composes the rewrite target at runtime, so the same wrapper renders correctly on `cluster.local`, `cozy.local`, or any custom tenant cluster-domain. Operators on a non-default cluster-domain can still override via `valuesOverride.ouroboros.controller.clusterDomain`. `dependsOn` grows a conditional `gateway-api-crds` entry on the same gate. If `addons.ouroboros.enabled` is true while `addons.ingressNginx.enabled` is false, 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. Tenant disable is single-step: flipping `addons.ouroboros.enabled: false` deletes the rendered HelmRelease, helm-controller runs `helm uninstall` on the tenant, and the chart's pre-delete cleanup hook (`coredns-cleanup-hook.yaml`, vendored at 0.7.0) nulls the `ouroboros.override` key on the tenant's `kube-system/coredns-custom` ConfigMap automatically. No render-time guard is wired on the tenant side — gating on a `lookup` for the leftover HelmRelease would deadlock the routine disable (the parent render runs before helm-controller applies the missing-child diff, so the lookup would always find the leftover at the moment of the flag flip). If the chart's hook fails to land (controller pod stuck CrashLooping, ConfigMap RBAC drift, Job timeout), operators recover manually via the documented tenant cleanup recipe. * `packages/apps/kubernetes/values.yaml` — new `OuroborosAddon` typedef with `enabled` + `valuesOverride`. `make generate` re-emitted README, JSON schema, the Go struct + deepcopy, and the kubernetes-rd ApplicationDefinition openAPISchema in lockstep. * `packages/core/platform/values.yaml` — new `publishing.proxyProtocol` flag with extensive comment covering the upstream-LB precondition (the L4 LB in front of ingress-nginx must already be injecting PROXY-v1 headers before the flag flips on, otherwise external traffic breaks), the host-vs-tenant fan-out (the flag does not cascade into tenants), the Talos re-render flap window, and the asymmetric disable path. The host disable path: flipping back to `false` reverts ingress-nginx PROXY-protocol config (flip the LB off first), but the host ouroboros itself stays installed because its Package CR carries `helm.sh/resource-policy: keep` — an explicit `kubectl delete package.cozystack.io cozystack.ouroboros` (or `bundles.disabledPackages` entry) actually triggers helm uninstall, at which point the chart's pre-delete cleanup hook fires and patches the host Corefile automatically. The values comment ships a manual `sed`-based cleanup recipe as a fallback for the rare case where the chart's hook fails to land (controller stuck in CrashLoop, ConfigMap RBAC drift, hook timeout); the `publishing.proxyProtocolAcknowledgeUnclean` gate keys off a `lookup` against the leftover Package CR to catch operators who flip the flag back without confirming the cleanup landed. * `packages/core/platform/templates/bundles/system.yaml` — flips between the default-form and explicit-form `cozystack.ingress-application` Package emission so the host ingress-nginx gets `use-proxy-protocol` + `real-ip-header=proxy_protocol` injected only when the flag is true (`use-forwarded-headers` and `compute-full-forwarded-for` are deliberately *not* on the list — they would let any upstream proxy spoof `X-Forwarded-For`); the same conditional emits `cozystack.ouroboros`. Host Gateway-API watching is left at the upstream chart's default of `false` because cozystack does not install Gateway-API CRDs cluster-wide on the host. * `hack/e2e-apps/ouroboros.bats` — auto-discovered smoke test for the tenant addon (HelmRelease ready, `coredns-custom` ConfigMap present in tenant, controller pod running, and after creating a TLS Ingress the rewrite snippet shows up in the import ConfigMap; an in-tenant `dig` from a netshoot pod also confirms CoreDNS actually serves the rewrite — pod-readiness alone passes vacuously since the upstream chart ships no readiness probes). ### Why not KEP-1860 `ipMode=Proxy` Cilium's `kubeProxyReplacement: true` (cozystack default) drops the LB frontend entirely when `ipMode != VIP`, breaking Service ingress for L2/BGP-announced IPs. KEP-1860's Proxy mode is reserved for clusters fronted by an external CCM-managed proxy LB. ouroboros is the correct fix for L2/BGP-announce topologies that cozystack ships by default. ### Coverage Covered by helm-unittest (host chart, coredns wrapper, tenant addon — including the `kubernetesProvider` mock that exercises the lookup-driven `proxyProtocolAcknowledgeUnclean` gate, the lookup-returns-nil CI/dry-run path, the gateway-api truthy-spelling acceptance across `true`/`"true"`/`"True"`/`"TRUE"`/`1`/`yes`/`on`, the assertion that `--proxy-service-name` + `--proxy-service-namespace` are emitted instead of a chart-baked `--proxy-fqdn` in both `coredns` and `coredns-import` modes, the assertion that `--target-service-name` + `--target-service-namespace` are emitted for the proxy backend, and the `_namespace.etcd`-empty render gate) and a tenant e2e bats test that creates a real Ingress, asserts the rewrite snippet lands in the import ConfigMap, and confirms in-tenant DNS serves it (with an in-pod retry loop tolerating CoreDNS reload latency). The host-level `publishing.proxyProtocol: true` path is not e2e-tested in this PR — it requires a PROXY-protocol-aware upstream LB, which the cozystack e2e harness does not stand up. That path is exercised manually and by docs review. End-to-end runtime auto-detect was verified manually on a `cozy.local` cluster (dev17): chart deployed at v0.7.0, controller logged `cluster-domain=cozy.local`, the rewrite block landed in `kube-system/coredns` Corefile pointing at `test-ouroboros-proxy.test-ouroboros.svc.cozy.local.`, an in-pod `dig <ingress-host>` resolved to the ouroboros-proxy ClusterIP on the first attempt, and the chart's pre-delete hook restored the Corefile cleanly on `helm uninstall`. ### Supply-chain note Image and chart are pulled from a contributor's personal namespace (`ghcr.io/lexfrei/...`). Existing precedent (`packages/system/etcd-operator/Makefile` pulls from `ghcr.io/aenix-io/...`) is similar but `aenix-io` is the cozystack-adjacent org while `lexfrei` is one human. Worth a maintainer call. Pairs with cozystack/website#527 (operator docs). ### Release note ```release-note feat(networking): add `publishing.proxyProtocol: true` to enable PROXY-protocol on host ingress-nginx and auto-deploy ouroboros (a Go reimplementation of compumike/hairpin-proxy) to solve the resulting hairpin-NAT problem; tenants can opt in per-cluster via `addons.ouroboros.enabled` (requires `addons.ingressNginx.enabled`). Default behaviour unchanged. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Ouroboros addon to address hairpin‑NAT for ingress‑nginx with configurable enablement and tenant overrides. * Added a packaged Ouroboros chart providing proxy, controller, CoreDNS integration, external‑DNS and etc‑hosts modes. * Introduced PROXY‑protocol gating with an explicit acknowledgement flag. * **Documentation** * Expanded addon reference and platform docs with detailed Ouroboros/PROXY‑protocol configuration and defaults. * **Tests** * Added extensive unit and e2e tests covering rendering, RBAC, uninstall cleanup, and DNS behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
a9c1298b42 |
[gpu-operator] Update to v26.3.1 and add experimental vGPU variant (#2323)
## What this PR does Updates the NVIDIA GPU Operator from v25.3.0 to v26.3.1 and adds an **experimental** `vgpu` variant for SR-IOV vGPU on Ada Lovelace and newer GPUs. Changes: - Update vendored upstream chart to v26.3.1 (CDI enabled by default, component version bumps). - Pin chart version in Makefile for reproducibility. - Add `values-vgpu.yaml` for the vGPU variant with `defaultWorkload: vm-vgpu` so the chart activates the vGPU Manager DaemonSet without per-node labelling. - Add `vgpu` variant to PackageSource alongside the existing `default` (passthrough). - Rename `values-talos.yaml` → `values-passthrough.yaml` and pin `defaultWorkload: vm-passthrough` for symmetry with the vgpu variant. The overrides apply to any host OS using vfio-pci, not Talos specifically. - Refresh `hack/dcgm-default-counters.csv` to match the new dcgm-exporter image tag (`4.5.1-4.8.0`) — adds `DCGM_FI_DEV_FB_RESERVED` and other fields introduced upstream. `hack/check-gpu-recording-rules.bats` passes against the new fixture. - Add `hack/check-gpu-operator-variants.bats` that renders both variants via `helm template` and asserts the pinned ClusterPolicy values, so the next chart bump cannot silently un-pin them. - Add internal documentation for the vGPU bring-up. The `vgpu` variant assumes the user supplies a proprietary vGPU Manager image via Package CR component values — the .run cannot be redistributed. ### `vgpu` variant is experimental NVIDIA's `vgpu-device-manager` walks `/sys/class/mdev_bus/`, which does not exist on Ada Lovelace and Blackwell — the DaemonSet errors with "no parent devices found" and is disabled by default. Until an SR-IOV-aware controller is shipped, profile assignment is an out-of-band step (`echo <id> > /sys/.../current_vgpu_type` per VF) that must be re-applied after every node reboot. Without it, `permittedHostDevices.pciHostDevices` reports zero allocatable resources. The variant ships so the chart bump and the underlying skeleton (DaemonSet + image plumbing + docs) land together; production deployments need an automated profile-assignment mechanism in addition to this PR. ### KubeVirt requirement KubeVirt advertises VFs via `permittedHostDevices.pciHostDevices` rather than `mediatedDevices`. This requires kubevirt/kubevirt#16890 ("vGPU: SRIOV support", merged 2026-04-10) which is `main`-only — released tags `v1.6.x` / `v1.7.x` / `v1.8.x` do not include it. The patch is targeted at the next minor KubeVirt release (v1.9.0); track the PR for the actual release tag. Backports to released branches are not planned. ### Behavior changes inherited from upstream chart v25 → v26 The upstream chart flipped two defaults that are pinned back to `false` here, so `default` (passthrough) deployments do not silently change footprint on upgrade: - `ccManager.enabled` was flipped to `true` upstream — would auto-enable Confidential Computing on Hopper hardware (H100/H200/B100) after upgrade. - `vgpuDeviceManager.enabled` was flipped to `true` upstream — DaemonSet would render even in passthrough mode. Both stay opt-in via Package CR overrides. The new bats test guards the pin. Other inherited changes that are user-visible and worth pinning down before upgrade: - `driver.licensingConfig.configMapName` is deprecated upstream in favour of `driver.licensingConfig.secretName`. The deprecation marker lives in the CRD's `description` field (visible via `kubectl explain`) — there is no helm-render-time warning, the chart template silently prefers `secretName` and falls back to `configMapName`. Operators with existing licensing overrides should migrate the key on the next chart upgrade. - `driver.version` upstream default jumped from `570.x` to `580.x`. Invisible on the `default` variant (which sets `driver.enabled: false`), user-visible for anyone running the upstream Ubuntu driver path. - `vfioManager.image` switched from `nvcr.io/nvidia/cuda:12.8.1-base-ubi9` to `nvcr.io/nvidia/cloud-native/k8s-driver-manager:v0.10.0`. Affects every `default` (passthrough) deployment on upgrade — different image, different layer, different image-pull surface. Worth pre-mirroring for air-gapped clusters. - `dcgmExporter.image` switched to the `-distroless` flavour. Smaller surface, fewer base-image CVEs, but no shell — operators who used to `kubectl exec` into the exporter for ad-hoc debugging will need to switch to `kubectl debug` or container ephemeral images. - `CDI` (Container Device Interface) is enabled by default in v26.3.1; this is upstream's recommended setting and is left as-is. - `node-feature-discovery` subchart source switched from the GitHub Pages Helm repo to `oci://registry.k8s.io/nfd/charts`. Air-gapped installations need to mirror the new OCI URL. ### Talos Talos is **not recommended** for the vGPU variant. NVIDIA does not publicly distribute the vGPU guest driver — it requires NVIDIA Enterprise Portal access — and Sidero closed siderolabs/extensions#461 noting that they cannot support vGPU "unless NVIDIA changes their licensing terms or provides us a way to obtain, test, and distribute the software". Building a Talos system extension that bakes in the vGPU driver is therefore not feasible without a private fork that violates the EULA. Passthrough (`default` variant) on Talos is unaffected. ### Release note ```release-note [gpu-operator] Update NVIDIA GPU Operator to v26.3.1 and add experimental `vgpu` variant for SR-IOV vGPU on Ada Lovelace / Blackwell (L4, L40, L40S, B100; Ubuntu host; requires KubeVirt v1.9.0+ for `pciHostDevices` advertising). The variant ships skeleton support — profile assignment (`current_vgpu_type` per VF) is currently out of band and must be automated by the operator before production use. On the existing `default` variant: `ccManager` and `vgpuDeviceManager` upstream defaults are pinned back to `false` to preserve prior behavior on chart upgrade; `defaultWorkload` is now pinned to `vm-passthrough` (was unset, falling back to `container`); `driver.licensingConfig.configMapName` is deprecated by upstream in favour of `secretName`; `driver.version` upstream default moves 570 → 580; `vfioManager.image` switches from CUDA UBI9 to k8s-driver-manager (relevant for air-gapped mirrors); `dcgmExporter` image switched to the `-distroless` flavour. NFD subchart source switched to `oci://registry.k8s.io/nfd/charts`. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Introduced vGPU support variant with end-to-end configuration guide, validation procedures, and troubleshooting documentation * **Updates** * Upgraded GPU Operator to v26.3.1 and Node Feature Discovery to v0.18.3 * Improved component health checks and added PodDisruptionBudget availability controls <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
7f01c7eb32 |
feat(platform): single switch for host PROXY-protocol + ouroboros
Adds publishing.proxyProtocol on the platform values: a single flag that both turns PROXY-protocol on at the host ingress-nginx (via the ingress-application Package CR's component values dict) and emits the new cozystack.ouroboros Package CR for the host install. The two pieces are coupled by design — turning PROXY-protocol on at ingress-nginx without ouroboros breaks intra-cluster traffic to the cluster's own public hostnames (kube-proxy / Cilium-KPR short-circuits the LB IP, the connection arrives at ingress-nginx without the PROXY header it now requires, and ingress-nginx closes the connection). The new sources/ouroboros.yaml PackageSource declares the host chart with dependsOn: cozystack.networking + cozystack.ingress-application so install ordering is deterministic. The bundle template emits the host ouroboros Package via the standard helper — the parent chart's Gateway-API watching default of false stays in place, because cozystack does not install Gateway-API CRDs cluster-wide on the host (system/ gateway-api-crds is referenced only from the kubernetes-application PackageSource, tenant scope). Forcing --gateway-api on a host without the CRDs would CrashLoop the controller on WaitForCacheSync against missing GVRs. The flag default is false. Clusters that do not run PROXY-protocol get zero new resources, zero RBAC delta, zero behaviour change. Disabling: flipping publishing.proxyProtocol back to false stops emitting the cozystack.ouroboros Package CR, but every Package CR carries helm.sh/resource-policy: keep — Helm leaves the existing Package on the cluster so ouroboros stays installed and the live Corefile rewrite block keeps pointing at the still-running ouroboros-proxy Service. Flag-flip alone is idempotent and safe. To actually uninstall ouroboros the operator removes the Package CR explicitly (or adds the package name to bundles.disabledPackages) and runs the documented Corefile cleanup recipe in the same window — otherwise stale rewrites point at a deleted Service and in-cluster DNS for hairpinned hostnames returns connection-refused. The values comment ships the cleanup recipe (a JSON merge-patch on data.Corefile that strips the BEGIN/END block while preserving labels, annotations, and other data keys including any Talos-managed metadata) and documents the upstream-LB precondition (the L4 LB in front of ingress-nginx must already be injecting PROXY-v1 headers before the flag flips on), the host-vs-tenant fan-out (the flag does not cascade into tenant clusters; tenants opt in per-Kubernetes-CR via addons.ouroboros.enabled), and the Talos re-render flap window (machine-config pushes wipe the BEGIN/END markers; the controller re-applies on next reconcile, ≤10 minutes by default). Also drops the stale "distro-full" entry from the bundles.system.variant options comment in core/platform/values.yaml — the variant was removed in v1.0.1 and was never restored. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
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> |
||
|
|
7520967d8d |
feat(operator): expose HelmRelease generation knobs (interval, retry-interval, timeouts, max-history) (#2509)
## Why
The `cozystack-operator` Package reconciler builds every `HelmRelease`
it generates with a handful of hardcoded values. Three of those have
caused recurring pain across the project:
- **`Spec.Interval = 5m`** (line 220 before this PR) — too long for E2E
install where dependent HRs reconcile out of order; previously addressed
by a manual `flux reconcile --force` workaround in
`hack/e2e-install-cozystack.bats`.
- **`Install.Remediation{Retries: -1}` + `Upgrade.Remediation{Retries:
-1}`** — semantically "retry forever, never remediate", but the
retry-on-failure timing was tied to `Spec.Interval` (5m). Audit of the
last 30 successful PR runs found that **every** run hit `kubectl wait
hr/seaweedfs-system` timeouts at the 2-min mark, recovering only after
the inline `flux reconcile --force` workaround.
- **`Install.Timeout = 10m` / `Upgrade.Timeout = 10m`** — directly
contradicts the per-Application timeout work in `pkg/config/config.go` +
`pkg/registry/apps/application/rest.go` (commit `7b146cbe`).
`packages/apps/tenant/templates/etcd.yaml` hand-rolls its HR YAML to
bypass this for cert rotation; harbor has had multiple commits chasing
the same problem.
- **`Spec.MaxHistory`** unset → Helm default 5. Test clusters bounce HRs
frequently and accumulate release-history Secrets.
This PR exposes all four as operator flags + helm chart values, with
defaults that preserve current behavior.
## What
### New operator flags
| Flag | Default | HR field |
|---|---|---|
| `--helmrelease-interval` | `5m` | `Spec.Interval` |
| `--helmrelease-retry-interval` | `30s` |
`Spec.{Install,Upgrade}.Strategy.RetryInterval` |
| `--helmrelease-install-timeout` | `10m` | `Spec.Install.Timeout` |
| `--helmrelease-upgrade-timeout` | `10m` | `Spec.Upgrade.Timeout` |
| `--helmrelease-max-history` | `5` | `Spec.MaxHistory` |
### Strategy switch
`Install.Strategy.Name` and `Upgrade.Strategy.Name` are now set to
`RetryOnFailure` (instead of leaving the default `RemediateOnFailure`),
with `RetryInterval` set from the new flag. Functionally equivalent to
the previous `Remediation{Retries:-1}` ("retry forever") since
remediation never fired anyway, but **decouples retry-on-failure timing
from `Spec.Interval`** so failed releases recover at 30s without polling
healthy releases at the same fast cadence.
### Chart exposure
`packages/core/installer/values.yaml` gets five new optional values
(`cozystackOperator.helmRelease{Interval,RetryInterval,InstallTimeout,UpgradeTimeout,MaxHistory}`),
each defaulting to empty so the operator's own default applies. The
chart conditionally renders the corresponding flag only when the value
is set.
## Production safety
- All static defaults match the previous hardcoded values (5m / 10m /
10m / 5). No new code path runs until an admin explicitly sets a flag.
- **One intentional behavior change: failed-release retry cadence**
drops from ~5m (previously coupled to `Spec.Interval`) to 30s
(`RetryInterval` default). This is the whole motivation for the strategy
switch — the recurring `kubectl wait hr/seaweedfs-system` flakes
documented above were caused by the previous coupling. Healthy-release
reconcile cadence is unchanged at 5m in production; CI overrides this to
30s via `--set cozystackOperator.helmReleaseInterval=30s` in
`hack/e2e-install-cozystack.bats` for faster convergence on E2E
clusters.
- `RetryOnFailure` is functionally equivalent to `RemediateOnFailure +
Retries:-1` since neither ever triggers remediation.
- Empty value in the chart → flag not rendered → operator falls back to
its built-in default. (`helmReleaseMaxHistory: 0` — unlimited history
per Helm semantics — is also honored; the chart guards on `ne (toString
…) ""` rather than truthiness so int `0` is not silently dropped.)
## Verification
- `go build ./...` clean
- `go test ./internal/operator/...` passes (covers `Interval`,
`MaxHistory`, `Install/Upgrade.Timeout`, `Strategy.Name`,
`RetryInterval`, `ChartRef` propagation, plus a regression test for
`MaxHistory=0`)
- `helm template installer packages/core/installer` renders cleanly with
default and overridden values; `--set
cozystackOperator.helmReleaseMaxHistory=0` correctly emits
`--helmrelease-max-history=0`
## Followups (deliberately not bundled)
- **Per-component timeout overrides** on `ComponentInstall` (mirroring
the `UpgradeCRDs` pattern from #2427). Charts like etcd that need a 30m
timeout would set it on their `Component` rather than tuning a global
flag. Touches the Package CR API + codegen — bigger lift, separate PR.
- **`Remediation.Retries` as a flag.** No current pain; skip.
- **`createOrUpdateHelmRelease` field preservation.** Currently
preserves `Suspend` but not `Test`/`Rollback`/`Uninstall`. Would
silently overwrite an SRE's manual edit. Minor cleanliness, separate PR.
## Test plan
- [ ] CI passes E2E with `helmReleaseInterval=30s` set in install bats
(existing T1 test path)
- [ ] No regression on production install path (default values)
- [ ] HR objects in a healthy cluster render with
`Strategy.Name=RetryOnFailure` and `RetryInterval=30s`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Operator and Helm chart now accept CLI flags and Helm values to
configure HelmRelease reconciliation intervals, retry behavior,
install/upgrade timeouts, and history retention.
* **Bug Fixes**
* Operator now validates HelmRelease timing/history inputs on startup
and will exit if values are invalid or non-positive.
* **Tests**
* Added unit and e2e coverage to verify HelmRelease configuration and a
chart install path using a custom reconciliation interval.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
086e9bb35b |
fix(installer): bootstrap cozy-system via --create-namespace + label hook (#2508)
## Why helm v3 has a chicken-and-egg with charts that ship their own Namespace resource: - **With `--create-namespace`** on the install: helm pre-creates the namespace via plain kubectl-create (no helm meta annotations); the chart's own Namespace apply then fails with `already exists`. - **Without `--create-namespace`**: helm fails immediately because it can't write its release-secret to a non-existent namespace. The cozy-installer chart shipped a `Namespace cozy-system` resource since the chart's introduction (December 2025). Every cold install since has hit this conflict. It was masked by the 3× retry on `Install Cozystack` in `.github/workflows/pull-requests.yaml`: attempt 1 fails with the conflict, attempt 2 sees the existing failed release and takes the upgrade code path which patch-merges instead of strict-create. Surfaced when retries on the install step were dropped. ## What - Remove `Namespace cozy-system` from `packages/core/installer/templates/cozystack-operator.yaml`. - Add `packages/core/installer/templates/cozy-system-labels.yaml`: a pre-install/pre-upgrade hook (ServiceAccount + ClusterRole + ClusterRoleBinding + Job) that patches `cozystack.io/system=true` and `pod-security.kubernetes.io/enforce=privileged` onto the namespace. - Hook RBAC is scoped to `verbs: [get, patch]` on `resourceNames: [cozy-system]` only. - `hook-delete-policy: before-hook-creation,hook-succeeded` so the hook surface only exists during install/upgrade. Install commands now use `helm upgrade --install --namespace cozy-system --create-namespace …` — standard pattern, matches kube-prometheus-stack / argo-cd / cert-manager. ## Verification End-to-end on a kind cluster: - Cold install: 3.3s clean, no retries needed - Upgrade (re-run): 3.0s, idempotent, namespace stays labeled - Helm uninstall + re-install: clean ## Test plan - [ ] CI on this PR passes E2E - [ ] Existing clusters: namespace already labeled correctly, hook re-asserts on next upgrade — no migration needed ```release-note fix(installer): cold installs no longer fail on the chart's Namespace resource colliding with `helm --create-namespace`; cozy-system is now bootstrapped via a pre-install hook. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an automated pre-install/pre-upgrade step that applies system identity and pod-security labels to the system namespace during deployment, run in a hardened one-shot task before normal workloads start. * **Chores** * Separated namespace initialization from the operator install flow so the operator no longer renders or retains the system namespace directly, improving installation sequencing. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
730037c85d |
fix(installer): address review feedback on cozy-system bootstrap
- Bump alpine/k8s to 1.33.4 (was 1.32.0, EOL); matches in-tree pin used by cozystack-api, seaweedfs, vm-disk, and vm-instance hooks. - Skip hostNetwork=true and not-ready/CNI tolerations on the hosted variant: managed clusters provide CNI before install, and kube-system may carry strict PSA there that would reject hostNetwork pods. - Add packages/core/installer/templates/cozy-system-namespace.yaml gated on .Values.bareNamespace (default false). The top-level Makefile's manifests target now passes --set bareNamespace=true and renders Namespace + operator into _out/assets/cozystack-operator-*.yaml so the kubectl-apply install path stays self-contained. Helm install/upgrade callers keep the default and bootstrap via --create-namespace + the pre-install labeler hook. - Add a bats assertion in hack/e2e-install-cozystack.bats checking cozy-system carries pod-security.kubernetes.io/enforce=privileged and cozystack.io/system=true after helm install. - Inline comment near hostNetwork=true documenting why dnsPolicy left at Default (cluster DNS not up at install time; ClusterFirstWithHostNet would not help). Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
6ca5566391 |
fix(operator): handle MaxHistory=0 and cover HelmRelease spec build
Chart template now uses `ne (toString …) ""` for the five helmRelease* knobs, so integer 0 (notably MaxHistory=0 = unlimited history per Helm semantics, documented in values.yaml) is no longer silently dropped. Extract buildHelmReleaseSpec from Reconcile and add unit tests asserting Interval, MaxHistory, Install/Upgrade timeouts, Strategy=RetryOnFailure, RetryInterval, and ChartRef propagate from PackageReconciler config to the generated HelmRelease. Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
85ced0d729 |
fix(installer): harden cozy-system-labeler hook and add hosted variant
Address review feedback on PR #2508: - Pin alpine/k8s by digest (matches operator image pinning style). - Add resources requests/limits and a minimal securityContext to the labeler Job: capabilities drop ALL, allowPrivilegeEscalation: false, seccompProfile RuntimeDefault. - Include hook-failed in helm.sh/hook-delete-policy across all four hook resources so a failed install doesn't leave SA / ClusterRole / ClusterRoleBinding / Job objects behind. - Add explicit hosted variant case in the env block, mirroring the pattern in cozystack-operator.yaml. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
4527320944 |
fix(installer): bootstrap cozy-system via --create-namespace + label hook
The chart used to ship `Namespace cozy-system` directly, which trips a
helm v3 chicken-and-egg whenever the install command uses
`--create-namespace`:
- WITH `--create-namespace`, helm pre-creates the namespace via plain
kubectl-create (no helm meta annotations); the chart's own Namespace
apply then fails with `already exists`.
- WITHOUT `--create-namespace`, helm fails immediately because it
cannot write its release-secret to a non-existent namespace.
The bug was hidden by the 3x retry on the install step in
`.github/workflows/pull-requests.yaml`: attempt 1 fails with the
conflict, attempt 2 sees the failed release and takes the upgrade code
path (patch-merge instead of strict-create). Surfaces cleanly when the
retry is dropped; reproducible on every cold install.
Fix has two pieces:
1. Drop the chart-side `Namespace cozy-system` resource. Install
commands now use the standard `helm upgrade --install --namespace
cozy-system --create-namespace` (matches kube-prometheus-stack /
argo-cd / cert-manager).
2. Add a pre-install/pre-upgrade Job hook (`cozy-system-labeler`) that
patches `cozystack.io/system=true` and
`pod-security.kubernetes.io/enforce=privileged` onto the namespace
so the cozystack-operator pod (hostNetwork=true) is admitted.
The hook runs in `kube-system`, not `cozy-system`. Reason: the operator
pod needs hostNetwork=true (no CNI yet at install time), which violates
PodSecurity baseline. cozy-system therefore needs enforce=privileged
*before* the operator pod is admitted. A labeler pod inside cozy-system
would itself need hostNetwork=true and hit the same admission denial:
pods "cozy-system-labeler-..." is forbidden: violates PodSecurity
"baseline:latest": host namespaces (hostNetwork=true)
Job admission denial means no pod is ever created, .status.failed never
increments and backoffLimit never trips. helm's --wait waits for
Complete=True forever and surfaces only the generic "timed out waiting
for the condition" at its budget.
`kube-system` is PSA-exempt on Talos
(`defaults.exemptions.namespaces` in the apiserver
PodSecurityConfiguration) and unlabeled on vanilla kubeadm/kind/k3s, so
hostNetwork pods are admitted there. The hook's ServiceAccount is bound
to a ClusterRole scoped via `resourceNames: ["cozy-system"]` to the one
target namespace, keeping the cluster permission grant minimal.
`hook-delete-policy: before-hook-creation,hook-succeeded` ensures the
SA, RBAC, and Job are cleaned up after a successful install/upgrade.
Verified end-to-end in CI on the parallel consolidation branch
feat/e2e-optimization (PR #2500): cold install passes admission, the
operator pod comes up, all downstream E2E tests pass.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
|
||
|
|
b5a1ae57de |
feat(etcd): update operator to v0.4.3 and expose S3 backup schedules (#2428)
## What this PR does
Bumps the vendored `etcd-operator` chart from `v0.4.2` to `v0.4.3` and
wires the new backup machinery through the cozystack `Etcd` app API.
Upstream v0.4.3 ships two new CRDs — `EtcdBackup` (one-shot snapshot)
and `EtcdBackupSchedule` (periodic snapshots) — along with the
`EtcdCluster.spec.bootstrap` restore field, a new `OPERATOR_IMAGE` env
on the operator deployment, and a replacement of the deprecated
`gcr.io/kubebuilder/kube-rbac-proxy` image. The chart portion of this PR
is a clean re-vendor via `make update` — no local patches.
On top of the bump, `packages/extra/etcd` exposes a new optional
`backup.*` values block. When `backup.enabled=true`, the chart renders
an `EtcdBackupSchedule` referencing the platform `EtcdCluster/etcd` and
a `Secret/etcd-s3-creds` with the S3 credentials expected by the
backup-agent. The values field names (`destinationPath`, `endpointURL`,
`s3AccessKey`, `s3SecretKey`, `schedule`) mirror the existing `postgres`
backup pattern for consistency. The chart `fail`s at template render if
any of the four required S3 fields are empty or if `destinationPath` is
malformed (missing `s3://` prefix, `//` segments, no bucket).
Test coverage: 15 `helm-unittest` cases across
`tests/backup-secret_test.yaml` and
`tests/etcd-backup-schedule_test.yaml` (wired into `make test`, picked
up by `hack/helm-unit-tests.sh`), plus three bats cases in
`hack/e2e-apps/etcd.bats` covering the baseline `Etcd` app, the `backup:
{}` default-off path, and the full `backup.enabled=true` case that
verifies the `EtcdBackupSchedule` materializes and the operator
generates the downstream `CronJob`.
`packages/core/platform/sources/etcd-operator.yaml` now sets
`upgradeCRDs: CreateReplace` on the etcd-operator component, so Flux
applies the new CRDs on upgrade of the chart instead of silently
skipping them.
Depends on #2427.
### Scope deferred to follow-ups
- The one-shot `EtcdBackup` CRD is vendored but not exposed through this
chart — users can still hand-apply the CRD directly.
- `EtcdCluster.spec.bootstrap` (restore from an existing snapshot) is
likewise vendored but not exposed. Both gaps are called out in
`packages/extra/etcd/README.md`.
### Release note
```release-note
feat(etcd): update etcd-operator to v0.4.3 and expose S3 backup schedules on the Etcd app. Set `backup.enabled=true` along with `backup.s3AccessKey`, `backup.s3SecretKey`, `backup.destinationPath`, and `backup.endpointURL` to schedule periodic snapshots.
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Scheduled S3 backups for etcd clusters (configurable cron, generated
S3 credentials) and restore/init from S3 or PVC via new backup
resources.
* **Tests**
* New end-to-end tests for etcd lifecycle and backup scenarios; added
Helm unit tests for backup templates and validations.
* **Documentation**
* Backup configuration guide and parameter reference added.
* **Chores**
* Operator chart version bump; RBAC, webhooks, and deployment wiring
updated; added chart test target.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
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 --> |
||
|
|
179b603cfc |
feat(talos): bump Talos to v1.13.0
Bumps Talos Linux from v1.12.7 to v1.13.0. The v1.13 release line also
contains the kernel fix for CVE-2026-31431 ("Copy Fail").
Refreshes the bundled system extension digests (amdgpu, i915, drbd, zfs)
to their 1.13.0-aligned builds.
Generated via `make update` in packages/core/talos.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
|
||
|
|
8549f3b9c3 |
fix(talos): bump Talos to v1.12.7 (CVE-2026-31431)
Bumps Talos Linux from v1.12.6 to v1.12.7, which includes the kernel
fix for CVE-2026-31431 ("Copy Fail") — an unprivileged-to-root local
privilege escalation in the Linux kernel crypto subsystem (algif_aead).
Also refreshes the bundled system extension digests (amd-ucode, amdgpu,
bnx2-bnx2x, i915, intel-ice-firmware, qlogic-firmware, drbd, zfs) to
their 1.12.7-aligned builds.
Generated via `make update` in packages/core/talos.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
|
||
|
|
e9bb00c074 |
fix(gpu-operator): set defaultWorkload=vm-vgpu, fix imagePullSecrets path, mark vgpu variant experimental
Addresses second review pass. Three blockers + one polish: * values-vgpu.yaml: set sandboxWorkloads.defaultWorkload=vm-vgpu. Without this the upstream default 'container' applies on any node without the per-node nvidia.com/gpu.workload.config label, the operator selects the container workload (driver/devicePlugin DaemonSets), and those are disabled in this variant — so nothing runs on the node and vgpu-manager never starts. The cluster-wide default makes the variant functional out of the box; per-node labels still override it for mixed clusters. * docs/gpu-vgpu.md: imagePullSecrets is per-component on this chart (vgpuManager.imagePullSecrets, driver.imagePullSecrets, …). The Package CR example previously placed the field at gpu-operator: root where it would be silently ignored at render. Move it under vgpuManager: where the chart actually reads it. * docs/gpu-vgpu.md, release note: mark the vgpu variant as experimental and call out the missing profile-assignment loop explicitly. Until an SR-IOV-aware controller writes current_vgpu_type per VF (and re-applies after every reboot — PCIe re-enumeration resets the value), the variant is not safe for production. The docs now lead with that warning instead of burying it in a caveat. * Rename packages/system/gpu-operator/values-talos.yaml to values-passthrough.yaml. The overrides (sandboxWorkloads.enabled, driver and devicePlugin disabled) apply to any host OS using the vfio-pci passthrough path — not Talos specifically. Update PackageSource ref accordingly. Comment in the file documents the rename so future readers understand the prior name. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
2e6152f30a |
docs(gpu-vgpu): document SR-IOV path on Ada+, KubeVirt #16890 dependency, DLS-based licensing
Rewrites docs/gpu-vgpu.md to match the actual NVIDIA driver model on current data-centre GPUs. Key changes: - Drop the implicit assumption that vGPU uses mediated devices. Ada Lovelace and newer (L4, L40, L40S, B100) on the vGPU 17/20 branch use SR-IOV with per-VF NVIDIA sysfs (current_vgpu_type); KubeVirt advertises them via permittedHostDevices.pciHostDevices. - Document KubeVirt PR #16890 as a hard dependency for the SR-IOV path. The PR is in main since 2026-04-10 and will ship in v1.9.0 (ETA July 2026); released tags v1.6.x/1.7.x/1.8.x do not include it and backports are not planned. - Replace the legacy NLS configuration block (ServerAddress=, ServerPort=7070, FeatureType=1) with the current DLS flow: ClientConfigToken file in /etc/nvidia/ClientConfigToken/, gridd.conf with FeatureType=0 (auto-detect). - Switch the build instructions from the archived gitlab.com/nvidia/container-images/driver to the upstream github.com/NVIDIA/gpu-driver-container. - Switch sample VirtualMachine spec from gpus: (mdev resource) to hostDevices: (PCI resource). - Note explicitly that Talos is not recommended for the vGPU variant — the proprietary .run cannot be redistributed in a Talos system extension, and siderolabs/extensions#461 was closed won't-fix. Passthrough on Talos is unaffected. Rename packages/system/gpu-operator/values-talos-vgpu.yaml to values-vgpu.yaml — the overrides (sandbox workloads, pod-side driver and device plugin disabled) are not Talos-specific, they apply to any VM-only GPU node. Update PackageSource accordingly. Default vgpuDeviceManager.enabled: false. The DaemonSet walks /sys/class/mdev_bus/, which does not exist on Ada+ — keeping it on by default would crashloop modern hardware. Operators of older GPUs (Pascal/Volta/Turing/Ampere) can flip the flag back on. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
3bbea7b359 |
feat(gpu-operator): add vGPU variant for mediated device support
Add vGPU mode as a new variant alongside the existing passthrough (default) variant. This enables sharing a single physical GPU across multiple VMs using NVIDIA mediated devices. Changes: - Add values-talos-vgpu.yaml with vGPU Manager and vGPU Device Manager enabled - Add 'vgpu' variant to PackageSource - Add documentation for vGPU setup (driver image, NLS licensing, profiles, KubeVirt) Users select the vgpu variant via Package CR and provide their proprietary vGPU Manager image coordinates through component values. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
f45facca77 |
feat(hami): add HAMi GPU virtualization system package (#2484)
## What this PR does Documentation: https://github.com/cozystack/website/pull/517 Integrates [HAMi](https://github.com/Project-HAMi/HAMi) v2.8.1 (CNCF Sandbox) into Cozystack as a system-level package for fractional GPU sharing in tenant Kubernetes clusters. The integration covers three layers: - **System chart** (`packages/system/hami/`): Vendors upstream HAMi Helm chart with device plugin, scheduler extender, mutating webhook, and RuntimeClass configuration. The broken DRA subchart was removed — it renders resources even when disabled and references unpublished images. - **Kubernetes app addon** (`packages/apps/kubernetes/`): HAMi exposed as an optional toggle (`hami.enabled`). Automatically disables GPU Operator's native device plugin when active to avoid conflicts. Enforces hard dependency on GPU Operator. - **Platform registration** (`packages/core/platform/`): HAMi declared as PackageSource with gpu-operator dependency, included in the iaas bundle. **Known limitation**: HAMi-core relies on a private glibc symbol (`_dl_sym`) removed in glibc 2.34, which breaks compute isolation on modern container images (Ubuntu 22.04+, PyTorch/TensorFlow official images). Alpine/musl is entirely incompatible. See the package README for details and upstream issue references. ### Screenshots N/A — no UI changes. ### Release note ```release-note feat(hami): add HAMi GPU virtualization as an optional system package for fractional GPU sharing. Enables memory and compute isolation for NVIDIA GPUs across tenant workloads. Integrates with GPU Operator and can be enabled per-cluster via the hami.enabled toggle. Note: compute isolation requires glibc < 2.34 in workload containers. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added HAMi GPU virtualization middleware as an optional cluster addon to enable fractional GPU sharing. * HAMi addon includes an enable toggle (default: disabled) and a customizable Helm values override; when enabled it enforces GPU Operator presence and adjusts GPU Operator values accordingly. * New HelmRelease integration for deploying HAMi with dependency ordering and conditional rendering. * **Documentation** * Added HAMi setup guide, compatibility notes, and updated cluster addon configuration reference. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
69ad74e457 |
fix(cozystack-engine): add managed Kubernetes without visible control-plane nodes support to lineage-controller-webhook (#2481)
<!-- 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 for system components: dashboard, platform, cilium, kube-ovn, linstor, fluxcd, cluster-api - Scopes for managed apps: postgres, mariadb, redis, kafka, clickhouse, virtual-machine, kubernetes - Scopes for 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. --> In environments where control-plane nodes are not available (e.g., AWS managed nodes, Cozy-in-Cozy tenant Kubernetes clusters), the DaemonSet cannot schedule any pods because no nodes match this selector. ## What this PR does This PR adds ability to replace DaemonSet by Deployment with custom number of replicas for HA purpose. Default values saves current behavior. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Optional Deployment mode for lineage-controller-webhook with configurable replicas * PodDisruptionBudget support when Deployment mode is enabled * **Updates** * Improved pod scheduling: node affinity replaces nodeSelector, optional tolerations, and pod anti-affinity when using Deployment * Clarified behavior for local API endpoint scheduling (only applies when pod lands on nodes running an API server) * **Documentation** * Added README with usage examples and parameter details <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9b5848ed26 |
fix(platform): register HAMi as kubernetes-application component
The HelmRelease at packages/apps/kubernetes/templates/helmreleases/hami.yaml references chartRef.name 'cozystack-kubernetes-application-kubevirt-kubernetes-hami', but that component was missing from the kubernetes-application PackageSource. The HelmRelease would sit in Stalled: ArtifactNotFound at install time. Add kubernetes-hami next to kubernetes-gpu-operator under variant kubevirt, mirroring the existing pattern for every other tenant-cluster addon. The standalone cozystack.hami PackageSource is retained — same shape as gpu-operator, which is registered both standalone (for bundles/iaas) and as a kubernetes-application component (for tenant-cluster HelmReleases). Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
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 |
||
|
|
1dc02d44f4 |
refactor(lineage-controller-webhook): align with cozystack-api shape
Collapse the chart to a single Deployment shape that mirrors cozystack-api: 2 replicas, soft (preferred) nodeAffinity to node-role.kubernetes.io/control-plane via Exists, permissive tolerations, soft podAntiAffinity on hostname, an unconditional PodDisruptionBudget with maxUnavailable: 1, and a Service with spec.trafficDistribution: PreferClose. The same shape works on Talos / kubeadm / k3s and on managed Kubernetes / Cozy-in-Cozy tenant clusters without per-distro overrides — fixing #2417 by making the soft control-plane affinity gracefully fall back to worker scheduling when no control-plane nodes are visible. Drop the DaemonSet path entirely. The previous PR's deployment.enabled toggle, the workload-kind switch in templates/workload.yaml, the fail-guard for localK8sAPIEndpoint+nodeAffinity=[], the conditional PDB, and the values-shape knobs for nodeAffinity and tolerations all go away as a consequence. Override the standard Deployment fields through the usual component-values mechanism if a non-default topology is ever needed. Mark localK8sAPIEndpoint.enabled deprecated and flip the default to false. The flag injects KUBERNETES_SERVICE_HOST=status.hostIP, which is only valid when the pod is actually scheduled on an apiserver- bearing node. With the new soft control-plane affinity, the pod can land off-control-plane and crash-loop. The latency motivation for the flag is real but pending separate webhook performance work; once addressed, the flag can be removed. Revert all changes to packages/core/platform/images/migrations/migrations/20. The earlier ds/...-or-deploy/... fallback was over-engineered: per run-migrations.sh, migration 20 fires only when CURRENT < 20 (i.e. direct upgrades from pre-0.37 to 1.3+), and that path is unsupported anyway. The original ds/... rollout-status line is dead code on every supported install and upgrade path. Add helm-unittest coverage for: the default Deployment shape (replicas, soft nodeAffinity, soft podAntiAffinity, tolerations); no env vars at the default localK8sAPIEndpoint.enabled=false; PDB rendered unconditionally with maxUnavailable: 1, including at replicas=1 (the no-op case); replicas value drives spec.replicas; and that localK8sAPIEndpoint.enabled=true does inject the env vars. The Service test continues to assert trafficDistribution: PreferClose with no internalTrafficPolicy. Add a slim README documenting the topology, parameters, and the deprecation note for localK8sAPIEndpoint. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Timofei Larkin <lllamnyp@gmail.com> |
||
|
|
e410b02e1d |
feat(operator): expose Install/Upgrade timeouts and MaxHistory as flags
The HelmRelease generation in package_reconciler hardcoded three values
that have caused recurring pain:
- Install.Timeout = 10m (line 214)
- Upgrade.Timeout = 10m (line 220)
- MaxHistory = unset (Helm default 5)
The 10m timeout in particular contradicts the per-Application timeout
work done in `pkg/config/config.go` and
`pkg/registry/apps/application/rest.go` (commit
|
||
|
|
c4c70e0e18 |
feat(operator): switch HelmRelease retries to Strategy=RetryOnFailure with configurable RetryInterval
The operator generated HelmReleases with `Install.Remediation{Retries: -1}`
and `Upgrade.Remediation{Retries: -1}`, which is the v2 way of saying
"retry forever, never remediate". Combined with the spec-level
`Interval: 5m`, this meant a failed install/upgrade waited a full 5
minutes before the next attempt — even when the underlying race that
caused the failure (e.g. a chart artifact not quite Ready, a HR
dependency reconciling out of order) cleared in seconds.
Audit of the last 30 successful PR runs found that every single run had
its `Install Cozystack` step's `kubectl wait hr/seaweedfs-system` time
out at the 2-minute mark and recover only after an inline
`flux reconcile --force` workaround. Same root cause: 5-minute retry
interval was longer than the test's wait window. That workaround can be
removed once this change lands.
Switch to `Strategy.Name=RetryOnFailure` with `RetryInterval` exposed via
a new `--helmrelease-retry-interval` operator flag (default 30s):
- Functionally equivalent to the previous configuration ("retry forever
on failure") since `Retries: -1` meant remediation never fired anyway.
- Decouples retry-on-failure timing from `spec.Interval` so failed
releases recover at 30s without polling healthy releases at the same
cadence (which would multiply controller load by ~10x for no benefit).
- Helm chart exposes `cozystackOperator.helmReleaseRetryInterval`,
defaulting to empty (operator uses its own 30s default).
- Same flag pattern as the existing `--helmrelease-interval` (5m default),
also added in this PR via cherry-pick.
Verified: go build green, go test ./internal/operator/... pass, helm
template renders both flags conditionally on values.
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
|
||
|
|
e197aba625 |
feat(operator): make HelmRelease Interval configurable, override to 30s in E2E
Adds --helmrelease-interval (default 5m, matching today's behaviour) to the operator and a corresponding cozystackOperator.helmReleaseInterval value on the cozy-installer chart (default empty -> no flag rendered -> operator default 5m applies). E2E install sets it to 30s. Rationale: with the 5m default, dependency- blocked HRs (waiting on cert-manager webhooks, CRDs) are requeued only every 5 min, producing an 8-10 min dead zone in the E2E install where the fast pack of HRs is Ready but the slow tail hasn't been retried yet. 30s collapses the gap. Production defaults are unchanged. The override is opt-in per install. Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
3fea7cd487 |
fix(security): bump first-party Dockerfile builders to golang:1.26
The root go.mod now declares go 1.26.2 (raised in the first commit of this PR). Every Dockerfile that copies the root go.mod and builds a cozystack-owned binary has to use a matching base image, otherwise the in-container go toolchain has to download go 1.26 on the fly (via GOTOOLCHAIN=auto) at every build, which slows builds and breaks in network-restricted CI sandboxes. Bumps: - packages/core/installer/images/cozystack-operator - packages/system/backup-controller/images/backup-controller - packages/system/backupstrategy-controller/images/backupstrategy-controller - packages/system/cozystack-api/images/cozystack-api - packages/system/cozystack-controller/images/cozystack-controller - packages/system/flux-plunger/images/flux-plunger - packages/system/kubeovn-plunger/images/kubeovn-plunger - packages/system/lineage-controller-webhook/images/lineage-controller-webhook All eight images go from golang:1.25-alpine to golang:1.26-alpine. Also normalizes the cozystack-operator Dockerfile FROM stage keyword from lowercase 'as' to uppercase 'AS' while touching it. Vendored Dockerfiles (kubevirt-cloud-provider, redis-operator, keycloak-operator, linstor-csi, multus-cni, objectstorage, s3manager, kamaji) are intentionally untouched: they pull upstream sources at build time and pin their go base image to whatever the upstream commit was tested against. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
385ea7a17f |
feat(platform): register HAMi as optional system package
Add PackageSource for HAMi with dependency on gpu-operator. Include HAMi in the iaas bundle as an optional package, allowing host-level deployment alongside the existing kubernetes addon path. Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
adc7abe5c1 |
feat(ingress): add loadBalancer exposure mode via CiliumLoadBalancerIPPool
Service.spec.externalIPs is deprecated upstream in Kubernetes v1.36 (KEP-5707, kubernetes#137293). The AllowServiceExternalIPs feature gate is expected to default to off around v1.40 and the implementation to be removed around v1.43. For bare-metal installs that rely on externalIPs today, cozystack needs a migration path. This change adds an opt-in 'loadBalancer' exposure mode for the ingress-nginx Service: - New platform value 'publishing.exposure' (enum: externalIPs | loadBalancer, default externalIPs). Plumbed through cozystack-values into each tenant's ingress HelmRelease via the new 'expose-mode' key. - Unknown values and loadBalancer with an empty externalIPs list fail the chart render with explicit error messages, rather than silently producing a broken Service. - When exposure=loadBalancer and the current namespace matches publishing.ingressName, the Service becomes type: LoadBalancer with externalTrafficPolicy: Local. - A new template renders a CiliumLoadBalancerIPPool whose blocks come from publishing.externalIPs (IPv4 addresses get /32, IPv6 addresses get /128) and whose serviceSelector uses Cilium's synthetic io.kubernetes.service.namespace key combined with the standard app.kubernetes.io/name: ingress-nginx label. No custom label is written to the Service itself, avoiding cross-tenant collisions from user-defined labels. Default behaviour is unchanged: without opting in, the Service is still ClusterIP + spec.externalIPs as today. Scope: only ingress-nginx is migrated by this setting. Other cozystack components that still write Service.spec.externalIPs directly (notably the vpn app) must be migrated separately before the v1.40 feature gate flip. Tests: packages/extra/ingress/tests/exposure_test.yaml adds 13 helm-unittest cases covering both modes, IPv4/IPv6, empty-token filtering, unknown-mode rejection, and the non-matching-namespace fallback. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
b52e2801b4 |
Prepare release v1.3.0
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com> |
||
|
|
6deca1b07d |
chore(etcd-operator): opt into CreateReplace upgrade policy for CRDs
v0.4.3 introduced new CRDs (EtcdBackup, EtcdBackupSchedule). Flux defaults to Skip on upgrade, so on existing clusters those CRDs never land without a manual kubectl apply. Setting upgradeCRDs: CreateReplace on the etcd-operator component tells PackageReconciler to wire hr.Spec.Upgrade.Crds accordingly. etcd-operator's CRDs evolve additively between versions, so CreateReplace is safe here. Depends on the PackageSource.Spec.Variants[].Components[].Install.UpgradeCRDs field — must ship with a cozystack-operator release that recognizes it. Without that, this field is ignored and the HelmRelease still defaults to Skip. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
12bf6b0e26 |
Prepare release v1.3.0-rc.1
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com> |
||
|
|
482d813d01 |
Add vm-default-images package (#2258)
<!-- Thank you for making a contribution! Here are some tips for you: - Start the PR title with the [label] of Cozystack component: - For system components: [platform], [system], [linstor], [cilium], [kube-ovn], [dashboard], [cluster-api], etc. - For managed apps: [apps], [tenant], [kubernetes], [postgres], [virtual-machine] etc. - For development and maintenance: [tests], [ci], [docs], [maintenance]. - 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 ### Release note <!-- Write a release note: - Explain what has changed internally and for users. - Start with the same [label] as in the PR title - Follow the guidelines at https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md. --> ```release-note [vm-default-images] Added package that brings set of images that can be used clusterwide [vm-disk] Updated source "image" for prettier dropdown selection [vm-disk] Added new source for vm-disk called disk - to use as source vm-disk from same namespace. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Clone VM disks by specifying an existing vm-disk as the source. * Global default image collection and chart to publish pre-provisioned images. * **UI** * Forms provide selectable lists for default images and existing VM disks. * **Migration** * Migration to rename existing image DataVolumes to the new default-images naming and bumped migration version. * **Documentation** * VM disk docs and README updated to reflect image sourcing and cloning. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
e6ba9817be |
[platform] Make vm-default-images opt-in, not default in iaas bundle
The package imports ~320Gi of golden-image PVCs (ubuntu-noble, fedora,
debian, centos, etc.) as soon as it's installed. On small test and dev
clusters that's enough to consume the entire replicated storage pool,
after which no tenant PVCs — including the ones E2E itself provisions —
can be bound. It's also unreasonable to force that cost on every iaas
user: many deployments don't need prebuilt images at all, and the ones
that do often want to curate their own subset.
Switch the bundle entry from 'package.default' to 'package.optional.default',
matching the treatment already applied to gpu-operator directly below it.
Users who want the golden images can opt in via:
bundles:
enabledPackages:
- cozystack.vm-default-images
The package, its Source, and migration 38 all stay in place — nothing
else changes for users who explicitly enable it. Users who previously
relied on the bundle auto-installing it will need to add the package
to enabledPackages on upgrade; this is called out in the release note.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
|
||
|
|
c6739cf95a |
[linstor-gui] Add package for LINBIT linstor-gui web UI (#2382)
## What this PR does Adds a new Cozystack system package `linstor-gui` that ships [LINBIT's LINSTOR web UI](https://github.com/LINBIT/linstor-gui) (GPL-3.0) so operators can manage nodes, resources, volumes and snapshots from a browser instead of the `linstor` CLI. Changes: - `packages/system/linstor-gui/` — umbrella chart with: - `images/linstor-gui/Dockerfile` builds the image from the upstream `pkg.linbit.com` tarball (v2.3.0) on top of `nginxinc/nginx-unprivileged:1.29-alpine`, mirroring the build pattern already used for `piraeus-server` and `linstor-csi`. - `templates/configmap-nginx.yaml` — chart-supplied `nginx.conf` that proxies `/v1` and `/metrics` to `linstor-controller.cozy-linstor.svc:3371` over **mTLS**, using the existing `linstor-client-tls` secret created by the `linstor` package. - `templates/deployment.yaml` — read-only rootfs, non-root (UID 101), `runAsNonRoot`, `RuntimeDefault` seccomp, no SA token mounted, `reloader.stakater.com/auto` on the Deployment. - `templates/service.yaml` — **ClusterIP only**. No Ingress is shipped because the LINSTOR controller API is a privileged cluster-wide storage surface and auth depends on the deployment's OIDC setup; operators wire up ingress + auth explicitly. - `tests/deployment_test.yaml` — `helm-unittest` covering Service shape, TLS secret mount, securityContext, and nginx proxy+mTLS config. - `packages/core/platform/sources/linstor-gui.yaml` — new `PackageSource` depending on `cozystack.linstor`. - `packages/core/platform/templates/bundles/system.yaml` — registers `linstor-gui` as an **optional** system package (controlled via `bundles.enabledPackages`), alongside `velero`, `telepresence`, etc. ### Why standalone (not a sidecar in the controller pod) LINBIT's upstream Dockerfile is designed for this standalone reverse-proxy topology, and keeping the UI as its own Deployment means it scales, upgrades, and restarts independently of `linstor-controller`. The price is the small amount of mTLS config in `nginx.conf`, all of which lives in the chart's ConfigMap. ### Release note ```release-note [linstor-gui] Add an opt-in system package that deploys LINBIT's linstor-gui web UI alongside the LINSTOR controller. Enable via `bundles.enabledPackages: [cozystack.linstor-gui]`, then `kubectl -n cozy-linstor port-forward svc/linstor-gui 3373:80` to access. ``` ## Test plan - [ ] `make unit-tests` (requires `helm-unittest` plugin) passes on the new `tests/deployment_test.yaml` - [ ] `make image` in `packages/system/linstor-gui/` builds and pushes the image - [ ] Install the package on a dev cluster with LINSTOR already deployed; confirm the pod comes up - [ ] `kubectl -n cozy-linstor port-forward svc/linstor-gui 3373:80` → browser loads the UI at `localhost:3373` - [ ] UI lists LINSTOR nodes, storage pools and resources correctly (proves mTLS proxy to `/v1` works) - [ ] Disable via `bundles.enabledPackages` removal; verify the `Package` resource is cleaned up <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * LINSTOR GUI added as an optional system package deployable to the cozy-linstor namespace. * Web UI serves on port 3373, proxies LINSTOR controller over mTLS, and exposes a /healthz endpoint. * Image build and release targets added to produce multi-arch container images. * **Documentation** * New README with deployment guidance, connection examples, and configurable options (endpoint, client secret, image, replicas). * **Tests** * Helm chart tests validating service, deployment, config and mTLS behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
0b5a1df3c2 |
[platform] Bump migration targetVersion to 39 for migration 38
Address review feedback from lexfrei on migrations/38:2: Migration 38 requires targetVersion 39 to execute, since run-migrations.sh iterates seq $CURRENT_VERSION $((TARGET_VERSION - 1)). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
b9a17295cc |
[platform] Wire vm-default-images into iaas bundle
The PackageSource for vm-default-images existed but was never included in any bundle, so it would never be installed. Add it to iaas.yaml immediately after kubevirt-cdi (which it depends on) so it is deployed together with the rest of the KubeVirt IaaS stack. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
6b6118ce56 |
[platform] Add migration 38 to rename vm-image-* DataVolumes to vm-default-images-*
The vm-disk package previously referenced golden-image DataVolumes with the prefix "vm-image-<name>" in the cozy-public namespace. The new vm-default-images package creates them as "vm-default-images-<name>", so any existing vm-disk that sources an image by name would fail to find its PVC after upgrade. Migration 38 renames every DataVolume in cozy-public whose name starts with "vm-image-" to "vm-default-images-" (dropping the old object and re-creating with the new name) so live vm-disk resources keep resolving their source PVC transparently. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
bec35e3aad |
[vm-default-images] Added new optional package
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
a8559e1a9b |
[scheduler] Make cozystack-scheduler storage-aware via LINSTOR extender (#2330)
## What this PR does Makes `cozystack-scheduler` LINSTOR storage-aware by configuring it to call the existing `linstor-scheduler-extender` as a scheduler extender. This fixes #2328 (phase 1): pods with both a `SchedulingClass` and LINSTOR PVCs now get storage-locality-aware placement instead of bypassing LINSTOR's filter/prioritize logic. Changes: - **linstor-scheduler package**: expose the extender sidecar (port 8099) via a new ClusterIP Service; patch the vendored deployment to add a `app.kubernetes.io/component: scheduler` label for clean Service targeting - **cozystack-scheduler package**: bump to v0.3.0 which adds configurable `extenders` support; set the LINSTOR extender URL in wrapper values - **tests**: helm-unittest for extender rendering in the ConfigMap ### Release note ```release-note [scheduler] cozystack-scheduler now calls the LINSTOR scheduler extender for storage-aware pod placement. Pods assigned to a SchedulingClass that also use LINSTOR-backed PVCs will prefer nodes with local volume replicas. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added scheduler extender support: exposes an extender Service and adds pod metadata to enable extender routing. * **Chores** * Bumped scheduler chart and image versions to 0.3.0. * Added default extender configuration and a sample remote extender entry. * Updated build scripts to apply an additional patch and added a test target. * **Tests** * Added Helm tests validating rendered ConfigMap with and without extender configuration. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |