From 7eca74675a7e55e566f70975ca2ee91d94b22b72 Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Thu, 26 Mar 2026 19:06:04 +0500 Subject: [PATCH 01/36] [platform] Added resource-policy to keep installed packages Signed-off-by: Myasnikov Daniil (cherry picked from commit a243f3d72a333caa52cb72ab7a9c8ea4e8b75af4) --- packages/core/platform/templates/_helpers.tpl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/core/platform/templates/_helpers.tpl b/packages/core/platform/templates/_helpers.tpl index 684ee812..4bc02429 100644 --- a/packages/core/platform/templates/_helpers.tpl +++ b/packages/core/platform/templates/_helpers.tpl @@ -13,6 +13,8 @@ apiVersion: cozystack.io/v1alpha1 kind: Package metadata: name: {{ $name }} + annotations: + helm.sh/resource-policy: keep spec: variant: {{ $variant }} {{- if $components }} @@ -40,6 +42,8 @@ apiVersion: cozystack.io/v1alpha1 kind: Package metadata: name: {{ $name }} + annotations: + helm.sh/resource-policy: keep spec: variant: {{ $variant }} {{- end }} From 56e6178685189f34af884f67087e2a664f7ad0db Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Sat, 28 Mar 2026 08:31:56 +0100 Subject: [PATCH 02/36] fix(linstor): preserve TCP ports during toggle-disk operations Update fix-duplicate-tcp-ports patch to preserve existing TCP ports when DrbdRscData is recreated during toggle-disk operations. Without this, removeLayerData() frees ports and ensureStackDataExists() may allocate different ones, causing port mismatches between controller and satellites if the satellite misses the update. Also add dh_strip_nondeterminism override in Dockerfile to fix build failures on some JAR files. Upstream: https://github.com/LINBIT/linstor-server/pull/476#issuecomment-4147527442 Co-Authored-By: Claude Signed-off-by: Andrei Kvapil (cherry picked from commit 812d4138bba92aec7c51c955cab1696cbbd8bcb3) --- .../linstor/images/piraeus-server/Dockerfile | 2 + .../images/piraeus-server/patches/README.md | 4 +- .../patches/fix-duplicate-tcp-ports.diff | 165 ++++++++++++------ 3 files changed, 112 insertions(+), 59 deletions(-) diff --git a/packages/system/linstor/images/piraeus-server/Dockerfile b/packages/system/linstor/images/piraeus-server/Dockerfile index 049a3f47..f93e494f 100644 --- a/packages/system/linstor/images/piraeus-server/Dockerfile +++ b/packages/system/linstor/images/piraeus-server/Dockerfile @@ -61,6 +61,8 @@ RUN test -d .gradlehome && echo ".gradlehome found in tarball" || (echo ".gradle # Build DEB packages from tarball # Override GRADLE_FLAGS to remove --offline flag, allowing Gradle to download missing dependencies RUN sed -i 's/GRADLE_FLAGS = --offline/GRADLE_FLAGS =/' debian/rules || true +# Skip dh_strip_nondeterminism to avoid failures on some JAR files (logback-core) +RUN printf '\noverride_dh_strip_nondeterminism:\n\ttrue\n' >> debian/rules RUN LD_LIBRARY_PATH='' dpkg-buildpackage -rfakeroot -b -uc # Copy built .deb packages to a location accessible from final image diff --git a/packages/system/linstor/images/piraeus-server/patches/README.md b/packages/system/linstor/images/piraeus-server/patches/README.md index 5ee29318..558fdfeb 100644 --- a/packages/system/linstor/images/piraeus-server/patches/README.md +++ b/packages/system/linstor/images/piraeus-server/patches/README.md @@ -8,7 +8,7 @@ Custom patches for piraeus-server (linstor-server) v1.32.3. - Upstream: [#475](https://github.com/LINBIT/linstor-server/pull/475) - **force-metadata-check-on-disk-add.diff** — Create metadata during toggle-disk from diskless to diskful - Upstream: [#474](https://github.com/LINBIT/linstor-server/pull/474) -- **fix-duplicate-tcp-ports.diff** — Prevent duplicate TCP ports after toggle-disk operations - - Upstream: [#476](https://github.com/LINBIT/linstor-server/pull/476) +- **fix-duplicate-tcp-ports.diff** — Preserve TCP ports during toggle-disk to prevent port mismatch between controller and satellites + - Upstream: [#476](https://github.com/LINBIT/linstor-server/pull/476) (superseded by this expanded fix) - **skip-adjust-when-device-inaccessible.diff** — Fix resources stuck in StandAlone after reboot, Unknown state race condition, and encrypted resource deletion - Upstream: [#477](https://github.com/LINBIT/linstor-server/pull/477) diff --git a/packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff b/packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff index 07cd9eac..b34a2500 100644 --- a/packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff +++ b/packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff @@ -1,80 +1,131 @@ -From 1250abe99d64a0501795e37d3b6af62410002239 Mon Sep 17 00:00:00 2001 +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil -Date: Mon, 12 Jan 2026 13:44:46 +0100 -Subject: [PATCH] fix(drbd): prevent duplicate TCP ports after toggle-disk - operations +Date: Fri, 28 Mar 2026 13:00:00 +0100 +Subject: [PATCH] fix(drbd): preserve TCP ports during toggle-disk operations -Remove redundant ensureStackDataExists() call with empty payload from -resetStoragePools() method that was causing TCP port conflicts after -toggle-disk operations. +Prevent TCP port mismatches after toggle-disk operations by preserving +existing TCP ports when rebuilding DrbdRscData. Root Cause: ----------- -The resetStoragePools() method, introduced in 2019 (commit 95cc17d0b8), -calls ensureStackDataExists() with an empty LayerPayload. This worked -correctly when TCP ports were stored at RscDfn level. +During toggle-disk operations, removeLayerData() deletes DrbdRscData +(freeing its TCP ports from the number pool), then ensureStackDataExists() +creates new DrbdRscData. Since the payload has no explicit tcpPorts, +the controller allocates new ports from the pool -- which may differ from +the old ports if other resources claimed them in the meantime. -After the TCP port migration to per-node level (commit f754943463, May -2025), this empty payload results in DrbdRscData being created without -TCP ports assigned. The controller then sends a Pojo with an empty port -Set to satellites. +The controller correctly avoids collisions in its own number pool, but +the satellite may miss the update (e.g. during controller restart or +network issues). When this happens, the satellite keeps the old ports +while peers receive the new ones, causing DRBD connection failures +(StandAlone/Connecting state). -On satellites, when DrbdRscData is initialized with an empty port list, -initPorts() uses preferredNewPortsRef from peer resources. Since -SatelliteDynamicNumberPool.tryAllocate() always returns true (no-op), -any port from preferredNewPortsRef is accepted without conflict checking, -leading to duplicate TCP port assignments. - -Impact: -------- -This regression affects toggle-disk operations, particularly: -- Snapshot creation/restore operations -- Manual toggle-disk operations -- Any operation calling resetStoragePools() - -Symptoms include: -- DRBD resources failing to adjust with "port is also used" errors -- Resources stuck in StandAlone or Connecting states -- Multiple resources on the same node using identical TCP ports +Additionally, remove the redundant ensureStackDataExists() call from +resetStoragePools() -- the caller already invokes it with the correct +payload. Solution: --------- -Remove the ensureStackDataExists() call from resetStoragePools() as it -is redundant. The calling code (e.g., CtrlRscToggleDiskApiCallHandler -line 1071) already invokes ensureStackDataExists() with the correct -payload immediately after resetStoragePools(). +1. Add copyDrbdTcpPortsIfExists() to save existing TCP ports into the + LayerPayload before removeLayerData() deletes them. +2. Call it from copyDrbdNodeIdIfExists() (covers both toggle-disk paths) + and from the needsDeactivate path (shared storage pool case). +3. Remove the redundant ensureStackDataExists() from resetStoragePools(). -This fix ensures: -1. resetStoragePools() only resets storage pool assignments -2. Layer data creation with proper TCP ports happens via the caller's - ensureStackDataExists() with correct payload -3. No DrbdRscData objects are created without TCP port assignments - -Related Issues: ---------------- -Fixes #454 - Duplicate TCP ports after backup/restore operations -Related to user reports of resources stuck in StandAlone after node -reboots when toggle-disk or backup operations were in progress. - -Testing: --------- -Verified that: -- Toggle-disk operations no longer create resources without TCP ports -- Backup/restore operations complete without TCP port conflicts -- Resources maintain unique TCP ports across toggle-disk cycles +This ensures the same TCP ports are reused when DrbdRscData is recreated, +eliminating the window for port mismatch between controller and satellites. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- - .../linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java | 2 -- - 1 file changed, 2 deletions(-) + .../controller/CtrlRscToggleDiskApiCallHandler.java | 40 +++++++++++++++++++-- + .../linstor/layer/resource/CtrlRscLayerDataFactory.java | 2 -- + 2 files changed, 38 insertions(+), 4 deletions(-) +diff --git a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java +index ccdb0cee5..b0554c2ec 100644 +--- a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java ++++ b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java +@@ -58,6 +58,7 @@ import com.linbit.linstor.stateflags.StateFlags; + import com.linbit.linstor.storage.StorageException; + import com.linbit.linstor.storage.data.adapter.drbd.DrbdRscData; + import com.linbit.linstor.storage.interfaces.categories.resource.AbsRscLayerObject; ++import com.linbit.linstor.core.types.TcpPortNumber; + import com.linbit.linstor.storage.interfaces.categories.resource.VlmProviderObject; + import com.linbit.linstor.storage.kinds.DeviceLayerKind; + import com.linbit.linstor.storage.kinds.DeviceProviderKind; +@@ -88,6 +89,7 @@ import java.util.LinkedHashMap; + import java.util.List; + import java.util.Map.Entry; + import java.util.Set; ++import java.util.TreeSet; + + import org.reactivestreams.Publisher; + import reactor.core.publisher.Flux; +@@ -587,8 +589,9 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL + + /* + * We also have to remove the currently diskless DrbdRscData and free up the node-id as now we must +- * use the shared resource's node-id ++ * use the shared resource's node-id. We still need to preserve TCP ports though. + */ ++ copyDrbdTcpPortsIfExists(rsc, payload); + } + else + { +@@ -726,7 +729,7 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL + /** + * Although we need to rebuild the layerData as the layerList might have changed, if we do not + * deactivate (i.e. down) the current resource, we need to make sure that deleting DrbdRscData +- * and recreating a new DrbdRscData ends up with the same node-id as before. ++ * and recreating a new DrbdRscData ends up with the same node-id and TCP ports as before. + */ + private void copyDrbdNodeIdIfExists(Resource rsc, LayerPayload payload) throws ImplementationError + { +@@ -743,6 +746,37 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL + DrbdRscData drbdRscData = (DrbdRscData) drbdRscDataSet.iterator().next(); + payload.drbdRsc.nodeId = drbdRscData.getNodeId().value; + } ++ copyDrbdTcpPortsIfExists(rsc, payload); ++ } ++ ++ /** ++ * Preserves existing TCP ports during toggle-disk operations. ++ * ++ * When removeLayerData() deletes DrbdRscData, the TCP ports are freed from the number pool. ++ * If ensureStackDataExists() then allocates different ports, and the satellite misses the update ++ * (e.g. due to controller restart or connectivity issues), the satellite keeps the old ports ++ * while peers get the new ones, causing DRBD connections to fail with StandAlone state. ++ */ ++ private void copyDrbdTcpPortsIfExists(Resource rsc, LayerPayload payload) throws ImplementationError ++ { ++ Set> drbdRscDataSet = LayerRscUtils.getRscDataByLayer( ++ getLayerData(apiCtx, rsc), ++ DeviceLayerKind.DRBD ++ ); ++ if (!drbdRscDataSet.isEmpty()) ++ { ++ DrbdRscData drbdRscData = (DrbdRscData) drbdRscDataSet.iterator().next(); ++ Collection tcpPorts = drbdRscData.getTcpPortList(); ++ if (tcpPorts != null && !tcpPorts.isEmpty()) ++ { ++ Set portInts = new TreeSet<>(); ++ for (TcpPortNumber port : tcpPorts) ++ { ++ portInts.add(port.value); ++ } ++ payload.drbdRsc.tcpPorts = portInts; ++ } ++ } + } + + private List removeLayerData(Resource rscRef) diff --git a/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java b/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java index 3538b380c..4f589145e 100644 --- a/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java +++ b/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java @@ -276,8 +276,6 @@ public class CtrlRscLayerDataFactory - + rscDataToProcess.addAll(rscData.getChildren()); } - @@ -82,6 +133,6 @@ index 3538b380c..4f589145e 100644 } catch (AccessDeniedException exc) { --- +-- 2.39.5 (Apple Git-154) From 982a6508a9668f475ff7ec3b049ad370a4538748 Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Mon, 30 Mar 2026 09:50:08 +0500 Subject: [PATCH 03/36] fix(platform): propagate resource allocation ratios to packages Since the bundle restructure in 2d022e38, the cpuAllocationRatio, memoryAllocationRatio and ephemeralStorageAllocationRatio values from platform values.yaml were no longer propagated to child packages. The old monolithic bundles read these values from the cozystack ConfigMap via lookup and passed them explicitly. After migration to the Package CRD model, the bridge was lost: cozy-lib expects the ratios in _cluster config (via cozystack-values Secret), but platform never wrote them there, so the hardcoded defaults were always used. This commit restores the propagation by: - Adding cpu/memory/ephemeral-storage allocation ratios to the _cluster section in the cozystack-values Secret, so cozy-lib in all managed applications picks them up. - Passing cpuAllocationRatio to the kubevirt Package component, so the KubeVirt CR gets the configured value. Assisted-By: Claude AI Signed-off-by: Kirill Ilin (cherry picked from commit b42a8ed7e4e1cfe4d6b6d455727f61c8926b6b84) --- packages/core/platform/templates/apps.yaml | 3 +++ packages/core/platform/templates/bundles/iaas.yaml | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/core/platform/templates/apps.yaml b/packages/core/platform/templates/apps.yaml index 16ef3c35..7ee0de52 100644 --- a/packages/core/platform/templates/apps.yaml +++ b/packages/core/platform/templates/apps.yaml @@ -36,6 +36,9 @@ stringData: branding: {{- . | toYaml | nindent 8 }} {{- end }} + cpu-allocation-ratio: {{ .Values.resources.cpuAllocationRatio | quote }} + memory-allocation-ratio: {{ .Values.resources.memoryAllocationRatio | quote }} + ephemeral-storage-allocation-ratio: {{ .Values.resources.ephemeralStorageAllocationRatio | quote }} {{- with .Values.scheduling }} scheduling: {{- . | toYaml | nindent 8 }} diff --git a/packages/core/platform/templates/bundles/iaas.yaml b/packages/core/platform/templates/bundles/iaas.yaml index 41dc5181..bd002c16 100644 --- a/packages/core/platform/templates/bundles/iaas.yaml +++ b/packages/core/platform/templates/bundles/iaas.yaml @@ -2,7 +2,11 @@ {{- fail "bundles.iaas.enabled can only be true when bundles.system.variant is 'isp-full' or 'isp-full-generic'" }} {{- end }} {{- if and .Values.bundles.iaas.enabled (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-full-generic")) }} -{{include "cozystack.platform.package.default" (list "cozystack.kubevirt" $) }} +{{- $kubevirtComponents := dict -}} +{{- if .Values.resources.cpuAllocationRatio -}} +{{- $_ := set $kubevirtComponents "kubevirt" (dict "values" (dict "cpuAllocationRatio" (.Values.resources.cpuAllocationRatio | int))) -}} +{{- end -}} +{{include "cozystack.platform.package" (list "cozystack.kubevirt" "default" $ $kubevirtComponents) }} {{include "cozystack.platform.package.default" (list "cozystack.kubevirt-cdi" $) }} {{include "cozystack.platform.package.optional.default" (list "cozystack.gpu-operator" $) }} {{include "cozystack.platform.package.default" (list "cozystack.kamaji" $) }} From 091f21fbd289627f9981fe7684a76799fae8ac6b Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Tue, 31 Mar 2026 00:46:47 +0300 Subject: [PATCH 04/36] feat(postgres): hardcode PostgreSQL 17 for monitoring and add migration Add migration 37 to backfill spec.version=v17 for existing PostgreSQL resources without a version set. Hardcode PostgreSQL 17.7 image in monitoring databases (Grafana and Alerta) to ensure compatibility with monitoring queries that expect PostgreSQL 17 features (pg_stat_checkpointer, updated pg_stat_bgwriter). Signed-off-by: IvanHunters (cherry picked from commit fd1714442e44946a90d8864577e73e942e2ebb8d) --- .../platform/images/migrations/migrations/37 | 43 +++++++++++++++++++ .../templates/alerta/alerta-db.yaml | 1 + .../monitoring/templates/grafana/db.yaml | 1 + 3 files changed, 45 insertions(+) create mode 100755 packages/core/platform/images/migrations/migrations/37 diff --git a/packages/core/platform/images/migrations/migrations/37 b/packages/core/platform/images/migrations/migrations/37 new file mode 100755 index 00000000..b8ff3147 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/37 @@ -0,0 +1,43 @@ +#!/bin/sh +# Migration 37 --> 38 +# Backfill spec.version on postgreses.apps.cozystack.io resources. +# +# Before this migration PostgreSQL had a default version field set to v18. +# This migration sets spec.version to "v17" for any postgres app resource that +# does not already have it set, to ensure compatibility with monitoring +# configurations that are hardcoded to PostgreSQL 17. + +set -euo pipefail + +DEFAULT_VERSION="v17" + +# Skip if the CRD does not exist (postgres was never installed) +if ! kubectl api-resources --api-group=apps.cozystack.io -o name 2>/dev/null | grep -q '^postgreses\.'; then + echo "CRD postgreses.apps.cozystack.io not found, skipping migration" + kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=38 --dry-run=client -o yaml | kubectl apply -f- + exit 0 +fi + +POSTGRESES=$(kubectl get postgreses.apps.cozystack.io -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}') +for resource in $POSTGRESES; do + NS="${resource%%/*}" + APP_NAME="${resource##*/}" + + # Skip if spec.version is already set + CURRENT_VER=$(kubectl get postgreses.apps.cozystack.io -n "$NS" "$APP_NAME" \ + -o jsonpath='{.spec.version}') + if [ -n "$CURRENT_VER" ]; then + echo "SKIP $NS/$APP_NAME: spec.version already set to '$CURRENT_VER'" + continue + fi + + echo "Patching postgres/$APP_NAME in $NS: setting version=$DEFAULT_VERSION" + + kubectl patch postgreses.apps.cozystack.io -n "$NS" "$APP_NAME" --type=merge \ + --patch "{\"spec\":{\"version\":\"${DEFAULT_VERSION}\"}}" +done + +# Stamp version +kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=38 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/system/monitoring/templates/alerta/alerta-db.yaml b/packages/system/monitoring/templates/alerta/alerta-db.yaml index fc03cd31..71df5428 100644 --- a/packages/system/monitoring/templates/alerta/alerta-db.yaml +++ b/packages/system/monitoring/templates/alerta/alerta-db.yaml @@ -5,6 +5,7 @@ metadata: name: alerta-db spec: instances: 2 + imageName: ghcr.io/cloudnative-pg/postgresql:17.7 {{- if .Values._cluster.scheduling }} {{- $rawConstraints := get .Values._cluster.scheduling "globalAppTopologySpreadConstraints" }} {{- if $rawConstraints }} diff --git a/packages/system/monitoring/templates/grafana/db.yaml b/packages/system/monitoring/templates/grafana/db.yaml index b20a8fd8..73d6502e 100644 --- a/packages/system/monitoring/templates/grafana/db.yaml +++ b/packages/system/monitoring/templates/grafana/db.yaml @@ -4,6 +4,7 @@ metadata: name: grafana-db spec: instances: 2 + imageName: ghcr.io/cloudnative-pg/postgresql:17.7 storage: size: {{ .Values.grafana.db.size }} {{- if .Values._cluster.scheduling }} From f6ce580126447bff56b91d8a2d7feeb19913c6cc Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Tue, 31 Mar 2026 09:01:44 +0300 Subject: [PATCH 05/36] feat(postgres): extend v17 hardcode to all system components Add explicit PostgreSQL 17.7 image to Harbor, SeaweedFS, and Keycloak databases to ensure consistent version across all system components. Signed-off-by: IvanHunters (cherry picked from commit 620c8fb3c0f30976666afb830b2c79388a5dc88e) --- packages/system/harbor/templates/database.yaml | 1 + packages/system/keycloak/templates/db.yaml | 1 + packages/system/seaweedfs/templates/database.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/packages/system/harbor/templates/database.yaml b/packages/system/harbor/templates/database.yaml index b1221e1c..02e11faa 100644 --- a/packages/system/harbor/templates/database.yaml +++ b/packages/system/harbor/templates/database.yaml @@ -5,6 +5,7 @@ metadata: name: {{ .Values.harbor.fullnameOverride }}-db spec: instances: {{ .Values.db.replicas }} + imageName: ghcr.io/cloudnative-pg/postgresql:17.7 storage: size: {{ .Values.db.size }} {{- with .Values.db.storageClass }} diff --git a/packages/system/keycloak/templates/db.yaml b/packages/system/keycloak/templates/db.yaml index 1cd98ffc..fb649a43 100644 --- a/packages/system/keycloak/templates/db.yaml +++ b/packages/system/keycloak/templates/db.yaml @@ -4,6 +4,7 @@ metadata: name: keycloak-db spec: instances: 2 + imageName: ghcr.io/cloudnative-pg/postgresql:17.7 storage: size: 20Gi {{- if .Values._cluster.scheduling }} diff --git a/packages/system/seaweedfs/templates/database.yaml b/packages/system/seaweedfs/templates/database.yaml index e78caa89..1c116dd0 100644 --- a/packages/system/seaweedfs/templates/database.yaml +++ b/packages/system/seaweedfs/templates/database.yaml @@ -5,6 +5,7 @@ metadata: name: seaweedfs-db spec: instances: {{ .Values.db.replicas }} + imageName: ghcr.io/cloudnative-pg/postgresql:17.7 storage: size: {{ .Values.db.size }} {{- with .Values.db.storageClass }} From e3336bf6f504f65109948f31db09dbcef9c366d5 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Mon, 30 Mar 2026 20:58:43 +0200 Subject: [PATCH 06/36] fix(linstor): set verify-alg to crc32c to avoid crct10dif unavailability The crct10dif kernel crypto module is no longer available in Talos v1.12.6 (kernel 6.18.18). DRBD auto-verify selects crct10dif by default, causing peer connections to fail with VERIFYAlgNotAvail. Setting verify-alg explicitly to crc32c at the controller level ensures all resources use an available algorithm. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil (cherry picked from commit 30616b73f34198a1fc5bc8069d7a38c705bc72d9) --- packages/system/linstor/templates/cluster.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/system/linstor/templates/cluster.yaml b/packages/system/linstor/templates/cluster.yaml index ba611e17..e45b1dff 100644 --- a/packages/system/linstor/templates/cluster.yaml +++ b/packages/system/linstor/templates/cluster.yaml @@ -20,6 +20,8 @@ spec: - name: DrbdOptions/auto-diskful-allow-cleanup value: {{ .Values.linstor.autoDiskful.allowCleanup | quote }} {{- end }} + - name: DrbdOptions/Net/verify-alg + value: "crc32c" - name: DrbdOptions/Net/connect-int value: "15" - name: DrbdOptions/Net/ping-int From 56f0484eb0783ba0b5451c1b8ea45b95e3e63bb3 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 31 Mar 2026 11:15:40 +0200 Subject: [PATCH 07/36] fix(multus): build custom image with DEL cache fix When CNI ADD never completes, multus DEL fails because delegate plugins require runtime state that only exists after a successful ADD. This creates a deadlock where containerd cannot release sandbox name reservations, permanently blocking pod creation on the affected node. Build a custom multus-cni image with a patch that returns success on DEL when no cache file exists (ADD was never completed). Upstream PR: https://github.com/k8snetworkplumbingwg/multus-cni/pull/1498 Tracking issue: https://github.com/cozystack/cozystack/issues/2310 Co-Authored-By: Claude Signed-off-by: Andrei Kvapil (cherry picked from commit 094b80ae16aef967b8f57167ee4f06c89b4a7d78) --- Makefile | 1 + packages/system/multus/Makefile | 12 +++++ packages/system/multus/images/multus-cni.json | 54 +++++++++++++++++++ .../multus/images/multus-cni/Dockerfile | 25 +++++++++ .../patches/fix-del-without-cache.diff | 49 +++++++++++++++++ .../templates/multus-daemonset-thick.yml | 4 +- 6 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 packages/system/multus/images/multus-cni.json create mode 100644 packages/system/multus/images/multus-cni/Dockerfile create mode 100644 packages/system/multus/images/multus-cni/patches/fix-del-without-cache.diff diff --git a/Makefile b/Makefile index ce6eb7b3..59a55bfb 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ build: build-deps make -C packages/system/dashboard image make -C packages/system/metallb image make -C packages/system/kamaji image + make -C packages/system/multus image make -C packages/system/bucket image make -C packages/system/objectstorage-controller image make -C packages/system/grafana-operator image diff --git a/packages/system/multus/Makefile b/packages/system/multus/Makefile index 9b34606d..399af728 100644 --- a/packages/system/multus/Makefile +++ b/packages/system/multus/Makefile @@ -11,3 +11,15 @@ update: wget -q https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/refs/tags/$(RELEASE)/deployments/multus-daemonset-thick.yml -O templates/multus-daemonset-thick.yml sed -i '/multus-cni/s/snapshot-thick/$(RELEASE)-thick/' templates/multus-daemonset-thick.yml && \ patch --no-backup-if-mismatch -p4 < patches/customize-deployment.patch + $(SED_INPLACE) "/ARG VERSION/ s|=.*|=$(RELEASE)|g" images/multus-cni/Dockerfile + +image: + docker buildx build images/multus-cni \ + --tag $(REGISTRY)/multus-cni:$(call settag,$(TAG)) \ + --cache-from type=registry,ref=$(REGISTRY)/multus-cni:latest \ + --cache-to type=inline \ + --metadata-file images/multus-cni.json \ + $(BUILDX_ARGS) + DIGEST=$$(yq e '."containerimage.digest"' images/multus-cni.json -o json -r) && \ + sed -i "s|image: .*multus-cni.*|image: $(REGISTRY)/multus-cni:$(TAG)@$${DIGEST}|g" templates/multus-daemonset-thick.yml + rm -f images/multus-cni.json diff --git a/packages/system/multus/images/multus-cni.json b/packages/system/multus/images/multus-cni.json new file mode 100644 index 00000000..9848b688 --- /dev/null +++ b/packages/system/multus/images/multus-cni.json @@ -0,0 +1,54 @@ +{ + "buildx.build.provenance": { + "buildType": "https://mobyproject.org/buildkit@v1", + "materials": [ + { + "uri": "pkg:docker/debian@stable-slim?platform=linux%2Famd64", + "digest": { + "sha256": "99fc6d2a0882fcbcdc452948d2d54eab91faafc7db037df82425edcdcf950e1f" + } + }, + { + "uri": "pkg:docker/golang@1.24?platform=linux%2Famd64", + "digest": { + "sha256": "d2d2bc1c84f7e60d7d2438a3836ae7d0c847f4888464e7ec9ba3a1339a1ee804" + } + } + ], + "invocation": { + "configSource": { + "entryPoint": "Dockerfile" + }, + "parameters": { + "frontend": "dockerfile.v0", + "args": { + "label:org.opencontainers.image.source": "https://github.com/cozystack/cozystack" + }, + "locals": [ + { + "name": "context" + }, + { + "name": "dockerfile" + } + ] + }, + "environment": { + "platform": "linux/amd64" + } + } + }, + "buildx.build.ref": "buildkit-builder/buildkit-builder0/84w636wch59pt7uaw1mvwfg2w", + "containerimage.config.digest": "sha256:407e75f5f1122abc86ec12ea1d70b233941f1c431ee03cf9b7cae55d890b9c98", + "containerimage.descriptor": { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "digest": "sha256:4b7d9081bdb9d6608ff9b941364fb3c71bf56a352ca3da1bbe14e6b64e6aa459", + "size": 1098, + "platform": { + "architecture": "amd64", + "os": "linux" + } + }, + "containerimage.digest": "sha256:4b7d9081bdb9d6608ff9b941364fb3c71bf56a352ca3da1bbe14e6b64e6aa459", + "image.name": "ghcr.io/cozystack/cozystack/multus-cni:latest" +} \ No newline at end of file diff --git a/packages/system/multus/images/multus-cni/Dockerfile b/packages/system/multus/images/multus-cni/Dockerfile new file mode 100644 index 00000000..feecbc6c --- /dev/null +++ b/packages/system/multus/images/multus-cni/Dockerfile @@ -0,0 +1,25 @@ +FROM --platform=$BUILDPLATFORM golang:1.24 AS build + +ARG VERSION=v4.2.3 +ARG TARGETPLATFORM + +WORKDIR /usr/src/multus-cni + +RUN curl -sSL https://github.com/k8snetworkplumbingwg/multus-cni/archive/refs/tags/${VERSION}.tar.gz | \ + tar -xzf - --strip=1 + +COPY patches /patches +RUN git init && git config user.email "build@cozystack" && git config user.name "build" && \ + git add -A && git commit -m "init" && git tag ${VERSION} && \ + git apply /patches/*.diff + +RUN ./hack/build-go.sh + +FROM debian:stable-slim +LABEL org.opencontainers.image.source=https://github.com/cozystack/cozystack +COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin +COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE +COPY --from=build /usr/src/multus-cni/bin/cert-approver / +WORKDIR / + +ENTRYPOINT ["/usr/src/multus-cni/bin/multus-daemon"] diff --git a/packages/system/multus/images/multus-cni/patches/fix-del-without-cache.diff b/packages/system/multus/images/multus-cni/patches/fix-del-without-cache.diff new file mode 100644 index 00000000..7595ddb2 --- /dev/null +++ b/packages/system/multus/images/multus-cni/patches/fix-del-without-cache.diff @@ -0,0 +1,49 @@ +diff --git a/pkg/multus/multus.go b/pkg/multus/multus.go +index a7941fe..eff0406 100644 +--- a/pkg/multus/multus.go ++++ b/pkg/multus/multus.go +@@ -954,33 +954,19 @@ func CmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) er + } + + if !useCacheConf { +- // Fetch delegates again if cache is not exist and pod info can be read +- if os.IsNotExist(err) && pod != nil { +- if in.ClusterNetwork != "" { +- _, err = k8s.GetDefaultNetworks(pod, in, kubeClient, nil) +- if err != nil { +- return cmdErr(k8sArgs, "failed to get clusterNetwork/defaultNetworks: %v", err) +- } +- // First delegate is always the master plugin +- in.Delegates[0].MasterPlugin = true +- } +- +- // Get pod annotation and so on +- _, _, err := k8s.TryLoadPodDelegates(pod, in, kubeClient, nil) +- if err != nil { +- if len(in.Delegates) == 0 { +- // No delegate available so send error +- return cmdErr(k8sArgs, "failed to get delegates: %v", err) +- } +- // Get clusterNetwork before, so continue to delete +- logging.Errorf("Multus: failed to get delegates: %v, but continue to delete clusterNetwork", err) +- } +- } else { +- // The options to continue with a delete have been exhausted (cachefile + API query didn't work) +- // We cannot exit with an error as this may cause a sandbox to never get deleted. +- logging.Errorf("Multus: failed to get the cached delegates file: %v, cannot properly delete", err) ++ // If cache does not exist, ADD was never completed for this sandbox. ++ // Attempting DEL with a config reconstructed from the API will fail because ++ // delegate plugins (e.g. kube-ovn) require runtime state that only exists ++ // after a successful ADD (socket paths, cached results, etc.). ++ // Return success to allow containerd to release the sandbox name reservation. ++ if os.IsNotExist(err) { ++ logging.Verbosef("Multus: no cache found for container %s (ADD was never completed), skip DEL", args.ContainerID) + return nil + } ++ // The options to continue with a delete have been exhausted ++ // We cannot exit with an error as this may cause a sandbox to never get deleted. ++ logging.Errorf("Multus: failed to get the cached delegates file: %v, cannot properly delete", err) ++ return nil + } + + // set CNIVersion in delegate CNI config if there is no CNIVersion and multus conf have CNIVersion. diff --git a/packages/system/multus/templates/multus-daemonset-thick.yml b/packages/system/multus/templates/multus-daemonset-thick.yml index 2f00de85..97144d67 100644 --- a/packages/system/multus/templates/multus-daemonset-thick.yml +++ b/packages/system/multus/templates/multus-daemonset-thick.yml @@ -154,7 +154,7 @@ spec: serviceAccountName: multus containers: - name: kube-multus - image: ghcr.io/k8snetworkplumbingwg/multus-cni:v4.2.3-thick + image: ghcr.io/cozystack/cozystack/multus-cni:latest@sha256:4b7d9081bdb9d6608ff9b941364fb3c71bf56a352ca3da1bbe14e6b64e6aa459 command: [ "/usr/src/multus-cni/bin/multus-daemon" ] resources: requests: @@ -200,7 +200,7 @@ spec: fieldPath: spec.nodeName initContainers: - name: install-multus-binary - image: ghcr.io/k8snetworkplumbingwg/multus-cni:v4.2.3-thick + image: ghcr.io/cozystack/cozystack/multus-cni:latest@sha256:4b7d9081bdb9d6608ff9b941364fb3c71bf56a352ca3da1bbe14e6b64e6aa459 command: - "/usr/src/multus-cni/bin/install_multus" - "-d" From 3ab03d3bce4c4a8f76ed64793589f38760b393b8 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 31 Mar 2026 11:15:45 +0200 Subject: [PATCH 08/36] chore: remove build artifact Co-Authored-By: Claude Signed-off-by: Andrei Kvapil (cherry picked from commit 1cd564330cb7137c2994afa1b5ca78a3b062dc08) --- packages/system/multus/images/multus-cni.json | 54 ------------------- 1 file changed, 54 deletions(-) delete mode 100644 packages/system/multus/images/multus-cni.json diff --git a/packages/system/multus/images/multus-cni.json b/packages/system/multus/images/multus-cni.json deleted file mode 100644 index 9848b688..00000000 --- a/packages/system/multus/images/multus-cni.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "buildx.build.provenance": { - "buildType": "https://mobyproject.org/buildkit@v1", - "materials": [ - { - "uri": "pkg:docker/debian@stable-slim?platform=linux%2Famd64", - "digest": { - "sha256": "99fc6d2a0882fcbcdc452948d2d54eab91faafc7db037df82425edcdcf950e1f" - } - }, - { - "uri": "pkg:docker/golang@1.24?platform=linux%2Famd64", - "digest": { - "sha256": "d2d2bc1c84f7e60d7d2438a3836ae7d0c847f4888464e7ec9ba3a1339a1ee804" - } - } - ], - "invocation": { - "configSource": { - "entryPoint": "Dockerfile" - }, - "parameters": { - "frontend": "dockerfile.v0", - "args": { - "label:org.opencontainers.image.source": "https://github.com/cozystack/cozystack" - }, - "locals": [ - { - "name": "context" - }, - { - "name": "dockerfile" - } - ] - }, - "environment": { - "platform": "linux/amd64" - } - } - }, - "buildx.build.ref": "buildkit-builder/buildkit-builder0/84w636wch59pt7uaw1mvwfg2w", - "containerimage.config.digest": "sha256:407e75f5f1122abc86ec12ea1d70b233941f1c431ee03cf9b7cae55d890b9c98", - "containerimage.descriptor": { - "mediaType": "application/vnd.docker.distribution.manifest.v2+json", - "digest": "sha256:4b7d9081bdb9d6608ff9b941364fb3c71bf56a352ca3da1bbe14e6b64e6aa459", - "size": 1098, - "platform": { - "architecture": "amd64", - "os": "linux" - } - }, - "containerimage.digest": "sha256:4b7d9081bdb9d6608ff9b941364fb3c71bf56a352ca3da1bbe14e6b64e6aa459", - "image.name": "ghcr.io/cozystack/cozystack/multus-cni:latest" -} \ No newline at end of file From aefd8b45ffaf3cac30443a0540a16c14f3bb10fa Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 31 Mar 2026 11:30:25 +0200 Subject: [PATCH 09/36] fix(multus): pin master CNI to 05-cilium.conflist Set multusMasterCNI to explicitly use 05-cilium.conflist instead of auto-detecting the first available conflist at boot. This prevents a race condition where multus picks 10-kube-ovn.conflist if it appears before 05-cilium.conflist during node startup, bypassing the Cilium CNI chain entirely. Upstream issue: https://github.com/k8snetworkplumbingwg/multus-cni/issues/1499 Tracking issue: https://github.com/cozystack/cozystack/issues/2310 Co-Authored-By: Claude Signed-off-by: Andrei Kvapil (cherry picked from commit e09cd0f37f7cf399878bcf7c13247a047d4f116f) --- packages/system/multus/templates/multus-daemonset-thick.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/system/multus/templates/multus-daemonset-thick.yml b/packages/system/multus/templates/multus-daemonset-thick.yml index 97144d67..3839ff61 100644 --- a/packages/system/multus/templates/multus-daemonset-thick.yml +++ b/packages/system/multus/templates/multus-daemonset-thick.yml @@ -119,6 +119,7 @@ data: "cniConfigDir": "/host/etc/cni/net.d", "multusAutoconfigDir": "/host/etc/cni/net.d", "multusConfigFile": "auto", + "multusMasterCNI": "05-cilium.conflist", "socketDir": "/host/run/multus/" } --- From d68f56ae196d360c5772854bf0b4f7ed1b9b0937 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 31 Mar 2026 13:14:21 +0200 Subject: [PATCH 10/36] fix(ci): make tags workflow idempotent on re-runs - Remove broken `git push origin HEAD` in detached HEAD state (commit artifacts are already pushed via the "Create release branch" step) - Make subtag push idempotent: use explicit refs/tags/ prefix and tolerate already-existing remote tags after verifying they point to the correct commit Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- .github/workflows/tags.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/tags.yaml b/.github/workflows/tags.yaml index f26ef632..2ef1c8f4 100644 --- a/.github/workflows/tags.yaml +++ b/.github/workflows/tags.yaml @@ -123,7 +123,6 @@ jobs: git config --unset-all http.https://github.com/.extraheader || true git add . git commit -m "Prepare release ${GITHUB_REF#refs/tags/}" -s || echo "No changes to commit" - git push origin HEAD || true # Tag the api/apps/v1alpha1 submodule for pkg.go.dev - name: Tag API submodule @@ -142,7 +141,7 @@ jobs: else git tag "${SUBTAG}" "$TARGET" fi - git push origin "${SUBTAG}" + git push origin "refs/tags/${SUBTAG}" 2>&1 || echo "Tag ${SUBTAG} already exists on remote" # Create or reuse draft release - name: Create / reuse draft release From 52a16dc5183a64bee0e97c6ede76c1580b568a38 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 31 Mar 2026 13:15:47 +0200 Subject: [PATCH 11/36] fix(ci): force-update API subtag on re-runs Always force-create and force-push the API submodule tag so it stays in sync with the main version tag, even when re-tagging. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- .github/workflows/tags.yaml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tags.yaml b/.github/workflows/tags.yaml index 2ef1c8f4..2bbdb7a6 100644 --- a/.github/workflows/tags.yaml +++ b/.github/workflows/tags.yaml @@ -136,12 +136,8 @@ jobs: git config user.email "217169706+cozystack-bot@users.noreply.github.com" git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY} TARGET="$(git rev-parse "${VTAG}^{}")" - if git rev-parse -q --verify "refs/tags/${SUBTAG}" >/dev/null; then - test "$(git rev-list -n1 "${SUBTAG}")" = "$TARGET" - else - git tag "${SUBTAG}" "$TARGET" - fi - git push origin "refs/tags/${SUBTAG}" 2>&1 || echo "Tag ${SUBTAG} already exists on remote" + git tag -f "${SUBTAG}" "$TARGET" + git push -f origin "refs/tags/${SUBTAG}" # Create or reuse draft release - name: Create / reuse draft release From 27638a14b59e27c174f3b3d7ecc14a1f608cebc4 Mon Sep 17 00:00:00 2001 From: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com> Date: Tue, 31 Mar 2026 11:32:30 +0000 Subject: [PATCH 12/36] Prepare release v1.2.1 Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com> --- packages/core/installer/values.yaml | 4 ++-- packages/core/platform/values.yaml | 2 +- packages/core/testing/values.yaml | 2 +- packages/extra/bootbox/images/matchbox.tag | 2 +- packages/extra/seaweedfs/images/objectstorage-sidecar.tag | 2 +- packages/system/backup-controller/values.yaml | 2 +- packages/system/backupstrategy-controller/values.yaml | 2 +- packages/system/cozystack-api/values.yaml | 2 +- packages/system/cozystack-controller/values.yaml | 2 +- packages/system/dashboard/templates/configmap.yaml | 2 +- packages/system/dashboard/values.yaml | 6 +++--- .../system/grafana-operator/images/grafana-dashboards.tag | 2 +- packages/system/kamaji/values.yaml | 4 ++-- packages/system/kubeovn-plunger/values.yaml | 2 +- packages/system/kubeovn-webhook/values.yaml | 2 +- packages/system/lineage-controller-webhook/values.yaml | 2 +- packages/system/linstor/values.yaml | 4 ++-- packages/system/multus/templates/multus-daemonset-thick.yml | 4 ++-- packages/system/objectstorage-controller/values.yaml | 2 +- packages/system/seaweedfs/values.yaml | 2 +- 20 files changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/core/installer/values.yaml b/packages/core/installer/values.yaml index 13c8fd4e..98443211 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -1,9 +1,9 @@ cozystackOperator: # Deployment variant: talos, generic, hosted variant: talos - image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.2.0@sha256:dbc0cbd99dbc2405769bed0d33d5e5d3f53e79e0269ad9ddaa26d9eebbcc6da2 + image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.2.1@sha256:c89352808022944c4791d63cf82cc95d78124ce799d355b60427f317087e8909 platformSourceUrl: 'oci://ghcr.io/cozystack/cozystack/cozystack-packages' - platformSourceRef: 'digest=sha256:c659905000e8279d32965169cde0817e16a5b46f81d305a3606defcd5446abae' + platformSourceRef: 'digest=sha256:d02f211f79d4912f04c21856de078b5b41bb3976d84504d37cf8d9108f7cbec6' # Generic variant configuration (only used when cozystackOperator.variant=generic) cozystack: # Kubernetes API server host (IP only, no protocol/port) diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index 8baa6845..33d3aa20 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -5,7 +5,7 @@ sourceRef: path: / migrations: enabled: false - image: ghcr.io/cozystack/cozystack/platform-migrations:v1.2.0@sha256:3a3d8cfa4323d8023b7c7800d8b552b250bc2de01ca7550fd024a10b12324f6d + image: ghcr.io/cozystack/cozystack/platform-migrations:v1.2.1@sha256:e8fcf006a4451fc0e961455e9b27a61b7103ee49b1a81fe5e4662ffed093fad6 targetVersion: 36 # Bundle deployment configuration bundles: diff --git a/packages/core/testing/values.yaml b/packages/core/testing/values.yaml index 8a1887bd..b17da614 100644 --- a/packages/core/testing/values.yaml +++ b/packages/core/testing/values.yaml @@ -1,2 +1,2 @@ e2e: - image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.2.0@sha256:7964a3e4b11053887be201d62f94138c3a7836c861036c26fb833aa1fcb934e1 + image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.2.1@sha256:7964a3e4b11053887be201d62f94138c3a7836c861036c26fb833aa1fcb934e1 diff --git a/packages/extra/bootbox/images/matchbox.tag b/packages/extra/bootbox/images/matchbox.tag index 67beab8c..0ba57d47 100644 --- a/packages/extra/bootbox/images/matchbox.tag +++ b/packages/extra/bootbox/images/matchbox.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/matchbox:v1.2.0@sha256:313f36db2e8b9dea0cd1a80d73f27a0694e996631d430bbd1b8391fbb9859020 +ghcr.io/cozystack/cozystack/matchbox:v1.2.1@sha256:64685523ba693964f22ce7d7dc545bf7bf96dea34e5ae525a2315614adef4b3d diff --git a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag index b4262b02..bb0702e8 100644 --- a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag +++ b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.0@sha256:b508ca451c6051b64e71cedd6209c80ac0e0bea8aa6bb47e838ec9d99bf4796a +ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.1@sha256:1ad07fb9e96477e5c322f240f6023ceedd8762261d8379ec784dde92797ee206 diff --git a/packages/system/backup-controller/values.yaml b/packages/system/backup-controller/values.yaml index a868fba0..ff5fe774 100644 --- a/packages/system/backup-controller/values.yaml +++ b/packages/system/backup-controller/values.yaml @@ -1,5 +1,5 @@ backupController: - image: "ghcr.io/cozystack/cozystack/backup-controller:v1.2.0@sha256:07bcd89bd725951801870d819f0fc536705069608f0fe5c20617607af6f8a61a" + image: "ghcr.io/cozystack/cozystack/backup-controller:v1.2.1@sha256:6e515cdfe302bb079dafa0f7f302c6ff7b7e9c02a75574880a45e35d5acb1f59" replicas: 2 debug: false metrics: diff --git a/packages/system/backupstrategy-controller/values.yaml b/packages/system/backupstrategy-controller/values.yaml index 39466430..bd3cad8c 100644 --- a/packages/system/backupstrategy-controller/values.yaml +++ b/packages/system/backupstrategy-controller/values.yaml @@ -1,5 +1,5 @@ backupStrategyController: - image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.2.0@sha256:0039ec58f531faf5a5ba3f70993c2c71d74dae4cc8e8e83bd21d2cc2adf10d8a" + image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.2.1@sha256:3544a08bfac2df6dcb1842f73747768b24625c6d6b97230be9a00f80637f0d32" replicas: 2 debug: false metrics: diff --git a/packages/system/cozystack-api/values.yaml b/packages/system/cozystack-api/values.yaml index cafd81c9..ca4cf5e2 100644 --- a/packages/system/cozystack-api/values.yaml +++ b/packages/system/cozystack-api/values.yaml @@ -1,3 +1,3 @@ cozystackAPI: - image: ghcr.io/cozystack/cozystack/cozystack-api:v1.2.0@sha256:b40ba5c87bb625d646dd69075d21dcc1aca18775b3de8e63d04466d60d42cee5 + image: ghcr.io/cozystack/cozystack/cozystack-api:v1.2.1@sha256:afcc72ec5b8ff6eea7fbf927d518f277a7bfead44948f45ed17eda90d47253d5 replicas: 2 diff --git a/packages/system/cozystack-controller/values.yaml b/packages/system/cozystack-controller/values.yaml index ab64e34d..030a4564 100644 --- a/packages/system/cozystack-controller/values.yaml +++ b/packages/system/cozystack-controller/values.yaml @@ -1,4 +1,4 @@ cozystackController: - image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.2.0@sha256:f56c8222411fed890331502939c8d4c5700489e697da39e8e46896cf674cce71 + image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.2.1@sha256:0b1d49b29e8860f13f7afc8d7b6265f36c9d24d6ad7c40f5164535dd8e933c73 debug: false disableTelemetry: false diff --git a/packages/system/dashboard/templates/configmap.yaml b/packages/system/dashboard/templates/configmap.yaml index 69541202..d260c5d9 100644 --- a/packages/system/dashboard/templates/configmap.yaml +++ b/packages/system/dashboard/templates/configmap.yaml @@ -1,6 +1,6 @@ {{- $brandingConfig := .Values._cluster.branding | default dict }} -{{- $tenantText := "v1.2.0" }} +{{- $tenantText := "v1.2.1" }} {{- $footerText := "Cozystack" }} {{- $titleText := "Cozystack Dashboard" }} {{- $logoText := "" }} diff --git a/packages/system/dashboard/values.yaml b/packages/system/dashboard/values.yaml index 5246eb1c..1aa69b09 100644 --- a/packages/system/dashboard/values.yaml +++ b/packages/system/dashboard/values.yaml @@ -1,6 +1,6 @@ openapiUI: - image: ghcr.io/cozystack/cozystack/openapi-ui:v1.2.0@sha256:bf68676a809be8b37ae3ab7416b2ae3185a4efa216b8b09358d916df081d0a3e + image: ghcr.io/cozystack/cozystack/openapi-ui:v1.2.1@sha256:a77c502c4b67fa5f0698f66a82eb2ad30e138f9bd2700aa40a63f73cb86a559d openapiUIK8sBff: - image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.2.0@sha256:89b5c62df0ed9a1984023ba909765e4b60af3f9a61e42ebdd67437d71ebbb645 + image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.2.1@sha256:1b3ea6d4c7dbbe6a8def3b2807fffdfab2ac4afc39d7a846e57dd491fa168f92 tokenProxy: - image: ghcr.io/cozystack/cozystack/token-proxy:v1.2.0@sha256:2e280991e07853ea48f97b0a42946afffa10d03d6a83d41099ed83e6ffc94fdc + image: ghcr.io/cozystack/cozystack/token-proxy:v1.2.1@sha256:2e280991e07853ea48f97b0a42946afffa10d03d6a83d41099ed83e6ffc94fdc diff --git a/packages/system/grafana-operator/images/grafana-dashboards.tag b/packages/system/grafana-operator/images/grafana-dashboards.tag index 60839fd4..08f6bd0b 100644 --- a/packages/system/grafana-operator/images/grafana-dashboards.tag +++ b/packages/system/grafana-operator/images/grafana-dashboards.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/grafana-dashboards:v1.2.0@sha256:2c9aa0b48e2bf6167db198f4d15882bfe51700108edf2e9f6d0942940a2c1204 +ghcr.io/cozystack/cozystack/grafana-dashboards:v1.2.1@sha256:2c9aa0b48e2bf6167db198f4d15882bfe51700108edf2e9f6d0942940a2c1204 diff --git a/packages/system/kamaji/values.yaml b/packages/system/kamaji/values.yaml index 63970e8f..9c6eea0e 100644 --- a/packages/system/kamaji/values.yaml +++ b/packages/system/kamaji/values.yaml @@ -3,7 +3,7 @@ kamaji: deploy: false image: pullPolicy: IfNotPresent - tag: v1.2.0@sha256:071f5f893f16918e6f03c3cf42315a3bce1c7624f10c155f3b3e6dfcd4d60c2a + tag: v1.2.1@sha256:34b9af510bbad3e64ea5cd7afdffa2b31c0751009c467355c8f67f8f9c5b3513 repository: ghcr.io/cozystack/cozystack/kamaji resources: limits: @@ -13,4 +13,4 @@ kamaji: cpu: 100m memory: 100Mi extraArgs: - - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.2.0@sha256:071f5f893f16918e6f03c3cf42315a3bce1c7624f10c155f3b3e6dfcd4d60c2a + - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.2.1@sha256:34b9af510bbad3e64ea5cd7afdffa2b31c0751009c467355c8f67f8f9c5b3513 diff --git a/packages/system/kubeovn-plunger/values.yaml b/packages/system/kubeovn-plunger/values.yaml index c52e699b..7691803e 100644 --- a/packages/system/kubeovn-plunger/values.yaml +++ b/packages/system/kubeovn-plunger/values.yaml @@ -1,4 +1,4 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.2.0@sha256:39f9f0e6ec42afdd91082f7c025699425096244bb10f4e2f7a1d17155ac0cebb +image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.2.1@sha256:71d487b9da211061921c7183d5a1e19a32c64dffe33a3fac0557c4cb25764dfe ovnCentralName: ovn-central diff --git a/packages/system/kubeovn-webhook/values.yaml b/packages/system/kubeovn-webhook/values.yaml index 8db1d9af..df5215d1 100644 --- a/packages/system/kubeovn-webhook/values.yaml +++ b/packages/system/kubeovn-webhook/values.yaml @@ -1,3 +1,3 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.2.0@sha256:e6334c29d3aaf0dea766c88e3e05b53ad623d1bb497b3c836e6f76adade45b29 +image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.2.1@sha256:e6334c29d3aaf0dea766c88e3e05b53ad623d1bb497b3c836e6f76adade45b29 diff --git a/packages/system/lineage-controller-webhook/values.yaml b/packages/system/lineage-controller-webhook/values.yaml index 0776861c..103dbf85 100644 --- a/packages/system/lineage-controller-webhook/values.yaml +++ b/packages/system/lineage-controller-webhook/values.yaml @@ -1,5 +1,5 @@ lineageControllerWebhook: - image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.2.0@sha256:fdbd4ed09c2e825880e84b8668d8bb6e0795da5c51d7c20b9d5eb4abe31d3ae0 + image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.2.1@sha256:0fef727af2ea8d87503ba998d2ec03c12eb432d03b6642a555b81b95fbec4cb0 debug: false localK8sAPIEndpoint: enabled: true diff --git a/packages/system/linstor/values.yaml b/packages/system/linstor/values.yaml index 3986fc4f..b3f13443 100644 --- a/packages/system/linstor/values.yaml +++ b/packages/system/linstor/values.yaml @@ -1,7 +1,7 @@ piraeusServer: image: repository: ghcr.io/cozystack/cozystack/piraeus-server - tag: 1.32.3@sha256:64c91357affc6317d9544fc35b3ff8b2fcb065ad8fc5ed26f7a2fa8ac991a1c1 + tag: 1.32.3@sha256:d78071fdc33220168e3f2ab112a86208be95898b75268a0c62763b8a04e145ad # Talos-specific workarounds (disable for generic Linux like Ubuntu/Debian) talos: enabled: true @@ -13,4 +13,4 @@ linstor: linstorCSI: image: repository: ghcr.io/cozystack/cozystack/linstor-csi - tag: v1.10.5@sha256:585ee6336036cb9e0b2183a6ab3f67e1ccb6d35a1df484d36e734444ee09eb4f + tag: v1.10.5@sha256:e80ae94d8acd8774cb35715b1f103071ae6cd3160f1ebea915e5e6ee49234448 diff --git a/packages/system/multus/templates/multus-daemonset-thick.yml b/packages/system/multus/templates/multus-daemonset-thick.yml index 3839ff61..bedffee3 100644 --- a/packages/system/multus/templates/multus-daemonset-thick.yml +++ b/packages/system/multus/templates/multus-daemonset-thick.yml @@ -155,7 +155,7 @@ spec: serviceAccountName: multus containers: - name: kube-multus - image: ghcr.io/cozystack/cozystack/multus-cni:latest@sha256:4b7d9081bdb9d6608ff9b941364fb3c71bf56a352ca3da1bbe14e6b64e6aa459 + image: ghcr.io/cozystack/cozystack/multus-cni:v1.2.1@sha256:aaf2ed6a5db1ee5acc0cd4f4683ea33570ff922c211bef37d407d6a01427b566 command: [ "/usr/src/multus-cni/bin/multus-daemon" ] resources: requests: @@ -201,7 +201,7 @@ spec: fieldPath: spec.nodeName initContainers: - name: install-multus-binary - image: ghcr.io/cozystack/cozystack/multus-cni:latest@sha256:4b7d9081bdb9d6608ff9b941364fb3c71bf56a352ca3da1bbe14e6b64e6aa459 + image: ghcr.io/cozystack/cozystack/multus-cni:v1.2.1@sha256:aaf2ed6a5db1ee5acc0cd4f4683ea33570ff922c211bef37d407d6a01427b566 command: - "/usr/src/multus-cni/bin/install_multus" - "-d" diff --git a/packages/system/objectstorage-controller/values.yaml b/packages/system/objectstorage-controller/values.yaml index d2a5fc7c..1439e583 100644 --- a/packages/system/objectstorage-controller/values.yaml +++ b/packages/system/objectstorage-controller/values.yaml @@ -1,3 +1,3 @@ objectstorage: controller: - image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.2.0@sha256:05aee8bf8b292150090868163c6bb49962d874ac63858c93f8e130f53dd40b85" + image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.2.1@sha256:036be10f26c4871c63fb63c6a294476289aa063610e7fbbc0dbd21d3cd7bfe5e" diff --git a/packages/system/seaweedfs/values.yaml b/packages/system/seaweedfs/values.yaml index 679203f5..7d598649 100644 --- a/packages/system/seaweedfs/values.yaml +++ b/packages/system/seaweedfs/values.yaml @@ -177,7 +177,7 @@ seaweedfs: bucketClassName: "seaweedfs" region: "" sidecar: - image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.0@sha256:b508ca451c6051b64e71cedd6209c80ac0e0bea8aa6bb47e838ec9d99bf4796a" + image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.1@sha256:1ad07fb9e96477e5c322f240f6023ceedd8762261d8379ec784dde92797ee206" certificates: commonName: "SeaweedFS CA" ipAddresses: [] From a0209327b4ca04dbbd7bccaa413e372ef970f2ea Mon Sep 17 00:00:00 2001 From: mattia-eleuteri Date: Wed, 8 Apr 2026 15:20:58 +0200 Subject: [PATCH 13/36] [virtual-machine] Exclude external VM services from Cilium BPF LB Add service.kubernetes.io/service-proxy-name label to LoadBalancer services when external: true. This prevents Cilium from adding the service to its BPF service map, fixing two issues: 1. Inter-tenant connectivity via public LB IPs: Cilium's kube-proxy replacement DNATs traffic to LB IPs before policy evaluation, causing the CiliumClusterwideNetworkPolicy to block legitimate cross-tenant traffic through public IPs. 2. WholeIP broken on Cilium 1.19+ (#2327): wildcard service drop entries block all ports not declared in the Service spec before traffic reaches cozy-proxy's nftables rules. With this label, Cilium completely ignores the Service. Routing is handled by kube-ovn (via the wholeIP annotation) and MetalLB continues to advertise the IP via L2 unaffected. Tested on Cilium 1.18.6 and 1.19.1: inter-tenant via LB IP works, pod IP isolation preserved, external access unaffected. Signed-off-by: mattia-eleuteri (cherry picked from commit 026b1c78116fbe4d25155050f7a8319783ae6964) --- packages/apps/vm-instance/templates/service.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/apps/vm-instance/templates/service.yaml b/packages/apps/vm-instance/templates/service.yaml index b12f7612..b7e3a420 100644 --- a/packages/apps/vm-instance/templates/service.yaml +++ b/packages/apps/vm-instance/templates/service.yaml @@ -7,6 +7,7 @@ metadata: apps.cozystack.io/user-service: "true" {{- include "virtual-machine.labels" . | nindent 4 }} {{- if .Values.external }} + service.kubernetes.io/service-proxy-name: "cozy-proxy" annotations: networking.cozystack.io/wholeIP: "true" {{- end }} From 3b2d6a6325e00f3814ddfde5b7ea4b08cd42d90e Mon Sep 17 00:00:00 2001 From: mattia-eleuteri Date: Thu, 9 Apr 2026 16:31:24 +0200 Subject: [PATCH 14/36] [monitoring] Fix infra dashboards missing in default variant The default variant deploys monitoring to the cozy-monitoring namespace, but the infra dashboards condition only checked for tenant-root. This adds cozy-monitoring to the condition, consistent with the existing pattern in vmagent.yaml. Fixes cozystack/cozystack#2349 Signed-off-by: mattia-eleuteri (cherry picked from commit b8d48ad7115cff008ae4b1238de62f40243e633b) --- packages/system/monitoring/templates/dashboards.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/system/monitoring/templates/dashboards.yaml b/packages/system/monitoring/templates/dashboards.yaml index 01f1c009..0427bfa0 100644 --- a/packages/system/monitoring/templates/dashboards.yaml +++ b/packages/system/monitoring/templates/dashboards.yaml @@ -14,7 +14,7 @@ spec: url: http://grafana-dashboards.cozy-grafana-operator.svc/{{ . }}.json {{- end }} {{- end }} -{{- if eq .Release.Namespace "tenant-root" }} +{{- if or (eq .Release.Namespace "tenant-root") (eq .Release.Namespace "cozy-monitoring") }} {{- range (split "\n" (.Files.Get "dashboards-infra.list")) }} {{- $parts := split "/" . }} {{- if eq (len $parts) 2 }} From 0726e1aa518ed50560b3aa140889ccfe7e183261 Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Fri, 10 Apr 2026 12:47:46 +0500 Subject: [PATCH 15/36] Fix system postgresql images to 17.7-standard-trixie Signed-off-by: Myasnikov Daniil (cherry picked from commit a3f50ba2bd92865d11caf2da6f659ccbf636f953) --- .../platform/images/migrations/migrations/37 | 89 +++++++++++++------ packages/core/platform/values.yaml | 2 +- .../system/harbor/templates/database.yaml | 4 +- packages/system/keycloak/templates/db.yaml | 4 +- .../templates/alerta/alerta-db.yaml | 4 +- .../monitoring/templates/grafana/db.yaml | 4 +- .../system/seaweedfs/templates/database.yaml | 4 +- 7 files changed, 78 insertions(+), 33 deletions(-) diff --git a/packages/core/platform/images/migrations/migrations/37 b/packages/core/platform/images/migrations/migrations/37 index b8ff3147..856da262 100755 --- a/packages/core/platform/images/migrations/migrations/37 +++ b/packages/core/platform/images/migrations/migrations/37 @@ -1,42 +1,77 @@ -#!/bin/sh +#!/bin/bash # Migration 37 --> 38 -# Backfill spec.version on postgreses.apps.cozystack.io resources. +# Pin PostgreSQL image to 17.7-standard-trixie for system databases. # -# Before this migration PostgreSQL had a default version field set to v18. -# This migration sets spec.version to "v17" for any postgres app resource that -# does not already have it set, to ensure compatibility with monitoring -# configurations that are hardcoded to PostgreSQL 17. +# This migration updates the imageName for all CNPG clusters belonging to +# system components (keycloak-db, grafana-db, alerta-db, seaweedfs-db, and +# harbor's dynamically-named DB): +# - If imageName is not set: pins to 17.7-standard-trixie +# - If imageName has any PG 17 tag: forces 17.7-standard-trixie +# - If imageName has a bare version tag (e.g. :18.1): appends -standard-trixie +# +# NOTE: This migration ONLY updates system CNPG Cluster resources (clusters.postgresql.cnpg.io), +# NOT user-created Postgres applications. set -euo pipefail -DEFAULT_VERSION="v17" +TARGET_IMAGE="ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie" -# Skip if the CRD does not exist (postgres was never installed) -if ! kubectl api-resources --api-group=apps.cozystack.io -o name 2>/dev/null | grep -q '^postgreses\.'; then - echo "CRD postgreses.apps.cozystack.io not found, skipping migration" - kubectl create configmap -n cozy-system cozystack-version \ - --from-literal=version=38 --dry-run=client -o yaml | kubectl apply -f- - exit 0 -fi +# Static system database names +STATIC_DB_NAMES="keycloak-db grafana-db alerta-db seaweedfs-db" -POSTGRESES=$(kubectl get postgreses.apps.cozystack.io -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}') -for resource in $POSTGRESES; do - NS="${resource%%/*}" - APP_NAME="${resource##*/}" +echo "=== Updating PostgreSQL images for system databases ===" +echo "Target image: $TARGET_IMAGE" - # Skip if spec.version is already set - CURRENT_VER=$(kubectl get postgreses.apps.cozystack.io -n "$NS" "$APP_NAME" \ - -o jsonpath='{.spec.version}') - if [ -n "$CURRENT_VER" ]; then - echo "SKIP $NS/$APP_NAME: spec.version already set to '$CURRENT_VER'" +# Fetch all CNPG clusters with their name, namespace, imageName, and Helm release annotation in one call +ALL_CLUSTERS=$(kubectl get clusters.postgresql.cnpg.io -A \ + -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{.spec.imageName}{"\t"}{.metadata.annotations.meta\.helm\.sh/release-name}{"\n"}{end}' 2>/dev/null || true) + +while IFS=$'\t' read -r NAMESPACE CLUSTER_NAME CURRENT_IMAGE HELM_RELEASE; do + [ -z "$NAMESPACE" ] && continue + + # Check if cluster name matches one of the static system databases + MATCH=false + for db_name in $STATIC_DB_NAMES; do + if [ "$CLUSTER_NAME" = "$db_name" ]; then + MATCH=true + break + fi + done + + # For harbor: match clusters ending with -db whose Helm release ends with -system + if [ "$MATCH" = false ] && [[ "$CLUSTER_NAME" == *-db ]] && [[ "$HELM_RELEASE" == *-system ]]; then + MATCH=true + fi + + if [ "$MATCH" = false ]; then continue fi - echo "Patching postgres/$APP_NAME in $NS: setting version=$DEFAULT_VERSION" + PATCH_IMAGE="" - kubectl patch postgreses.apps.cozystack.io -n "$NS" "$APP_NAME" --type=merge \ - --patch "{\"spec\":{\"version\":\"${DEFAULT_VERSION}\"}}" -done + if [ -z "$CURRENT_IMAGE" ]; then + # imageName not set — pin to target + PATCH_IMAGE="$TARGET_IMAGE" + echo "PATCH $NAMESPACE/$CLUSTER_NAME: imageName not set, setting to $PATCH_IMAGE" + elif [[ "$CURRENT_IMAGE" =~ :17\. ]]; then + # Any PG 17 image — force to the pinned 17.7-standard-trixie + PATCH_IMAGE="$TARGET_IMAGE" + echo "PATCH $NAMESPACE/$CLUSTER_NAME: PG 17 detected, $CURRENT_IMAGE -> $PATCH_IMAGE" + elif [[ "$CURRENT_IMAGE" =~ :[0-9]+\.[0-9]+$ ]]; then + # Bare version tag for other majors (e.g. :18.1) — append -standard-trixie suffix + PATCH_IMAGE="${CURRENT_IMAGE}-standard-trixie" + echo "PATCH $NAMESPACE/$CLUSTER_NAME: bare tag detected, $CURRENT_IMAGE -> $PATCH_IMAGE" + else + echo "SKIP $NAMESPACE/$CLUSTER_NAME: imageName already set to $CURRENT_IMAGE" + continue + fi + + kubectl patch clusters.postgresql.cnpg.io -n "$NAMESPACE" "$CLUSTER_NAME" \ + --type=merge \ + --patch "{\"spec\":{\"imageName\":\"${PATCH_IMAGE}\"}}" +done <<< "$ALL_CLUSTERS" + +echo "=== PostgreSQL image update completed ===" # Stamp version kubectl create configmap -n cozy-system cozystack-version \ diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index 33d3aa20..77eb00d7 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -6,7 +6,7 @@ sourceRef: migrations: enabled: false image: ghcr.io/cozystack/cozystack/platform-migrations:v1.2.1@sha256:e8fcf006a4451fc0e961455e9b27a61b7103ee49b1a81fe5e4662ffed093fad6 - targetVersion: 36 + targetVersion: 38 # Bundle deployment configuration bundles: system: diff --git a/packages/system/harbor/templates/database.yaml b/packages/system/harbor/templates/database.yaml index 02e11faa..9a948601 100644 --- a/packages/system/harbor/templates/database.yaml +++ b/packages/system/harbor/templates/database.yaml @@ -5,7 +5,9 @@ metadata: name: {{ .Values.harbor.fullnameOverride }}-db spec: instances: {{ .Values.db.replicas }} - imageName: ghcr.io/cloudnative-pg/postgresql:17.7 + {{- $existingCluster := lookup "postgresql.cnpg.io/v1" "Cluster" .Release.Namespace (printf "%s-db" .Values.harbor.fullnameOverride) }} + {{- $image := dig "spec" "imageName" "ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie" $existingCluster }} + imageName: {{ if regexMatch ":17\\." $image }}ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie{{ else if regexMatch ":[0-9]+\\.[0-9]+$" $image }}{{ printf "%s-standard-trixie" $image }}{{ else }}{{ $image }}{{ end }} storage: size: {{ .Values.db.size }} {{- with .Values.db.storageClass }} diff --git a/packages/system/keycloak/templates/db.yaml b/packages/system/keycloak/templates/db.yaml index fb649a43..6a57e93a 100644 --- a/packages/system/keycloak/templates/db.yaml +++ b/packages/system/keycloak/templates/db.yaml @@ -4,7 +4,9 @@ metadata: name: keycloak-db spec: instances: 2 - imageName: ghcr.io/cloudnative-pg/postgresql:17.7 + {{- $existingCluster := lookup "postgresql.cnpg.io/v1" "Cluster" .Release.Namespace "keycloak-db" }} + {{- $image := dig "spec" "imageName" "ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie" $existingCluster }} + imageName: {{ if regexMatch ":17\\." $image }}ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie{{ else if regexMatch ":[0-9]+\\.[0-9]+$" $image }}{{ printf "%s-standard-trixie" $image }}{{ else }}{{ $image }}{{ end }} storage: size: 20Gi {{- if .Values._cluster.scheduling }} diff --git a/packages/system/monitoring/templates/alerta/alerta-db.yaml b/packages/system/monitoring/templates/alerta/alerta-db.yaml index 71df5428..af9dc72c 100644 --- a/packages/system/monitoring/templates/alerta/alerta-db.yaml +++ b/packages/system/monitoring/templates/alerta/alerta-db.yaml @@ -5,7 +5,9 @@ metadata: name: alerta-db spec: instances: 2 - imageName: ghcr.io/cloudnative-pg/postgresql:17.7 + {{- $existingCluster := lookup "postgresql.cnpg.io/v1" "Cluster" .Release.Namespace "alerta-db" }} + {{- $image := dig "spec" "imageName" "ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie" $existingCluster }} + imageName: {{ if regexMatch ":17\\." $image }}ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie{{ else if regexMatch ":[0-9]+\\.[0-9]+$" $image }}{{ printf "%s-standard-trixie" $image }}{{ else }}{{ $image }}{{ end }} {{- if .Values._cluster.scheduling }} {{- $rawConstraints := get .Values._cluster.scheduling "globalAppTopologySpreadConstraints" }} {{- if $rawConstraints }} diff --git a/packages/system/monitoring/templates/grafana/db.yaml b/packages/system/monitoring/templates/grafana/db.yaml index 73d6502e..afc2d0d7 100644 --- a/packages/system/monitoring/templates/grafana/db.yaml +++ b/packages/system/monitoring/templates/grafana/db.yaml @@ -4,7 +4,9 @@ metadata: name: grafana-db spec: instances: 2 - imageName: ghcr.io/cloudnative-pg/postgresql:17.7 + {{- $existingCluster := lookup "postgresql.cnpg.io/v1" "Cluster" .Release.Namespace "grafana-db" }} + {{- $image := dig "spec" "imageName" "ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie" $existingCluster }} + imageName: {{ if regexMatch ":17\\." $image }}ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie{{ else if regexMatch ":[0-9]+\\.[0-9]+$" $image }}{{ printf "%s-standard-trixie" $image }}{{ else }}{{ $image }}{{ end }} storage: size: {{ .Values.grafana.db.size }} {{- if .Values._cluster.scheduling }} diff --git a/packages/system/seaweedfs/templates/database.yaml b/packages/system/seaweedfs/templates/database.yaml index 1c116dd0..b9b50c49 100644 --- a/packages/system/seaweedfs/templates/database.yaml +++ b/packages/system/seaweedfs/templates/database.yaml @@ -5,7 +5,9 @@ metadata: name: seaweedfs-db spec: instances: {{ .Values.db.replicas }} - imageName: ghcr.io/cloudnative-pg/postgresql:17.7 + {{- $existingCluster := lookup "postgresql.cnpg.io/v1" "Cluster" .Release.Namespace "seaweedfs-db" }} + {{- $image := dig "spec" "imageName" "ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie" $existingCluster }} + imageName: {{ if regexMatch ":17\\." $image }}ghcr.io/cloudnative-pg/postgresql:17.7-standard-trixie{{ else if regexMatch ":[0-9]+\\.[0-9]+$" $image }}{{ printf "%s-standard-trixie" $image }}{{ else }}{{ $image }}{{ end }} storage: size: {{ .Values.db.size }} {{- with .Values.db.storageClass }} From 8d1628df49cc79ffce5e584ea84b0bcb0cad1205 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Fri, 3 Apr 2026 20:24:25 +0200 Subject: [PATCH 16/36] linstor: update piraeus-server patches for v1.33.1 Signed-off-by: Andrei Kvapil (cherry picked from commit e4468148f6f217b8791677cf7af1de17a9363236) --- packages/system/linstor/Makefile | 2 +- .../images/piraeus-server/patches/README.md | 22 +- .../patches/adjust-on-resfile-change.diff | 48 -- .../patches/allow-toggle-disk-retry.diff | 406 +++++++------ .../patches/fix-duplicate-tcp-ports.diff | 165 +++-- .../patches/fix-luks-header-size.diff | 570 ++++++++++++++++++ .../force-metadata-check-on-disk-add.diff | 63 -- .../skip-adjust-when-device-inaccessible.diff | 155 ----- 8 files changed, 883 insertions(+), 548 deletions(-) delete mode 100644 packages/system/linstor/images/piraeus-server/patches/adjust-on-resfile-change.diff create mode 100644 packages/system/linstor/images/piraeus-server/patches/fix-luks-header-size.diff delete mode 100644 packages/system/linstor/images/piraeus-server/patches/force-metadata-check-on-disk-add.diff delete mode 100644 packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff diff --git a/packages/system/linstor/Makefile b/packages/system/linstor/Makefile index b211baec..2bc3a2a1 100644 --- a/packages/system/linstor/Makefile +++ b/packages/system/linstor/Makefile @@ -4,7 +4,7 @@ export NAMESPACE=cozy-$(NAME) include ../../../hack/common-envs.mk include ../../../hack/package.mk -LINSTOR_VERSION ?= 1.32.3 +LINSTOR_VERSION ?= 1.33.1 LINSTOR_CSI_VERSION ?= v1.10.5 image: image-piraeus-server image-linstor-csi diff --git a/packages/system/linstor/images/piraeus-server/patches/README.md b/packages/system/linstor/images/piraeus-server/patches/README.md index 558fdfeb..db497b7e 100644 --- a/packages/system/linstor/images/piraeus-server/patches/README.md +++ b/packages/system/linstor/images/piraeus-server/patches/README.md @@ -1,14 +1,14 @@ # LINSTOR Server Patches -Custom patches for piraeus-server (linstor-server) v1.32.3. +Custom patches for piraeus-server (linstor-server) v1.33.1. -- **adjust-on-resfile-change.diff** — Use actual device path in res file during toggle-disk; fix LUKS data offset - - Upstream: [#473](https://github.com/LINBIT/linstor-server/pull/473), [#472](https://github.com/LINBIT/linstor-server/pull/472) -- **allow-toggle-disk-retry.diff** — Allow retry and cancellation of failed toggle-disk operations - - Upstream: [#475](https://github.com/LINBIT/linstor-server/pull/475) -- **force-metadata-check-on-disk-add.diff** — Create metadata during toggle-disk from diskless to diskful - - Upstream: [#474](https://github.com/LINBIT/linstor-server/pull/474) -- **fix-duplicate-tcp-ports.diff** — Preserve TCP ports during toggle-disk to prevent port mismatch between controller and satellites - - Upstream: [#476](https://github.com/LINBIT/linstor-server/pull/476) (superseded by this expanded fix) -- **skip-adjust-when-device-inaccessible.diff** — Fix resources stuck in StandAlone after reboot, Unknown state race condition, and encrypted resource deletion - - Upstream: [#477](https://github.com/LINBIT/linstor-server/pull/477) +- **allow-toggle-disk-retry.diff** — Backport maintainer implementation of toggle-disk retry/abort + - Source PR/comment: [#475](https://github.com/LINBIT/linstor-server/pull/475), [maintainer note](https://github.com/LINBIT/linstor-server/pull/475#issuecomment-3949630419) + - Backported from upstream commit: [`3d97f71c9`](https://github.com/LINBIT/linstor-server/commit/3d97f71c95a493588d3d521c63eac4d846935fb3) +- **fix-duplicate-tcp-ports.diff** — Preserve DRBD TCP ports during toggle-disk and avoid redundant `ensureStackDataExists()` + - Source PR/review: [#476](https://github.com/LINBIT/linstor-server/pull/476), [review suggestion](https://github.com/LINBIT/linstor-server/pull/476#discussion_r3007725079) + - Backported from commits: [`79d6375c5`](https://github.com/kvaps/linstor-server/commit/79d6375c55d6181b35a7b7f0fe8dbdfb86e126cd), [`bcc89902f`](https://github.com/kvaps/linstor-server/commit/bcc89902f4f61ac1589dd07ebb7f5aae1935370d) +- **fix-luks-header-size.diff** — Account for LUKS2 `--offset`, metadata/keyslots sizing, and device `optimal_io_size` + - Source PR/comment: [#472](https://github.com/LINBIT/linstor-server/pull/472), [maintainer note](https://github.com/LINBIT/linstor-server/pull/472#issuecomment-3949687603) + - Backported from commits: [`ccc85fbd2`](https://github.com/LINBIT/linstor-server/commit/ccc85fbd2c65f0b97c52403fa80f1efdb886ec4e), [`71b601554`](https://github.com/LINBIT/linstor-server/commit/71b601554f41bcb50cd5bd06989c5b0d3a814acd) + - Note: upstream commit [`3d0402a0c`](https://github.com/LINBIT/linstor-server/commit/3d0402a0c25f0a4b57b380321f10e89982f26e7a) is already included in `v1.33.1` diff --git a/packages/system/linstor/images/piraeus-server/patches/adjust-on-resfile-change.diff b/packages/system/linstor/images/piraeus-server/patches/adjust-on-resfile-change.diff deleted file mode 100644 index 6788efaf..00000000 --- a/packages/system/linstor/images/piraeus-server/patches/adjust-on-resfile-change.diff +++ /dev/null @@ -1,48 +0,0 @@ -diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/ConfFileBuilder.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/ConfFileBuilder.java -index 36c52ccf8..c0bb7b967 100644 ---- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/ConfFileBuilder.java -+++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/ConfFileBuilder.java -@@ -894,12 +894,16 @@ public class ConfFileBuilder - if (((Volume) vlmData.getVolume()).getFlags().isUnset(localAccCtx, Volume.Flags.DELETE)) - { - final String disk; -+ // Check if we're in toggle-disk operation (adding disk to diskless resource) -+ boolean isDiskAdding = vlmData.getVolume().getAbsResource().getStateFlags().isSomeSet( -+ localAccCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING); - if ((!isPeerRsc && vlmData.getDataDevice() == null) || - (isPeerRsc && - // FIXME: vlmData.getRscLayerObject().getFlags should be used here - vlmData.getVolume().getAbsResource().disklessForDrbdPeers(accCtx) - ) || -- (!isPeerRsc && -+ // For toggle-disk: if dataDevice is set and we're adding disk, use the actual device -+ (!isPeerRsc && !isDiskAdding && - // FIXME: vlmData.getRscLayerObject().getFlags should be used here - vlmData.getVolume().getAbsResource().isDrbdDiskless(accCtx) - ) -diff --git a/satellite/src/main/java/com/linbit/linstor/layer/luks/CryptSetupCommands.java b/satellite/src/main/java/com/linbit/linstor/layer/luks/CryptSetupCommands.java -index 54dd5c19f..018de58cf 100644 ---- a/satellite/src/main/java/com/linbit/linstor/layer/luks/CryptSetupCommands.java -+++ b/satellite/src/main/java/com/linbit/linstor/layer/luks/CryptSetupCommands.java -@@ -34,6 +34,9 @@ public class CryptSetupCommands implements Luks - private static final Version V2_1_0 = new Version(2, 1, 0); - private static final Version V2_0_0 = new Version(2, 0, 0); - private static final String PBDKF_MAX_MEMORY_KIB = "262144"; // 256 MiB -+ // Fixed LUKS2 data offset in 512-byte sectors (16 MiB = 32768 sectors) -+ // This ensures consistent LUKS header size across all nodes regardless of system defaults -+ private static final String LUKS2_DATA_OFFSET_SECTORS = "32768"; - - @SuppressWarnings("unused") - private final ErrorReporter errorReporter; -@@ -78,6 +81,11 @@ public class CryptSetupCommands implements Luks - command.add(CRYPTSETUP); - command.add("-q"); - command.add("luksFormat"); -+ // Always specify explicit offset to ensure consistent LUKS header size across all nodes -+ // Without this, different systems may create LUKS with different header sizes (16MiB vs 32MiB) -+ // which causes "Low.dev. smaller than requested DRBD-dev. size" errors during toggle-disk -+ command.add("--offset"); -+ command.add(LUKS2_DATA_OFFSET_SECTORS); - if (version.greaterOrEqual(V2_0_0)) - { - command.add("--pbkdf-memory"); diff --git a/packages/system/linstor/images/piraeus-server/patches/allow-toggle-disk-retry.diff b/packages/system/linstor/images/piraeus-server/patches/allow-toggle-disk-retry.diff index 264e0221..952ba16c 100644 --- a/packages/system/linstor/images/piraeus-server/patches/allow-toggle-disk-retry.diff +++ b/packages/system/linstor/images/piraeus-server/patches/allow-toggle-disk-retry.diff @@ -1,168 +1,232 @@ diff --git a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java -index d93a18014..cc8ce4f04 100644 +index d93a18014..a944cb809 100644 --- a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java +++ b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java -@@ -57,7 +57,9 @@ import com.linbit.linstor.stateflags.StateFlags; - import com.linbit.linstor.storage.StorageException; - import com.linbit.linstor.storage.data.adapter.drbd.DrbdRscData; - import com.linbit.linstor.storage.interfaces.categories.resource.AbsRscLayerObject; -+import com.linbit.linstor.storage.interfaces.categories.resource.VlmProviderObject; - import com.linbit.linstor.storage.kinds.DeviceLayerKind; -+import com.linbit.linstor.storage.kinds.DeviceProviderKind; - import com.linbit.linstor.storage.utils.LayerUtils; - import com.linbit.linstor.tasks.AutoDiskfulTask; - import com.linbit.linstor.utils.layer.LayerRscUtils; -@@ -387,21 +389,84 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL +@@ -111,6 +111,14 @@ import reactor.util.function.Tuple2; + @Singleton + public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionListener + { ++ private enum ToggleDiskAction ++ { ++ NOOP, ++ NORMAL, ++ ABORT, ++ RETRY ++ } ++ + private final AccessContext apiCtx; + private final ScopeRunner scopeRunner; + private final BackgroundRunner backgroundRunner; +@@ -386,69 +394,33 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL + "Toggle Disk on %s/%s %s", nodeNameStr, rscNameStr, removeDisk ? "removing disk" : "adding disk"); Resource rsc = ctrlApiDataLoader.loadRsc(nodeName, rscName, true); ++ ResourceDefinition rscDfn = rsc.getResourceDefinition(); -+ // Allow retry of the same operation if the previous attempt failed -+ // (the requested flag remains set for retry on reconnection, but we should also allow manual retry) -+ // Also allow cancellation of a failed operation by requesting the opposite operation - if (hasDiskAddRequested(rsc)) - { +- if (hasDiskAddRequested(rsc)) +- { - throw new ApiRcException(ApiCallRcImpl.simpleEntry( - ApiConsts.FAIL_RSC_BUSY, - "Addition of disk to resource already requested", - true - )); -+ if (removeDisk) -+ { -+ // User wants to cancel the failed add-disk operation and go back to diskless -+ // Use the existing disk removal flow to properly cleanup storage on satellite -+ errorReporter.logInfo( -+ "Toggle Disk cancel on %s/%s - cancelling failed DISK_ADD_REQUESTED, reverting to diskless", -+ nodeNameStr, rscNameStr); -+ unmarkDiskAddRequested(rsc); -+ // Also clear DISK_ADDING if it was set -+ unmarkDiskAdding(rsc); -+ -+ // Set storage pool to diskless pool (overwrite the diskful pool that was set) -+ Props rscProps = ctrlPropsHelper.getProps(rsc); -+ rscProps.map().put(ApiConsts.KEY_STOR_POOL_NAME, LinStor.DISKLESS_STOR_POOL_NAME); -+ -+ // Set DISK_REMOVE_REQUESTED to use the existing disk removal flow -+ // This will: -+ // 1. updateAndAdjustDisk sets DISK_REMOVING flag -+ // 2. Satellite sees DISK_REMOVING and deletes LUKS/storage devices -+ // 3. finishOperation rebuilds layer stack as diskless -+ // We keep the existing layer data so satellite can properly cleanup -+ markDiskRemoveRequested(rsc); -+ -+ ctrlTransactionHelper.commit(); -+ -+ // Use existing disk removal flow - this will properly cleanup storage on satellite -+ return Flux -+ .just(ApiCallRcImpl.singleApiCallRc( -+ ApiConsts.MODIFIED, -+ "Cancelling disk addition, reverting to diskless" -+ )) -+ .concatWith(updateAndAdjustDisk(nodeName, rscName, true, toggleIntoTiebreakerRef, context)) -+ .concatWith(ctrlRscDfnApiCallHandler.get().updateProps(rsc.getResourceDefinition())); -+ } -+ // If adding disk and DISK_ADD_REQUESTED is already set, treat as retry -+ // Simply retry the operation with existing layer data - satellite will handle it idempotently -+ // NOTE: We don't remove/recreate layer data here because removeLayerData() only deletes -+ // from controller DB without calling drbdadm down on satellite, leaving orphaned DRBD devices -+ errorReporter.logInfo( -+ "Toggle Disk retry on %s/%s - DISK_ADD_REQUESTED already set, retrying operation", -+ nodeNameStr, rscNameStr); -+ ctrlTransactionHelper.commit(); -+ return Flux -+ .just(new ApiCallRcImpl()) -+ .concatWith(updateAndAdjustDisk(nodeName, rscName, false, toggleIntoTiebreakerRef, context)) -+ .concatWith(ctrlRscDfnApiCallHandler.get().updateProps(rsc.getResourceDefinition())); - } - if (hasDiskRemoveRequested(rsc)) - { +- } +- if (hasDiskRemoveRequested(rsc)) +- { - throw new ApiRcException(ApiCallRcImpl.simpleEntry( - ApiConsts.FAIL_RSC_BUSY, - "Removal of disk from resource already requested", - true - )); -+ if (!removeDisk) -+ { -+ // User wants to cancel the failed remove-disk operation +- } ++ ToggleDiskAction action = determineToggleDiskAction(rsc, removeDisk); ++ errorReporter.logDebug("Toggle Disk action: %s", action); + +- if (!removeDisk && !ctrlVlmCrtApiHelper.isDiskless(rsc)) ++ switch (action) + { +- throw new ApiRcException(ApiCallRcImpl.simpleEntry( +- ApiConsts.WARN_RSC_ALREADY_HAS_DISK, +- "Resource already has disk", +- true +- )); +- } +- if (removeDisk && ctrlVlmCrtApiHelper.isDiskless(rsc)) +- { +- throw new ApiRcException(ApiCallRcImpl.simpleEntry( +- ApiConsts.WARN_RSC_ALREADY_DISKLESS, +- "Resource already diskless", +- true +- )); ++ case NOOP: ++ return handleNoopAction(rsc, removeDisk); ++ case RETRY: ++ return handleRetryAction(rsc, removeDisk, toggleIntoTiebreakerRef, context); ++ case ABORT: ++ clearToggleDiskFlags(rsc); + errorReporter.logInfo( -+ "Toggle Disk cancel on %s/%s - cancelling failed DISK_REMOVE_REQUESTED", -+ nodeNameStr, rscNameStr); -+ unmarkDiskRemoveRequested(rsc); -+ ctrlTransactionHelper.commit(); -+ return Flux.just( -+ ApiCallRcImpl.singleApiCallRc( -+ ApiConsts.MODIFIED, -+ "Cancelled disk removal request" -+ ) ++ "Aborting previous toggle disk transition, starting new transition to %s", ++ removeDisk ? "diskless" : "diskful" + ); -+ } -+ // If removing disk and DISK_REMOVE_REQUESTED is already set, treat as retry -+ errorReporter.logInfo( -+ "Toggle Disk retry on %s/%s - DISK_REMOVE_REQUESTED already set, continuing operation", -+ nodeNameStr, rscNameStr); -+ ctrlTransactionHelper.commit(); -+ return Flux -+ .just(new ApiCallRcImpl()) -+ .concatWith(updateAndAdjustDisk(nodeName, rscName, true, toggleIntoTiebreakerRef, context)) -+ .concatWith(ctrlRscDfnApiCallHandler.get().updateProps(rsc.getResourceDefinition())); ++ break; ++ case NORMAL: ++ break; ++ default: ++ throw new ImplementationError("Unhandled case: " + action); } - if (!removeDisk && !ctrlVlmCrtApiHelper.isDiskless(rsc)) -@@ -412,17 +477,43 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL - true - )); - } -+ ResourceDefinition rscDfn = rsc.getResourceDefinition(); -+ AccessContext peerCtx = peerAccCtx.get(); -+ - if (removeDisk && ctrlVlmCrtApiHelper.isDiskless(rsc)) - { -+ // Resource is marked as diskless - check if it has orphaned storage layers that need cleanup -+ AbsRscLayerObject layerData = getLayerData(peerCtx, rsc); -+ if (layerData != null && (LayerUtils.hasLayer(layerData, DeviceLayerKind.LUKS) || -+ hasNonDisklessStorageLayer(layerData))) -+ { -+ // Resource is marked as diskless but has orphaned storage layers - need cleanup -+ // Use the existing disk removal flow to properly cleanup storage on satellite -+ errorReporter.logInfo( -+ "Toggle Disk cleanup on %s/%s - resource is diskless but has orphaned storage layers, cleaning up", -+ nodeNameStr, rscNameStr); -+ -+ // Set DISK_REMOVE_REQUESTED to use the existing disk removal flow -+ // This will trigger proper satellite cleanup via DISK_REMOVING flag -+ markDiskRemoveRequested(rsc); -+ -+ ctrlTransactionHelper.commit(); -+ -+ // Use existing disk removal flow - this will properly cleanup storage on satellite -+ return Flux -+ .just(ApiCallRcImpl.singleApiCallRc( -+ ApiConsts.MODIFIED, -+ "Cleaning up orphaned storage layers" -+ )) -+ .concatWith(updateAndAdjustDisk(nodeName, rscName, true, toggleIntoTiebreakerRef, context)) -+ .concatWith(ctrlRscDfnApiCallHandler.get().updateProps(rsc.getResourceDefinition())); -+ } - throw new ApiRcException(ApiCallRcImpl.simpleEntry( - ApiConsts.WARN_RSC_ALREADY_DISKLESS, - "Resource already diskless", - true - )); - } -- - ResourceDefinition rscDfn = rsc.getResourceDefinition(); - AccessContext peerCtx = peerAccCtx.get(); - if (removeDisk) - { - // Prevent removal of the last disk -@@ -1446,6 +1537,30 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL +- if (removeDisk) +- { +- // Prevent removal of the last disk +- int haveDiskCount = countDisksAndIsOnline(rscDfn); +- if (haveDiskCount <= 1) +- { +- throw new ApiRcException(ApiCallRcImpl.simpleEntry( +- ApiConsts.FAIL_INSUFFICIENT_REPLICA_COUNT, +- "Cannot remove the disk from the only online resource with a disk", +- true +- )); +- } ++ validateToggleDiskPreconditions(rsc, removeDisk); + +- if (!LayerUtils.hasLayer(getLayerData(peerCtx, rsc), DeviceLayerKind.DRBD)) +- { +- throw new ApiRcException(ApiCallRcImpl.simpleEntry( +- ApiConsts.FAIL_INVLD_LAYER_STACK, +- "Toggle disk is only supported in combination with DRBD", +- true +- )); +- } +- } +- else +- { +- ensureAllPeersHavePeerSlotLeft(rscDfn); +- } ++ AccessContext peerCtx = peerAccCtx.get(); + + // Save the requested storage pool in the resource properties. + // This does not cause the storage pool to be used automatically. +@@ -628,10 +600,10 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL + + ctrlTransactionHelper.commit(); + +- String action = removeDisk ? "Removal of disk from" : "Addition of disk to"; ++ String actionStr = removeDisk ? "Removal of disk from" : "Addition of disk to"; + responses.addEntry(ApiCallRcImpl.simpleEntry( + ApiConsts.MODIFIED, +- action + " resource '" + rscDfn.getName().displayValue + "' " + ++ actionStr + " resource '" + rscDfn.getName().displayValue + "' " + + "on node '" + rsc.getNode().getName().displayValue + "' registered" + )); + +@@ -651,6 +623,40 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL + ); + } + ++ private Flux handleNoopAction(Resource rscRef, boolean removeDiskRef) ++ { ++ String state = removeDiskRef ? "diskless" : "diskful"; ++ ApiCallRcImpl responses = new ApiCallRcImpl(); ++ responses.addEntry(ApiCallRcImpl.simpleEntry( ++ ApiConsts.INFO_NOOP, ++ "Resource '" + rscRef.getResourceDefinition().getName().displayValue + "' on node '" + ++ rscRef.getNode().getName().displayValue + "' is already " + state ++ )); ++ return Flux.just(responses); ++ } ++ ++ private Flux handleRetryAction( ++ Resource rscRef, ++ boolean removeDisk, ++ boolean toggleIntoTiebreakerRef, ++ ResponseContext context ++ ) ++ { ++ String direction = removeDisk ? "diskless" : "diskful"; ++ ApiCallRcImpl responses = new ApiCallRcImpl(); ++ NodeName nodeName = rscRef.getNode().getName(); ++ ResourceName rscName = rscRef.getResourceDefinition().getName(); ++ responses.addEntry(ApiCallRcImpl.simpleEntry( ++ ApiConsts.INFO_NOOP, ++ "Retrying toggle disk to " + direction + " for resource '" + rscName.displayValue + ++ "' on node '" + nodeName.displayValue + "'" ++ )); ++ ++ return Flux ++ .just(responses) ++ .concatWith(updateAndAdjustDisk(nodeName, rscName, removeDisk, toggleIntoTiebreakerRef, context)); ++ } ++ + private long getVlmDfnSizePrivileged(VolumeDefinition vlmDfnRef) + { + try +@@ -781,6 +787,96 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL } } -+ private void unmarkDiskAddRequested(Resource rsc) ++ private ToggleDiskAction determineToggleDiskAction(Resource rsc, boolean removeDisk) ++ { ++ boolean isDiskless = ctrlVlmCrtApiHelper.isDiskless(rsc); ++ boolean diskAddRequested = hasDiskAddRequested(rsc); ++ boolean diskRemoveRequested = hasDiskRemoveRequested(rsc); ++ ++ ToggleDiskAction action; ++ if (isDiskless) ++ { ++ if (diskAddRequested) ++ { ++ action = removeDisk ? ToggleDiskAction.ABORT : ToggleDiskAction.RETRY; ++ } ++ else if (diskRemoveRequested) ++ { ++ action = removeDisk ? ToggleDiskAction.RETRY : ToggleDiskAction.ABORT; ++ } ++ else ++ { ++ action = removeDisk ? ToggleDiskAction.NOOP : ToggleDiskAction.NORMAL; ++ } ++ } ++ else ++ { ++ if (diskRemoveRequested) ++ { ++ action = removeDisk ? ToggleDiskAction.RETRY : ToggleDiskAction.ABORT; ++ } ++ else ++ { ++ action = removeDisk ? ToggleDiskAction.NORMAL : ToggleDiskAction.NOOP; ++ } ++ } ++ ++ return action; ++ } ++ ++ private void validateToggleDiskPreconditions(Resource rsc, boolean removeDisk) ++ { ++ ResourceDefinition rscDfn = rsc.getResourceDefinition(); ++ if (removeDisk) ++ { ++ validateDiskRemovalAllowed(rsc, rscDfn); ++ } ++ else ++ { ++ ensureAllPeersHavePeerSlotLeft(rscDfn); ++ } ++ } ++ ++ private void clearToggleDiskFlags(Resource rsc) + { + try + { -+ rsc.getStateFlags().disableFlags(apiCtx, Resource.Flags.DISK_ADD_REQUESTED); ++ rsc.getStateFlags().disableFlags( ++ apiCtx, ++ Resource.Flags.DISK_ADD_REQUESTED, ++ Resource.Flags.DISK_ADDING, ++ Resource.Flags.DISK_REMOVE_REQUESTED, ++ Resource.Flags.DISK_REMOVING ++ ); + } + catch (AccessDeniedException | DatabaseException exc) + { @@ -170,60 +234,28 @@ index d93a18014..cc8ce4f04 100644 + } + } + -+ private void unmarkDiskRemoveRequested(Resource rsc) ++ private void validateDiskRemovalAllowed(Resource rsc, ResourceDefinition rscDfn) + { -+ try ++ int haveDiskCount = countDisksAndIsOnline(rscDfn); ++ if (haveDiskCount <= 1) + { -+ rsc.getStateFlags().disableFlags(apiCtx, Resource.Flags.DISK_REMOVE_REQUESTED); ++ throw new ApiRcException(ApiCallRcImpl.simpleEntry( ++ ApiConsts.FAIL_INSUFFICIENT_REPLICA_COUNT, ++ "Cannot remove the disk from the only online resource with a disk", ++ true ++ )); + } -+ catch (AccessDeniedException | DatabaseException exc) ++ ++ if (!LayerUtils.hasLayer(getLayerData(peerAccCtx.get(), rsc), DeviceLayerKind.DRBD)) + { -+ throw new ImplementationError(exc); ++ throw new ApiRcException(ApiCallRcImpl.simpleEntry( ++ ApiConsts.FAIL_INVLD_LAYER_STACK, ++ "Toggle disk is only supported in combination with DRBD", ++ true ++ )); + } + } + - private void markDiskAdded(Resource rscData) - { - try -@@ -1511,6 +1626,41 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL - return layerData; - } - -+ /** -+ * Check if the layer stack has a non-diskless STORAGE layer. -+ * This is used to detect orphaned storage layers that need cleanup. -+ */ -+ private boolean hasNonDisklessStorageLayer(AbsRscLayerObject layerDataRef) -+ { -+ boolean hasNonDiskless = false; -+ if (layerDataRef != null) -+ { -+ if (layerDataRef.getLayerKind() == DeviceLayerKind.STORAGE) -+ { -+ for (VlmProviderObject vlmData : layerDataRef.getVlmLayerObjects().values()) -+ { -+ if (vlmData.getProviderKind() != DeviceProviderKind.DISKLESS) -+ { -+ hasNonDiskless = true; -+ break; -+ } -+ } -+ } -+ if (!hasNonDiskless) -+ { -+ for (AbsRscLayerObject child : layerDataRef.getChildren()) -+ { -+ if (hasNonDisklessStorageLayer(child)) -+ { -+ hasNonDiskless = true; -+ break; -+ } -+ } -+ } -+ } -+ return hasNonDiskless; -+ } -+ - private LockGuard createLockGuard() - { - return lockGuardFactory.buildDeferred(LockType.WRITE, LockObj.NODES_MAP, LockObj.RSC_DFN_MAP); + /** + * Although we need to rebuild the layerData as the layerList might have changed, if we do not + * deactivate (i.e. down) the current resource, we need to make sure that deleting DrbdRscData diff --git a/packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff b/packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff index b34a2500..ed106f98 100644 --- a/packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff +++ b/packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff @@ -1,69 +1,25 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Andrei Kvapil -Date: Fri, 28 Mar 2026 13:00:00 +0100 -Subject: [PATCH] fix(drbd): preserve TCP ports during toggle-disk operations - -Prevent TCP port mismatches after toggle-disk operations by preserving -existing TCP ports when rebuilding DrbdRscData. - -Root Cause: ------------ -During toggle-disk operations, removeLayerData() deletes DrbdRscData -(freeing its TCP ports from the number pool), then ensureStackDataExists() -creates new DrbdRscData. Since the payload has no explicit tcpPorts, -the controller allocates new ports from the pool -- which may differ from -the old ports if other resources claimed them in the meantime. - -The controller correctly avoids collisions in its own number pool, but -the satellite may miss the update (e.g. during controller restart or -network issues). When this happens, the satellite keeps the old ports -while peers receive the new ones, causing DRBD connection failures -(StandAlone/Connecting state). - -Additionally, remove the redundant ensureStackDataExists() call from -resetStoragePools() -- the caller already invokes it with the correct -payload. - -Solution: ---------- -1. Add copyDrbdTcpPortsIfExists() to save existing TCP ports into the - LayerPayload before removeLayerData() deletes them. -2. Call it from copyDrbdNodeIdIfExists() (covers both toggle-disk paths) - and from the needsDeactivate path (shared storage pool case). -3. Remove the redundant ensureStackDataExists() from resetStoragePools(). - -This ensures the same TCP ports are reused when DrbdRscData is recreated, -eliminating the window for port mismatch between controller and satellites. - -Co-Authored-By: Claude -Signed-off-by: Andrei Kvapil ---- - .../controller/CtrlRscToggleDiskApiCallHandler.java | 40 +++++++++++++++++++-- - .../linstor/layer/resource/CtrlRscLayerDataFactory.java | 2 -- - 2 files changed, 38 insertions(+), 4 deletions(-) - diff --git a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java -index ccdb0cee5..b0554c2ec 100644 +index d93a18014..01cfbbacf 100644 --- a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java +++ b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java -@@ -58,6 +58,7 @@ import com.linbit.linstor.stateflags.StateFlags; - import com.linbit.linstor.storage.StorageException; - import com.linbit.linstor.storage.data.adapter.drbd.DrbdRscData; - import com.linbit.linstor.storage.interfaces.categories.resource.AbsRscLayerObject; +@@ -37,6 +37,7 @@ import com.linbit.linstor.core.objects.StorPool; + import com.linbit.linstor.core.objects.Volume; + import com.linbit.linstor.core.objects.VolumeDefinition; + import com.linbit.linstor.core.objects.utils.MixedStorPoolHelper; +import com.linbit.linstor.core.types.TcpPortNumber; - import com.linbit.linstor.storage.interfaces.categories.resource.VlmProviderObject; - import com.linbit.linstor.storage.kinds.DeviceLayerKind; - import com.linbit.linstor.storage.kinds.DeviceProviderKind; -@@ -88,6 +89,7 @@ import java.util.LinkedHashMap; - import java.util.List; + import com.linbit.linstor.dbdrivers.DatabaseException; + import com.linbit.linstor.event.EventWaiter; + import com.linbit.linstor.event.ObjectIdentifier; +@@ -85,6 +86,7 @@ import java.util.List; + import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.TreeSet; - + import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; -@@ -587,8 +589,9 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL - +@@ -575,12 +577,13 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL + /* * We also have to remove the currently diskless DrbdRscData and free up the node-id as now we must - * use the shared resource's node-id @@ -73,30 +29,39 @@ index ccdb0cee5..b0554c2ec 100644 } else { -@@ -726,7 +729,7 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL +- copyDrbdNodeIdIfExists(rsc, payload); ++ copyDrbdSettings(rsc, payload); + } + /* + * rebuilds the layerdata in case we just removed it.. +@@ -782,10 +785,20 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL + } + /** - * Although we need to rebuild the layerData as the layerList might have changed, if we do not - * deactivate (i.e. down) the current resource, we need to make sure that deleting DrbdRscData +- * Although we need to rebuild the layerData as the layerList might have changed, if we do not +- * deactivate (i.e. down) the current resource, we need to make sure that deleting DrbdRscData - * and recreating a new DrbdRscData ends up with the same node-id as before. -+ * and recreating a new DrbdRscData ends up with the same node-id and TCP ports as before. ++ * Copies DRBD settings (node-id and TCP ports) from the existing DrbdRscData into the payload ++ * before removeLayerData() deletes them. This ensures that recreated DrbdRscData ends up with ++ * the same node-id and TCP ports as before. ++ * ++ * TCP ports must be preserved because if the satellite misses the update (e.g. due to controller ++ * restart or connectivity issues), it will keep the old ports while peers receive the new ones, ++ * causing DRBD connections to fail with StandAlone state. */ - private void copyDrbdNodeIdIfExists(Resource rsc, LayerPayload payload) throws ImplementationError - { -@@ -743,6 +746,37 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL - DrbdRscData drbdRscData = (DrbdRscData) drbdRscDataSet.iterator().next(); - payload.drbdRsc.nodeId = drbdRscData.getNodeId().value; - } ++ private void copyDrbdSettings(Resource rsc, LayerPayload payload) throws ImplementationError ++ { ++ copyDrbdNodeIdIfExists(rsc, payload); + copyDrbdTcpPortsIfExists(rsc, payload); + } + -+ /** -+ * Preserves existing TCP ports during toggle-disk operations. -+ * -+ * When removeLayerData() deletes DrbdRscData, the TCP ports are freed from the number pool. -+ * If ensureStackDataExists() then allocates different ports, and the satellite misses the update -+ * (e.g. due to controller restart or connectivity issues), the satellite keeps the old ports -+ * while peers get the new ones, causing DRBD connections to fail with StandAlone state. -+ */ + private void copyDrbdNodeIdIfExists(Resource rsc, LayerPayload payload) throws ImplementationError + { + Set> drbdRscDataSet = LayerRscUtils.getRscDataByLayer( +@@ -804,6 +817,28 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL + } + } + + private void copyDrbdTcpPortsIfExists(Resource rsc, LayerPayload payload) throws ImplementationError + { + Set> drbdRscDataSet = LayerRscUtils.getRscDataByLayer( @@ -117,22 +82,56 @@ index ccdb0cee5..b0554c2ec 100644 + payload.drbdRsc.tcpPorts = portInts; + } + } - } - ++ } ++ private List removeLayerData(Resource rscRef) + { + List layerList; +@@ -1058,15 +1093,15 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL + /* + * We also have to remove the possible meta-children of previous StorageRscData. + * LayerData will be recreated with ensureStackDataExists. +- * However, we still need to remember our node-id if we had / have DRBD in the list ++ * However, we still need to remember our DRBD settings if we had / have DRBD in the list + */ +- copyDrbdNodeIdIfExists(rsc, payload); ++ copyDrbdSettings(rsc, payload); + layerList = removeLayerData(rsc); + } + else + { + markDiskAdded(rsc); +- ctrlLayerStackHelper.resetStoragePools(rsc); ++ ctrlLayerStackHelper.resetStoragePools(rsc, false); + } + ctrlLayerStackHelper.ensureStackDataExists(rsc, layerList, payload); + diff --git a/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java b/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java -index 3538b380c..4f589145e 100644 +index 3538b380c..f9733b6f1 100644 --- a/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java +++ b/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java -@@ -276,8 +276,6 @@ public class CtrlRscLayerDataFactory - +@@ -263,6 +263,11 @@ public class CtrlRscLayerDataFactory + } + + public void resetStoragePools(Resource rscRef) ++ { ++ resetStoragePools(rscRef, true); ++ } ++ ++ public void resetStoragePools(Resource rscRef, boolean callEnsureStackDataExistsRef) + { + try + { +@@ -276,8 +281,10 @@ public class CtrlRscLayerDataFactory + rscDataToProcess.addAll(rscData.getChildren()); } - - ensureStackDataExists(rscRef, null, new LayerPayload()); ++ if (callEnsureStackDataExistsRef) ++ { ++ ensureStackDataExists(rscRef, null, new LayerPayload()); ++ } } catch (AccessDeniedException exc) { --- -2.39.5 (Apple Git-154) - diff --git a/packages/system/linstor/images/piraeus-server/patches/fix-luks-header-size.diff b/packages/system/linstor/images/piraeus-server/patches/fix-luks-header-size.diff new file mode 100644 index 00000000..8611825b --- /dev/null +++ b/packages/system/linstor/images/piraeus-server/patches/fix-luks-header-size.diff @@ -0,0 +1,570 @@ +diff --git a/satellite/src/main/java/com/linbit/linstor/layer/storage/AbsStorageProvider.java b/satellite/src/main/java/com/linbit/linstor/layer/storage/AbsStorageProvider.java +--- a/satellite/src/main/java/com/linbit/linstor/layer/storage/AbsStorageProvider.java ++++ b/satellite/src/main/java/com/linbit/linstor/layer/storage/AbsStorageProvider.java +@@ -2081,7 +2081,6 @@ public abstract class AbsStorageProvider< + ) + throws IOException, AccessDeniedException + { +- final StorPoolName storPoolObjName = storPoolObj.getName(); + if (storDevicePath != null) + { + errorReporter.logDebug("updateMinIoSize: Have storDevicePath \"%s\"", storDevicePath); +@@ -2094,35 +2093,18 @@ public abstract class AbsStorageProvider< + "updateMinIoSize: Block device path is \"%s\"", + blockDevicePath.toString() + ); +- final long minIoSize = BlockSizeInfo.getBlockSize(blockDevicePath); +- +- boolean updateValue = true; +- +- final String propKey = StorageConstants.NAMESPACE_INTERNAL + '/' + +- StorageConstants.BLK_DEV_MIN_IO_SIZE; +- final Props storPoolProps = storPoolObj.getProps(storDriverAccCtx); +- final @Nullable String currentPropValue = storPoolProps.getProp(propKey); +- if (currentPropValue != null) +- { +- try +- { +- final long currentPropMinIoSize = Long.parseLong(currentPropValue); +- updateValue = currentPropMinIoSize != minIoSize; +- } +- catch (NumberFormatException ignored) +- { +- } +- } +- +- if (updateValue) +- { +- final String propValue = Long.toString(minIoSize); +- errorReporter.logDebug( +- "Storage pool \"%s\": Set property \"%s\" = \"%s\"", +- storPoolObjName.displayValue, propKey, propValue +- ); +- propsChange.changeStorPoolProp(storPoolObj, propKey, propValue); +- } ++ updatePropIfNeeded( ++ propsChange, ++ storPoolObj, ++ StorageConstants.NAMESPACE_INTERNAL + '/' + StorageConstants.BLK_DEV_MIN_IO_SIZE, ++ Long.toString(BlockSizeInfo.getBlockSize(blockDevicePath)) ++ ); ++ updatePropIfNeeded( ++ propsChange, ++ storPoolObj, ++ StorageConstants.NAMESPACE_INTERNAL + '/' + StorageConstants.BLK_DEV_OPT_IO_SIZE, ++ Long.toString(BlockSizeInfo.getOptimalIoSize(blockDevicePath)) ++ ); + } + else + { +@@ -2130,6 +2112,28 @@ public abstract class AbsStorageProvider< + } + } + ++ private void updatePropIfNeeded( ++ LocalPropsChangePojo propsChangeRef, ++ StorPool storPoolObjRef, ++ String propKeyRef, ++ String propValueRef ++ ) ++ throws AccessDeniedException ++ { ++ final Props storPoolProps = storPoolObjRef.getProps(storDriverAccCtx); ++ final @Nullable String currentPropValue = storPoolProps.getProp(propKeyRef); ++ if (currentPropValue == null || !currentPropValue.equals(propValueRef)) ++ { ++ errorReporter.logDebug( ++ "Storage pool \"%s\": Set property \"%s\" = \"%s\"", ++ storPoolObjRef.getName().displayValue, ++ propKeyRef, ++ propValueRef ++ ); ++ propsChangeRef.changeStorPoolProp(storPoolObjRef, propKeyRef, propValueRef); ++ } ++ } ++ + @SuppressWarnings("unchecked") + @Override + public void updateAllocatedSize(VlmProviderObject vlmDataRef) +diff --git a/satellite/src/main/java/com/linbit/linstor/layer/storage/utils/BlockSizeInfo.java b/satellite/src/main/java/com/linbit/linstor/layer/storage/utils/BlockSizeInfo.java +--- a/satellite/src/main/java/com/linbit/linstor/layer/storage/utils/BlockSizeInfo.java ++++ b/satellite/src/main/java/com/linbit/linstor/layer/storage/utils/BlockSizeInfo.java +@@ -3,8 +3,11 @@ package com.linbit.linstor.layer.storage.utils; + import com.linbit.utils.MathUtils; + import com.linbit.utils.SymbolicLinkResolver; + ++import static com.linbit.linstor.layer.storage.BlockSizeConsts.DFLT_OPT_IO_SIZE; + import static com.linbit.linstor.layer.storage.BlockSizeConsts.DFLT_IO_SIZE; ++import static com.linbit.linstor.layer.storage.BlockSizeConsts.MAX_OPT_IO_SIZE; + import static com.linbit.linstor.layer.storage.BlockSizeConsts.MAX_IO_SIZE; ++import static com.linbit.linstor.layer.storage.BlockSizeConsts.MIN_OPT_IO_SIZE; + import static com.linbit.linstor.layer.storage.BlockSizeConsts.MIN_IO_SIZE; + + import java.io.FileInputStream; +@@ -13,6 +16,8 @@ import java.nio.file.Path; + + public class BlockSizeInfo + { ++ private static final int NUMBER_BUFFER_SIZE = 32; ++ + /** + * Determines the blocksize, aka minimum I/O size, for the specified backing storage path. + * +@@ -51,7 +56,7 @@ public class BlockSizeInfo + final Path infoSourceName = blockDevice.getFileName(); + final Path infoSource = Path.of("/sys/block", infoSourceName.toString(), "queue/physical_block_size"); + +- final byte[] data = new byte[32]; ++ final byte[] data = new byte[NUMBER_BUFFER_SIZE]; + try (final FileInputStream fileIn = new FileInputStream(infoSource.toString())) + { + final int readCount = fileIn.read(data); +@@ -75,4 +80,38 @@ public class BlockSizeInfo + } + return blockSize; + } ++ ++ public static long getOptimalIoSize(final Path storageObj) ++ { ++ long optIoSize = DFLT_OPT_IO_SIZE; ++ try ++ { ++ final Path blockDevice = SymbolicLinkResolver.resolveSymLink(storageObj); ++ final Path infoSourceName = blockDevice.getFileName(); ++ final Path infoSource = Path.of("/sys/block", infoSourceName.toString(), "queue/optimal_io_size"); ++ ++ final byte[] data = new byte[NUMBER_BUFFER_SIZE]; ++ try (final FileInputStream fileIn = new FileInputStream(infoSource.toString())) ++ { ++ final int readCount = fileIn.read(data); ++ if (readCount > 0) ++ { ++ String numberStr = new String(data, 0, readCount); ++ numberStr = numberStr.trim(); ++ try ++ { ++ final long unboundedOptIoSize = Long.parseLong(numberStr); ++ optIoSize = MathUtils.bounds(MIN_OPT_IO_SIZE, unboundedOptIoSize, MAX_OPT_IO_SIZE); ++ } ++ catch (NumberFormatException ignored) ++ { ++ } ++ } ++ } ++ } ++ catch (IOException ignored) ++ { ++ } ++ return optIoSize; ++ } + } +diff --git a/server/src/main/java/com/linbit/SizeConv.java b/server/src/main/java/com/linbit/SizeConv.java +--- a/server/src/main/java/com/linbit/SizeConv.java ++++ b/server/src/main/java/com/linbit/SizeConv.java +@@ -16,6 +16,7 @@ public class SizeConv + public enum SizeUnit + { + UNIT_B, ++ UNIT_SECTORS, + UNIT_KiB, + UNIT_MiB, + UNIT_GiB, +@@ -41,6 +42,9 @@ public class SizeConv + case UNIT_B: + factor = FACTOR_B; + break; ++ case UNIT_SECTORS: ++ factor = FACTOR_SECTORS; ++ break; + case UNIT_KiB: + factor = FACTOR_KiB; + break; +@@ -111,6 +115,9 @@ public class SizeConv + case "b": + unit = SizeUnit.UNIT_B; + break; ++ case "s": ++ unit = SizeUnit.UNIT_SECTORS; ++ break; + case "k": + // fall-through + case "kb": +@@ -217,6 +224,9 @@ public class SizeConv + // Factor 1 + public static final BigInteger FACTOR_B = BigInteger.valueOf(1L); + ++ // Factor 512 ++ public static final BigInteger FACTOR_SECTORS = BigInteger.valueOf(512L); ++ + // Factor 1,024 + // Naming convention exception: SI unit capitalization rules + @SuppressWarnings("checkstyle:constantname") +diff --git a/server/src/main/java/com/linbit/linstor/layer/luks/LuksLayerSizeCalculator.java b/server/src/main/java/com/linbit/linstor/layer/luks/LuksLayerSizeCalculator.java +--- a/server/src/main/java/com/linbit/linstor/layer/luks/LuksLayerSizeCalculator.java ++++ b/server/src/main/java/com/linbit/linstor/layer/luks/LuksLayerSizeCalculator.java +@@ -1,27 +1,62 @@ + package com.linbit.linstor.layer.luks; + ++import com.linbit.SizeConv; ++import com.linbit.SizeConv.SizeUnit; + import com.linbit.exceptions.InvalidSizeException; ++import com.linbit.linstor.PriorityProps; ++import com.linbit.linstor.annotation.Nullable; ++import com.linbit.linstor.api.ApiConsts; ++import com.linbit.linstor.core.objects.AbsResource; ++import com.linbit.linstor.core.objects.AbsVolume; ++import com.linbit.linstor.core.objects.Resource; ++import com.linbit.linstor.core.objects.ResourceDefinition; ++import com.linbit.linstor.core.objects.ResourceGroup; ++import com.linbit.linstor.core.objects.Snapshot; ++import com.linbit.linstor.core.objects.SnapshotVolume; ++import com.linbit.linstor.core.objects.StorPool; ++import com.linbit.linstor.core.objects.Volume; ++import com.linbit.linstor.core.objects.VolumeDefinition; + import com.linbit.linstor.dbdrivers.DatabaseException; + import com.linbit.linstor.layer.AbsLayerSizeCalculator; ++import com.linbit.linstor.netcom.Peer; ++import com.linbit.linstor.propscon.InvalidKeyException; ++import com.linbit.linstor.propscon.ReadOnlyProps; + import com.linbit.linstor.security.AccessDeniedException; ++import com.linbit.linstor.storage.StorageConstants; + import com.linbit.linstor.storage.data.adapter.luks.LuksVlmData; + import com.linbit.linstor.storage.interfaces.categories.resource.VlmProviderObject; + import com.linbit.linstor.storage.kinds.DeviceLayerKind; + import com.linbit.linstor.storage.kinds.ExtTools; + import com.linbit.linstor.storage.kinds.ExtToolsInfo; + import com.linbit.linstor.storage.kinds.ExtToolsInfo.Version; ++import com.linbit.linstor.utils.layer.LayerVlmUtils; ++import com.linbit.utils.ShellUtils; ++import com.linbit.utils.SignedAlign; + + import javax.inject.Inject; + import javax.inject.Singleton; + ++import java.util.Iterator; ++import java.util.LinkedList; ++import java.util.List; ++import java.util.Set; ++import java.util.regex.Matcher; ++import java.util.regex.Pattern; ++ + @Singleton + public class LuksLayerSizeCalculator extends AbsLayerSizeCalculator> + { ++ public static final String LUKS2_OPT_METADATA_SIZE = "--luks2-metadata-size"; ++ public static final String LUKS2_OPT_KEYSLOTS_SIZE = "--luks2-keyslots-size"; ++ public static final String LUKS2_OPT_OFFSET = "--offset"; ++ private static final String LUKS2_OPT_ALIGN_PAYLOAD = "--align-payload"; ++ private static final long DFLT_LUKS2_METADATA_SIZE_IN_BYTES = 16L << 10; ++ private static final long DFLT_ALIGNMENT_1MIB_IN_BYTES = 1L << 20; ++ private static final long LUKS1_HEADER_SIZE_IN_KIB = 2L << 10; ++ private static final long LUKS2_HEADER_SIZE_IN_KIB = 16L << 10; ++ private static final long LUKS2_HEADER_SIZE_IN_BYTES = LUKS2_HEADER_SIZE_IN_KIB << 10; + +- // linstor calculates in KiB +- private static final int MIB = 1024; +- private static final int LUKS1_HEADER_SIZE = 2 * MIB; +- private static final int LUKS2_HEADER_SIZE = 16 * MIB; ++ private static final Pattern PATTERN_SIZE = Pattern.compile("(\\d+)([kKmMgGtT]i?[bB]?|[sS]|)"); + + @Inject + public LuksLayerSizeCalculator(AbsLayerSizeCalculatorInit initRef) +@@ -74,30 +109,266 @@ public class LuksLayerSizeCalculator extends AbsLayerSizeCalculator vlmDataRef) +- throws AccessDeniedException ++ throws AccessDeniedException, InvalidSizeException + { +- ExtToolsInfo cryptSetupInfo = vlmDataRef.getRscLayerObject() ++ @Nullable Peer peer = vlmDataRef.getRscLayerObject() + .getAbsResource() + .getNode() +- .getPeer(sysCtx) +- .getExtToolsManager() ++ .getPeer(sysCtx); ++ if (peer == null) ++ { ++ throw new InvalidSizeException( ++ "Could not calculate size of LUKS volume, since cryptsetup's version could not be determined", ++ null ++ ); ++ } ++ ExtToolsInfo cryptSetupInfo = peer.getExtToolsManager() + .getExtToolInfo(ExtTools.CRYPT_SETUP); +- long luksHeaderSize; ++ ++ final long luksHeaderSize; + if (cryptSetupInfo != null && cryptSetupInfo.isSupported()) + { + if (cryptSetupInfo.hasVersionOrHigher(new Version(2, 1))) + { +- luksHeaderSize = LUKS2_HEADER_SIZE; ++ luksHeaderSize = calcLuks2HeaderSize(vlmDataRef); + } + else + { +- luksHeaderSize = LUKS1_HEADER_SIZE; ++ luksHeaderSize = LUKS1_HEADER_SIZE_IN_KIB; + } + } + else + { +- luksHeaderSize = -1; ++ throw new InvalidSizeException( ++ "Could not calculate size of LUKS volume, since cryptsetup's version could not be determined", ++ null ++ ); + } + return luksHeaderSize; + } ++ ++ private long calcLuks2HeaderSize(VlmProviderObject vlmDataRef) throws AccessDeniedException ++ { ++ PriorityProps prioProps = getPrioProps(vlmDataRef); ++ ++ final @Nullable String userOptProp = prioProps.getProp( ++ ApiConsts.KEY_STOR_DRIVER_LUKS_FORMAT_OPTIONS, ++ ApiConsts.NAMESPC_STORAGE_DRIVER ++ ); ++ List userOptions = userOptProp != null ? ++ ShellUtils.shellSplit(userOptProp) : ++ new LinkedList<>(); ++ ++ final long alignedHeaderSizeInBytes; ++ final long unalignedHeaderSizeInBytes; ++ @Nullable Long cryptsetupOffsetInBytes = getLongOptionValue( ++ userOptions, ++ LUKS2_OPT_OFFSET, ++ SizeUnit.UNIT_SECTORS ++ ); ++ if (cryptsetupOffsetInBytes != null) ++ { ++ alignedHeaderSizeInBytes = cryptsetupOffsetInBytes; ++ } ++ else ++ { ++ @Nullable Long cryptsetupLuks2KeyslotsSize = getLongOptionValue( ++ userOptions, ++ LUKS2_OPT_KEYSLOTS_SIZE ++ ); ++ if (cryptsetupLuks2KeyslotsSize == null) ++ { ++ unalignedHeaderSizeInBytes = LUKS2_HEADER_SIZE_IN_BYTES; ++ } ++ else ++ { ++ @Nullable Long cryptsetupLuks2MetadataSize = getLongOptionValue( ++ userOptions, ++ LUKS2_OPT_METADATA_SIZE ++ ); ++ cryptsetupLuks2MetadataSize = cryptsetupLuks2MetadataSize == null ? ++ DFLT_LUKS2_METADATA_SIZE_IN_BYTES : ++ cryptsetupLuks2MetadataSize; ++ ++ unalignedHeaderSizeInBytes = 2 * cryptsetupLuks2MetadataSize + cryptsetupLuks2KeyslotsSize; ++ } ++ ++ long alignment = getAlignment(vlmDataRef, userOptions); ++ alignedHeaderSizeInBytes = new SignedAlign(alignment).ceiling(unalignedHeaderSizeInBytes); ++ } ++ return SizeConv.convert( ++ alignedHeaderSizeInBytes, ++ SizeUnit.UNIT_B, ++ SizeUnit.UNIT_KiB ++ ); ++ } ++ ++ private long getAlignment(VlmProviderObject vlmDataRef, List userOptions) throws AccessDeniedException ++ { ++ @Nullable Long cryptsetupAlignPayloadInBytes = getLongOptionValue( ++ userOptions, ++ LUKS2_OPT_ALIGN_PAYLOAD, ++ SizeUnit.UNIT_SECTORS ++ ); ++ ++ long alignment = DFLT_ALIGNMENT_1MIB_IN_BYTES; ++ if (cryptsetupAlignPayloadInBytes != null) ++ { ++ alignment = cryptsetupAlignPayloadInBytes; ++ } ++ else ++ { ++ final long maxOptIoSize = getMaxOptIoSize(vlmDataRef); ++ alignment = Math.max(alignment, maxOptIoSize); ++ } ++ return alignment; ++ } ++ ++ private long getMaxOptIoSize(VlmProviderObject vlmDataRef) throws InvalidKeyException, AccessDeniedException ++ { ++ long ret = 0; ++ Set storPoolSet = LayerVlmUtils.getStorPoolSet(vlmDataRef, sysCtx); ++ for (StorPool sp : storPoolSet) ++ { ++ @Nullable String strValue = sp.getProps(sysCtx) ++ .getProp( ++ StorageConstants.BLK_DEV_OPT_IO_SIZE, ++ StorageConstants.NAMESPACE_INTERNAL ++ ); ++ if (strValue != null) ++ { ++ try ++ { ++ long parsed = Long.parseLong(strValue); ++ ret = Math.max(parsed, ret); ++ } ++ catch (NumberFormatException ignored) ++ { ++ errorReporter.logWarning( ++ "LuksHeaderSize: Failed to parse '%s' from prop %s. Defaulting to 0 opt_io_size " + ++ "(no recommendation/hint)", ++ strValue, ++ StorageConstants.NAMESPACE_INTERNAL + "/" + StorageConstants.BLK_DEV_OPT_IO_SIZE ++ ); ++ } ++ } ++ } ++ return ret; ++ } ++ ++ private @Nullable Long getLongOptionValue(List userOptionsRef, String optRef) ++ { ++ return getLongOptionValue(userOptionsRef, optRef, SizeUnit.UNIT_B); ++ } ++ ++ @SuppressWarnings("checkstyle:magicnumber") ++ private @Nullable Long getLongOptionValue(List userOptionsRef, String optRef, SizeUnit dfltSizeUnit) ++ { ++ @Nullable Long ret = null; ++ @Nullable String val = findLastValue(userOptionsRef, optRef); ++ if (val != null && !val.isBlank()) ++ { ++ Matcher matcher = PATTERN_SIZE.matcher(val); ++ if (matcher.matches()) ++ { ++ try ++ { ++ ret = Long.parseLong(matcher.group(1)); ++ String unit = matcher.group(2); ++ SizeUnit sizeUnit; ++ if (unit.isBlank()) ++ { ++ sizeUnit = dfltSizeUnit; ++ } ++ else ++ { ++ boolean forcePowerOfTwo = unit.length() == 1 || unit.length() == 3; ++ sizeUnit = SizeUnit.parse(unit, forcePowerOfTwo); ++ } ++ ++ ret = SizeConv.convert(ret, sizeUnit, SizeUnit.UNIT_B); ++ } ++ catch (NumberFormatException ignored) ++ { ++ errorReporter.logWarning( ++ "LuksHeaderSize: Failed to parse '%s' from option '%s %s'.", ++ matcher.group(1), ++ optRef, ++ val ++ ); ++ } ++ } ++ } ++ return ret; ++ } ++ ++ private @Nullable String findLastValue(List userOptionsRef, String optRef) ++ { ++ Iterator it = userOptionsRef.iterator(); ++ @Nullable String val = null; ++ while (it.hasNext()) ++ { ++ String opt = it.next(); ++ if (opt.equals(optRef)) ++ { ++ if (it.hasNext()) ++ { ++ val = it.next(); ++ } ++ else ++ { ++ val = null; ++ } ++ } ++ else if (opt.startsWith(optRef + "=")) ++ { ++ val = opt.substring(optRef.length() + 1); ++ if (val.isBlank()) ++ { ++ val = null; ++ } ++ } ++ } ++ return val; ++ } ++ ++ private PriorityProps getPrioProps(VlmProviderObject vlmDataRef) throws AccessDeniedException ++ { ++ final AbsVolume vlm = vlmDataRef.getVolume(); ++ final AbsResource rsc = vlm.getAbsResource(); ++ final ResourceDefinition rscDfn = vlm.getResourceDefinition(); ++ final VolumeDefinition vlmDfn = vlm.getVolumeDefinition(); ++ final ResourceGroup rscGrp = rscDfn.getResourceGroup(); ++ ++ final ReadOnlyProps vlmProps; ++ final ReadOnlyProps rscProps; ++ if (vlm instanceof Volume) ++ { ++ vlmProps = ((Volume) vlm).getProps(sysCtx); ++ rscProps = ((Resource) rsc).getProps(sysCtx); ++ } ++ else ++ { ++ vlmProps = ((SnapshotVolume) vlm).getVlmProps(sysCtx); ++ rscProps = ((Snapshot) rsc).getRscProps(sysCtx); ++ } ++ ++ final PriorityProps prioProps = new PriorityProps( ++ vlmProps, ++ rscProps ++ ); ++ for (StorPool storPool : LayerVlmUtils.getStorPoolSet(vlmDataRef, sysCtx)) ++ { ++ prioProps.addProps(storPool.getProps(sysCtx)); ++ } ++ prioProps.addProps( ++ rsc.getNode().getProps(sysCtx), ++ vlmDfn.getProps(sysCtx), ++ rscDfn.getProps(sysCtx), ++ rscGrp.getVolumeGroupProps(sysCtx, vlmDfn.getVolumeNumber()), ++ rscGrp.getProps(sysCtx), ++ stltProps ++ ); ++ return prioProps; ++ } + } +diff --git a/server/src/main/java/com/linbit/linstor/layer/storage/BlockSizeConsts.java b/server/src/main/java/com/linbit/linstor/layer/storage/BlockSizeConsts.java +--- a/server/src/main/java/com/linbit/linstor/layer/storage/BlockSizeConsts.java ++++ b/server/src/main/java/com/linbit/linstor/layer/storage/BlockSizeConsts.java +@@ -13,4 +13,9 @@ public class BlockSizeConsts + + // Default value for the minimum_io_size value of non-storage layers + public static final long DFLT_SPECIAL_IO_SIZE = (1L << 12); ++ ++ // optimal_io_size may be 0 to indicate no recommendation. ++ public static final long MIN_OPT_IO_SIZE = 0; ++ public static final long DFLT_OPT_IO_SIZE = 0; ++ public static final long MAX_OPT_IO_SIZE = Long.MAX_VALUE; + } +diff --git a/server/src/main/java/com/linbit/linstor/storage/StorageConstants.java b/server/src/main/java/com/linbit/linstor/storage/StorageConstants.java +--- a/server/src/main/java/com/linbit/linstor/storage/StorageConstants.java ++++ b/server/src/main/java/com/linbit/linstor/storage/StorageConstants.java +@@ -11,6 +11,7 @@ public class StorageConstants + public static final String NAMESPACE_NVME = ApiConsts.NAMESPC_STORAGE_DRIVER + "/NVME"; + public static final String NAMESPACE_INTERNAL = NAMESPACE_STOR_DRIVER + "/internal/"; + ++ public static final String BLK_DEV_OPT_IO_SIZE = "optIoSize"; + public static final String BLK_DEV_MIN_IO_SIZE = "minIoSize"; + public static final String BLK_DEV_MIN_IO_SIZE_AUTO = "minIoSizeAuto"; + public static final String BLK_DEV_MAX_BIO_SIZE = "maxBioSize"; diff --git a/packages/system/linstor/images/piraeus-server/patches/force-metadata-check-on-disk-add.diff b/packages/system/linstor/images/piraeus-server/patches/force-metadata-check-on-disk-add.diff deleted file mode 100644 index 0c413005..00000000 --- a/packages/system/linstor/images/piraeus-server/patches/force-metadata-check-on-disk-add.diff +++ /dev/null @@ -1,63 +0,0 @@ -diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java -index a302ee835..01967a31f 100644 ---- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java -+++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java -@@ -371,10 +371,13 @@ public class DrbdLayer implements DeviceLayer - boolean isDiskless = drbdRscData.getAbsResource().isDrbdDiskless(workerCtx); - StateFlags rscFlags = drbdRscData.getAbsResource().getStateFlags(); - boolean isDiskRemoving = rscFlags.isSet(workerCtx, Resource.Flags.DISK_REMOVING); -+ // Check if we're in toggle-disk operation (adding disk to diskless resource) -+ boolean isDiskAdding = rscFlags.isSomeSet(workerCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING); - - boolean contProcess = isDiskless; - -- boolean processChildren = !isDiskless || isDiskRemoving; -+ // Process children when: has disk, removing disk, OR adding disk (toggle-disk) -+ boolean processChildren = !isDiskless || isDiskRemoving || isDiskAdding; - // do not process children when ONLY DRBD_DELETE flag is set (DELETE flag is still unset) - processChildren &= (!rscFlags.isSet(workerCtx, Resource.Flags.DRBD_DELETE) || - rscFlags.isSet(workerCtx, Resource.Flags.DELETE)); -@@ -570,7 +573,11 @@ public class DrbdLayer implements DeviceLayer - { - // hasMetaData needs to be run after child-resource processed - List> createMetaData = new ArrayList<>(); -- if (!drbdRscData.getAbsResource().isDrbdDiskless(workerCtx) && !skipDisk) -+ // Check if we're in toggle-disk operation (adding disk to diskless resource) -+ boolean isDiskAddingForMd = drbdRscData.getAbsResource().getStateFlags() -+ .isSomeSet(workerCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING); -+ // Create metadata when: has disk OR adding disk (toggle-disk), and skipDisk is disabled -+ if ((!drbdRscData.getAbsResource().isDrbdDiskless(workerCtx) || isDiskAddingForMd) && !skipDisk) - { - // do not try to create meta data while the resource is diskless or skipDisk is enabled - for (DrbdVlmData drbdVlmData : checkMetaData) -@@ -988,8 +995,10 @@ public class DrbdLayer implements DeviceLayer - { - List> checkMetaData = new ArrayList<>(); - Resource rsc = drbdRscData.getAbsResource(); -+ // Include DISK_ADD_REQUESTED/DISK_ADDING for toggle-disk scenario where we need to check/create metadata - if (!rsc.isDrbdDiskless(workerCtx) || -- rsc.getStateFlags().isSet(workerCtx, Resource.Flags.DISK_REMOVING) -+ rsc.getStateFlags().isSet(workerCtx, Resource.Flags.DISK_REMOVING) || -+ rsc.getStateFlags().isSomeSet(workerCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING) - ) - { - // using a dedicated list to prevent concurrentModificationException -@@ -1177,9 +1186,16 @@ public class DrbdLayer implements DeviceLayer - - boolean hasMetaData; - -+ // Check if we need to verify/create metadata -+ // Force metadata check when: -+ // 1. checkMetaData is enabled -+ // 2. volume doesn't have disk yet (diskless -> diskful transition) -+ // 3. DISK_ADD_REQUESTED/DISK_ADDING flag is set (retry scenario where storage exists but no metadata) -+ boolean isDiskAddingState = drbdVlmData.getRscLayerObject().getAbsResource().getStateFlags() -+ .isSomeSet(workerCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING); - if (drbdVlmData.checkMetaData() || -- // when adding a disk, DRBD believes that it is diskless but we still need to create metadata -- !drbdVlmData.hasDisk()) -+ !drbdVlmData.hasDisk() || -+ isDiskAddingState) - { - if (mdUtils.hasMetaData()) - { diff --git a/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff b/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff deleted file mode 100644 index a93d7811..00000000 --- a/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff +++ /dev/null @@ -1,155 +0,0 @@ -diff --git a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java -index 49138a8fd..2f768ca0d 100644 ---- a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java -+++ b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java -@@ -83,6 +83,8 @@ import java.util.TreeMap; - import java.util.TreeSet; - import java.util.concurrent.atomic.AtomicBoolean; - import java.util.function.Function; -+import java.nio.file.Files; -+import java.nio.file.Paths; - - @Singleton - public class DeviceHandlerImpl implements DeviceHandler -@@ -1646,7 +1648,10 @@ public class DeviceHandlerImpl implements DeviceHandler - private void updateDiscGran(VlmProviderObject vlmData) throws DatabaseException, StorageException - { - String devicePath = vlmData.getDevicePath(); -- if (devicePath != null && vlmData.exists()) -+ // Check if device path physically exists before calling lsblk -+ // This is important for DRBD devices which might be temporarily unavailable during adjust -+ // (drbdadm adjust brings devices down/up, and kernel might not have created the device node yet) -+ if (devicePath != null && vlmData.exists() && Files.exists(Paths.get(devicePath))) - { - if (vlmData.getDiscGran() == VlmProviderObject.UNINITIALIZED_SIZE) - { -diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java -index 01967a31f..78b8195a4 100644 ---- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java -+++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java -@@ -592,7 +592,29 @@ public class DrbdLayer implements DeviceLayer - // The .res file might not have been generated in the prepare method since it was - // missing information from the child-layers. Now that we have processed them, we - // need to make sure the .res file exists in all circumstances. -- regenerateResFile(drbdRscData); -+ // However, if the underlying devices are not accessible (e.g., LUKS device is closed -+ // during resource deletion), we skip regenerating the res file to avoid errors -+ boolean canRegenerateResFile = true; -+ if (!skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx)) -+ { -+ AbsRscLayerObject dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA); -+ if (dataChild != null) -+ { -+ for (DrbdVlmData drbdVlmData : drbdRscData.getVlmLayerObjects().values()) -+ { -+ VlmProviderObject childVlm = dataChild.getVlmProviderObject(drbdVlmData.getVlmNr()); -+ if (childVlm == null || !childVlm.exists() || childVlm.getDevicePath() == null) -+ { -+ canRegenerateResFile = false; -+ break; -+ } -+ } -+ } -+ } -+ if (canRegenerateResFile) -+ { -+ regenerateResFile(drbdRscData); -+ } - - // createMetaData needs rendered resFile - for (DrbdVlmData drbdVlmData : createMetaData) -@@ -766,19 +788,72 @@ public class DrbdLayer implements DeviceLayer - - if (drbdRscData.isAdjustRequired()) - { -- try -+ // Check if underlying devices are accessible before adjusting -+ // This is important for encrypted resources (LUKS) where the device -+ // might be closed during deletion -+ boolean canAdjust = true; -+ -+ // IMPORTANT: Check child volumes only when disk access is actually needed. -+ // For network reconnect (StandAlone -> Connected), disk access is not required. -+ boolean needsDiskAccess = false; -+ -+ // Check if there are pending operations that require disk access -+ for (DrbdVlmData drbdVlmData : drbdRscData.getVlmLayerObjects().values()) - { -- drbdUtils.adjust( -- drbdRscData, -- false, -- skipDisk, -- false -- ); -+ Volume vlm = (Volume) drbdVlmData.getVolume(); -+ StateFlags vlmFlags = vlm.getFlags(); -+ -+ // Disk access is needed if: -+ // - creating a new volume -+ // - resizing -+ // - checking/creating metadata -+ if (!drbdVlmData.exists() || -+ drbdVlmData.checkMetaData() || -+ vlmFlags.isSomeSet(workerCtx, Volume.Flags.RESIZE, Volume.Flags.DRBD_RESIZE)) -+ { -+ needsDiskAccess = true; -+ break; -+ } -+ } -+ -+ // Check child volumes only if disk access is actually needed -+ if (needsDiskAccess && !skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx)) -+ { -+ AbsRscLayerObject dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA); -+ if (dataChild != null) -+ { -+ for (DrbdVlmData drbdVlmData : drbdRscData.getVlmLayerObjects().values()) -+ { -+ VlmProviderObject childVlm = dataChild.getVlmProviderObject(drbdVlmData.getVlmNr()); -+ if (childVlm == null || !childVlm.exists() || childVlm.getDevicePath() == null) -+ { -+ canAdjust = false; -+ break; -+ } -+ } -+ } - } -- catch (ExtCmdFailedException extCmdExc) -+ -+ if (canAdjust) -+ { -+ try -+ { -+ drbdUtils.adjust( -+ drbdRscData, -+ false, -+ skipDisk, -+ false -+ ); -+ } -+ catch (ExtCmdFailedException extCmdExc) -+ { -+ restoreBackupResFile(drbdRscData); -+ throw extCmdExc; -+ } -+ } -+ else - { -- restoreBackupResFile(drbdRscData); -- throw extCmdExc; -+ drbdRscData.setAdjustRequired(false); - } - } - -diff --git a/satellite/src/main/java/com/linbit/linstor/layer/luks/LuksLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/luks/LuksLayer.java -index cdca0b6d2..89c8be9da 100644 ---- a/satellite/src/main/java/com/linbit/linstor/layer/luks/LuksLayer.java -+++ b/satellite/src/main/java/com/linbit/linstor/layer/luks/LuksLayer.java -@@ -383,6 +383,7 @@ public class LuksLayer implements DeviceLayer - vlmData.setSizeState(Size.AS_EXPECTED); - - vlmData.setOpened(true); -+ vlmData.setExists(true); - vlmData.setFailed(false); - } - } From 198c57c1ea6c72822b34d55d828b902c783fd011 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Fri, 3 Apr 2026 21:50:52 +0200 Subject: [PATCH 17/36] linstor: add stale bitmap adjust retry patch Signed-off-by: Andrei Kvapil (cherry picked from commit fd6bae62b2ea9a353230637ac2b717c473d7de7d) --- .../images/piraeus-server/patches/README.md | 3 + .../retry-adjust-after-stale-bitmap.diff | 141 ++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 packages/system/linstor/images/piraeus-server/patches/retry-adjust-after-stale-bitmap.diff diff --git a/packages/system/linstor/images/piraeus-server/patches/README.md b/packages/system/linstor/images/piraeus-server/patches/README.md index db497b7e..47e5f635 100644 --- a/packages/system/linstor/images/piraeus-server/patches/README.md +++ b/packages/system/linstor/images/piraeus-server/patches/README.md @@ -12,3 +12,6 @@ Custom patches for piraeus-server (linstor-server) v1.33.1. - Source PR/comment: [#472](https://github.com/LINBIT/linstor-server/pull/472), [maintainer note](https://github.com/LINBIT/linstor-server/pull/472#issuecomment-3949687603) - Backported from commits: [`ccc85fbd2`](https://github.com/LINBIT/linstor-server/commit/ccc85fbd2c65f0b97c52403fa80f1efdb886ec4e), [`71b601554`](https://github.com/LINBIT/linstor-server/commit/71b601554f41bcb50cd5bd06989c5b0d3a814acd) - Note: upstream commit [`3d0402a0c`](https://github.com/LINBIT/linstor-server/commit/3d0402a0c25f0a4b57b380321f10e89982f26e7a) is already included in `v1.33.1` +- **retry-adjust-after-stale-bitmap.diff** — Retry `drbdadm adjust` after detaching a stale local bitmap state + - Source PR: [#491](https://github.com/LINBIT/linstor-server/pull/491) + - Backported from commit: [`51ae50a84`](https://github.com/kvaps/linstor-server/commit/51ae50a84dcb98093f543b819652c750a94d96c9) diff --git a/packages/system/linstor/images/piraeus-server/patches/retry-adjust-after-stale-bitmap.diff b/packages/system/linstor/images/piraeus-server/patches/retry-adjust-after-stale-bitmap.diff new file mode 100644 index 00000000..00959167 --- /dev/null +++ b/packages/system/linstor/images/piraeus-server/patches/retry-adjust-after-stale-bitmap.diff @@ -0,0 +1,141 @@ +diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/DrbdAdm.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/DrbdAdm.java +index 5627d1be8..ece191292 100644 +--- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/DrbdAdm.java ++++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/DrbdAdm.java +@@ -42,6 +42,9 @@ import java.util.Arrays; + import java.util.List; + import java.util.concurrent.ArrayBlockingQueue; + import java.util.concurrent.TimeUnit; ++import java.nio.charset.StandardCharsets; ++import java.util.regex.Matcher; ++import java.util.regex.Pattern; + import java.util.stream.Collectors; + + @Singleton +@@ -56,6 +58,9 @@ public class DrbdAdm + + public static final int WAIT_CONNECT_RES_TIME = 10; + private static final long DOWN_WAIT_TIMEOUT_SEC = 5; ++ private static final long FORCE_DETACH_RETRY_WAIT_MS = 250; ++ private static final String BITMAP_LEAK_ERR_MSG = "already has a bitmap, this should not happen"; ++ private static final Pattern BITMAP_LEAK_MINOR_PATTERN = Pattern.compile("\\bminor\\s+(\\d+)\\b"); + + private final ExtCmdFactory extCmdFactory; + private final AccessContext sysCtx; +@@ -131,8 +136,38 @@ public class DrbdAdm + // command.add(resName); + command.add(drbdRscData.getSuffixedResourceName()); + // execute(Arrays.asList("drbdsetup", "show", drbdRscData.getSuffixedResourceName())); +- execute(command); +- // execute(Arrays.asList("drbdsetup", "show", drbdRscData.getSuffixedResourceName())); ++ String[] commandArr = command.toArray(new String[0]); ++ try ++ { ++ File nullDevice = new File(Platform.nullDevice()); ++ ExtCmd extCmd = extCmdFactory.create(); ++ if (Platform.isWindows()) ++ { ++ extCmd.setTimeout(TimeoutType.WAIT, 5 * 60 * 1000); ++ } ++ ++ OutputData outputData = extCmd.pipeExec(ProcessBuilder.Redirect.from(nullDevice), commandArr); ++ if ( ++ outputData.exitCode != 0 && ++ isBitmapLeakOnAttach(outputData) && ++ cleanupStaleBitmapAndRetry(extCmd, nullDevice, outputData) ++ ) ++ { ++ outputData = extCmd.pipeExec(ProcessBuilder.Redirect.from(nullDevice), commandArr); ++ } ++ if (outputData.exitCode != 0) ++ { ++ throw new ExtCmdFailedException(commandArr, outputData); ++ } ++ } ++ catch (ChildProcessTimeoutException timeoutExc) ++ { ++ throw new ExtCmdFailedException(commandArr, timeoutExc); ++ } ++ catch (IOException ioExc) ++ { ++ throw new ExtCmdFailedException(commandArr, ioExc); ++ } + + drbdRscData.setAdjustRequired(false); + } +@@ -805,6 +840,75 @@ public class DrbdAdm + } + } + ++ static boolean isBitmapLeakOnAttach(OutputData outputData) ++ { ++ return extractBitmapLeakMinor(outputData) != null; ++ } ++ ++ static @Nullable Integer extractBitmapLeakMinor(OutputData outputData) ++ { ++ String stderr = new String(outputData.stderrData, StandardCharsets.UTF_8); ++ if (!stderr.contains(BITMAP_LEAK_ERR_MSG)) ++ { ++ return null; ++ } ++ ++ Matcher matcher = BITMAP_LEAK_MINOR_PATTERN.matcher(stderr); ++ if (!matcher.find()) ++ { ++ return null; ++ } ++ ++ return Integer.parseInt(matcher.group(1)); ++ } ++ ++ private boolean cleanupStaleBitmapAndRetry( ++ ExtCmd extCmd, ++ File nullDevice, ++ OutputData outputData ++ ) ++ throws IOException, ChildProcessTimeoutException, ExtCmdFailedException ++ { ++ @Nullable Integer minor = extractBitmapLeakMinor(outputData); ++ if (minor == null) ++ { ++ return false; ++ } ++ ++ OutputData detachOut = extCmd.pipeExec( ++ ProcessBuilder.Redirect.from(nullDevice), ++ DRBDSETUP_UTIL, ++ "detach", ++ Integer.toString(minor) ++ ); ++ if (detachOut.exitCode == 0) ++ { ++ return true; ++ } ++ ++ OutputData forceDetachOut = extCmd.pipeExec( ++ ProcessBuilder.Redirect.from(nullDevice), ++ DRBDSETUP_UTIL, ++ "detach", ++ Integer.toString(minor), ++ "--force" ++ ); ++ if (forceDetachOut.exitCode != 0) ++ { ++ throw new ExtCmdFailedException(forceDetachOut.executedCommand, forceDetachOut); ++ } ++ ++ try ++ { ++ Thread.sleep(FORCE_DETACH_RETRY_WAIT_MS); ++ } ++ catch (InterruptedException ignored) ++ { ++ Thread.currentThread().interrupt(); ++ } ++ return true; ++ } ++ + public static class DrbdPrimary implements AutoCloseable + { + private final DrbdAdm drbdAdm; From 69ccaa772ef82d24820cff6cadafa9940d6e0775 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Wed, 8 Apr 2026 17:08:54 +0200 Subject: [PATCH 18/36] linstor: bump piraeus-server from v1.33.1 to v1.33.2 All existing patches (allow-toggle-disk-retry, fix-duplicate-tcp-ports, fix-luks-header-size, retry-adjust-after-stale-bitmap) apply cleanly on the new version without modifications. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil (cherry picked from commit 7fb0c575c01f039d747e7a112fdabc0b21681b4b) --- packages/system/linstor/Makefile | 2 +- packages/system/linstor/images/piraeus-server/patches/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/system/linstor/Makefile b/packages/system/linstor/Makefile index 2bc3a2a1..2ad4def9 100644 --- a/packages/system/linstor/Makefile +++ b/packages/system/linstor/Makefile @@ -4,7 +4,7 @@ export NAMESPACE=cozy-$(NAME) include ../../../hack/common-envs.mk include ../../../hack/package.mk -LINSTOR_VERSION ?= 1.33.1 +LINSTOR_VERSION ?= 1.33.2 LINSTOR_CSI_VERSION ?= v1.10.5 image: image-piraeus-server image-linstor-csi diff --git a/packages/system/linstor/images/piraeus-server/patches/README.md b/packages/system/linstor/images/piraeus-server/patches/README.md index 47e5f635..dfe75c2e 100644 --- a/packages/system/linstor/images/piraeus-server/patches/README.md +++ b/packages/system/linstor/images/piraeus-server/patches/README.md @@ -1,6 +1,6 @@ # LINSTOR Server Patches -Custom patches for piraeus-server (linstor-server) v1.33.1. +Custom patches for piraeus-server (linstor-server) v1.33.2. - **allow-toggle-disk-retry.diff** — Backport maintainer implementation of toggle-disk retry/abort - Source PR/comment: [#475](https://github.com/LINBIT/linstor-server/pull/475), [maintainer note](https://github.com/LINBIT/linstor-server/pull/475#issuecomment-3949630419) From 06dce029c9dc8e07e44cd8fb0329d6544ab42e4d Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Sat, 11 Apr 2026 13:23:33 +0300 Subject: [PATCH 19/36] [cilium] Opt-out of cri-containerd.apparmor.d for nsenter init containers Cilium 1.19 init containers mount-cgroup, apply-sysctl-overwrites, mount-bpf-fs and clean-cilium-state call nsenter --ptrace, which is denied by the cri-containerd.apparmor.d profile on Ubuntu 22.04+ and any other distro loading this AppArmor profile. As a result cilium agent pods land in Init:CrashLoopBackOff and downstream HelmReleases cascade into "dependency not ready". The modern k8s >= 1.30 pod-level appArmorProfile: Unconfined field is accepted into the DaemonSet spec but not honoured by containerd CRI at runtime (kubernetes/kubernetes#125069), so the deprecated per-container AppArmor annotations remain the only reliable opt-out. Add the annotations via podAnnotations in the wrapper values.yaml. The change is harmless on systems without AppArmor (Talos, RHEL/Rocky/AlmaLinux with SELinux) because kubelet silently ignores unknown AppArmor annotations when the LSM is not loaded. Assisted-By: Claude Signed-off-by: Aleksei Sviridkin (cherry picked from commit 6c0ae2570ec36f3edbb09c8136535b4c1807c273) --- packages/system/cilium/values.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/system/cilium/values.yaml b/packages/system/cilium/values.yaml index cfb2cd6b..048c4312 100644 --- a/packages/system/cilium/values.yaml +++ b/packages/system/cilium/values.yaml @@ -23,3 +23,18 @@ cilium: operator: rollOutPods: true replicas: 1 + # Opt out of the cri-containerd.apparmor.d profile for containers that invoke + # nsenter --ptrace to join the host cgroup/mount namespace. Required on + # Ubuntu 22.04+ and other distros that load this AppArmor profile by default, + # where the denial puts cilium-agent into Init:CrashLoopBackOff. + # The deprecated annotations are used because k8s >= 1.30 pod-level + # appArmorProfile: Unconfined is not honoured by containerd CRI today + # (kubernetes/kubernetes#125069). + # Safe on Talos / RHEL family without AppArmor: kubelet ignores these + # annotations when the AppArmor LSM is not loaded. + podAnnotations: + container.apparmor.security.beta.kubernetes.io/cilium-agent: unconfined + container.apparmor.security.beta.kubernetes.io/clean-cilium-state: unconfined + container.apparmor.security.beta.kubernetes.io/mount-cgroup: unconfined + container.apparmor.security.beta.kubernetes.io/apply-sysctl-overwrites: unconfined + container.apparmor.security.beta.kubernetes.io/mount-bpf-fs: unconfined From 5bf0112bed678075b4db825dbd718d1082698590 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Sat, 11 Apr 2026 13:41:01 +0300 Subject: [PATCH 20/36] [cilium] Drop mount-bpf-fs from AppArmor unconfined annotations mount-bpf-fs runs as privileged in the vendored chart, and the Linux kernel does not apply AppArmor profiles to privileged containers. The per-container unconfined annotation for this container is a no-op and only widens the pod's admission-policy surface for no reason. Keep the four meaningful annotations (cilium-agent, clean-cilium-state, mount-cgroup, apply-sysctl-overwrites) which target unprivileged agent and init containers actually affected by the cri-containerd.apparmor.d profile. Assisted-By: Claude Signed-off-by: Aleksei Sviridkin (cherry picked from commit 2531ab661a34ea9f07040db965d8db5d2ffe7201) --- packages/system/cilium/values.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/system/cilium/values.yaml b/packages/system/cilium/values.yaml index 048c4312..4b2e1ff3 100644 --- a/packages/system/cilium/values.yaml +++ b/packages/system/cilium/values.yaml @@ -32,9 +32,10 @@ cilium: # (kubernetes/kubernetes#125069). # Safe on Talos / RHEL family without AppArmor: kubelet ignores these # annotations when the AppArmor LSM is not loaded. + # mount-bpf-fs is intentionally excluded: it runs as privileged, and the + # kernel does not apply AppArmor profiles to privileged containers. podAnnotations: container.apparmor.security.beta.kubernetes.io/cilium-agent: unconfined container.apparmor.security.beta.kubernetes.io/clean-cilium-state: unconfined container.apparmor.security.beta.kubernetes.io/mount-cgroup: unconfined container.apparmor.security.beta.kubernetes.io/apply-sysctl-overwrites: unconfined - container.apparmor.security.beta.kubernetes.io/mount-bpf-fs: unconfined From 1433adf88f2d9d5af925c6ee4c55f55d40799ba2 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Sat, 11 Apr 2026 13:54:16 +0300 Subject: [PATCH 21/36] [cilium] Document duplicate-key rendering on unsupported k8s < 1.30 On Kubernetes versions below 1.30 the upstream cilium chart still emits the deprecated per-container AppArmor annotations from its own semverCompare '<1.30.0' branch. Our podAnnotations override then emits the same keys again, yielding a YAML mapping with duplicate entries (identical values, last-wins by kubernetes API server JSON semantics). Cozystack's supported Kubernetes matrix starts at 1.30 (packages/apps/kubernetes/files/versions.yaml), so this rendering quirk never affects any shipped version. Document the behaviour in a comment so future readers are not surprised. No functional change. Assisted-By: Claude Signed-off-by: Aleksei Sviridkin (cherry picked from commit 703dbca734c07162036d4d9dc5c12180ce59b438) --- packages/system/cilium/values.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/system/cilium/values.yaml b/packages/system/cilium/values.yaml index 4b2e1ff3..cd31c107 100644 --- a/packages/system/cilium/values.yaml +++ b/packages/system/cilium/values.yaml @@ -34,6 +34,11 @@ cilium: # annotations when the AppArmor LSM is not loaded. # mount-bpf-fs is intentionally excluded: it runs as privileged, and the # kernel does not apply AppArmor profiles to privileged containers. + # On unsupported k8s < 1.30 the upstream chart renders the same keys + # inside its own <1.30 branch, yielding a duplicate mapping with + # identical values (last-wins). The supported matrix starts at 1.30 + # (packages/apps/kubernetes/files/versions.yaml), so this does not + # affect any shipped version. podAnnotations: container.apparmor.security.beta.kubernetes.io/cilium-agent: unconfined container.apparmor.security.beta.kubernetes.io/clean-cilium-state: unconfined From d093ec2eb0570823fd1113a6c0aa6b43d6ee0316 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Sat, 11 Apr 2026 14:39:37 +0300 Subject: [PATCH 22/36] [cilium] Strip hardcoded AppArmor annotations from vendored daemonset The upstream chart emits per-container AppArmor unconfined annotations inside a k8s<1.30 branch of cilium-agent/daemonset.yaml. Cozystack now owns these annotations via cilium.podAnnotations in values.yaml so they also take effect on k8s>=1.30 (where upstream drops them in favour of the containerd-broken podSecurityContext.appArmorProfile). Having both sources emit the same keys produced a duplicate mapping on k8s<1.30. Add a new SED_INPLACE invocation to the package Makefile that removes the four hardcoded annotation lines from the vendored template, and apply the patch to the currently vendored copy so make update will reproduce the same state on future refreshes. Assisted-By: Claude Signed-off-by: Aleksei Sviridkin (cherry picked from commit 182fc0b52d936a94144d1ba85723d3afd7b090dc) --- packages/system/cilium/Makefile | 10 ++++++++++ .../cilium/templates/cilium-agent/daemonset.yaml | 4 ---- packages/system/cilium/values.yaml | 5 ----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/system/cilium/Makefile b/packages/system/cilium/Makefile index 07d4ca97..3c90fd21 100644 --- a/packages/system/cilium/Makefile +++ b/packages/system/cilium/Makefile @@ -12,6 +12,16 @@ update: helm repo update cilium helm pull cilium/cilium --untar --untardir charts --version 1.19 $(SED_INPLACE) -e '/Used in iptables/d' -e '/SYS_MODULE/d' charts/cilium/values.yaml + # Drop the hardcoded AppArmor unconfined annotations from the cilium-agent + # DaemonSet's k8s<1.30 branch. Cozystack manages these through + # cilium.podAnnotations in values.yaml for all k8s versions, and keeping the + # upstream block would produce duplicate mapping keys on k8s<1.30. + $(SED_INPLACE) \ + -e '/container\.apparmor\.security\.beta\.kubernetes\.io\/cilium-agent: "unconfined"/d' \ + -e '/container\.apparmor\.security\.beta\.kubernetes\.io\/clean-cilium-state: "unconfined"/d' \ + -e '/container\.apparmor\.security\.beta\.kubernetes\.io\/mount-cgroup: "unconfined"/d' \ + -e '/container\.apparmor\.security\.beta\.kubernetes\.io\/apply-sysctl-overwrites: "unconfined"/d' \ + charts/cilium/templates/cilium-agent/daemonset.yaml version=$$(awk '$$1 == "version:" {print $$2}' charts/cilium/Chart.yaml) && \ $(SED_INPLACE) "s/ARG VERSION=.*/ARG VERSION=v$${version}/" images/cilium/Dockerfile diff --git a/packages/system/cilium/charts/cilium/templates/cilium-agent/daemonset.yaml b/packages/system/cilium/charts/cilium/templates/cilium-agent/daemonset.yaml index f9a4cec7..bb2c5a15 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-agent/daemonset.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-agent/daemonset.yaml @@ -62,11 +62,7 @@ spec: # Set app AppArmor's profile to "unconfined". The value of this annotation # can be modified as long users know which profiles they have available # in AppArmor. - container.apparmor.security.beta.kubernetes.io/cilium-agent: "unconfined" - container.apparmor.security.beta.kubernetes.io/clean-cilium-state: "unconfined" {{- if .Values.cgroup.autoMount.enabled }} - container.apparmor.security.beta.kubernetes.io/mount-cgroup: "unconfined" - container.apparmor.security.beta.kubernetes.io/apply-sysctl-overwrites: "unconfined" {{- end }} {{- end }} {{- end }} diff --git a/packages/system/cilium/values.yaml b/packages/system/cilium/values.yaml index cd31c107..4b2e1ff3 100644 --- a/packages/system/cilium/values.yaml +++ b/packages/system/cilium/values.yaml @@ -34,11 +34,6 @@ cilium: # annotations when the AppArmor LSM is not loaded. # mount-bpf-fs is intentionally excluded: it runs as privileged, and the # kernel does not apply AppArmor profiles to privileged containers. - # On unsupported k8s < 1.30 the upstream chart renders the same keys - # inside its own <1.30 branch, yielding a duplicate mapping with - # identical values (last-wins). The supported matrix starts at 1.30 - # (packages/apps/kubernetes/files/versions.yaml), so this does not - # affect any shipped version. podAnnotations: container.apparmor.security.beta.kubernetes.io/cilium-agent: unconfined container.apparmor.security.beta.kubernetes.io/clean-cilium-state: unconfined From b23775dc7ba86d4bc284cebcbf6148b32f49bfbe Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Sat, 11 Apr 2026 14:54:47 +0300 Subject: [PATCH 23/36] [cilium] Strip the entire k8s<1.30 AppArmor block from vendored daemonset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous sed patch removed only the four annotation lines and left an empty nested '{{- if ... }}{{- end }}{{- end }}{{- end }}' skeleton behind. That skeleton was dead code, but it also meant that the next make update would re-fetch the upstream chart with the annotations back inside the same block, and the sed would strip them again — leaving the skeleton behind forever. Replace the per-line sed patch with a single perl -i -0pe multi-line delete that removes the entire <1.30 AppArmor block in one step, and apply the same patch to the currently vendored copy for consistency. Assisted-By: Claude Signed-off-by: Aleksei Sviridkin (cherry picked from commit 5a50df800d803d3947db70e0bc5bac52891ccb1a) --- packages/system/cilium/Makefile | 14 +++++--------- .../cilium/templates/cilium-agent/daemonset.yaml | 9 --------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/packages/system/cilium/Makefile b/packages/system/cilium/Makefile index 3c90fd21..4a9799f0 100644 --- a/packages/system/cilium/Makefile +++ b/packages/system/cilium/Makefile @@ -12,15 +12,11 @@ update: helm repo update cilium helm pull cilium/cilium --untar --untardir charts --version 1.19 $(SED_INPLACE) -e '/Used in iptables/d' -e '/SYS_MODULE/d' charts/cilium/values.yaml - # Drop the hardcoded AppArmor unconfined annotations from the cilium-agent - # DaemonSet's k8s<1.30 branch. Cozystack manages these through - # cilium.podAnnotations in values.yaml for all k8s versions, and keeping the - # upstream block would produce duplicate mapping keys on k8s<1.30. - $(SED_INPLACE) \ - -e '/container\.apparmor\.security\.beta\.kubernetes\.io\/cilium-agent: "unconfined"/d' \ - -e '/container\.apparmor\.security\.beta\.kubernetes\.io\/clean-cilium-state: "unconfined"/d' \ - -e '/container\.apparmor\.security\.beta\.kubernetes\.io\/mount-cgroup: "unconfined"/d' \ - -e '/container\.apparmor\.security\.beta\.kubernetes\.io\/apply-sysctl-overwrites: "unconfined"/d' \ + # Drop the whole k8s<1.30 AppArmor annotations block from the cilium-agent + # DaemonSet. Cozystack manages these annotations through cilium.podAnnotations + # in values.yaml on every k8s version, so keeping the upstream block would + # produce duplicate mapping keys on k8s<1.30. + perl -i -0pe 's|\n \{\{- if not \.Values\.securityContext\.privileged \}\}\n \{\{- if semverCompare "<1\.30\.0".*?\n \{\{- end \}\}\n \{\{- end \}\}\n \{\{- end \}\}||s' \ charts/cilium/templates/cilium-agent/daemonset.yaml version=$$(awk '$$1 == "version:" {print $$2}' charts/cilium/Chart.yaml) && \ $(SED_INPLACE) "s/ARG VERSION=.*/ARG VERSION=v$${version}/" images/cilium/Dockerfile diff --git a/packages/system/cilium/charts/cilium/templates/cilium-agent/daemonset.yaml b/packages/system/cilium/charts/cilium/templates/cilium-agent/daemonset.yaml index bb2c5a15..fa3afc14 100644 --- a/packages/system/cilium/charts/cilium/templates/cilium-agent/daemonset.yaml +++ b/packages/system/cilium/charts/cilium/templates/cilium-agent/daemonset.yaml @@ -57,15 +57,6 @@ spec: # ensure pods roll when configmap updates cilium.io/cilium-configmap-checksum: {{ include (print $.Template.BasePath "/cilium-configmap.yaml") . | sha256sum | quote }} {{- end }} - {{- if not .Values.securityContext.privileged }} - {{- if semverCompare "<1.30.0" (printf "%d.%d.0" (semver .Capabilities.KubeVersion.Version).Major (semver .Capabilities.KubeVersion.Version).Minor) }} - # Set app AppArmor's profile to "unconfined". The value of this annotation - # can be modified as long users know which profiles they have available - # in AppArmor. - {{- if .Values.cgroup.autoMount.enabled }} - {{- end }} - {{- end }} - {{- end }} {{- with .Values.podAnnotations }} {{- toYaml . | nindent 8 }} {{- end }} From 016fe585b542e4085a1341b28677c21549248d40 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Sat, 11 Apr 2026 15:03:54 +0300 Subject: [PATCH 24/36] [cilium] Add fail-fast guard for the vendored daemonset patch The perl multi-line delete in the update: target silently becomes a no-op if the upstream template is ever reformatted. Verify the patch applied by grepping for one of the stripped annotation keys after the perl runs; fail the update: target loudly if it still exists, so a future upstream reformat is caught instead of silently reintroducing the duplicate-key rendering on k8s < 1.30. Assisted-By: Claude Signed-off-by: Aleksei Sviridkin (cherry picked from commit 39cd2658b53547f570cb57caa667eedfd15cb5ce) --- packages/system/cilium/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/system/cilium/Makefile b/packages/system/cilium/Makefile index 4a9799f0..14abbf54 100644 --- a/packages/system/cilium/Makefile +++ b/packages/system/cilium/Makefile @@ -18,6 +18,9 @@ update: # produce duplicate mapping keys on k8s<1.30. perl -i -0pe 's|\n \{\{- if not \.Values\.securityContext\.privileged \}\}\n \{\{- if semverCompare "<1\.30\.0".*?\n \{\{- end \}\}\n \{\{- end \}\}\n \{\{- end \}\}||s' \ charts/cilium/templates/cilium-agent/daemonset.yaml + @! grep -q 'container\.apparmor\.security\.beta\.kubernetes\.io/cilium-agent: "unconfined"' \ + charts/cilium/templates/cilium-agent/daemonset.yaml || \ + { echo 'ERROR: perl patch did not remove the upstream AppArmor block from cilium-agent/daemonset.yaml (upstream template format may have changed)' >&2; exit 1; } version=$$(awk '$$1 == "version:" {print $$2}' charts/cilium/Chart.yaml) && \ $(SED_INPLACE) "s/ARG VERSION=.*/ARG VERSION=v$${version}/" images/cilium/Dockerfile From caacf9250768f0c99311e16c34ec5805be98a9cf Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Sat, 11 Apr 2026 15:13:03 +0300 Subject: [PATCH 25/36] [cilium] Clarify nsenter/AppArmor comment wording Replace the misleading 'nsenter --ptrace' shorthand with an accurate description: nsenter joins the host cgroup/mount namespace, and the cri-containerd.apparmor.d profile reports the denial as a blocked 'ptrace' operation (operation name, not an nsenter CLI flag). Assisted-By: Claude Signed-off-by: Aleksei Sviridkin (cherry picked from commit 0b0d3de99d63ae02cc7d8d1db234dc34c9c544b4) --- packages/system/cilium/values.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/system/cilium/values.yaml b/packages/system/cilium/values.yaml index 4b2e1ff3..3902287c 100644 --- a/packages/system/cilium/values.yaml +++ b/packages/system/cilium/values.yaml @@ -24,9 +24,10 @@ cilium: rollOutPods: true replicas: 1 # Opt out of the cri-containerd.apparmor.d profile for containers that invoke - # nsenter --ptrace to join the host cgroup/mount namespace. Required on - # Ubuntu 22.04+ and other distros that load this AppArmor profile by default, - # where the denial puts cilium-agent into Init:CrashLoopBackOff. + # nsenter to join the host cgroup/mount namespace. The kernel reports the + # denial as a blocked "ptrace" operation. Required on Ubuntu 22.04+ and + # other distros that load this AppArmor profile by default, where the + # denial otherwise puts cilium-agent into Init:CrashLoopBackOff. # The deprecated annotations are used because k8s >= 1.30 pod-level # appArmorProfile: Unconfined is not honoured by containerd CRI today # (kubernetes/kubernetes#125069). From b0ca7ce5775e749857b5d905834f19b30d6716da Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Sat, 11 Apr 2026 15:20:55 +0300 Subject: [PATCH 26/36] [cilium] Harden vendoring guard and clarify Talos annotation comment - Broaden the grep guard in the Makefile update: target to match any container.apparmor.security.beta.kubernetes.io key, not only the quoted cilium-agent line. The previous pattern would silently pass if upstream switched to an unquoted annotation value, masking a failed perl patch; grepping for the prefix catches any residual hardcoded AppArmor annotation regardless of format. - Rewrite the values.yaml comment to distinguish the two Talos-safe reasons: mount-cgroup is simply not rendered when cgroup.autoMount.enabled is false (Talos default), while the other annotations are harmless because kubelet ignores AppArmor metadata on nodes without the LSM loaded. Assisted-By: Claude Signed-off-by: Aleksei Sviridkin (cherry picked from commit fc9ef55c425dcd281f12eeb962282034f561b958) --- packages/system/cilium/Makefile | 2 +- packages/system/cilium/values.yaml | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/system/cilium/Makefile b/packages/system/cilium/Makefile index 14abbf54..71b47a77 100644 --- a/packages/system/cilium/Makefile +++ b/packages/system/cilium/Makefile @@ -18,7 +18,7 @@ update: # produce duplicate mapping keys on k8s<1.30. perl -i -0pe 's|\n \{\{- if not \.Values\.securityContext\.privileged \}\}\n \{\{- if semverCompare "<1\.30\.0".*?\n \{\{- end \}\}\n \{\{- end \}\}\n \{\{- end \}\}||s' \ charts/cilium/templates/cilium-agent/daemonset.yaml - @! grep -q 'container\.apparmor\.security\.beta\.kubernetes\.io/cilium-agent: "unconfined"' \ + @! grep -q 'container\.apparmor\.security\.beta\.kubernetes\.io' \ charts/cilium/templates/cilium-agent/daemonset.yaml || \ { echo 'ERROR: perl patch did not remove the upstream AppArmor block from cilium-agent/daemonset.yaml (upstream template format may have changed)' >&2; exit 1; } version=$$(awk '$$1 == "version:" {print $$2}' charts/cilium/Chart.yaml) && \ diff --git a/packages/system/cilium/values.yaml b/packages/system/cilium/values.yaml index 3902287c..8aee34d9 100644 --- a/packages/system/cilium/values.yaml +++ b/packages/system/cilium/values.yaml @@ -31,10 +31,13 @@ cilium: # The deprecated annotations are used because k8s >= 1.30 pod-level # appArmorProfile: Unconfined is not honoured by containerd CRI today # (kubernetes/kubernetes#125069). - # Safe on Talos / RHEL family without AppArmor: kubelet ignores these - # annotations when the AppArmor LSM is not loaded. - # mount-bpf-fs is intentionally excluded: it runs as privileged, and the - # kernel does not apply AppArmor profiles to privileged containers. + # On Talos mount-cgroup is not rendered (cgroup.autoMount.enabled=false in + # values-talos.yaml) so its annotation is inert there. On RHEL-family and + # other AppArmor-less hosts kubelet silently ignores these annotations + # because the AppArmor LSM is not loaded. + # mount-bpf-fs is intentionally excluded: it renders with its own + # securityContext.privileged=true, and the kernel does not apply AppArmor + # profiles to privileged containers. podAnnotations: container.apparmor.security.beta.kubernetes.io/cilium-agent: unconfined container.apparmor.security.beta.kubernetes.io/clean-cilium-state: unconfined From 5fc12d44b73c5b6ecc20e1d2310ee622c3a7648f Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Sat, 11 Apr 2026 16:39:58 +0300 Subject: [PATCH 27/36] [cilium] Move AppArmor podAnnotations to a non-Talos-only values file E2E revealed that the kube-apiserver rejects a DaemonSet carrying a per-container AppArmor annotation for a container that is not present in the pod spec: DaemonSet.apps 'cilium' is invalid: spec.template.annotations[container.apparmor.security.beta.kubernetes.io/mount-cgroup]: Invalid value: 'mount-cgroup': container not found On Talos (isp-full bundle) values-talos.yaml sets cgroup.autoMount.enabled=false, so the upstream chart does not render the mount-cgroup init container, and our unconditional podAnnotations entry for it becomes an invalid reference. Move the podAnnotations block from the shared values.yaml into a new values-apparmor.yaml and include it only in the cilium-generic and kubeovn-cilium-generic PackageSource variants, where cgroup.autoMount.enabled is explicitly true. Talos variants (cilium, cilium-kilo, kubeovn-cilium) get no AppArmor annotations, which is safe because the Talos kernel does not load the AppArmor LSM anyway. Assisted-By: Claude Signed-off-by: Aleksei Sviridkin (cherry picked from commit abd6667eb399f7e785c947f55a6fe16a8a8ecbba) --- .../core/platform/sources/networking.yaml | 2 ++ packages/system/cilium/values-apparmor.yaml | 20 +++++++++++++++++++ packages/system/cilium/values.yaml | 20 ------------------- 3 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 packages/system/cilium/values-apparmor.yaml diff --git a/packages/core/platform/sources/networking.yaml b/packages/core/platform/sources/networking.yaml index 8e8485b3..bece76f8 100644 --- a/packages/core/platform/sources/networking.yaml +++ b/packages/core/platform/sources/networking.yaml @@ -66,6 +66,7 @@ spec: path: system/cilium valuesFiles: - values.yaml + - values-apparmor.yaml install: privileged: true namespace: cozy-cilium @@ -117,6 +118,7 @@ spec: path: system/cilium valuesFiles: - values.yaml + - values-apparmor.yaml - values-kubeovn.yaml install: privileged: true diff --git a/packages/system/cilium/values-apparmor.yaml b/packages/system/cilium/values-apparmor.yaml new file mode 100644 index 00000000..0712836c --- /dev/null +++ b/packages/system/cilium/values-apparmor.yaml @@ -0,0 +1,20 @@ +cilium: + # Opt out of the cri-containerd.apparmor.d profile for containers that invoke + # nsenter to join the host cgroup/mount namespace. The kernel reports the + # denial as a blocked "ptrace" operation. Required on Ubuntu 22.04+ and + # other distros that load this AppArmor profile by default, where the + # denial otherwise puts cilium-agent into Init:CrashLoopBackOff. + # The deprecated annotations are used because k8s >= 1.30 pod-level + # appArmorProfile: Unconfined is not honoured by containerd CRI today + # (kubernetes/kubernetes#125069). + # Only applied on non-Talos cilium variants where cgroup.autoMount.enabled is + # true — on Talos mount-cgroup is not rendered and the kube-apiserver would + # reject an annotation referencing a non-existent container. + # mount-bpf-fs is intentionally excluded: it renders with its own + # securityContext.privileged=true, and the kernel does not apply AppArmor + # profiles to privileged containers. + podAnnotations: + container.apparmor.security.beta.kubernetes.io/cilium-agent: unconfined + container.apparmor.security.beta.kubernetes.io/clean-cilium-state: unconfined + container.apparmor.security.beta.kubernetes.io/mount-cgroup: unconfined + container.apparmor.security.beta.kubernetes.io/apply-sysctl-overwrites: unconfined diff --git a/packages/system/cilium/values.yaml b/packages/system/cilium/values.yaml index 8aee34d9..cfb2cd6b 100644 --- a/packages/system/cilium/values.yaml +++ b/packages/system/cilium/values.yaml @@ -23,23 +23,3 @@ cilium: operator: rollOutPods: true replicas: 1 - # Opt out of the cri-containerd.apparmor.d profile for containers that invoke - # nsenter to join the host cgroup/mount namespace. The kernel reports the - # denial as a blocked "ptrace" operation. Required on Ubuntu 22.04+ and - # other distros that load this AppArmor profile by default, where the - # denial otherwise puts cilium-agent into Init:CrashLoopBackOff. - # The deprecated annotations are used because k8s >= 1.30 pod-level - # appArmorProfile: Unconfined is not honoured by containerd CRI today - # (kubernetes/kubernetes#125069). - # On Talos mount-cgroup is not rendered (cgroup.autoMount.enabled=false in - # values-talos.yaml) so its annotation is inert there. On RHEL-family and - # other AppArmor-less hosts kubelet silently ignores these annotations - # because the AppArmor LSM is not loaded. - # mount-bpf-fs is intentionally excluded: it renders with its own - # securityContext.privileged=true, and the kernel does not apply AppArmor - # profiles to privileged containers. - podAnnotations: - container.apparmor.security.beta.kubernetes.io/cilium-agent: unconfined - container.apparmor.security.beta.kubernetes.io/clean-cilium-state: unconfined - container.apparmor.security.beta.kubernetes.io/mount-cgroup: unconfined - container.apparmor.security.beta.kubernetes.io/apply-sysctl-overwrites: unconfined From 8b1f29975cd92b36c2839a145410a8551f29d5cc Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Sun, 12 Apr 2026 11:28:25 +0200 Subject: [PATCH 28/36] Replace cozystack-bot PAT with cozystack-ci GitHub App (#2351) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Replaces the `cozystack-bot` user account + personal access token (`GH_PAT`) with the `cozystack-ci` GitHub App across all CI/CD release workflows. ### Why - **Security**: GitHub App tokens are short-lived (1h) and scoped, vs. a long-lived PAT tied to a user account - **Auditability**: App actions are clearly attributed to `cozystack-ci[bot]` - **No 2FA issue**: The bot account lacked 2FA; GitHub Apps don't require it - **Best practice**: GitHub recommends Apps over PATs for automation ### Changes | Workflow | What changed | |---|---| | `tags.yaml` | App token in all 3 jobs: prepare-release, generate-changelog, update-website-docs | | `auto-release.yaml` | App token for daily auto-patch tagging | | `pull-requests-release.yaml` | App token for release PR finalization | All `cozystack-bot` git identity replaced with `cozystack-ci[bot]`. ### New secrets (already configured at org level) - `COZYSTACK_CI_APP_ID` — GitHub App ID - `COZYSTACK_CI_PRIVATE_KEY` — GitHub App private key ### After merge - [ ] Delete `GH_PAT` repo-level secret - [ ] Remove `cozystack-bot` from the cozystack org 🤖 Generated with [Claude Code](https://claude.com/claude-code) ## Summary by CodeRabbit * **Chores** * Updated CI/CD automation authentication mechanisms across release and tagging workflows for improved security practices. Signed-off-by: Andrei Kvapil --- .github/workflows/auto-release.yaml | 30 +++++--- .github/workflows/pull-requests-release.yaml | 18 +++-- .github/workflows/tags.yaml | 74 +++++++++++++------- 3 files changed, 81 insertions(+), 41 deletions(-) diff --git a/.github/workflows/auto-release.yaml b/.github/workflows/auto-release.yaml index 19d4b460..d3b892d3 100644 --- a/.github/workflows/auto-release.yaml +++ b/.github/workflows/auto-release.yaml @@ -20,6 +20,14 @@ jobs: pull-requests: read steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + - name: Checkout code uses: actions/checkout@v4 with: @@ -28,27 +36,27 @@ jobs: - name: Configure git env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | - git config user.name "cozystack-bot" - git config user.email "217169706+cozystack-bot@users.noreply.github.com" - git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY} + git config user.name "cozystack-ci[bot]" + git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" + git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} git config --unset-all http.https://github.com/.extraheader || true - name: Process release branches uses: actions/github-script@v7 env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} with: - github-token: ${{ secrets.GH_PAT }} + github-token: ${{ steps.app-token.outputs.token }} script: | const { execSync } = require('child_process'); - // Configure git to use PAT for authentication - execSync('git config user.name "cozystack-bot"', { encoding: 'utf8' }); - execSync('git config user.email "217169706+cozystack-bot@users.noreply.github.com"', { encoding: 'utf8' }); - execSync(`git remote set-url origin https://cozystack-bot:${process.env.GH_PAT}@github.com/${process.env.GITHUB_REPOSITORY}`, { encoding: 'utf8' }); - // Remove GITHUB_TOKEN extraheader to ensure PAT is used (needed to trigger other workflows) + // Configure git to use GitHub App token for authentication + execSync('git config user.name "cozystack-ci[bot]"', { encoding: 'utf8' }); + execSync('git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com"', { encoding: 'utf8' }); + execSync(`git remote set-url origin https://x-access-token:${process.env.APP_TOKEN}@github.com/${process.env.GITHUB_REPOSITORY}`, { encoding: 'utf8' }); + // Remove GITHUB_TOKEN extraheader to ensure App token is used (needed to trigger other workflows) execSync('git config --unset-all http.https://github.com/.extraheader || true', { encoding: 'utf8' }); // Get all release-X.Y branches diff --git a/.github/workflows/pull-requests-release.yaml b/.github/workflows/pull-requests-release.yaml index 72f31b54..a1dc08d7 100644 --- a/.github/workflows/pull-requests-release.yaml +++ b/.github/workflows/pull-requests-release.yaml @@ -23,6 +23,14 @@ jobs: contains(github.event.pull_request.labels.*.name, 'release') steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + # Extract tag from branch name (branch = release-X.Y.Z*) - name: Extract tag from branch name id: get_tag @@ -47,11 +55,11 @@ jobs: - name: Create tag on merge commit env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | - git config user.name "cozystack-bot" - git config user.email "217169706+cozystack-bot@users.noreply.github.com" - git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY} + git config user.name "cozystack-ci[bot]" + git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" + git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} git tag -f ${{ steps.get_tag.outputs.tag }} ${{ github.sha }} git push -f origin ${{ steps.get_tag.outputs.tag }} @@ -59,7 +67,7 @@ jobs: - name: Ensure maintenance branch release-X.Y uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_PAT }} + github-token: ${{ steps.app-token.outputs.token }} script: | const tag = '${{ steps.get_tag.outputs.tag }}'; // e.g. v0.1.3 or v0.1.3-rc3 const match = tag.match(/^v(\d+)\.(\d+)\.\d+(?:[-\w\.]+)?$/); diff --git a/.github/workflows/tags.yaml b/.github/workflows/tags.yaml index 2bbdb7a6..2725d594 100644 --- a/.github/workflows/tags.yaml +++ b/.github/workflows/tags.yaml @@ -25,6 +25,14 @@ jobs: actions: write steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + # Check if a non-draft release with this tag already exists - name: Check if release already exists id: check_release @@ -115,11 +123,11 @@ jobs: - name: Commit release artifacts if: steps.check_release.outputs.skip == 'false' env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | - git config user.name "cozystack-bot" - git config user.email "217169706+cozystack-bot@users.noreply.github.com" - git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY} + git config user.name "cozystack-ci[bot]" + git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" + git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} git config --unset-all http.https://github.com/.extraheader || true git add . git commit -m "Prepare release ${GITHUB_REF#refs/tags/}" -s || echo "No changes to commit" @@ -128,13 +136,13 @@ jobs: - name: Tag API submodule if: steps.check_release.outputs.skip == 'false' env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | VTAG="${{ steps.tag.outputs.tag }}" SUBTAG="api/apps/v1alpha1/${VTAG}" - git config user.name "cozystack-bot" - git config user.email "217169706+cozystack-bot@users.noreply.github.com" - git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY} + git config user.name "cozystack-ci[bot]" + git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" + git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} TARGET="$(git rev-parse "${VTAG}^{}")" git tag -f "${SUBTAG}" "$TARGET" git push -f origin "refs/tags/${SUBTAG}" @@ -183,11 +191,11 @@ jobs: - name: Create release branch if: steps.check_release.outputs.skip == 'false' env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | - git config user.name "cozystack-bot" - git config user.email "217169706+cozystack-bot@users.noreply.github.com" - git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY} + git config user.name "cozystack-ci[bot]" + git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" + git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} BRANCH="release-${GITHUB_REF#refs/tags/v}" git branch -f "$BRANCH" git push -f origin "$BRANCH" @@ -197,7 +205,7 @@ jobs: if: steps.check_release.outputs.skip == 'false' uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_PAT }} + github-token: ${{ steps.app-token.outputs.token }} script: | const version = context.ref.replace('refs/tags/v', ''); const base = '${{ steps.get_base.outputs.branch }}'; @@ -239,6 +247,14 @@ jobs: pull-requests: write if: needs.prepare-release.result == 'success' steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + - name: Parse tag id: tag uses: actions/github-script@v7 @@ -261,7 +277,7 @@ jobs: ref: main fetch-depth: 0 fetch-tags: true - token: ${{ secrets.GH_PAT }} + token: ${{ steps.app-token.outputs.token }} - name: Check if changelog already exists id: check_changelog @@ -289,7 +305,7 @@ jobs: if: steps.check_changelog.outputs.exists == 'false' env: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - GH_TOKEN: ${{ secrets.GH_PAT }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} run: | copilot --prompt "prepare changelog file for tagged release v${{ steps.tag.outputs.version }}, use @docs/agents/changelog.md for it. Create the changelog file at docs/changelogs/v${{ steps.tag.outputs.version }}.md" \ --allow-all-tools --allow-all-paths < /dev/null @@ -297,11 +313,11 @@ jobs: - name: Create changelog branch and commit if: steps.check_changelog.outputs.exists == 'false' env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | - git config user.name "cozystack-bot" - git config user.email "217169706+cozystack-bot@users.noreply.github.com" - git remote set-url origin https://cozystack-bot:${GH_PAT}@github.com/${GITHUB_REPOSITORY} + git config user.name "cozystack-ci[bot]" + git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" + git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} CHANGELOG_FILE="docs/changelogs/v${{ steps.tag.outputs.version }}.md" CHANGELOG_BRANCH="changelog-v${{ steps.tag.outputs.version }}" @@ -336,7 +352,7 @@ jobs: if: steps.check_changelog.outputs.exists == 'false' uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_PAT }} + github-token: ${{ steps.app-token.outputs.token }} script: | const version = '${{ steps.tag.outputs.version }}'; const changelogBranch = `changelog-v${version}`; @@ -395,6 +411,14 @@ jobs: permissions: contents: read steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + - name: Parse tag id: tag uses: actions/github-script@v7 @@ -414,19 +438,19 @@ jobs: uses: actions/checkout@v4 with: repository: cozystack/website - token: ${{ secrets.GH_PAT }} + token: ${{ steps.app-token.outputs.token }} ref: main - name: Update docs from release branch env: - GH_TOKEN: ${{ secrets.GH_PAT }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} run: make update-all BRANCH=release-${{ steps.tag.outputs.version }} RELEASE_TAG=${{ steps.tag.outputs.tag }} - name: Commit and push id: commit run: | - git config user.name "cozystack-bot" - git config user.email "217169706+cozystack-bot@users.noreply.github.com" + git config user.name "cozystack-ci[bot]" + git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" git add content if git diff --cached --quiet; then echo "No changes to commit" @@ -443,7 +467,7 @@ jobs: - name: Open pull request if: steps.commit.outputs.changed == 'true' env: - GH_TOKEN: ${{ secrets.GH_PAT }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} run: | BRANCH="update-docs-v${{ steps.tag.outputs.version }}" pr_state=$(gh pr view "$BRANCH" --repo cozystack/website --json state --jq .state 2>/dev/null || echo "") From 1c78614bbf7531fedcafe538ee7f7caefe3abb8e Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Mon, 13 Apr 2026 09:28:52 +0200 Subject: [PATCH 29/36] ci(pull-requests): replace GH_PAT with cozystack-ci GitHub App token The GH_PAT secret tied to cozystack-bot is no longer valid after migrating release workflows to the cozystack-ci GitHub App in #2351. This broke the resolve_assets and e2e jobs for release PRs. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil (cherry picked from commit 1144211a85571654b2e09a40d816db93253b54f6) --- .github/workflows/pull-requests.yaml | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pull-requests.yaml b/.github/workflows/pull-requests.yaml index 76dbfc17..d9898616 100644 --- a/.github/workflows/pull-requests.yaml +++ b/.github/workflows/pull-requests.yaml @@ -99,6 +99,14 @@ jobs: disk_id: ${{ steps.fetch_assets.outputs.disk_id }} steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + - name: Checkout code if: contains(github.event.pull_request.labels.*.name, 'release') uses: actions/checkout@v4 @@ -125,7 +133,7 @@ jobs: id: fetch_assets uses: actions/github-script@v7 with: - github-token: ${{ secrets.GH_PAT }} + github-token: ${{ steps.app-token.outputs.token }} script: | const tag = '${{ steps.get_tag.outputs.tag }}'; const releases = await github.rest.repos.listReleases({ @@ -159,6 +167,15 @@ jobs: if: ${{ always() && (needs.build.result == 'success' || needs.resolve_assets.result == 'success') }} steps: + - name: Generate GitHub App token + if: contains(github.event.pull_request.labels.*.name, 'release') + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.COZYSTACK_CI_APP_ID }} + private-key: ${{ secrets.COZYSTACK_CI_PRIVATE_KEY }} + owner: cozystack + # ▸ Checkout and prepare the codebase - name: Checkout code uses: actions/checkout@v4 @@ -188,11 +205,11 @@ jobs: if: contains(github.event.pull_request.labels.*.name, 'release') run: | mkdir -p _out/assets - curl -sSL -H "Authorization: token ${GH_PAT}" -H "Accept: application/octet-stream" \ + curl -sSL -H "Authorization: token ${APP_TOKEN}" -H "Accept: application/octet-stream" \ -o _out/assets/nocloud-amd64.raw.xz \ "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.disk_id }}" env: - GH_PAT: ${{ secrets.GH_PAT }} + APP_TOKEN: ${{ steps.app-token.outputs.token }} - name: Set sandbox ID run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV From a8b11eea7bed875195cddd5b3a241e0579b2d416 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Mon, 13 Apr 2026 14:25:25 +0200 Subject: [PATCH 30/36] fix(build): filter git describe to match only v* tags The api/apps/v1alpha1/* subtags share the same commit as the release tags. git describe --exact-match picks the first match alphabetically, returning api/apps/v1alpha1/vX.Y.Z instead of vX.Y.Z, which produces invalid Docker image tags. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil (cherry picked from commit fbbccdbb7b53f2d612801229143db0f6365da2e0) --- hack/common-envs.mk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hack/common-envs.mk b/hack/common-envs.mk index 56eb5478..9b8db5db 100644 --- a/hack/common-envs.mk +++ b/hack/common-envs.mk @@ -6,13 +6,13 @@ else endif REGISTRY ?= ghcr.io/cozystack/cozystack -TAG = $(shell git describe --tags --exact-match 2>/dev/null || echo latest) +TAG = $(shell git describe --tags --exact-match --match 'v*' 2>/dev/null || echo latest) PUSH := 1 LOAD := 0 BUILDER ?= PLATFORM ?= BUILDX_EXTRA_ARGS ?= -COZYSTACK_VERSION = $(patsubst v%,%,$(shell git describe --tags)) +COZYSTACK_VERSION = $(patsubst v%,%,$(shell git describe --tags --match 'v*')) BUILDX_ARGS := --provenance=false --push=$(PUSH) --load=$(LOAD) \ --label org.opencontainers.image.source=https://github.com/cozystack/cozystack \ @@ -28,6 +28,6 @@ endef ifeq ($(COZYSTACK_VERSION),) $(shell git remote add upstream https://github.com/cozystack/cozystack.git || true) $(shell git fetch upstream --tags) - COZYSTACK_VERSION = $(patsubst v%,%,$(shell git describe --tags)) + COZYSTACK_VERSION = $(patsubst v%,%,$(shell git describe --tags --match 'v*')) endif From 14c652c8eb4c1b6effcb8ecb92d20e7669dde819 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Mon, 13 Apr 2026 16:21:14 +0200 Subject: [PATCH 31/36] ci: use cozystack org noreply email for bot commits The cozystack-ci[bot] noreply email with brackets is rejected by DCO probot as an invalid email address, causing release PRs to fail DCO checks. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil (cherry picked from commit ce0e709be8aa1effb8fd792a65c66fbd8f53fc47) --- .github/workflows/auto-release.yaml | 4 ++-- .github/workflows/pull-requests-release.yaml | 2 +- .github/workflows/tags.yaml | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/auto-release.yaml b/.github/workflows/auto-release.yaml index d3b892d3..44ca8ca6 100644 --- a/.github/workflows/auto-release.yaml +++ b/.github/workflows/auto-release.yaml @@ -39,7 +39,7 @@ jobs: APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | git config user.name "cozystack-ci[bot]" - git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} git config --unset-all http.https://github.com/.extraheader || true @@ -54,7 +54,7 @@ jobs: // Configure git to use GitHub App token for authentication execSync('git config user.name "cozystack-ci[bot]"', { encoding: 'utf8' }); - execSync('git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com"', { encoding: 'utf8' }); + execSync('git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com"', { encoding: 'utf8' }); execSync(`git remote set-url origin https://x-access-token:${process.env.APP_TOKEN}@github.com/${process.env.GITHUB_REPOSITORY}`, { encoding: 'utf8' }); // Remove GITHUB_TOKEN extraheader to ensure App token is used (needed to trigger other workflows) execSync('git config --unset-all http.https://github.com/.extraheader || true', { encoding: 'utf8' }); diff --git a/.github/workflows/pull-requests-release.yaml b/.github/workflows/pull-requests-release.yaml index a1dc08d7..bb7f5da5 100644 --- a/.github/workflows/pull-requests-release.yaml +++ b/.github/workflows/pull-requests-release.yaml @@ -58,7 +58,7 @@ jobs: APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | git config user.name "cozystack-ci[bot]" - git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} git tag -f ${{ steps.get_tag.outputs.tag }} ${{ github.sha }} git push -f origin ${{ steps.get_tag.outputs.tag }} diff --git a/.github/workflows/tags.yaml b/.github/workflows/tags.yaml index 2725d594..d698333e 100644 --- a/.github/workflows/tags.yaml +++ b/.github/workflows/tags.yaml @@ -126,7 +126,7 @@ jobs: APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | git config user.name "cozystack-ci[bot]" - git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} git config --unset-all http.https://github.com/.extraheader || true git add . @@ -141,7 +141,7 @@ jobs: VTAG="${{ steps.tag.outputs.tag }}" SUBTAG="api/apps/v1alpha1/${VTAG}" git config user.name "cozystack-ci[bot]" - git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} TARGET="$(git rev-parse "${VTAG}^{}")" git tag -f "${SUBTAG}" "$TARGET" @@ -194,7 +194,7 @@ jobs: APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | git config user.name "cozystack-ci[bot]" - git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} BRANCH="release-${GITHUB_REF#refs/tags/v}" git branch -f "$BRANCH" @@ -316,7 +316,7 @@ jobs: APP_TOKEN: ${{ steps.app-token.outputs.token }} run: | git config user.name "cozystack-ci[bot]" - git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} CHANGELOG_FILE="docs/changelogs/v${{ steps.tag.outputs.version }}.md" @@ -450,7 +450,7 @@ jobs: id: commit run: | git config user.name "cozystack-ci[bot]" - git config user.email "3297617+cozystack-ci[bot]@users.noreply.github.com" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" git add content if git diff --cached --quiet; then echo "No changes to commit" From 6ccdcf8d55ce34a1e175e77dab99f826ff77879e Mon Sep 17 00:00:00 2001 From: "cozystack-ci[bot]" <274107086+cozystack-ci[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 01:37:02 +0000 Subject: [PATCH 32/36] Prepare release v1.2.2 Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com> --- packages/apps/http-cache/images/nginx-cache.tag | 2 +- packages/apps/kubernetes/images/kubevirt-csi-driver.tag | 2 +- .../apps/kubernetes/images/ubuntu-container-disk-v1.30.tag | 2 +- .../apps/kubernetes/images/ubuntu-container-disk-v1.31.tag | 2 +- .../apps/kubernetes/images/ubuntu-container-disk-v1.32.tag | 2 +- .../apps/kubernetes/images/ubuntu-container-disk-v1.33.tag | 2 +- .../apps/kubernetes/images/ubuntu-container-disk-v1.34.tag | 2 +- .../apps/kubernetes/images/ubuntu-container-disk-v1.35.tag | 2 +- packages/core/installer/values.yaml | 4 ++-- packages/core/platform/values.yaml | 2 +- packages/core/testing/values.yaml | 2 +- packages/extra/bootbox/images/matchbox.tag | 2 +- packages/extra/seaweedfs/images/objectstorage-sidecar.tag | 2 +- packages/system/backup-controller/values.yaml | 2 +- packages/system/backupstrategy-controller/values.yaml | 2 +- packages/system/bucket/images/s3manager.tag | 2 +- packages/system/cozystack-api/values.yaml | 2 +- packages/system/cozystack-controller/values.yaml | 2 +- packages/system/dashboard/templates/configmap.yaml | 2 +- packages/system/dashboard/values.yaml | 6 +++--- .../system/grafana-operator/images/grafana-dashboards.tag | 2 +- packages/system/kamaji/values.yaml | 4 ++-- packages/system/kubeovn-plunger/values.yaml | 2 +- packages/system/kubeovn-webhook/values.yaml | 2 +- packages/system/kubevirt-csi-node/values.yaml | 2 +- packages/system/lineage-controller-webhook/values.yaml | 2 +- packages/system/linstor/values.yaml | 4 ++-- packages/system/multus/templates/multus-daemonset-thick.yml | 4 ++-- packages/system/objectstorage-controller/values.yaml | 2 +- packages/system/seaweedfs/values.yaml | 2 +- 30 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/apps/http-cache/images/nginx-cache.tag b/packages/apps/http-cache/images/nginx-cache.tag index 1da929ec..cfefd32b 100644 --- a/packages/apps/http-cache/images/nginx-cache.tag +++ b/packages/apps/http-cache/images/nginx-cache.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:2f987017ff95d3c782e16ef0d99928831f520daf154d08a2fa38c2d68363e036 +ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:95b2790e6caa0f2fbad48951c30e7848c0ef7b1bc433b2e5f07c5f4940f20783 diff --git a/packages/apps/kubernetes/images/kubevirt-csi-driver.tag b/packages/apps/kubernetes/images/kubevirt-csi-driver.tag index e623b778..1110427d 100644 --- a/packages/apps/kubernetes/images/kubevirt-csi-driver.tag +++ b/packages/apps/kubernetes/images/kubevirt-csi-driver.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:5b209248acba3d8cf0d999edd8a915915a2e565a1c8ac1f5dde0dffec20fa02e +ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:73645c21f3d39dd11b2cf79d975b57479c5907a41ed73d452f96e7229cc8841e diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag index 2d1e4d52..05b465ca 100644 --- a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.30@sha256:51e272db23d64eb20bc44e38cdbb70199fd00a49a958b76c2a89d4a46fec9256 +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.30@sha256:9474613009b8c10f2653a22c4ebf83977768c593079478933a4dd9376f6dd68b diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag index 9698df5c..887ad3a2 100644 --- a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.31@sha256:31c3290305159fe484caf6c5780960cf071dc3939528295336da4abfa731a81b +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.31@sha256:f55cc5387ee13c83b4b6e1c0ede490fc903331787e9292ac104e8e7bdeeed68b diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag index 90545820..4730d2a7 100644 --- a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.32@sha256:9efb1fe18cffb045530b43ac4b39910f1802d3c16798d733165dcd1c67de9237 +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.32@sha256:62909d198c78b2a07d203384ee904b0c74b6e9c5f2587c007eb337159d712bb2 diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag index 64345fab..a396e21e 100644 --- a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:a0afca1d6f720cfff764a1bc912bfe404b5e97ff731f6c126a7abdb51ed23f85 +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:4f2b0933d2251c24a3a7b53a3827d4f809e2250c1c4af4e44776ab661960ed34 diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag index f6916755..65ecb1f5 100644 --- a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.34@sha256:51dfe5ce3e2f8765fde9422ae7e9fba677016ec5e2be41559c5f11a56958b0e6 +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.34@sha256:4833c9cbc7d06fb183f397d1e1c577bb01682f682cfc06d86f55406a8c6b58f1 diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag index b80ce821..e0a9cd18 100644 --- a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.35@sha256:c4ae418b2a2c139794cd9630c3b2c2171870cc7748ac200344d2c4ed4283cf0b +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.35@sha256:9673eee5db99b537060bda711b962730fd6a89b8ba87a3d984c89574a729b7fe diff --git a/packages/core/installer/values.yaml b/packages/core/installer/values.yaml index 98443211..68cad5e4 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -1,9 +1,9 @@ cozystackOperator: # Deployment variant: talos, generic, hosted variant: talos - image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.2.1@sha256:c89352808022944c4791d63cf82cc95d78124ce799d355b60427f317087e8909 + image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.2.2@sha256:d7d00762e373f672490e7fc48bc1f515df12bf51748b74e8d9e31b61bbd9d0c0 platformSourceUrl: 'oci://ghcr.io/cozystack/cozystack/cozystack-packages' - platformSourceRef: 'digest=sha256:d02f211f79d4912f04c21856de078b5b41bb3976d84504d37cf8d9108f7cbec6' + platformSourceRef: 'digest=sha256:2e998f83db355d981b4a7ce9073637cdbf6147b918376f1fea00e286a81571c6' # Generic variant configuration (only used when cozystackOperator.variant=generic) cozystack: # Kubernetes API server host (IP only, no protocol/port) diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index 77eb00d7..b9d008d6 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -5,7 +5,7 @@ sourceRef: path: / migrations: enabled: false - image: ghcr.io/cozystack/cozystack/platform-migrations:v1.2.1@sha256:e8fcf006a4451fc0e961455e9b27a61b7103ee49b1a81fe5e4662ffed093fad6 + image: ghcr.io/cozystack/cozystack/platform-migrations:v1.2.2@sha256:72af209576eb42793dbf241c024d713fd23cc1435673dc2f9a81f70c237df124 targetVersion: 38 # Bundle deployment configuration bundles: diff --git a/packages/core/testing/values.yaml b/packages/core/testing/values.yaml index b17da614..8bc7e1e7 100644 --- a/packages/core/testing/values.yaml +++ b/packages/core/testing/values.yaml @@ -1,2 +1,2 @@ e2e: - image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.2.1@sha256:7964a3e4b11053887be201d62f94138c3a7836c861036c26fb833aa1fcb934e1 + image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.2.2@sha256:892663fe8c17596b2e01f49e13a1d76181e92f1b8d2f02b80065ba6824c6c80d diff --git a/packages/extra/bootbox/images/matchbox.tag b/packages/extra/bootbox/images/matchbox.tag index 0ba57d47..988b9409 100644 --- a/packages/extra/bootbox/images/matchbox.tag +++ b/packages/extra/bootbox/images/matchbox.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/matchbox:v1.2.1@sha256:64685523ba693964f22ce7d7dc545bf7bf96dea34e5ae525a2315614adef4b3d +ghcr.io/cozystack/cozystack/matchbox:v1.2.2@sha256:1461c101394cb2930db7eea2444614b398eb8e64b0bdebef4a213dfc5d476434 diff --git a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag index bb0702e8..ac47b1d7 100644 --- a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag +++ b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.1@sha256:1ad07fb9e96477e5c322f240f6023ceedd8762261d8379ec784dde92797ee206 +ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.2@sha256:1ad07fb9e96477e5c322f240f6023ceedd8762261d8379ec784dde92797ee206 diff --git a/packages/system/backup-controller/values.yaml b/packages/system/backup-controller/values.yaml index ff5fe774..e5731b0d 100644 --- a/packages/system/backup-controller/values.yaml +++ b/packages/system/backup-controller/values.yaml @@ -1,5 +1,5 @@ backupController: - image: "ghcr.io/cozystack/cozystack/backup-controller:v1.2.1@sha256:6e515cdfe302bb079dafa0f7f302c6ff7b7e9c02a75574880a45e35d5acb1f59" + image: "ghcr.io/cozystack/cozystack/backup-controller:v1.2.2@sha256:61dbd7becf26b888558ba2c6eb6ba3ddb78f2ecce741558c07a8b6837c1b4eac" replicas: 2 debug: false metrics: diff --git a/packages/system/backupstrategy-controller/values.yaml b/packages/system/backupstrategy-controller/values.yaml index bd3cad8c..c52fb860 100644 --- a/packages/system/backupstrategy-controller/values.yaml +++ b/packages/system/backupstrategy-controller/values.yaml @@ -1,5 +1,5 @@ backupStrategyController: - image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.2.1@sha256:3544a08bfac2df6dcb1842f73747768b24625c6d6b97230be9a00f80637f0d32" + image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.2.2@sha256:9bf3f569fca8773f274f67c62f15f7e2a5ffc1df16f6207176e835588fabca80" replicas: 2 debug: false metrics: diff --git a/packages/system/bucket/images/s3manager.tag b/packages/system/bucket/images/s3manager.tag index 6c3d1449..d3d5eb51 100644 --- a/packages/system/bucket/images/s3manager.tag +++ b/packages/system/bucket/images/s3manager.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:73382d7911274e6528cc2272ca9bfdb095c59e78845a3cdffd39d6c8ac29d920 +ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:36235971fc1790b11d38210894cd24cae46a54d88445aa6c4821977c139a58f2 diff --git a/packages/system/cozystack-api/values.yaml b/packages/system/cozystack-api/values.yaml index ca4cf5e2..1d344462 100644 --- a/packages/system/cozystack-api/values.yaml +++ b/packages/system/cozystack-api/values.yaml @@ -1,3 +1,3 @@ cozystackAPI: - image: ghcr.io/cozystack/cozystack/cozystack-api:v1.2.1@sha256:afcc72ec5b8ff6eea7fbf927d518f277a7bfead44948f45ed17eda90d47253d5 + image: ghcr.io/cozystack/cozystack/cozystack-api:v1.2.2@sha256:c9cd01ba6c720f03567996ba46ac9e5a8b90729ae6d9726fdefd36ec252d33e0 replicas: 2 diff --git a/packages/system/cozystack-controller/values.yaml b/packages/system/cozystack-controller/values.yaml index 030a4564..c9437db2 100644 --- a/packages/system/cozystack-controller/values.yaml +++ b/packages/system/cozystack-controller/values.yaml @@ -1,4 +1,4 @@ cozystackController: - image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.2.1@sha256:0b1d49b29e8860f13f7afc8d7b6265f36c9d24d6ad7c40f5164535dd8e933c73 + image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.2.2@sha256:f5817f275386af00362d579a29c4762a4b2e3daf99f110c3db0c5f791d305792 debug: false disableTelemetry: false diff --git a/packages/system/dashboard/templates/configmap.yaml b/packages/system/dashboard/templates/configmap.yaml index d260c5d9..5a00deb0 100644 --- a/packages/system/dashboard/templates/configmap.yaml +++ b/packages/system/dashboard/templates/configmap.yaml @@ -1,6 +1,6 @@ {{- $brandingConfig := .Values._cluster.branding | default dict }} -{{- $tenantText := "v1.2.1" }} +{{- $tenantText := "v1.2.2" }} {{- $footerText := "Cozystack" }} {{- $titleText := "Cozystack Dashboard" }} {{- $logoText := "" }} diff --git a/packages/system/dashboard/values.yaml b/packages/system/dashboard/values.yaml index 1aa69b09..d2315a7e 100644 --- a/packages/system/dashboard/values.yaml +++ b/packages/system/dashboard/values.yaml @@ -1,6 +1,6 @@ openapiUI: - image: ghcr.io/cozystack/cozystack/openapi-ui:v1.2.1@sha256:a77c502c4b67fa5f0698f66a82eb2ad30e138f9bd2700aa40a63f73cb86a559d + image: ghcr.io/cozystack/cozystack/openapi-ui:v1.2.2@sha256:1b0c641150d4ac65208e78107cfc87f9a692299c51f5a93e49cf5fb5ac3bde57 openapiUIK8sBff: - image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.2.1@sha256:1b3ea6d4c7dbbe6a8def3b2807fffdfab2ac4afc39d7a846e57dd491fa168f92 + image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.2.2@sha256:c938fee904acd948800d4dc5e121c4c5cd64cb4a3160fb8d2f9dbff0e5168740 tokenProxy: - image: ghcr.io/cozystack/cozystack/token-proxy:v1.2.1@sha256:2e280991e07853ea48f97b0a42946afffa10d03d6a83d41099ed83e6ffc94fdc + image: ghcr.io/cozystack/cozystack/token-proxy:v1.2.2@sha256:2e280991e07853ea48f97b0a42946afffa10d03d6a83d41099ed83e6ffc94fdc diff --git a/packages/system/grafana-operator/images/grafana-dashboards.tag b/packages/system/grafana-operator/images/grafana-dashboards.tag index 08f6bd0b..f998a508 100644 --- a/packages/system/grafana-operator/images/grafana-dashboards.tag +++ b/packages/system/grafana-operator/images/grafana-dashboards.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/grafana-dashboards:v1.2.1@sha256:2c9aa0b48e2bf6167db198f4d15882bfe51700108edf2e9f6d0942940a2c1204 +ghcr.io/cozystack/cozystack/grafana-dashboards:v1.2.2@sha256:2c9aa0b48e2bf6167db198f4d15882bfe51700108edf2e9f6d0942940a2c1204 diff --git a/packages/system/kamaji/values.yaml b/packages/system/kamaji/values.yaml index 9c6eea0e..fbd06f54 100644 --- a/packages/system/kamaji/values.yaml +++ b/packages/system/kamaji/values.yaml @@ -3,7 +3,7 @@ kamaji: deploy: false image: pullPolicy: IfNotPresent - tag: v1.2.1@sha256:34b9af510bbad3e64ea5cd7afdffa2b31c0751009c467355c8f67f8f9c5b3513 + tag: v1.2.2@sha256:a2ab1c507fb1b1249364823bd43bbf9db933009e66f110ca64cc933ce231d10c repository: ghcr.io/cozystack/cozystack/kamaji resources: limits: @@ -13,4 +13,4 @@ kamaji: cpu: 100m memory: 100Mi extraArgs: - - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.2.1@sha256:34b9af510bbad3e64ea5cd7afdffa2b31c0751009c467355c8f67f8f9c5b3513 + - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.2.2@sha256:a2ab1c507fb1b1249364823bd43bbf9db933009e66f110ca64cc933ce231d10c diff --git a/packages/system/kubeovn-plunger/values.yaml b/packages/system/kubeovn-plunger/values.yaml index 7691803e..01e0fa7f 100644 --- a/packages/system/kubeovn-plunger/values.yaml +++ b/packages/system/kubeovn-plunger/values.yaml @@ -1,4 +1,4 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.2.1@sha256:71d487b9da211061921c7183d5a1e19a32c64dffe33a3fac0557c4cb25764dfe +image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.2.2@sha256:267959a7e58e9b369f0c9c544f626c0d6cdfa8ef01d121b9f615d65d4e7efe9b ovnCentralName: ovn-central diff --git a/packages/system/kubeovn-webhook/values.yaml b/packages/system/kubeovn-webhook/values.yaml index df5215d1..6ae1d440 100644 --- a/packages/system/kubeovn-webhook/values.yaml +++ b/packages/system/kubeovn-webhook/values.yaml @@ -1,3 +1,3 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.2.1@sha256:e6334c29d3aaf0dea766c88e3e05b53ad623d1bb497b3c836e6f76adade45b29 +image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.2.2@sha256:e18f9fd679e38f65362a8d0042f25468272f6d081136ad47027168d8e7e07a4a diff --git a/packages/system/kubevirt-csi-node/values.yaml b/packages/system/kubevirt-csi-node/values.yaml index ad7e6f01..3b604202 100644 --- a/packages/system/kubevirt-csi-node/values.yaml +++ b/packages/system/kubevirt-csi-node/values.yaml @@ -1,3 +1,3 @@ storageClass: replicated csiDriver: - image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:5b209248acba3d8cf0d999edd8a915915a2e565a1c8ac1f5dde0dffec20fa02e + image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:73645c21f3d39dd11b2cf79d975b57479c5907a41ed73d452f96e7229cc8841e diff --git a/packages/system/lineage-controller-webhook/values.yaml b/packages/system/lineage-controller-webhook/values.yaml index 103dbf85..d870ac5d 100644 --- a/packages/system/lineage-controller-webhook/values.yaml +++ b/packages/system/lineage-controller-webhook/values.yaml @@ -1,5 +1,5 @@ lineageControllerWebhook: - image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.2.1@sha256:0fef727af2ea8d87503ba998d2ec03c12eb432d03b6642a555b81b95fbec4cb0 + image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.2.2@sha256:def28f05a9d3060bcd9ff78b491e373a0d4a3c09bf53fc88d18918ad6aaff1ff debug: false localK8sAPIEndpoint: enabled: true diff --git a/packages/system/linstor/values.yaml b/packages/system/linstor/values.yaml index b3f13443..57f5f331 100644 --- a/packages/system/linstor/values.yaml +++ b/packages/system/linstor/values.yaml @@ -1,7 +1,7 @@ piraeusServer: image: repository: ghcr.io/cozystack/cozystack/piraeus-server - tag: 1.32.3@sha256:d78071fdc33220168e3f2ab112a86208be95898b75268a0c62763b8a04e145ad + tag: 1.33.2@sha256:e748ea7b2adb285b0387fde4453320155fc03e4470db7d9af1d5447087bf18b4 # Talos-specific workarounds (disable for generic Linux like Ubuntu/Debian) talos: enabled: true @@ -13,4 +13,4 @@ linstor: linstorCSI: image: repository: ghcr.io/cozystack/cozystack/linstor-csi - tag: v1.10.5@sha256:e80ae94d8acd8774cb35715b1f103071ae6cd3160f1ebea915e5e6ee49234448 + tag: v1.10.5@sha256:a80d39f96a988085e84be2d6e896f4b4a008fccd5bbd9eaa2b8534b076429942 diff --git a/packages/system/multus/templates/multus-daemonset-thick.yml b/packages/system/multus/templates/multus-daemonset-thick.yml index bedffee3..241f2ab6 100644 --- a/packages/system/multus/templates/multus-daemonset-thick.yml +++ b/packages/system/multus/templates/multus-daemonset-thick.yml @@ -155,7 +155,7 @@ spec: serviceAccountName: multus containers: - name: kube-multus - image: ghcr.io/cozystack/cozystack/multus-cni:v1.2.1@sha256:aaf2ed6a5db1ee5acc0cd4f4683ea33570ff922c211bef37d407d6a01427b566 + image: ghcr.io/cozystack/cozystack/multus-cni:v1.2.2@sha256:d76cc81c9ef1521e1317aacdc98360325f4fbecdfc687b97852e2f5c275551b1 command: [ "/usr/src/multus-cni/bin/multus-daemon" ] resources: requests: @@ -201,7 +201,7 @@ spec: fieldPath: spec.nodeName initContainers: - name: install-multus-binary - image: ghcr.io/cozystack/cozystack/multus-cni:v1.2.1@sha256:aaf2ed6a5db1ee5acc0cd4f4683ea33570ff922c211bef37d407d6a01427b566 + image: ghcr.io/cozystack/cozystack/multus-cni:v1.2.2@sha256:d76cc81c9ef1521e1317aacdc98360325f4fbecdfc687b97852e2f5c275551b1 command: - "/usr/src/multus-cni/bin/install_multus" - "-d" diff --git a/packages/system/objectstorage-controller/values.yaml b/packages/system/objectstorage-controller/values.yaml index 1439e583..f2d52957 100644 --- a/packages/system/objectstorage-controller/values.yaml +++ b/packages/system/objectstorage-controller/values.yaml @@ -1,3 +1,3 @@ objectstorage: controller: - image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.2.1@sha256:036be10f26c4871c63fb63c6a294476289aa063610e7fbbc0dbd21d3cd7bfe5e" + image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.2.2@sha256:036be10f26c4871c63fb63c6a294476289aa063610e7fbbc0dbd21d3cd7bfe5e" diff --git a/packages/system/seaweedfs/values.yaml b/packages/system/seaweedfs/values.yaml index 7d598649..3e760193 100644 --- a/packages/system/seaweedfs/values.yaml +++ b/packages/system/seaweedfs/values.yaml @@ -177,7 +177,7 @@ seaweedfs: bucketClassName: "seaweedfs" region: "" sidecar: - image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.1@sha256:1ad07fb9e96477e5c322f240f6023ceedd8762261d8379ec784dde92797ee206" + image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.2@sha256:1ad07fb9e96477e5c322f240f6023ceedd8762261d8379ec784dde92797ee206" certificates: commonName: "SeaweedFS CA" ipAddresses: [] From 9accd4a7c948bc84493e7bc3f2efc1c952f92474 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 31 Mar 2026 12:48:07 +0200 Subject: [PATCH 33/36] fix(kubernetes): set explicit ephemeral-storage on virt-launcher pods Without explicit ephemeral-storage resources on the VirtualMachine spec, virt-launcher pods inherit default limits from the namespace LimitRange, which can be much lower than the emptyDisk capacity. This causes kubelet to evict the pod when the VM's containerd unpacks images exceeding the limit. Set domain.resources with ephemeral-storage calculated via cozy-lib allocation ratio based on the configured ephemeralStorage value. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil (cherry picked from commit 94d42749a7b1fd01ba4f99dec7dc975b19f0f2e7) --- packages/apps/kubernetes/templates/cluster.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/apps/kubernetes/templates/cluster.yaml b/packages/apps/kubernetes/templates/cluster.yaml index 7d71858b..10d6fd80 100644 --- a/packages/apps/kubernetes/templates/cluster.yaml +++ b/packages/apps/kubernetes/templates/cluster.yaml @@ -70,6 +70,8 @@ spec: memory: guest: {{ .group.resources.memory }} {{- end }} + resources: + {{- include "cozy-lib.resources.sanitize" (list (dict "ephemeral-storage" .group.ephemeralStorage) $) | nindent 14 }} evictionStrategy: External volumes: - name: system From 4e32be953ccea801112d84f5d05ac379d0acd646 Mon Sep 17 00:00:00 2001 From: "cozystack-ci[bot]" <274107086+cozystack-ci[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 01:36:49 +0000 Subject: [PATCH 34/36] Prepare release v1.2.3 Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com> --- packages/apps/http-cache/images/nginx-cache.tag | 2 +- packages/apps/kubernetes/images/kubevirt-csi-driver.tag | 2 +- .../apps/kubernetes/images/ubuntu-container-disk-v1.30.tag | 2 +- .../apps/kubernetes/images/ubuntu-container-disk-v1.31.tag | 2 +- .../apps/kubernetes/images/ubuntu-container-disk-v1.32.tag | 2 +- .../apps/kubernetes/images/ubuntu-container-disk-v1.33.tag | 2 +- .../apps/kubernetes/images/ubuntu-container-disk-v1.34.tag | 2 +- .../apps/kubernetes/images/ubuntu-container-disk-v1.35.tag | 2 +- packages/apps/mariadb/images/mariadb-backup.tag | 2 +- packages/core/installer/values.yaml | 4 ++-- packages/core/platform/values.yaml | 2 +- packages/core/testing/values.yaml | 2 +- packages/extra/bootbox/images/matchbox.tag | 2 +- packages/extra/seaweedfs/images/objectstorage-sidecar.tag | 2 +- packages/system/backup-controller/values.yaml | 2 +- packages/system/backupstrategy-controller/values.yaml | 2 +- packages/system/bucket/images/s3manager.tag | 2 +- packages/system/cozystack-api/values.yaml | 2 +- packages/system/cozystack-controller/values.yaml | 2 +- packages/system/dashboard/templates/configmap.yaml | 2 +- packages/system/dashboard/values.yaml | 6 +++--- .../system/grafana-operator/images/grafana-dashboards.tag | 2 +- packages/system/kamaji/values.yaml | 4 ++-- packages/system/kubeovn-plunger/values.yaml | 2 +- packages/system/kubeovn-webhook/values.yaml | 2 +- packages/system/kubevirt-csi-node/values.yaml | 2 +- packages/system/lineage-controller-webhook/values.yaml | 2 +- packages/system/linstor/values.yaml | 2 +- packages/system/multus/templates/multus-daemonset-thick.yml | 4 ++-- packages/system/objectstorage-controller/values.yaml | 2 +- packages/system/seaweedfs/values.yaml | 2 +- 31 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/apps/http-cache/images/nginx-cache.tag b/packages/apps/http-cache/images/nginx-cache.tag index cfefd32b..b08a374e 100644 --- a/packages/apps/http-cache/images/nginx-cache.tag +++ b/packages/apps/http-cache/images/nginx-cache.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:95b2790e6caa0f2fbad48951c30e7848c0ef7b1bc433b2e5f07c5f4940f20783 +ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:d397781152ab9123b11b8191d92eba7a0d2faa376aa2c15ddeb67842a9b59bab diff --git a/packages/apps/kubernetes/images/kubevirt-csi-driver.tag b/packages/apps/kubernetes/images/kubevirt-csi-driver.tag index 1110427d..ea4a21b3 100644 --- a/packages/apps/kubernetes/images/kubevirt-csi-driver.tag +++ b/packages/apps/kubernetes/images/kubevirt-csi-driver.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:73645c21f3d39dd11b2cf79d975b57479c5907a41ed73d452f96e7229cc8841e +ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:36e26a6b9063761ca3f5597bbd6272651d8a502abbc1c4e3a20e6b7e45a2875a diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag index 05b465ca..f49fffe6 100644 --- a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.30.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.30@sha256:9474613009b8c10f2653a22c4ebf83977768c593079478933a4dd9376f6dd68b +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.30@sha256:f460ed1fd5a721aed423e6c3b8be0105d7f693cd86ad992d9c15fd4b27e58cec diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag index 887ad3a2..b5bdc79a 100644 --- a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.31.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.31@sha256:f55cc5387ee13c83b4b6e1c0ede490fc903331787e9292ac104e8e7bdeeed68b +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.31@sha256:aaa2dfa8ee53ae26295f44d2491330f51412457bbe4aa6ba256297cc0bd8a0da diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag index 4730d2a7..ca285644 100644 --- a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.32.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.32@sha256:62909d198c78b2a07d203384ee904b0c74b6e9c5f2587c007eb337159d712bb2 +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.32@sha256:7acb4728ef6b9c0ff3344bf486bb00f2a083f2098452344a362242235013db01 diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag index a396e21e..bb58690a 100644 --- a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.33.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:4f2b0933d2251c24a3a7b53a3827d4f809e2250c1c4af4e44776ab661960ed34 +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:a2024339ab9edb980a96b43ad2744b7aa31afaef0983ddbc6a546068b4cfa87a diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag index 65ecb1f5..36eed7a7 100644 --- a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.34.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.34@sha256:4833c9cbc7d06fb183f397d1e1c577bb01682f682cfc06d86f55406a8c6b58f1 +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.34@sha256:97bcf946a5687889c6a421d14e5adece11f0978151b2165dc87b28f77f7607db diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag index e0a9cd18..faae83d2 100644 --- a/packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag +++ b/packages/apps/kubernetes/images/ubuntu-container-disk-v1.35.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.35@sha256:9673eee5db99b537060bda711b962730fd6a89b8ba87a3d984c89574a729b7fe +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.35@sha256:364c6d454891f1eb1a598fddb69cf328a14dbc451a8ac65812b038a7756da60a diff --git a/packages/apps/mariadb/images/mariadb-backup.tag b/packages/apps/mariadb/images/mariadb-backup.tag index 1e381661..6c830892 100644 --- a/packages/apps/mariadb/images/mariadb-backup.tag +++ b/packages/apps/mariadb/images/mariadb-backup.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:0ddbbec0568dcb9fbc317cd9cc654e826dbe88ba3f184fa9b6b58aacb93b4570 +ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:3841eb171416711977dea0cf8cd45d32344caac9727af760c37d5e1dd41ee4bb diff --git a/packages/core/installer/values.yaml b/packages/core/installer/values.yaml index 68cad5e4..70682e37 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -1,9 +1,9 @@ cozystackOperator: # Deployment variant: talos, generic, hosted variant: talos - image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.2.2@sha256:d7d00762e373f672490e7fc48bc1f515df12bf51748b74e8d9e31b61bbd9d0c0 + image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.2.3@sha256:aaedbe019abb34df56579d476c815da8ae4d15f44c126ead1eea21699de659de platformSourceUrl: 'oci://ghcr.io/cozystack/cozystack/cozystack-packages' - platformSourceRef: 'digest=sha256:2e998f83db355d981b4a7ce9073637cdbf6147b918376f1fea00e286a81571c6' + platformSourceRef: 'digest=sha256:d36991c88b872d7c5bd3b66d0bce038afbeb21247d989155fa700d5868ce0609' # Generic variant configuration (only used when cozystackOperator.variant=generic) cozystack: # Kubernetes API server host (IP only, no protocol/port) diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index b9d008d6..bba969f0 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -5,7 +5,7 @@ sourceRef: path: / migrations: enabled: false - image: ghcr.io/cozystack/cozystack/platform-migrations:v1.2.2@sha256:72af209576eb42793dbf241c024d713fd23cc1435673dc2f9a81f70c237df124 + image: ghcr.io/cozystack/cozystack/platform-migrations:v1.2.3@sha256:72af209576eb42793dbf241c024d713fd23cc1435673dc2f9a81f70c237df124 targetVersion: 38 # Bundle deployment configuration bundles: diff --git a/packages/core/testing/values.yaml b/packages/core/testing/values.yaml index 8bc7e1e7..6c01d671 100644 --- a/packages/core/testing/values.yaml +++ b/packages/core/testing/values.yaml @@ -1,2 +1,2 @@ e2e: - image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.2.2@sha256:892663fe8c17596b2e01f49e13a1d76181e92f1b8d2f02b80065ba6824c6c80d + image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.2.3@sha256:0367a03b981df2a3ea13f411d4cb7869c2bf2c89c07d3d5c8971b9a28921ccef diff --git a/packages/extra/bootbox/images/matchbox.tag b/packages/extra/bootbox/images/matchbox.tag index 988b9409..487ae6b2 100644 --- a/packages/extra/bootbox/images/matchbox.tag +++ b/packages/extra/bootbox/images/matchbox.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/matchbox:v1.2.2@sha256:1461c101394cb2930db7eea2444614b398eb8e64b0bdebef4a213dfc5d476434 +ghcr.io/cozystack/cozystack/matchbox:v1.2.3@sha256:78864d7876d2bdc22cc72b919b2f7f92ea52bc44265d18e813a80931d4a54da0 diff --git a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag index ac47b1d7..daac8d91 100644 --- a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag +++ b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.2@sha256:1ad07fb9e96477e5c322f240f6023ceedd8762261d8379ec784dde92797ee206 +ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.3@sha256:5bcccbdb13979a16cee535eb5fbcdf0d87973689010a10b7b25e55c5be3edaa6 diff --git a/packages/system/backup-controller/values.yaml b/packages/system/backup-controller/values.yaml index e5731b0d..eb87af02 100644 --- a/packages/system/backup-controller/values.yaml +++ b/packages/system/backup-controller/values.yaml @@ -1,5 +1,5 @@ backupController: - image: "ghcr.io/cozystack/cozystack/backup-controller:v1.2.2@sha256:61dbd7becf26b888558ba2c6eb6ba3ddb78f2ecce741558c07a8b6837c1b4eac" + image: "ghcr.io/cozystack/cozystack/backup-controller:v1.2.3@sha256:98a7a3dc956a3da50e781541ab8b85a7fbbb1a6975d6a2e72fa71ac15907db41" replicas: 2 debug: false metrics: diff --git a/packages/system/backupstrategy-controller/values.yaml b/packages/system/backupstrategy-controller/values.yaml index c52fb860..2571b786 100644 --- a/packages/system/backupstrategy-controller/values.yaml +++ b/packages/system/backupstrategy-controller/values.yaml @@ -1,5 +1,5 @@ backupStrategyController: - image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.2.2@sha256:9bf3f569fca8773f274f67c62f15f7e2a5ffc1df16f6207176e835588fabca80" + image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.2.3@sha256:9c8ce160768e5393f7591ed62f39f4b1fba726de76e08f358055a932ccd6283a" replicas: 2 debug: false metrics: diff --git a/packages/system/bucket/images/s3manager.tag b/packages/system/bucket/images/s3manager.tag index d3d5eb51..30b082df 100644 --- a/packages/system/bucket/images/s3manager.tag +++ b/packages/system/bucket/images/s3manager.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:36235971fc1790b11d38210894cd24cae46a54d88445aa6c4821977c139a58f2 +ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:cfffa1267ad72d138c6e9eed6fa282f4fa23698bef2180be98c0be39c073ec28 diff --git a/packages/system/cozystack-api/values.yaml b/packages/system/cozystack-api/values.yaml index 1d344462..68cd3c0b 100644 --- a/packages/system/cozystack-api/values.yaml +++ b/packages/system/cozystack-api/values.yaml @@ -1,3 +1,3 @@ cozystackAPI: - image: ghcr.io/cozystack/cozystack/cozystack-api:v1.2.2@sha256:c9cd01ba6c720f03567996ba46ac9e5a8b90729ae6d9726fdefd36ec252d33e0 + image: ghcr.io/cozystack/cozystack/cozystack-api:v1.2.3@sha256:4d6d0efffe2d37b83890563485eac6423acb4795d87690cc145e2742515f5b38 replicas: 2 diff --git a/packages/system/cozystack-controller/values.yaml b/packages/system/cozystack-controller/values.yaml index c9437db2..4833f09d 100644 --- a/packages/system/cozystack-controller/values.yaml +++ b/packages/system/cozystack-controller/values.yaml @@ -1,4 +1,4 @@ cozystackController: - image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.2.2@sha256:f5817f275386af00362d579a29c4762a4b2e3daf99f110c3db0c5f791d305792 + image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.2.3@sha256:ff7f683382e21e380df289268a6ad4a35ffd1065e72a15d0749f6465f76d60ea debug: false disableTelemetry: false diff --git a/packages/system/dashboard/templates/configmap.yaml b/packages/system/dashboard/templates/configmap.yaml index 5a00deb0..777a9fa7 100644 --- a/packages/system/dashboard/templates/configmap.yaml +++ b/packages/system/dashboard/templates/configmap.yaml @@ -1,6 +1,6 @@ {{- $brandingConfig := .Values._cluster.branding | default dict }} -{{- $tenantText := "v1.2.2" }} +{{- $tenantText := "v1.2.3" }} {{- $footerText := "Cozystack" }} {{- $titleText := "Cozystack Dashboard" }} {{- $logoText := "" }} diff --git a/packages/system/dashboard/values.yaml b/packages/system/dashboard/values.yaml index d2315a7e..9371d1d3 100644 --- a/packages/system/dashboard/values.yaml +++ b/packages/system/dashboard/values.yaml @@ -1,6 +1,6 @@ openapiUI: - image: ghcr.io/cozystack/cozystack/openapi-ui:v1.2.2@sha256:1b0c641150d4ac65208e78107cfc87f9a692299c51f5a93e49cf5fb5ac3bde57 + image: ghcr.io/cozystack/cozystack/openapi-ui:v1.2.3@sha256:3275a752bd82692cf73233a666bee76ecd3867dac697c81fe25b0ed4a2efd305 openapiUIK8sBff: - image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.2.2@sha256:c938fee904acd948800d4dc5e121c4c5cd64cb4a3160fb8d2f9dbff0e5168740 + image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.2.3@sha256:a17f73592c4b37ac8dbb9697e2866bdea301ff4364e235bb58c665b5f41143c4 tokenProxy: - image: ghcr.io/cozystack/cozystack/token-proxy:v1.2.2@sha256:2e280991e07853ea48f97b0a42946afffa10d03d6a83d41099ed83e6ffc94fdc + image: ghcr.io/cozystack/cozystack/token-proxy:v1.2.3@sha256:2e280991e07853ea48f97b0a42946afffa10d03d6a83d41099ed83e6ffc94fdc diff --git a/packages/system/grafana-operator/images/grafana-dashboards.tag b/packages/system/grafana-operator/images/grafana-dashboards.tag index f998a508..fcb6fb30 100644 --- a/packages/system/grafana-operator/images/grafana-dashboards.tag +++ b/packages/system/grafana-operator/images/grafana-dashboards.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/grafana-dashboards:v1.2.2@sha256:2c9aa0b48e2bf6167db198f4d15882bfe51700108edf2e9f6d0942940a2c1204 +ghcr.io/cozystack/cozystack/grafana-dashboards:v1.2.3@sha256:2c9aa0b48e2bf6167db198f4d15882bfe51700108edf2e9f6d0942940a2c1204 diff --git a/packages/system/kamaji/values.yaml b/packages/system/kamaji/values.yaml index fbd06f54..f938795c 100644 --- a/packages/system/kamaji/values.yaml +++ b/packages/system/kamaji/values.yaml @@ -3,7 +3,7 @@ kamaji: deploy: false image: pullPolicy: IfNotPresent - tag: v1.2.2@sha256:a2ab1c507fb1b1249364823bd43bbf9db933009e66f110ca64cc933ce231d10c + tag: v1.2.3@sha256:a2ab1c507fb1b1249364823bd43bbf9db933009e66f110ca64cc933ce231d10c repository: ghcr.io/cozystack/cozystack/kamaji resources: limits: @@ -13,4 +13,4 @@ kamaji: cpu: 100m memory: 100Mi extraArgs: - - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.2.2@sha256:a2ab1c507fb1b1249364823bd43bbf9db933009e66f110ca64cc933ce231d10c + - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.2.3@sha256:a2ab1c507fb1b1249364823bd43bbf9db933009e66f110ca64cc933ce231d10c diff --git a/packages/system/kubeovn-plunger/values.yaml b/packages/system/kubeovn-plunger/values.yaml index 01e0fa7f..4b1e53a3 100644 --- a/packages/system/kubeovn-plunger/values.yaml +++ b/packages/system/kubeovn-plunger/values.yaml @@ -1,4 +1,4 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.2.2@sha256:267959a7e58e9b369f0c9c544f626c0d6cdfa8ef01d121b9f615d65d4e7efe9b +image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.2.3@sha256:57c4217a5f8e981b85f8f4601f3a7af1d1670022bbccc5e32392faeb44595d2f ovnCentralName: ovn-central diff --git a/packages/system/kubeovn-webhook/values.yaml b/packages/system/kubeovn-webhook/values.yaml index 6ae1d440..fa33abe0 100644 --- a/packages/system/kubeovn-webhook/values.yaml +++ b/packages/system/kubeovn-webhook/values.yaml @@ -1,3 +1,3 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.2.2@sha256:e18f9fd679e38f65362a8d0042f25468272f6d081136ad47027168d8e7e07a4a +image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.2.3@sha256:e18f9fd679e38f65362a8d0042f25468272f6d081136ad47027168d8e7e07a4a diff --git a/packages/system/kubevirt-csi-node/values.yaml b/packages/system/kubevirt-csi-node/values.yaml index 3b604202..a4d51b79 100644 --- a/packages/system/kubevirt-csi-node/values.yaml +++ b/packages/system/kubevirt-csi-node/values.yaml @@ -1,3 +1,3 @@ storageClass: replicated csiDriver: - image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:73645c21f3d39dd11b2cf79d975b57479c5907a41ed73d452f96e7229cc8841e + image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:36e26a6b9063761ca3f5597bbd6272651d8a502abbc1c4e3a20e6b7e45a2875a diff --git a/packages/system/lineage-controller-webhook/values.yaml b/packages/system/lineage-controller-webhook/values.yaml index d870ac5d..23ce3bf5 100644 --- a/packages/system/lineage-controller-webhook/values.yaml +++ b/packages/system/lineage-controller-webhook/values.yaml @@ -1,5 +1,5 @@ lineageControllerWebhook: - image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.2.2@sha256:def28f05a9d3060bcd9ff78b491e373a0d4a3c09bf53fc88d18918ad6aaff1ff + image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.2.3@sha256:f2843506673e6cd0c7e99d395d337aef93e519151e5d58d3c8fdc1df5813a8f5 debug: false localK8sAPIEndpoint: enabled: true diff --git a/packages/system/linstor/values.yaml b/packages/system/linstor/values.yaml index 57f5f331..6b7420f1 100644 --- a/packages/system/linstor/values.yaml +++ b/packages/system/linstor/values.yaml @@ -13,4 +13,4 @@ linstor: linstorCSI: image: repository: ghcr.io/cozystack/cozystack/linstor-csi - tag: v1.10.5@sha256:a80d39f96a988085e84be2d6e896f4b4a008fccd5bbd9eaa2b8534b076429942 + tag: v1.10.5@sha256:f7724789da9b77f83f561336ff52e67a9a313e3d55f6591b901d4c461ae75a1a diff --git a/packages/system/multus/templates/multus-daemonset-thick.yml b/packages/system/multus/templates/multus-daemonset-thick.yml index 241f2ab6..cedadee2 100644 --- a/packages/system/multus/templates/multus-daemonset-thick.yml +++ b/packages/system/multus/templates/multus-daemonset-thick.yml @@ -155,7 +155,7 @@ spec: serviceAccountName: multus containers: - name: kube-multus - image: ghcr.io/cozystack/cozystack/multus-cni:v1.2.2@sha256:d76cc81c9ef1521e1317aacdc98360325f4fbecdfc687b97852e2f5c275551b1 + image: ghcr.io/cozystack/cozystack/multus-cni:v1.2.3@sha256:d76cc81c9ef1521e1317aacdc98360325f4fbecdfc687b97852e2f5c275551b1 command: [ "/usr/src/multus-cni/bin/multus-daemon" ] resources: requests: @@ -201,7 +201,7 @@ spec: fieldPath: spec.nodeName initContainers: - name: install-multus-binary - image: ghcr.io/cozystack/cozystack/multus-cni:v1.2.2@sha256:d76cc81c9ef1521e1317aacdc98360325f4fbecdfc687b97852e2f5c275551b1 + image: ghcr.io/cozystack/cozystack/multus-cni:v1.2.3@sha256:d76cc81c9ef1521e1317aacdc98360325f4fbecdfc687b97852e2f5c275551b1 command: - "/usr/src/multus-cni/bin/install_multus" - "-d" diff --git a/packages/system/objectstorage-controller/values.yaml b/packages/system/objectstorage-controller/values.yaml index f2d52957..3d3dde6c 100644 --- a/packages/system/objectstorage-controller/values.yaml +++ b/packages/system/objectstorage-controller/values.yaml @@ -1,3 +1,3 @@ objectstorage: controller: - image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.2.2@sha256:036be10f26c4871c63fb63c6a294476289aa063610e7fbbc0dbd21d3cd7bfe5e" + image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.2.3@sha256:f81da9941bec6b52d8d2ad327421886fa0979776181e59380a08ad28d13c2cff" diff --git a/packages/system/seaweedfs/values.yaml b/packages/system/seaweedfs/values.yaml index 3e760193..56e6f78e 100644 --- a/packages/system/seaweedfs/values.yaml +++ b/packages/system/seaweedfs/values.yaml @@ -177,7 +177,7 @@ seaweedfs: bucketClassName: "seaweedfs" region: "" sidecar: - image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.2@sha256:1ad07fb9e96477e5c322f240f6023ceedd8762261d8379ec784dde92797ee206" + image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.2.3@sha256:5bcccbdb13979a16cee535eb5fbcdf0d87973689010a10b7b25e55c5be3edaa6" certificates: commonName: "SeaweedFS CA" ipAddresses: [] From f7ca9880a46b719fc9b8b035def35c3e4e6597fe Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Tue, 21 Apr 2026 17:00:31 +0500 Subject: [PATCH 35/36] fix(platform): migrate ACME HTTP-01 to ingressClassName API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ClusterIssuer solver referenced IngressClass "nginx" which does not exist on cozystack clusters — real classes are named after tenant namespaces (e.g. tenant-root). Cert issuance only worked because every requesting Ingress overrode the ClusterIssuer via the legacy acme.cert-manager.io/http01-ingress-class annotation. Switch both sides to the modern cert-manager API (available since cert-manager 1.12; cozystack ships 1.19.3): - ClusterIssuer: http01.ingress.ingressClassName, value parameterized from _cluster.expose-ingress (default "tenant-root") - Ingress annotation: http01-ingress-ingressclassname These must migrate together — mixing ingressClassName (ClusterIssuer) with the old http01-ingress-class annotation triggers cert-manager's "fields ingressClassName and class cannot be set at the same time" validation and breaks issuance. Assisted-By: Claude Signed-off-by: Myasnikov Daniil (cherry picked from commit 2b6e20cc3f96505ee8abdb40be64f1234a40b29d) --- packages/apps/harbor/templates/ingress.yaml | 2 +- .../extra/bootbox/templates/matchbox/ingress.yaml | 2 +- packages/extra/seaweedfs/templates/seaweedfs.yaml | 2 +- packages/system/bucket/templates/ingress.yaml | 2 +- .../templates/cluster-issuers.yaml | 15 ++++++++------- packages/system/dashboard/templates/ingress.yaml | 2 +- packages/system/keycloak/templates/ingress.yaml | 2 +- .../monitoring/templates/alerta/alerta.yaml | 2 +- .../monitoring/templates/grafana/grafana.yaml | 2 +- packages/system/seaweedfs/values.yaml | 2 +- 10 files changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/apps/harbor/templates/ingress.yaml b/packages/apps/harbor/templates/ingress.yaml index bb6ef07c..70933f5f 100644 --- a/packages/apps/harbor/templates/ingress.yaml +++ b/packages/apps/harbor/templates/ingress.yaml @@ -15,7 +15,7 @@ metadata: nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/backend-protocol: "HTTP" {{- if eq $solver "http01" }} - acme.cert-manager.io/http01-ingress-class: {{ $ingress }} + acme.cert-manager.io/http01-ingress-ingressclassname: {{ $ingress }} {{- end }} cert-manager.io/cluster-issuer: {{ $clusterIssuer }} spec: diff --git a/packages/extra/bootbox/templates/matchbox/ingress.yaml b/packages/extra/bootbox/templates/matchbox/ingress.yaml index 4262cd10..5eef3489 100644 --- a/packages/extra/bootbox/templates/matchbox/ingress.yaml +++ b/packages/extra/bootbox/templates/matchbox/ingress.yaml @@ -10,7 +10,7 @@ metadata: app: bootbox annotations: {{- if eq $solver "http01" }} - acme.cert-manager.io/http01-ingress-class: {{ $ingress }} + acme.cert-manager.io/http01-ingress-ingressclassname: {{ $ingress }} {{- end }} cert-manager.io/cluster-issuer: {{ $clusterIssuer }} {{- if .Values.whitelistHTTP }} diff --git a/packages/extra/seaweedfs/templates/seaweedfs.yaml b/packages/extra/seaweedfs/templates/seaweedfs.yaml index 42441b26..2f5720ca 100644 --- a/packages/extra/seaweedfs/templates/seaweedfs.yaml +++ b/packages/extra/seaweedfs/templates/seaweedfs.yaml @@ -243,7 +243,7 @@ spec: nginx.ingress.kubernetes.io/proxy-body-size: "0" nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" {{- if eq $solver "http01" }} - acme.cert-manager.io/http01-ingress-class: {{ $ingress }} + acme.cert-manager.io/http01-ingress-ingressclassname: {{ $ingress }} {{- end }} cert-manager.io/cluster-issuer: {{ $clusterIssuer }} tls: diff --git a/packages/system/bucket/templates/ingress.yaml b/packages/system/bucket/templates/ingress.yaml index 6e5028fc..b7ffaf8b 100644 --- a/packages/system/bucket/templates/ingress.yaml +++ b/packages/system/bucket/templates/ingress.yaml @@ -12,7 +12,7 @@ metadata: nginx.ingress.kubernetes.io/proxy-read-timeout: "99999" nginx.ingress.kubernetes.io/proxy-send-timeout: "99999" {{- if eq $solver "http01" }} - acme.cert-manager.io/http01-ingress-class: {{ $ingress }} + acme.cert-manager.io/http01-ingress-ingressclassname: {{ $ingress }} {{- end }} cert-manager.io/cluster-issuer: {{ $clusterIssuer }} spec: diff --git a/packages/system/cert-manager-issuers/templates/cluster-issuers.yaml b/packages/system/cert-manager-issuers/templates/cluster-issuers.yaml index 442f1fb3..3b582082 100644 --- a/packages/system/cert-manager-issuers/templates/cluster-issuers.yaml +++ b/packages/system/cert-manager-issuers/templates/cluster-issuers.yaml @@ -1,6 +1,7 @@ {{- $solver := (index .Values._cluster "solver") | default "http01" }} +{{- $exposeIngress := (index .Values._cluster "expose-ingress") | default "tenant-root" }} -apiVersion: cert-manager.io/v1 +apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-prod @@ -17,9 +18,9 @@ spec: name: cloudflare-api-token-secret key: api-token {{- else }} - http01: - ingress: - class: nginx + http01: + ingress: + ingressClassName: {{ $exposeIngress }} {{- end }} --- @@ -41,9 +42,9 @@ spec: name: cloudflare-api-token-secret key: api-token {{- else }} - http01: - ingress: - class: nginx + http01: + ingress: + ingressClassName: {{ $exposeIngress }} {{- end }} --- diff --git a/packages/system/dashboard/templates/ingress.yaml b/packages/system/dashboard/templates/ingress.yaml index a10797fb..6cf01490 100644 --- a/packages/system/dashboard/templates/ingress.yaml +++ b/packages/system/dashboard/templates/ingress.yaml @@ -11,7 +11,7 @@ metadata: annotations: cert-manager.io/cluster-issuer: {{ $clusterIssuer }} {{- if eq $solver "http01" }} - acme.cert-manager.io/http01-ingress-class: {{ $exposeIngress }} + acme.cert-manager.io/http01-ingress-ingressclassname: {{ $exposeIngress }} {{- end }} nginx.ingress.kubernetes.io/rewrite-target: / nginx.ingress.kubernetes.io/client-max-body-size: 100m diff --git a/packages/system/keycloak/templates/ingress.yaml b/packages/system/keycloak/templates/ingress.yaml index aa282f71..7f9fd476 100644 --- a/packages/system/keycloak/templates/ingress.yaml +++ b/packages/system/keycloak/templates/ingress.yaml @@ -11,7 +11,7 @@ metadata: {{- with .Values.ingress.annotations }} annotations: {{- if eq $solver "http01" }} - acme.cert-manager.io/http01-ingress-class: {{ $exposeIngress }} + acme.cert-manager.io/http01-ingress-ingressclassname: {{ $exposeIngress }} {{- end }} cert-manager.io/cluster-issuer: {{ $clusterIssuer }} {{- toYaml . | nindent 4 }} diff --git a/packages/system/monitoring/templates/alerta/alerta.yaml b/packages/system/monitoring/templates/alerta/alerta.yaml index 44046d1f..d30e698d 100644 --- a/packages/system/monitoring/templates/alerta/alerta.yaml +++ b/packages/system/monitoring/templates/alerta/alerta.yaml @@ -181,7 +181,7 @@ metadata: app: alerta annotations: {{- if eq $solver "http01" }} - acme.cert-manager.io/http01-ingress-class: {{ $ingress }} + acme.cert-manager.io/http01-ingress-ingressclassname: {{ $ingress }} {{- end }} cert-manager.io/cluster-issuer: {{ $clusterIssuer }} spec: diff --git a/packages/system/monitoring/templates/grafana/grafana.yaml b/packages/system/monitoring/templates/grafana/grafana.yaml index d7f51e31..d65a7dc4 100644 --- a/packages/system/monitoring/templates/grafana/grafana.yaml +++ b/packages/system/monitoring/templates/grafana/grafana.yaml @@ -74,7 +74,7 @@ spec: metadata: annotations: {{- if eq $solver "http01" }} - acme.cert-manager.io/http01-ingress-class: "{{ $ingress }}" + acme.cert-manager.io/http01-ingress-ingressclassname: "{{ $ingress }}" {{- end }} cert-manager.io/cluster-issuer: {{ $clusterIssuer }} spec: diff --git a/packages/system/seaweedfs/values.yaml b/packages/system/seaweedfs/values.yaml index 56e6f78e..c81dbf22 100644 --- a/packages/system/seaweedfs/values.yaml +++ b/packages/system/seaweedfs/values.yaml @@ -111,7 +111,7 @@ seaweedfs: nginx.ingress.kubernetes.io/client-body-timeout: "3600" nginx.ingress.kubernetes.io/client-header-timeout: "120" nginx.ingress.kubernetes.io/service-upstream: "true" - acme.cert-manager.io/http01-ingress-class: tenant-root + acme.cert-manager.io/http01-ingress-ingressclassname: tenant-root cert-manager.io/cluster-issuer: letsencrypt-prod tls: - hosts: From b184b06a6a169f238f56bb96d6e929d26bcdb888 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 21 Apr 2026 20:26:13 +0200 Subject: [PATCH 36/36] fix(harbor): remove incorrect tenant module flags Harbor is a PaaS service, not a tenant module. It is not deployed automatically into tenant namespaces (no manifest in packages/apps/tenant/templates/). Remove the misplaced `dashboard.module: true` flag and `internal.cozystack.io/tenantmodule: "true"` release label so Harbor appears under the PaaS category in the dashboard and is not treated as a tenant module by the controllers. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil (cherry picked from commit 68a624dccbe0a50c3731111ba2761f47467c8157) --- packages/system/harbor-rd/cozyrds/harbor.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/system/harbor-rd/cozyrds/harbor.yaml b/packages/system/harbor-rd/cozyrds/harbor.yaml index 5abc725f..5682c053 100644 --- a/packages/system/harbor-rd/cozyrds/harbor.yaml +++ b/packages/system/harbor-rd/cozyrds/harbor.yaml @@ -13,7 +13,6 @@ spec: prefix: "harbor-" labels: sharding.fluxcd.io/key: tenants - internal.cozystack.io/tenantmodule: "true" chartRef: kind: ExternalArtifact name: cozystack-harbor-application-default-harbor @@ -24,7 +23,6 @@ spec: plural: Harbor name: harbor description: Managed Harbor container registry - module: true tags: - registry - container