mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-09 14:02:56 +09:00
ci/cozystack-api-pin-securitygroup
673
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
41ff94eca9 |
fix(flux): tune source-watcher concurrency and CPU under load
source-watcher reconciles one ArtifactGenerator per PackageSource and builds the resulting ExternalArtifacts. On the management cluster that is ~100 artifact builds during install, and the platform HelmRelease blocks until all of them are Ready. It was the only Flux controller left untuned. In the bootstrap all-in-one pod it ran without an explicit --concurrent (binary default 10) and with the shared 100m CPU request; in the production FluxInstance it was missing from the --concurrent / CPU patch that source-, kustomize- and helm-controller already receive. Under load its per-artifact builds finish at staggered times, leaving a random tail of not-yet-Ready artifacts that times out the platform install. Raise source-watcher to --concurrent=20 in both deployments. In the bootstrap all-in-one pod also give it a 500m CPU request; the pod has no CPU limits, so the request is what drives its CPU share under contention. In the production FluxInstance add it to the shared reconciler patch, so it now receives the same --requeue-dependency=5s and 2000m/2048Mi limits the other tuned controllers already have. The flux-aio module exposes a resources override for the watcher (set via CUE) but no args override, so the bootstrap --concurrent bump is a post-render patch in the Makefile. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
cbc314a8c5 |
fix(kubevirt-cdi): order behind cozystack-basics so cdi-clone-dv RoleBinding finds cozy-public
The kubevirt-cdi default variant only depended on cozystack.networking, which reconciles long before cozystack-basics provisions the cozy-public namespace. kubevirt-cdi ships the cdi-clone-dv RoleBinding into cozy-public, so on a fresh install the RoleBinding can race namespace creation and fail with `namespaces "cozy-public" not found`. Flux retries normally mask it, but under slow reconcile it surfaces as a hard UpgradeFailed. Add a cozystack.cozystack-basics edge so the namespace exists first, mirroring the backupstrategy-controller ordering. No cycle: cozystack-basics' own dependencies (tenant-application, cozystack-engine, gateway-api-crds) do not reach kubevirt-cdi. Pin the edge with a helm-unittest regression guard. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
bf14a73b39 |
feat(api): add SecurityGroup network policy resource (sdn.cozystack.io) (#2922)
## What this PR does
Introduces a tenant-facing `SecurityGroup` resource in a new
`sdn.cozystack.io/v1alpha1` API group, served by the Cozystack
aggregated API server as a 1:1 projection of a `CiliumNetworkPolicy`. A
SecurityGroup attaches to one of the tenant's managed applications by
reference (`spec.targetRef: {apiGroup, kind, name}`) and declares
allow-list ingress/egress for that application. Tenants manage network
policy for their own apps entirely through
`securitygroups.sdn.cozystack.io`, without being granted any access to
the `cilium.io` API group.
The translation is synchronous and stateless — the API server converts
each SecurityGroup into a CiliumNetworkPolicy in the same namespace on
write and back on read; there is no controller. The backing policy's
`endpointSelector` is **derived** from `targetRef` (matched on the
application's lineage labels
`apps.cozystack.io/application.{group,kind,name}`), never copied from
tenant input, so the selector is machine-generated and can only ever
match the referenced application's own pods in the tenant's namespace.
Policies owned by this API carry an `sdn.cozystack.io/securitygroup`
marker label that the storage always re-asserts and hides from the view,
so it only ever surfaces or mutates its own policies and never touches
platform-managed network policies.
The value is self-service network policy on a Cozystack-native,
RBAC-scoped surface, without exposing Cilium's version-coupled CRDs to
tenants. Because the selector is derived from an authorized application
reference rather than free-form tenant input, the RBAC boundary is
structural, not selector-validated: a tenant cannot express network
policy against arbitrary or platform-owned pods. The full design —
selector derivation from lineage labels, the structural boundary, the
additive-policy reality (a SecurityGroup widens a managed application's
allowed traffic but cannot sever its platform-managed management plane,
and equally cannot tighten below the platform baseline), the in-tree
CiliumNetworkPolicy mirror that avoids a Cilium Go-module dependency,
RBAC, and deferred extensions (raw-pod targeting, cluster scope,
reusable groups) — is in `pkg/apis/sdn/DESIGN.md`.
Implements the attachment model from #1614.
### Release note
```release-note
feat(api): add the SecurityGroup resource (sdn.cozystack.io/v1alpha1) — a tenant-facing, namespace-scoped firewall that attaches to a managed application by reference and projects 1:1 onto a CiliumNetworkPolicy with a machine-derived endpoint selector, letting tenants manage their application's network policy without cilium.io access.
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added the `SecurityGroup` API resource (`sdn.cozystack.io` v1alpha1)
with a namespaced projection backed by Cilium network policies.
* Enabled the SDN `APIService` and wired SDN routing, scheme, and REST
support.
* Expanded RBAC to include SecurityGroup access and required Cilium
policy permissions.
* **Documentation**
* Added a design document describing projection behavior and rule
semantics.
* **Bug Fixes**
* Improved API rule validation coverage for missing list-type fields.
* **Tests**
* Added SDN round-trip, fuzzing, RBAC, and comprehensive REST
create/get/list/update/delete/watch/resource-version tests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
f9d1138c6e |
feat(keycloak): configurable login theme via platform branding (#3124)
## What this PR does
Makes a realm's Keycloak login theme configurable through platform
`branding` values, closing a gap: `branding` could set the realm display
name but not its login theme, and the keycloak chart's `themes:` list
was not reachable from platform values.
Two new keys under `branding` (propagated via `_cluster.branding`, the
same path that already feeds `displayName`):
- `branding.loginTheme` — keycloak-configure renders it as
`ClusterKeycloakRealm.spec.themes.loginTheme`, so the realm selects that
login theme.
- `branding.loginThemeImage` — when set together with `loginTheme`, the
keycloak chart appends `{name: loginTheme, image: loginThemeImage}` to
its effective theme list, so the existing init-container mounts the
theme image into `/opt/keycloak/themes/`. No StatefulSet patching or
per-package values plumbing.
`loginTheme` alone (no image) still selects the theme — for themes
already baked into the Keycloak image. Explicit chart `themes:` entries
keep working; the branding-derived entry is appended after them.
Flow: platform `branding` → `_cluster.branding` (cozystack-values
Secret) → keycloak-configure (realm loginTheme) + keycloak (theme init
container). Covered by helm-unittest in both charts.
### Screenshots
No bundled UI assets — this PR adds the mechanism only; the login theme
is operator-supplied via `loginThemeImage`, so the visual result depends
on that image.
### Release note
```release-note
feat(keycloak): select the realm login theme and ship its theme image through platform `branding` values (`branding.loginTheme`, `branding.loginThemeImage`)
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added support for configuring a custom login theme from platform
branding, including automatic selection in the realm setup.
* Theme entries can now be combined with existing theme settings and
rendered in the expected order.
* **Documentation**
* Expanded configuration guidance for branding and theme-related
settings with clearer examples.
* **Tests**
* Added coverage for login theme rendering and fallback behavior when
theme settings are missing.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
31c15b282f |
feat(network): mechanism-agnostic LoadBalancer exposure (ExposureClass + ServiceExposure) (#3081)
## What this PR does Lays the foundation for migrating tenant LoadBalancer exposure off the deprecated `Service.spec.externalIPs` (KEP-5707) and, eventually, off MetalLB onto Cilium-native announcement — the mechanism-agnostic shape prescribed when the earlier chart-side approach (#2468) was reverted in #2603. It adds a new `network.cozystack.io/v1alpha1` API group and a controller that translates intent into whichever LoadBalancer backend the cluster admin configured: - `ExposureClass` (cluster-scoped, admin): binds a logical class name to a backend (`externalIPs` | `metallb` | `cilium` | `robotlb`) and its VIP address scope — the StorageClass/IngressClass pattern. - `ServiceExposure` (namespaced, tenant intent): "expose this Service via class X"; status reports the resolved backend and assigned IPs. - A `serviceexposure` controller renders the backend's pool + announcer once per class (MetalLB `IPAddressPool` + `L2Advertisement`; Cilium `CiliumLoadBalancerIPPool` + `CiliumL2AnnouncementPolicy`), scoped to the namespaces that opted in. Backends are pluggable behind a small Go interface, so adding BGP or another cloud LB is one implementation, not an API change. The controller never mutates the target Service — the chart still owns its shape. Platform wiring: `publishing.exposureClass.*` renders the admin `ExposureClass` and the host ingress publishes through it. The default (no class set, `externalIPs`) leaves every existing cluster's behaviour byte-for-byte unchanged; the MetalLB→Cilium announcer migration becomes a single `ExposureClass.spec.backend` change. This is the foundation only — Part of #2586. Converting per-app exposure, flipping Cilium `l2announcements` on, changing the default, and removing MetalLB are deferred to later phases. ### Release note ```release-note feat(network): add ExposureClass and ServiceExposure for mechanism-agnostic LoadBalancer exposure. Opt-in foundation; the default externalIPs publishing path is unchanged. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added new `ExposureClass` and `ServiceExposure` custom resources with support for multiple backends (externalIPs, MetalLB, Cilium, RobotLB), including optional L2 and address configuration. * Added Helm wiring and templates to enable service/ingress publishing via `ServiceExposure`, selecting a configured (or default) `ExposureClass`. * **Bug Fixes** * Improved reconciliation, finalizer-based cleanup, and status updates for readiness and assigned IP reporting, including safer handling when backends or managed resources change. * **Tests** * Added end-to-end and unit test coverage for backend rendering, readiness, and cleanup flows. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b9749cec80 |
fix(platform): md0 migration -- fail closed and preserve the no-delete label
Migration 47 pins the implicit default md0 node group before the kubernetes chart upgrade so the live md0 MachineDeployment is not pruned. Two correctness fixes: - Stamp the cozystack-version ConfigMap via the shared stamp_cozystack_version helper instead of `kubectl create configmap | kubectl apply`. The label-less apply by the same field manager stripped the platform.cozystack.io/no-delete label migration 42 added, dropping cozystack-version out of the cozystack-no-delete-guardrail policy; the helper re-emits the label and is enforced for every migration >= 42 by hack/cozystack-version-stamp.bats. - Fail closed. A non-NotFound read error is now counted as a failure rather than silently skipped, and any failure exits 1 so the pre-upgrade hook aborts before Helm rolls out the new chart against clusters whose md0 is not yet pinned. The version stamp stays at 47 and the migration retries next upgrade. Also narrow the merge patch to only md0 so a concurrent edit to a sibling node group between the read and the patch cannot be reverted. Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
602b8d06fd |
fix(platform): add migration 47 to pin implicit md0 node group
After the kubernetes chart stops force-merging a default md0 node group, clusters that relied on the implicit md0 (defined other nodeGroups without md0) would have their live md0 MachineDeployment pruned by Helm on the next reconcile. Migration 47 makes md0 explicit on those clusters to preserve the current node topology; it is idempotent, treats a missing CRD as a no-op, and skips clusters that already define md0. Signed-off-by: mattia-eleuteri <mattia@hidora.io> |
||
|
|
60c9b39578 |
docs(platform): document branding keys including login theme
The branding map shipped as an undocumented empty map. Describe the keys it actually supports (brandName, brandHtmlName) and the new login-theme keys (loginTheme, loginThemeImage) consumed by the Keycloak charts, with an example. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
70320e0abf |
fix(platform): stamp migrations 45/46 via the cozystack-version helper
Migrations 45 and 46 (added by #2931) stamp the cozystack-version ConfigMap with an inline `kubectl apply` heredoc instead of sourcing lib/cozystack-version.sh and calling stamp_cozystack_version, the way 42/43/44 do. The cozystack-version-stamp.bats guard forbids any migration >= 42 from bypassing the helper, so `make bats-unit-tests` fails on migration 45. This only surfaces in PR CI (make unit-tests); main-push CI does not run the bats suite, so #2931 landed red on this test and now blocks every PR that merges current main. Route both stamps through the helper. The helper renders a byte-identical labeled manifest (golden-pinned in the bats suite), so this is behaviour- preserving — it just restores the no-label-drift guarantee the heredoc form could silently lose. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
1df8c7c557 |
feat(kubernetes): bootstrap tenant workers with Talos via CABPT (#2931)
## What this PR does > **Supersedes #2610.** Same work, rebased cleanly onto current `main` so it now sits on top of #2867 (the `kube-controller-manager` VAP type-checker fix). That fix is what was killing the base install in #2610's E2E *before* the `kubernetes` app test ever ran, leaving the Talos worker-bootstrap rewrite verified only by code reading. #2610's 67-commit branch is squashed here into 8 logical commits with original authorship preserved (@kvaps and @IvanHunters as authors); the tree is **byte-identical** to #2610's head — no code changes, just the rebase onto `main` (to pick up #2867) plus history cleanup. The detailed review history lives on #2610. Phase 1 of the Kubernetes-app split design (see cozystack/community#8): replace the Ubuntu+kubeadm worker bootstrap path with Talos, driven by `cluster-api-bootstrap-provider-talos` (CABPT) and a `clastix/talos-csr-signer` sidecar embedded in the Kamaji control-plane pod. Existing tenants keep working — old machines roll out and get replaced by Talos workers without manual intervention. Highlights: - Add CABPT (v0.6.12) as a second `BootstrapProvider` alongside the existing kubeadm one in `capi-providers-bootstrap`. - Bump the Kamaji control-plane provider with an upstream-bound patch that exposes `KamajiControlPlane.spec.network.additionalServicePorts`, used to publish trustd (50001/TCP) on the apiserver service. - Generate Talos PKI (Ed25519 CA + trustd TLS) via cert-manager and stable random Talos secrets (`token`, `clusterId`, `clusterSecret`, `bootstrapToken`) using the helm lookup-and-reuse pattern. - Materialise a kubeadm-format `bootstrap-token-<id>` Secret inside the tenant `kube-system` via a Helm post-install/upgrade Job. - Render a `TalosConfigTemplate` (worker machineconfig, `generateType: none`) and switch the `MachineDeployment.bootstrap` reference from `KubeadmConfigTemplate` to `TalosConfigTemplate`, gated on Talos secrets and the Kamaji apiserver service being ready. - Boot workers from the Talos openstack image via a CDI `DataVolume` (`source.http.url` → `factory.talos.dev`), expose the system disk as virtio-blk with `blockSize.custom: logical=512, physical=4096` so 4Ki-native block storage backends (LINSTOR/DRBD) play nicely with QEMU's O_DIRECT writes and SeaBIOS still boots, drop the separate kubelet disk (Talos lays out EPHEMERAL itself). - Pin pod/service CIDRs in the worker machineconfig to the tenant ranges (`10.243.0.0/16` / `10.95.0.0/16`) to avoid Talos's address-overlap diagnostic against the host pod CIDR. - Set `cluster.controlPlane.endpoint` and `cilium.k8sServiceHost` to the Kamaji apiserver Service ClusterIP (looked up at render time), so bootstrap survives the chicken-and-egg moment where tenant DNS does not exist yet and the host CoreDNS does not serve the tenant zone. End-to-end on a dev cluster: a fresh tenant brings up a Talos worker, kubelet CSRs get approved (apiserver-client by Kamaji, kubelet-serving by the talos-csr-signer sidecar), Cilium initialises with hostNetwork against the apiserver ClusterIP, CoreDNS comes up, and the node transitions to `Ready`. ### Release note ```release-note feat(kubernetes): bootstrap tenant workers with Talos Linux instead of Ubuntu+kubeadm. Existing clusters roll over to Talos workers automatically; new tenants come up on Talos from the start. Powered by cluster-api-bootstrap-provider-talos and the talos-csr-signer sidecar in the Kamaji control-plane pod. Note: the tenant `kubernetes` HelmRelease now reports Ready as soon as Helm completes (Install/Upgrade DisableWait) and no longer blocks on worker/addon readiness — worker-rollout health is tracked by WorkloadMonitor, not the HelmRelease. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes **New Features** * Added Talos worker configuration for `talos.version` and `talos.schematicID` * Introduced `nodeHealthCheck` (`maxUnhealthy`, `nodeStartupTimeout`) for worker remediation tuning * Added image overrides for `kubectl` and `talosCsrSigner` * Exposed additional tenant control-plane Service ports support **Breaking Changes** * Removed Kubernetes v1.30; supported versions start at v1.31 * Renamed `nodeGroups.ephemeralStorage` → `nodeGroups.diskSize` with consolidated Talos system-disk semantics **Removals** * Removed Ubuntu container-disk images and related containerd patch mechanisms * Air-gapped tenant workers temporarily unsupported during the Talos transition **Documentation** * Updated chart docs, parameters, and GitOps upgrade guidance for version bumps <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5cb6ef435f |
feat(keycloak): opt-in separate hostname and route for the admin console (#2954)
## What this PR does
Adds an opt-in way to serve the Keycloak admin console and
Administration
REST API on a separate hostname and route from the public login
endpoints,
so the admin surface can be kept off the public internet.
- New `ingress.adminHost` in the keycloak chart. When set:
- `KC_HOSTNAME_ADMIN` is configured so the admin console generates its
URLs on that hostname;
- the public (login) route is narrowed to the OIDC paths only
(`/realms`, `/resources`, `/.well-known`), so the admin console and
Administration REST API are no longer served on the login hostname;
- a dedicated route (Ingress or HTTPRoute, depending on
`gateway-enabled`)
serves the admin hostname.
- New platform `publishing.ingressNameAdmin`, propagated as
`_cluster.expose-ingress-admin`. The admin route attaches to the
Gateway/ingressClass it names, defaulting to `expose-ingress`. Pointing
it
at a separate (for example, private-IP) Gateway moves admin traffic off
the public one. The separate Gateway itself is provisioned out of band;
this only selects where the admin route attaches.
Everything is empty by default, so existing deployments are unchanged.
Note: `KC_HOSTNAME_ADMIN` on its own is not a security boundary — the
Administration REST API stays reachable on any hostname routed to
Keycloak
(per the Keycloak docs). The path and network separation introduced here
is
what restricts access; full isolation requires attaching the admin route
to
a non-public Gateway.
Covered by helm-unittest suites for the keycloak chart and the platform
value wiring.
### Screenshots
N/A — no UI changes.
### Release note
```release-note
feat(keycloak): add opt-in `ingress.adminHost` to serve the admin console and Administration REST API on a separate hostname/route, plus `publishing.ingressNameAdmin` (`_cluster.expose-ingress-admin`) to attach that route to a separate (e.g. private) Gateway/ingressClass. Disabled by default; existing deployments are unchanged.
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added optional configuration to serve the Keycloak admin console on a
dedicated hostname, with separate routing and ingress/gateway attachment
so admin UI/API can be isolated from public login/OIDC paths.
* Introduced a cluster-level setting to optionally expose admin routes
via a separate ingress/gateway class/namespace.
* **Tests**
* Added new YAML test coverage to validate defaulting, hostname split
behavior, and correct routing for both gateway-enabled and
gateway-disabled modes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
67692b4544 |
feat(platform,ingress): publish host ingress via ServiceExposure
Add the operator-facing knob that drives the new exposure model. When publishing.exposureClass.name is set, the platform renders a cluster-scoped ExposureClass binding the chosen backend (externalIPs, metallb, cilium, robotlb) and VIP address scope, and carries the class name through the cozystack-values Secret. The host ingress then renders a ServiceExposure for its ingress-nginx Service, which the cozystack-controller translates into the backend's pool and announcer. The default (empty name) renders neither the ExposureClass nor the ServiceExposure, so clusters on the externalIPs path are unchanged on upgrade. The ingress Service shape is untouched: it is already published as type LoadBalancer whenever externalIPs is empty. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
e0597e3d99 |
fix(platform): make *-rd sources depend on cozystack-engine (#3041)
## Problem On an unlucky Flux reconcile order, a `*-application` PackageSource that ships a `system/*-rd` component can install before the `applicationdefinitions.cozystack.io` CRD exists. Every `*-rd` chart renders an `ApplicationDefinition` (`cozystack.io/v1alpha1`); that CRD is installed by the `application-definition-crd` component of `cozystack.cozystack-engine`. When an `*-rd` HelmRelease renders first, it hard-fails with `no matches for kind "ApplicationDefinition" in version "cozystack.io/v1alpha1"`, and that cascades into a broad install timeout — surfacing even on changes that have nothing to do with ordering, because it is a latent race. ## Fix Add `- cozystack.cozystack-engine` to the `dependsOn` of every `*-application` source that ships a `system/*-rd` component (29 sources). `gateway-application` already carried this edge — it was added earlier for the TenantGateway CRD, the same bug class, but only for that one source. A new invariant test fails if any source with a `system/*-rd` component is missing the edge. ## Why The ordering was previously implicit, relying on Flux happening to reconcile `cozystack-engine` before the `*-application` sources. Making the edge explicit removes the race deterministically. No dependency cycle is introduced: `cozystack-engine` depends only on `networking` and `cert-manager` (plus keycloak in its oidc variant), none of which reach back to any `*-application`. ## Test - New generic guard `hack/check-applicationdefinition-crd-ordering.bats` (auto-discovered by `make bats-unit-tests`, runs in CI via `make unit-tests`): parses every source and fails if any PackageSource variant with a `system/*-rd` component lacks the `cozystack.cozystack-engine` edge — so future sources are covered, not just today's set. - `helm unittest packages/core/platform`: 13 suites / 66 tests pass; the source edits do not disturb existing assertions. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved dependency ordering across several platform packages to ensure related components install in the correct sequence. * Prevented end-to-end test selection from being overly broad when only ordering-related dependencies change. * **Tests** * Added checks to catch missing dependency requirements and to verify test selection behavior stays accurate. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f6d5086190 |
fix(platform): order backupstrategy-controller after cozystack-basics (#3037)
## Problem On a fresh install, the install step can fail with: ```text Error creating resource via patch ... apps.cozystack.io/v1alpha1 Kind=Bucket name=cozy-backups namespace=tenant-root: namespaces "tenant-root" not found ``` `backupstrategy-controller` renders the `cozy-backups` Bucket **into** the `tenant-root` namespace, but its `dependsOn` only guards the CRDs it needs (backup-controller / bucket-application / objectstorage-controller / velero) — not the existence of the target namespace. `tenant-root` is provisioned by `cozystack-basics` (the static tenant-root release), and there is no ordering edge to it. On an unlucky Flux reconcile order the Bucket patch races namespace creation, fails, and that transient failure races the install script's `kubectl wait` deadline → the run fails. This is latent and not introduced by any particular change — the Bucket-into-tenant-root predates recent branches — so it flakes installs on changes that have nothing to do with backups. ## Fix Add `cozystack.cozystack-basics` to `backupstrategy-controller`'s `dependsOn` so the `tenant-root` namespace exists before the Bucket is applied. `cozystack-basics` is a dependency-graph leaf (nothing depends on it), so no cycle is introduced. Pinned with a helm-unittest regression guard (`tests/sources_backupstrategy_dependson_test.yaml`) asserting the edge — mirroring the existing `sources_basics_dependson_test.yaml` pattern. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved install ordering so the backup strategy component waits for required foundational services before deploying, reducing race conditions during fresh installs. * Clarified dependency relationships for cluster-wide platform components to better reflect safe upgrade and installation behavior. * **Tests** * Added regression coverage to verify the backup strategy package declares all required dependencies, including namespace and CRD providers. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
69247ba80c |
feat(platform): deploy the securitygroup-controller
Add the securitygroup-controller Helm package (Deployment, ServiceAccount, ClusterRole and binding, image build) and register it as a default system package so it ships with the platform. The ClusterRole grants cluster-wide pod patch — the controller stamps the SecurityGroup membership label on managed-app pods across dynamically-created tenant namespaces, so the grant cannot be namespace-scoped — plus read and finalizer patch on the backing CiliumNetworkPolicies, and leader-election leases. Tenants gain no new permissions; the membership label is written exclusively by this platform controller. helm-unittest pins the pod-patch and ciliumnetworkpolicies grants so a dropped verb fails the suite rather than silently failing closed at runtime. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
4593b72cd1 |
feat(operator): support private OCI registry credentials for platform source (#2472)
## Summary Implements support for private OCI registry and Git repository credentials in platform sources. Adds `--platform-source-secret` flag to the operator and corresponding Helm chart parameter. Closes #2455 ## Changes ### Operator (`cmd/cozystack-operator/main.go`) - Added `--platform-source-secret` flag for specifying Secret name - Updated `generateOCIRepository()` to set `spec.secretRef` when secret is provided - Updated `generateGitRepository()` to set `spec.secretRef` when secret is provided - Secret is expected to be in `cozy-system` namespace ### Helm Chart - **`packages/core/installer/values.yaml`**: Added `cozystackOperator.platformSourceSecret` parameter with documentation - **`packages/core/installer/templates/cozystack-operator.yaml`**: Pass `--platform-source-secret` flag to operator container ## Usage Example ### For private OCI registry (e.g., GitLab Container Registry): ```bash # Create registry credentials secret kubectl create secret docker-registry gitlab-registry-creds \ --namespace cozy-system \ --docker-server=registry.gitlab.example.com \ --docker-username=<username> \ --docker-password=<token> ``` ```yaml # Helm values cozystackOperator: platformSourceUrl: 'oci://registry.gitlab.example.com/mygroup/cozystack-packages' platformSourceRef: 'tag=v1.0.0' platformSourceSecret: 'gitlab-registry-creds' ``` ### For private Git repository: ```bash # Create Git credentials secret (SSH key or basic auth) kubectl create secret generic git-creds \ --namespace cozy-system \ --from-literal=username=<username> \ --from-literal=password=<token> ``` ```yaml # Helm values cozystackOperator: platformSourceUrl: 'https://github.com/private-org/cozystack-packages' platformSourceRef: 'branch=main' platformSourceSecret: 'git-creds' ``` ## Testing Manually verified: - OCIRepository resource is created with `spec.secretRef` when secret name is provided - GitRepository resource is created with `spec.secretRef` when secret name is provided - Resources are created without `spec.secretRef` when secret name is empty (backward compatible) - Helm chart renders correctly with and without `platformSourceSecret` parameter ## Checklist - [x] Code follows project style and conventions - [x] Operator supports both OCI and Git source types - [x] Helm chart parameter documented with comments - [x] Backward compatible (empty secret name is handled correctly) - [x] Closes referenced issue (#2455) ## Release note ```release-note feat(operator): add `--platform-source-secret` flag to authenticate the platform package source against private OCI registries (kubernetes.io/dockerconfigjson) and private Git repositories (basic-auth, bearer token, or SSH key). Empty value preserves existing public-registry behaviour. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for configuring a Kubernetes Secret for the platform source. * Installer settings and startup options now let you provide a secret name for Flux source credentials. * **Bug Fixes** * Generated platform source resources can now reference the provided Secret, enabling authenticated access for OCI and Git sources. * **Documentation** * Updated configuration docs to explain the new secret setting and required secret format. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
caeb2c466f |
feat(platform): add publishing.ingressNameAdmin for a separate admin gateway
Expose a new opt-in publishing.ingressNameAdmin that is propagated to _cluster.expose-ingress-admin. System components (currently Keycloak) use it to attach their admin routes to a separate Gateway/ingressClass, e.g. one bound to a private IP and unreachable from the public internet. Empty by default, so admin endpoints keep sharing expose-ingress. The private Gateway itself is provisioned out of band; this only selects where the admin route attaches. Assisted-By: Claude AI Signed-off-by: Kirill Ilin <stitch14@yandex.ru> |
||
|
|
fff9e827d1 |
fix(operator): address review feedback on private platform source
- Run `go mod tidy` to move github.com/fluxcd/pkg/apis/meta from indirect to direct require: it became a direct import after the SecretRef type fix. - Refine the --platform-source-secret flag docstring and the matching values.yaml comment: drop identity.pub from the SSH required-keys list (Flux source-controller only consumes identity + known_hosts) and mention bearerToken as an alternative to username+password for Git over HTTPS. - Note in values.yaml that the Secret must exist in cozy-system before the operator reconciles; otherwise source-controller reports a failed condition until the Secret is created. Signed-off-by: Ivan Okhotnikov <ivan.okhotnikov@aenix.io> |
||
|
|
0427b184fa |
feat(operator): support private OCI registry credentials for platform source
Added --platform-source-secret flag to cozystack-operator that allows
specifying a Secret name for registry authentication. This enables using
private OCI registries and Git repositories as platform sources.
Changes:
- cmd/cozystack-operator/main.go:
- Add --platform-source-secret flag
- Pass secretName to generateOCIRepository() and generateGitRepository()
- Set spec.secretRef in generated resources when secret is specified
- packages/core/installer/values.yaml:
- Add cozystackOperator.platformSourceSecret parameter
- packages/core/installer/templates/cozystack-operator.yaml:
- Pass --platform-source-secret flag to operator container
Usage:
1. Create registry credentials secret in cozy-system namespace:
kubectl create secret docker-registry my-registry-creds \
--namespace cozy-system \
--docker-server=registry.example.com \
--docker-username=user \
--docker-password=token
2. Configure Helm values:
cozystackOperator:
platformSourceUrl: 'oci://registry.example.com/repo/packages'
platformSourceSecret: 'my-registry-creds'
Closes #2455
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
|
||
|
|
fd6e121b28 |
fix(platform): make *-rd sources depend on cozystack-engine
Every *-application PackageSource that ships a system/*-rd component renders an ApplicationDefinition (cozystack.io/v1alpha1). That CRD is installed by the application-definition-crd component of cozystack-engine. Without an explicit dependsOn edge, a fresh install where Flux reconciles a *-application source before cozystack-engine fails to render with `no matches for kind "ApplicationDefinition" in version "cozystack.io/v1alpha1"`, which cascades into a broad install timeout unrelated to whatever change triggered the reconcile. Add `- cozystack.cozystack-engine` to the dependsOn of every source that ships a system/*-rd component; gateway-application already carried the edge for the TenantGateway CRD. Add a generic invariant test that fails if any such source is missing the edge, so future sources cannot reintroduce the race. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
ee853b00bd |
fix(platform): order backupstrategy-controller after cozystack-basics
backupstrategy-controller creates the cozy-backups Bucket into the tenant-root namespace, which cozystack-basics provisions. On a fresh install the Bucket patch can race namespace creation and fail with `namespaces "tenant-root" not found`, which then races the install wait deadline and fails the run — on changes that have nothing to do with backup ordering. Add the cozystack-basics dependsOn edge so the target namespace exists first, pinned with a helm-unittest regression guard. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
bf92f3f3b2 |
chore(kubernetes): make migration 45 executable and drop tracking-PR refs
Set the executable bit on migration 45 to match its siblings (44, 46). Replace "PR #2610" references in migrations 45/46 with a description of the change. Correct migration 45's comment: the KubeadmConfig children CAPI spawns from the template are owned by their Machine (not Helm), so they carry no app.kubernetes.io/managed-by=Helm label, are not matched by the selector, and are never at risk of Helm pruning. Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
de44ecf15a |
test(platform): enforce go-forward version stamps route through the helper
The helper header and run-migrations.sh claimed every migration-driven stamp routes through the helper, but migrations 1-41 still stamp the cozystack-version ConfigMap label-less and are backfilled by migration 42. Scope the comments to go-forward stamps (migration 42 onward and the bootstrap path), and add a unittest asserting no migration numbered >= 42 nor run-migrations.sh stamps cozystack-version without the helper, so the no-drift invariant is enforced rather than aspirational. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
985b4b0954 |
fix(platform): guard stamp_cozystack_version against a missing version
Mirror render_cozystack_version_manifest's argument guard in stamp_cozystack_version so a missing version aborts before the kubectl pipe. Without it, a render failure in a shell without pipefail would leave the pipeline exit status to kubectl, which would apply empty input. Add a unittest covering the guard. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
3aabb0cb7c |
fix(platform): stamp the no-delete label on bootstrap cozystack-version
The bootstrap path that creates cozystack-version when the ConfigMap is absent applied it without the platform.cozystack.io/no-delete label, leaving it unguarded by the cozystack-no-delete-guardrail ValidatingAdmissionPolicy. Route it through the shared helper so the bootstrap ConfigMap carries the label like every migration-driven stamp. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
b199b5e438 |
refactor(platform): stamp cozystack-version via shared helper in migrations
Migrations 42, 43 and 44 each carried a copy-pasted heredoc that applied the labeled cozystack-version ConfigMap. Source the shared helper and call it instead, so the labeled manifest has a single definition and future migrations cannot reintroduce a label-less apply. Behavior-preserving: the rendered manifest is byte-identical to the previous heredocs. Migration 42 keeps its MIGRATION_DRY_RUN path, now building its manifest from the helper's pure renderer. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
50344f1ab9 |
refactor(platform): add shared cozystack-version stamp helper
Extract the labeled cozystack-version ConfigMap stamp into a sourced shell helper so migrations cannot drift back to a label-less apply that strips the platform.cozystack.io/no-delete label. render_cozystack_version_manifest emits the manifest on stdout (pure, unit-testable); stamp_cozystack_version renders and applies it. The namespace defaults to cozy-system and honors $NAMESPACE. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
2494843599 |
chore(testing): refresh e2e-sandbox tools and base to current upstream (#3010)
## What this PR does Rebuilds the e2e test sandbox on current upstream to clear the published advisories (containerd, helm, go-git, moby/spdystream, Go stdlib) carried by the stale tool binaries and base layers. - base: `ubuntu:22.04` → `24.04`; `netcat` → `netcat-openbsd` (on 24.04 `netcat` is a candidate-less virtual package, so the concrete provider must be named) - kubectl: `1.33.2` → `1.33.13` — stays on the 1.33 line that the e2e control plane pins at `v1.33.12` (`hack/e2e-prepare-cluster.bats`), so client/server skew is unchanged - talosctl: `1.10.4` → `1.13.5` — aligns the client with the v1.13.x Talos nodes the sandbox provisions - helm: `3.18.3` → `3.21.2` (latest helm 3) — the e2e flow drives the v3 CLI, so it stays on the 3.x line rather than moving to the breaking 4.x - yq: `4.44.3` → `4.53.3` - `values.yaml`: re-pinned to the rebuilt multi-arch digest (`linux/amd64` + `linux/arm64`) `crust-gather` and `cozyhr` already track their latest releases; `flux` and `mc` install the current release at build time. The built image's tool versions were verified in place (kubectl `v1.33.13`, talosctl `v1.13.5`, helm `v3.21.2`, yq `v4.53.3`, flux `2.8.8`, ubuntu `24.04`). Closes #2891 ### Release note ```release-note chore(testing): refresh e2e-sandbox tools and base (ubuntu 24.04, kubectl 1.33.13, talosctl 1.13.5, helm 3.21.2) ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated the e2e sandbox image to a new fixed digest for more consistent test runs. * Refreshed the sandbox build environment (Ubuntu 24.04) and upgraded tooling (Kubernetes, Talos, Helm, and yq) to newer stable versions. * Improved OS package setup by switching from `netcat` to `netcat-openbsd` and consolidating apt cleanup to reduce image size. * **Documentation** * Updated e2e testing guidance: removed the recommendation to pin the management cluster Kubernetes version, and added guidance to snapshot/restore cluster state between test groups. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
bebf4db0df |
chore(build): refresh first-party Alpine runtime bases to 3.24 (#3009)
## What this PR does Moves the pinned Alpine runtime bases of the first-party images forward to `alpine:3.24`, clearing the published `musl` / `zlib` advisories carried by the stale layers. | image | base | | --- | --- | | mariadb-backup | 3.20 → 3.24 | | cozystack-operator | 3.22 → 3.24 | | platform-migrations | 3.22 → 3.24 | | grafana-dashboards | 3.22 → 3.24 | | kubeovn-webhook | 3.21.3 → 3.24 | | redis-operator | latest → 3.24 | Each runtime `apk` set was verified to resolve on `alpine:3.24`. These images rebuild on the release asset pipeline, which re-pins the digests in `values.yaml` on the next build — matching how the rest of the ~30 first-party images pick up the shared base (the issue's "rebuild and run the standard test pipeline"). **Scope notes:** - The build-only `alpine AS source` stages (keycloak-operator, objectstorage-controller) are left as-is: their final images are distroless, so no Alpine layer ships. - The s3manager base is owned by the separate bucket bump (#2887), and kilo's base moves in its own image rebuild (#2898). - **Side effect for platform-migrations**: its `kubectl` / `helm` come from the Alpine repo, so the base bump moves them `1.33 → 1.36` and `3.18 → 3.19`. Both are current upstream stable and in skew with the modern control plane for the migration scripts' operations (apply / get / label / helm release ops). Closes #2902 ### Release note ```release-note chore(images): refresh first-party Alpine runtime bases to 3.24 to clear the musl/zlib advisories ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated Alpine Linux base images across multiple application containers to version 3.24 (including backup utilities, platform/operator components, monitoring dashboards, and networking/webhook components) to improve security and build consistency. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
51c5273d0b |
feat(platform): ACME wildcard certificate on the default ingress-nginx path (#2988)
## What this PR does On the default ingress-nginx path (`gateway.enabled=false`) every published hostname still minted its own per-host ACME certificate via ingress-shim, even with a DNS-01 solver configured. That hits the Let's Encrypt rate limit (50 certs per registered domain per week) once a deployment exceeds ~50 endpoints. The Gateway API path already issues a single per-apex wildcard in DNS-01 mode; this brings the ingress-nginx path to parity. It implements the remaining ingress-nginx gap that #2400 was narrowed to in its triage comment (DNS-01 is already multi-provider, and the Gateway path already issues wildcards). The new `publishing.certificates.wildcard` toggle is opt-in and OFF by default. When set to `true` with `solver=dns01` and no operator-provided wildcard Secret, the platform issues one shared wildcard `Certificate` for `<root-host>` + `*.<root-host>` and serves the resulting Secret as the publishing controller's `--default-ssl-certificate`. The system service Ingresses (dashboard, grafana, keycloak, harbor, …) then stop requesting a per-host cert. How it works: - `core/platform` `apps.yaml` computes the effective wildcard Secret name and a new `_cluster.wildcard-issue` signal. An operator-provided `wildcardSecretName` (the BYO path) always wins and never triggers issuance; HTTP-01 cannot issue wildcards; and when Gateway API is enabled the TenantGateway controller issues the wildcard instead — so all three are excluded from auto-issuance. - `extra/ingress` renders the wildcard `Certificate` only on the publishing controller (`Release.Namespace == expose-ingress`), because the Secret must be same-namespace for ingress-nginx to read it. The Certificate references the DNS-01 `ClusterIssuer` that `cert-manager-issuers` already renders in `dns01` mode, so no per-tenant Issuer is minted. This reuses the existing wildcard-secret consumption path, so no per-service Ingress template changes were needed — only the issuance side is new. Why opt-in (default off): the chosen wildcard Secret name rides the `_cluster` channel, which every child tenant inherits verbatim, so this is not root-tenant scoped. A single-label wildcard `*.<root-host>` covers `<service>.<root-host>` but not a custom service host outside it (a keycloak `ingress.host`, harbor `host`, or grafana `host` pointed at another domain) nor a child tenant's nested host (`<service>.<tenant>.<root-host>`). Enabling issuance makes every such service drop its per-host ACME cert and fall back to the default certificate, which does not cover it. Leaving it off by default means a dns01 cluster is never silently switched on upgrade; an operator who enables it accepts the same coverage responsibility as the operator-provided wildcard path, which propagates identically. The tradeoff is pinned by contract tests (keycloak custom host, bucket nested child-tenant host). Per-tenant wildcards remain on the Gateway path and are tracked separately. Docs: cozystack/website#588 Closes #2400. Part of #2811. ### Screenshots N/A — no UI changes. ### Release note ```release-note feat(platform): add opt-in shared wildcard certificate issuance on the default ingress-nginx path via publishing.certificates.wildcard (default false). When enabled with a DNS-01 solver, the platform issues one *.<root-host> wildcard for system services instead of a per-host ACME certificate, avoiding Let's Encrypt rate limits at scale. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added automatic wildcard certificate issuance support for DNS-01 ACME solver. * New `certificates.wildcard` configuration option to enable shared wildcard TLS certificates. * Ingress rules now properly utilize wildcard certificates when enabled. * **Tests** * Added comprehensive test suites validating wildcard certificate generation and ingress integration across multiple scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
553c0be708 |
chore(mongodb): bump psmdb-operator and CR to 1.22.0 (#2968)
## What this PR does Bumps the Percona Operator for MongoDB stack from 1.21.x to 1.22.0 (latest upstream release), picking up the published security fixes in the operator image that the issue enumerates. Two coupled changes: - `system/mongodb-operator`: re-vendors the upstream psmdb-operator chart 1.21.2 → 1.22.0 via `make update` (operator image → `percona/percona-server-mongodb-operator:1.22.0`). The vendored tree is byte-for-byte identical to a fresh upstream pull. - `apps/mongodb`: bumps the managed CR `spec.crVersion` 1.21.1 → 1.22.0 so the platform tracks the deployed operator instead of running at the N-1 skew, plus the matching helm-unittest assertion. The 1.22.0 CRD diff is additive (native MinIO backup storage, storage scaling, `hookScript`, `PBMReady` condition, new `metrics.k8s.io` RBAC that activates under our `watchAllNamespaces: true`). The only schema removal — the restore-spec `replset` string, renamed upstream to `replsetRemapping` — is unused by the cozystack mongodb app (restore template uses `clusterName`/`pitr`/`backupSource` only). Deprecations (PSMDB 6.0, PMM2, `enableVolumeExpansion`) stay backward-compatible. The PBM (`2.11.0`) and PMM (`2.44.1`, disabled by default) sidecars stay compatible with 1.22.0 and are out of scope here. Verified: `helm unittest packages/apps/mongodb` 94/94 pass; `helm template` renders the operator with the new image. **Upgrade note:** Helm v3 does not update CRDs on `helm upgrade`. On existing clusters the 1.22.0 CRD must be applied manually (`kubectl apply --server-side -f .../crds/crd.yaml`); the operator pod itself runs fine against the older CRD until then. Closes #2888 ### Release note ```release-note chore(mongodb): bump psmdb-operator to 1.22.0 and managed CR to crVersion 1.22.0 ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * MongoDB Operator upgraded to version 1.22.0 with enhanced custom resource definitions supporting backup storage configuration, hook scripts, and storage autoscaling capabilities * Added configurable namespace override for flexible operator deployment * Extended RBAC permissions for metrics collection * **Chores** * Kubernetes minimum version requirement updated to 1.32+ * Helm chart version bumped to 1.22.0 * **Documentation** * Expanded CRD upgrade and installation guidance with multiple upgrade strategies <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
16041736f2 |
chore(talos): bump core/talos to Talos v1.13.5 and matchbox v0.11.0 (#3008)
## What this PR does Refreshes the `core/talos` asset/profile layer onto current upstream to clear the published advisories the scanner flagged in the stale pinned images (`libcrypto3` plus the Go-module advisories compiled into the installer). - **profiles**: regenerated by `hack/gen-profiles.sh v1.13.5`, moving the base installer and every system extension to the v1.13.5 line — installer `v1.13.0` → `v1.13.5`; `amdgpu`/`i915`/`drbd`/`zfs` to their `-v1.13.5` builds; `amd-ucode` and the firmware blobs to the `20260519`/`20260512` snapshots. The extension digests come verbatim from the official `ghcr.io/siderolabs/extensions:v1.13.5` `image-digests` manifest. v1.13.5 rebuilds the installer on the Go 1.26.4 toolchain and current runtime (kernel, containerd, runc, OpenSSL). - **matchbox**: base image `quay.io/poseidon/matchbox` `v0.10.0` → `v0.11.0`, clearing the `libcrypto3` advisories in the matchbox runtime. `v1.13.5` is the latest stable Talos release (no `v1.14` line yet). `cozy-talos` ships no runtime templates — it drives the node-image / installer / matchbox asset builds. The Talos installer and matchbox images are rebuilt from these sources by the release asset pipeline (the privileged imager build), which also re-pins the generated `matchbox.tag` digest; this change is the source bump. ### Release note ```release-note chore(talos): bump core/talos to Talos v1.13.5 and matchbox v0.11.0 ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Chores * Updated Talos infrastructure components to v1.13.5 * Updated matchbox service to v0.11.0 * Updated system extension firmware and driver components <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b5cc2ae120 |
fix(kubernetes): address review findings on Talos worker bootstrap
- talos-reconcile-job: emit a `---` separator inside the nodeGroups range so a tenant with multiple node groups renders valid multi-document YAML instead of two Jobs concatenated into one doc - talos-reconcile + bootstrap-token jobs: add a restricted securityContext (drop ALL caps, no privilege escalation, read-only root fs) and CPU/memory requests+limits; drop the unused management SA token mount on the bootstrap-token job, which only talks to the tenant apiserver via the mounted admin kubeconfig - migration 44: list Helm-managed resources by the app.kubernetes.io/managed-by=Helm label, not meta.helm.sh/release-name (an annotation, so it never matched as a --selector — the keep annotation was silently never applied) - cluster.yaml: fail fast on a malformed nodeHealthCheck.maxUnhealthy rather than silently coercing it to 0 via `| int` - config: make helm-install-disable-wait parsing genuinely case-insensitive (EqualFold + trim) to match its documented contract - capi-providers bootstrap/cpprovider Makefiles: guard the patches/*.diff loop against an empty glob Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
cc9485e425 |
fix(platform): pre-Talos-rollover migrations to protect existing tenants
Migration 44 pins Helm-managed KubeadmConfigTemplates with resource-policy=keep so Helm cannot prune them mid-rollover; migration 45 bumps tenants on spec.version v1.30 to v1.31 ahead of the Talos support-matrix guard. Both stamp cozystack-version with its deletion-protection label. targetVersion is bumped to 46. Co-authored-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: IvanHunters <xorokhotnikov@gmail.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
381edde331 |
chore(build): refresh first-party Alpine runtime bases to 3.24
Moves the pinned Alpine runtime bases of the first-party images forward to alpine:3.24 to clear the published musl / zlib advisories carried by the stale layers: - mariadb-backup: 3.20 -> 3.24 - cozystack-operator: 3.22 -> 3.24 - platform-migrations: 3.22 -> 3.24 - grafana-dashboards: 3.22 -> 3.24 - kubeovn-webhook: 3.21.3 -> 3.24 (also drops the over-specific patch pin in favour of the minor-float convention used elsewhere) - redis-operator: latest -> 3.24 (pins the previously-floating runtime base) These rebuild on the release asset pipeline; the digests pinned in values.yaml are re-pinned there on the next build, matching how the rest of the ~30 first-party images pick up the shared base. Side effect for platform-migrations: its kubectl / helm come from the Alpine repo, so the base bump moves them 1.33 -> 1.36 and 3.18 -> 3.19. Both are current upstream stable and compatible with the management cluster; for the cozystack migration scripts (apply / get / label / helm release ops) the newer kubectl is in skew with the modern control plane. The build-only `alpine AS source` stages (keycloak-operator, objectstorage-controller) are left as-is: their final images are distroless, so no Alpine layer ships. The s3manager base is owned by the separate bucket bump, and kilo's base moves in its own image rebuild. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
e3ed42a608 |
chore(testing): refresh e2e-sandbox tools and base to current upstream
Rebuilds the e2e test sandbox on current upstream to clear the published advisories (containerd, helm, go-git, moby/spdystream, Go stdlib) carried by the stale tool binaries and base layers: - base: ubuntu 22.04 -> 24.04; netcat -> netcat-openbsd, since `netcat` is a candidate-less virtual package on 24.04 - kubectl: 1.33.2 -> 1.33.13, staying on the 1.33 line that the e2e control plane pins at v1.33.12, so client/server skew is unchanged - talosctl: 1.10.4 -> 1.13.5, aligning the client with the v1.13.x Talos nodes the sandbox provisions - helm: 3.18.3 -> 3.21.2 (latest helm 3); the e2e flow drives the v3 CLI, so it stays on the 3.x line rather than moving to the breaking 4.x - yq: 4.44.3 -> 4.53.3 - values.yaml: re-pinned to the rebuilt multi-arch digest crust-gather and cozyhr already track their latest releases; flux and mc install the current release at build time. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
cac799518c |
chore(talos): bump core/talos to Talos v1.13.5 and matchbox v0.11.0
Refreshes the core/talos asset/profile layer onto current upstream to clear the published advisories the scanner flagged in the stale pinned images. - profiles: regenerated by hack/gen-profiles.sh v1.13.5, moving the base installer and every system extension to the v1.13.5 line (installer v1.13.0 -> v1.13.5; amdgpu/i915/drbd/zfs to their -v1.13.5 builds; amd-ucode and the firmware blobs to the 20260519/20260512 snapshots). v1.13.5 rebuilds the installer on the Go 1.26.4 toolchain and current runtime (kernel, containerd, runc, OpenSSL), clearing the Go and libcrypto advisories compiled into the stale v1.13.0 installer. - matchbox: base image quay.io/poseidon/matchbox v0.10.0 -> v0.11.0, clearing the libcrypto3 advisories in the matchbox runtime. cozy-talos ships no runtime templates -- it drives the node image / installer / matchbox asset builds. The Talos installer and matchbox images are rebuilt from these sources by the release asset pipeline (the privileged imager build), which also re-pins the generated matchbox digest; this change is the source bump. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
34563ce8c7 |
feat(platform): add opt-in shared wildcard issuance for ingress path
On the default ingress-nginx path (gateway.enabled=false) a DNS-01 solver still minted a per-host ACME certificate for every system service, hitting Let's Encrypt rate limits once a deployment exceeds ~50 endpoints. Add an opt-in publishing.certificates.wildcard toggle (default false) and compute, in apps.yaml, an effective wildcard secret name plus a wildcard-issue signal: when enabled with solver=dns01 and no operator-provided wildcardSecretName, the platform targets one shared wildcard cert (*.<root-host> + <root-host>) instead of per-host certs. Issuance is off by default because a single-label wildcard does not cover a custom service host outside *.<root-host> (e.g. a keycloak ingress.host, harbor host, or grafana host pointed at another domain); enabling it would otherwise switch such a host to the controller's default certificate and serve the wrong certificate. The operator-provided (BYO) name always wins and never triggers issuance; http01 cannot issue wildcards; and the Gateway API path issues its own wildcard via the controller, so all three are excluded. This reuses the existing wildcard-secret-name consumption path, so no per-service ingress template changes are needed. Part of #2811. Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
4a9be4db80 |
chore(maintenance): align remaining alpine/k8s tools-image refs to 1.36.2
Six further references to the alpine/k8s tools image were still pinned at 1.33.4 across the installer, the cozystack-api post-upgrade hook, the vm-disk and vm-instance resize hooks, the vertical-pod-autoscaler CRD-install override, and the e2e cilium leak healer. Move them all to 1.36.2 so the whole repo runs a single current tools-image version (the two digest-pinned refs get the matching multi-arch digest sha256:44ef4942...). Same stale-stdlib remediation as the seaweedfs bump in this PR. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
a730306774 |
chore(packages/core/talos): bump matchbox base image to v0.11.0
Refreshes the matchbox PXE image base from the 2021-era v0.10.0 to v0.11.0, clearing the long-fixed golang.org/x/crypto and x/net versions baked into the old build. v0.11.0 only bumps butane (v0.17->v0.18) and adds darwin arm64 artifacts; no behavior change for Talos PXE boot. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
e9aaee286e |
fix(mongodb-operator): apply CRD updates on upgrade via CreateReplace
psmdb-operator evolves its CRD schema between minor versions; 1.22.0 adds spec.storageScaling/storageAutoscaling, vault and other fields. Helm never upgrades CRDs shipped in crds/, so without an explicit policy the operator binary upgrades on an existing cluster while the live CRD stays behind and the apiserver rejects the new fields. Opt the mongodb-operator PackageSource into CreateReplace, consistent with etcd-operator, and add a platform test pinning the policy. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
e1aaa90ed3 |
Prepare release v1.5.0
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com> |
||
|
|
dc9427285d |
Merge branch 'main' into feat/flux-shard-operator-impl
Resolve go.mod conflict: keep main's go 1.26.4 and golang.org/x/* bumps, and keep gomodules.xyz/jsonpatch/v2 v2.4.0 as a direct dependency (imported by the flux-shard-operator admission webhook) instead of main's indirect entry. go mod tidy leaves go.sum consistent. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
aeaedca4a4 |
ci: consolidated platform & e2e stabilization batch (#2948)
## What Consolidated landing branch for a set of interdependent platform/CI stabilization fixes. It began as the source PRs in the table below (in dependency order) and has since grown with review-driven fixes and a few production-behavior changes — the full current inventory is under **Scope** below. Landing them as one unit lets CI run against the real combined state instead of a fragile bottom-up merge train. ## Source PRs (dependency order) | # | Commit | Source PR | Addresses | |---|--------|-----------|-----------| | 1 | fix(capi): startupProbe on capi-controller-manager | #2946 | capi crashloop during cert provisioning | | 2 | test(e2e): bucket.bats port-forward + S3 client reliability | #2944 | flaky bucket test | | 3 | fix(seaweedfs): restore -lock BucketClass, s3 svc name, drop volumeSizeLimitMB | #2943 | missing bucket/access classes | | 4 | test(e2e): in-cluster Cilium endpoint-leak healer (install + apps) | #2874 | cilium "IP already in use" leak | | 5 | refactor(build): mode=max registry cache + main-only warmer | #2938 | build cache | | 6 | ci(build): isolate each PR build on its own ephemeral runner VM | #2939 | shared-buildkit contention | | 7 | fix(e2e): LINSTOR post-install waits on a single 15m deadline | #2928 | LINSTOR provisioning timeouts | | 8 | test(metallb): assert digest-pinned image form, not version literal | #2873 | brittle metallb assertion | ## Why consolidated These form a dependency DAG (verified from CI logs): e.g. 2943 needs 2946, 2938 needs 2874, 2928 needs 2938/2939, 2873 needs 2928. Tested bottom-up, each lower PR runs with none of the fixes above it and can't go green alone. This branch carries all of them, so CI runs against the real combined state. ## Verification status CI is green on the latest head (`5653c30`): full-suite E2E passes end-to-end and is reproducible (2 of 3 runs on this SHA green). The one red run was a LINSTOR tie-breaker / DRBD-metadata infra flake on the sandbox, unrelated to the diff — a different environmental subsystem failed each run (details in the comments below). The earlier 3-hour crust-gather snapshot hang is fixed in `5653c30`. ## Scope beyond the original 8 The branch has grown past the 8 source PRs above with review-driven fixes and a few production-behavior changes. For reviewer transparency, the full set: **Review fixes (@lexfrei review):** - **B1 (blocker)** — `8f41910` converts the SeaweedFS `-lock`/`-readonly` BucketClasses and the s3 service-name override into `patches/`, wired into `make update`, so a re-vendor no longer drops them. - **FU1** — `cc99230` grants the cilium leak-healer `delete` on pods (both delete remedies were RBAC-forbidden). - **FU3** — `014de3f` adds a unit test for the HelmRelease update conflict-retry path. - **FU4** — `6deddc6` makes the leak-healer refuse a disconnect for any non-terminal owner phase, not just `Running`. - **FU7** — `f3914d2` sets `persist-credentials: false` on the build-cache checkout (SHA-pinning is handled repo-wide in #2849). **Production-behavior changes (not in the table above):** - VPA `updateMode: Auto → Initial` for etcd (`6e9ff90`) and monitoring (`0997105`), plus `vmselect`/`vmstorage` `minAllowed` floors — stops install-time eviction churn. - cozystack-api: `retry.RetryOnConflict` on the Application→HelmRelease update path (`728f676`). **E2E robustness follow-ups:** per-test crust-gather snapshots (`50e5f94`), EtcdBackupSchedule wait (`763b85e`), tenant-node single-deadline wait (`9e43d45`), harbor BucketClaim 10m budget (`d7f244f`), tenant API via LoadBalancer (`9beda4e`), crust-gather pin (`95f42aa`). **Not addressed (by decision):** FU2 — the s3 Service keeps `seaweedfs.name`-based naming via patch (rendered output unchanged; cozystack renders `fullname == seaweedfs`, so it matches the `componentName`/`fullname` siblings in practice). FU6 — fork PR build push is handled by the existing mirror-to-same-repo-branch workflow. ## Relates to #2946 #2944 #2943 #2874 #2938 #2939 #2928 #2873 — if this lands, those can be closed; otherwise they remain the granular per-PR review path. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added CI build cache warming for main branch builds. * Added a best-effort in-cluster Cilium endpoint self-heal watchdog for e2e installs. * Extended SeaweedFS COSI with object-lock support plus separate readonly access. * **Improvements** * Reduced e2e install flakiness with a shared readiness deadline, safer waits, and improved cleanup/diagnostics capture. * Prevented install-time churn by switching VPA update mode to **Initial**. * Added container startup probes and updated SeaweedFS volume sizing. * **Tests** * Improved e2e robustness (etcd backup schedule waiting, digest-pinned image checks, and corrected S3 port-forwarding). <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
95f42aac31 |
fix(e2e-sandbox): pin crust-gather to v0.15.2 (tag needs leading v)
The crust-gather install.sh substitutes the version verbatim into both the release-tag path and the asset filename: releases/download/<ver>/kubectl-crust-gather_<ver>_<target>.tar.gz The release tag is 'v0.15.2', so passing '0.15.2' 404s on the tag path (curl exit 22) and fails the image build. The 'v0.15.2' asset variant exists too, so the leading v satisfies both. Verified HTTP 200. Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
50e5f94132 |
test(e2e): per-test crust-gather snapshots + cleanup-on-exit via cozytest.sh
cozytest.sh runs each .bats as one invocation, exits on the first failing @test, and never invokes bats setup/teardown. Add a single EXIT-trap hook: on failure, snapshot the host AND every nested tenant cluster (tenantkubeconfig-*) with crust-gather into per-app dirs BEFORE cleanup — so each failure is independently inspectable (crust-gather serve) instead of one end-of-suite host-only dump; then ALWAYS run the file's cozy_cleanup() if defined. run-kubernetes.sh gains cozy_cleanup() (delete tenant clusters, wait for PVCs to free): a failed kubernetes test otherwise leaves tens of GiB of worker-VM PVCs in tenant-test, exhausting the shared tenant-quota and cascade-failing every storage-heavy app after it. The kubernetes .bats now source run-kubernetes.sh at file scope so the parent-shell trap can reach the hook. Ship pinned crust-gather 0.15.2 in the e2e-sandbox image; fold per-test snapshots into cozyreport.tgz. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
d3958c5bcb |
refactor(build): mode=max registry cache with a main-only warmer (#2938)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> |
||
|
|
7f994f2d75 |
fix(platform): avoid SIGPIPE in migration 44 drain check
head -n 1 closing the pipe early under pipefail aborts the migration once the HelmRelease name list outgrows the pipe buffer; count with wc -l instead. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io> |
||
|
|
6e219c241b |
feat(platform,gateway): distribute operator wildcard secret name
Add publishing.certificates.wildcardSecretName to the platform chart and propagate it through the cozystack-values channel as _cluster.wildcard-secret-name. Only the Secret NAME travels — the cert/key material is never broadcast, unlike the public kube-root-ca that already rides this channel. When the key is set, the gateway chart switches the rendered TenantGateway to certMode=existingSecret with a wildcardSecretRef pointing at the operator-supplied Secret, and skips the ACME solver/provider/issuer validations that no longer apply. Wildcard mode takes precedence over solver=dns01. TLS-passthrough services and attached namespaces continue to render unchanged. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
1d747b3d8d |
feat(platform): auto-wire KubeVirt permittedHostDevices and HostDevices feature gate when gpu-operator is enabled (#2768)
Closes #2765. ## What this PR does Makes the passthrough and vGPU variants of `cozystack.gpu-operator` produce a KubeVirt CR that is immediately usable, without an out-of-band `kubectl patch`. Today the gpu-operator stack reports green, `nvidia.com/<model>` shows up under node `.status.allocatable`, but every `VirtualMachine` referencing the resource via `domain.devices.hostDevices` is rejected by the admission webhook because `HostDevices` is not in `developerConfiguration.featureGates` and the resource is not listed under `permittedHostDevices`. That manual reconciliation is now driven by the platform. The change has three parts. `packages/system/kubevirt` now parameterizes the KubeVirt CR template. `developerConfiguration.featureGates` keeps its existing static defaults and appends `.Values.extraFeatureGates`; `spec.configuration.permittedHostDevices` renders verbatim from `.Values.permittedHostDevices` when set; `spec.configuration.mediatedDevicesConfiguration` does the same. Defaults stay empty so the rendered CR is byte-identical to the previous template when no overrides are supplied. The chart's `make update` target re-applies the four sed transforms after fetching the upstream CR, so refreshing the KubeVirt release version stays a single command. `packages/core/platform` adds two defaults tables under `files/`. `gpu-passthrough-defaults.yaml` covers Hopper (H100/H200), Ada Lovelace (L4/L40/L40S), Ampere (A100 PCIe/SXM, A40, A30, A10), Turing (T4) and Volta (V100/V100S); PCI vendor:device pairs are stable, `resourceName` slugs follow the `<arch>_<model>_<form>_<mem>` convention `nvidia-sandbox-device-plugin` generates as of v25.x, and `externalResourceProvider: true` is set everywhere because those resources are advertised by the sandbox plugin rather than KubeVirt's in-tree device plugin. `gpu-vgpu-defaults.yaml` covers the Pascal–Ampere mdev profile range (A100-40C/80C, A40-24Q/48Q, A30-24C, A10-24Q, V100D-32C, T4-16Q) — the same family span the upstream `vgpu-device-manager` walks `/sys/class/mdev_bus/` for. Ada Lovelace and Blackwell SR-IOV vGPU is intentionally out of scope for the default table and is expected to be advertised via the user-override hook described below. `templates/bundles/iaas.yaml` now selects between the two tables via a new `bundles.iaas.gpuOperatorVariant` knob (`default` for passthrough, `vgpu` for mediated; default is passthrough). The same variant flows through to the gpu-operator Package CR, replacing the previous always-default call. When the package is enabled, the platform injects `HostDevices` into `extraFeatureGates` plus the chosen defaults into `permittedHostDevices` for the kubevirt component's values. Operators extend or replace via top-level platform values: ```yaml gpu: replaceDefaults: false # append (default) vs. wipe-and-replace permittedHostDevices: pciHostDevices: [] # cluster-specific PCI passthrough entries mediatedDevices: [] # cluster-specific vGPU entries mediatedDevicesConfiguration: {} # extra mediated-device types / per-node mappings ``` `docs/gpu-vgpu.md` is updated to drop the manual `kubectl patch` step and describe the auto-wiring contract, the per-variant defaults, the escape hatches, and the sandbox-device-plugin `resourceName` slug convention so an operator whose node publishes a different name knows where to override. ## Test coverage `helm-unittest` covers eight branches in `packages/core/platform/tests/bundles_gpu_kubevirt_wiring_test.yaml`: gpu-operator disabled emits no extras; gpu-operator enabled at default variant emits the HostDevices gate plus `pciHostDevices`; vgpu variant switches to `mediatedDevices` plus `mediatedDevicesConfiguration`; user overrides append by default; `replaceDefaults: true` wipes the platform table; an unknown variant fails fast with a descriptive error. `packages/system/kubevirt/tests/kubevirt_cr_test.yaml` pins the empty-values render and the three populated shapes at the CR template level. `make helm-unit-tests` passes locally on the full repo. ### Release note ```release-note feat(platform): the KubeVirt CR is now auto-configured for GPU host-device passthrough (HostDevices feature gate plus permittedHostDevices populated from NVIDIA defaults) whenever cozystack.gpu-operator is in bundles.enabledPackages. Operators no longer need a manual kubectl patch to make VMs schedule against a GPU. The variant is selected via bundles.iaas.gpuOperatorVariant (default: passthrough; vgpu: mediated). Extend or replace the NVIDIA defaults via .gpu.permittedHostDevices and .gpu.replaceDefaults. **Upgrade note:** the bundle now OWNS spec.configuration.permittedHostDevices on the KubeVirt CR. If you previously hand-edited it (kubectl edit kubevirt), the first reconcile after upgrade overwrites your manual entries with the rendered NVIDIA default table. Before upgrading, move any custom entries into .gpu.permittedHostDevices (set .gpu.replaceDefaults: true to keep only your list) and verify each resourceName against what your nodes advertise (kubectl describe node <node> | grep nvidia.com/) — the default table now carries the exact slug the sandbox device plugin generates, which upper-cases the card's PCI-IDs name and keeps the Tesla/GL tokens (e.g. a Tesla T4 is nvidia.com/TU104GL_TESLA_T4), but a different plugin build can publish a different string; a mismatch makes the admission webhook reject GPU VMs on their next restart or migration. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Bundle-managed GPU Operator with selectable passthrough vs vGPU variants; platform now injects GPU settings into KubeVirt and forwards vGPU manager overrides when enabled. * Default platform mappings for PCI passthrough and vGPU mediated-device resources added. * **Documentation** * Expanded GPU/vGPU deployment guide with bundle-managed vs manual guidance, wiring/precedence rules, resource-name conventions, and verification steps. * **Tests** * New and expanded tests for bundle→KubeVirt wiring, variant behaviors, default/override merging, and idempotent template parameterization. * **Chores** * Added Helm test target and hardened KubeVirt template update/idempotency checks. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |