Compare commits

...

75 Commits

Author SHA1 Message Date
Andrei Kvapil
074725f69c Release v0.39.5 (#1854)
This PR prepares the release `v0.39.5`.
2026-01-13 08:04:19 +01:00
cozystack-bot
ea0abcc465 Prepare release v0.39.5
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2026-01-13 01:39:12 +00:00
Andrei Kvapil
cf96f19a70 [Backport release-0.39] [linstor] Update piraeus-server patches with critical fixes (#1853)
# Description
Backport of #1850 to `release-0.39`.
2026-01-12 23:24:07 +01:00
Andrei Kvapil
274a3a57a8 [linstor] Update piraeus-server patches with critical fixes
Update piraeus-server patches to address critical production issues:

- Add fix-duplicate-tcp-ports.diff to prevent duplicate TCP ports
  after toggle-disk operations (upstream PR #476)

- Update skip-adjust-when-device-inaccessible.diff with comprehensive
  fixes for resources stuck in StandAlone after reboot, Unknown state
  race condition, and encrypted LUKS resource deletion (upstream PR #477)

```release-note
[linstor] Fix DRBD resources stuck in StandAlone state after reboot and encrypted resource deletion issues
```

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit dc2773ba26)
2026-01-12 22:23:42 +00:00
Andrei Kvapil
5d56029be6 Release v0.39.4 (#1849)
This PR prepares the release `v0.39.4`.
2026-01-12 11:02:36 +01:00
cozystack-bot
8e9b398a21 Prepare release v0.39.4
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2026-01-12 01:38:28 +00:00
Andrei Kvapil
93339820f8 Release v0.39.4 (#1847)
This PR prepares the release `v0.39.4`.
2026-01-10 03:55:57 +01:00
cozystack-bot
909997da1d Prepare release v0.39.4
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2026-01-10 01:36:19 +00:00
Andrei Kvapil
98cab21ab8 [0.39][paas-full] Add multus dependencies similar to other CNIs (#1835)
## What this PR does
Adds multus as a dependency as other CNIs

### Release note
```release-note
Adds multus as a dependency as other CNIs
```
2026-01-09 21:58:31 +01:00
nbykov0
ce6ec2c0bb [paas-full] Add multus dependency as other CNIs
Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com>
2026-01-09 20:03:14 +03:00
Andrei Kvapil
d90882c4af Release v0.39.3 (#1821)
This PR prepares the release `v0.39.3`.
2026-01-09 10:06:52 +01:00
cozystack-bot
cbe36e4ccf Prepare release v0.39.3
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2026-01-09 01:33:57 +00:00
Andrei Kvapil
ee600b0ce6 [multus] Remove memory limit (#1834)
Removes multus memory limit due to short but unpredictable and large
memory consumption in some cases, such as starting up after a node
reboot (reported up to 3Gi).
The root cause will be fixed in future releases.

```release-note
Multus memory limit removed.
```

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

* **Chores**
* Updated Multus system namespace configuration and DaemonSet naming for
improved environment organization.
* Adjusted container resource allocation: increased memory requests and
refined memory limits for optimized performance.
* Updated deployment template specifications to reflect infrastructure
changes.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-08 23:24:01 +01:00
Andrei Kvapil
2282afcd6a [Backport release-0.39] [linstor] fix: prevent DRBD device race condition in updateDiscGran (#1836)
# Description
Backport of #1829 to `release-0.39`.
2026-01-08 21:05:49 +01:00
Andrei Kvapil
8298763562 [Backport release-0.39] [kube-ovn] Update to v1.14.25 (#1837)
# Description
Backport of #1819 to `release-0.39`.
2026-01-08 21:05:35 +01:00
Andrei Kvapil
3028dc9f1a feat(kubeovn): update to v1.14.25
Update kube-ovn from v1.14.11 to v1.14.25.
Sync chart templates with upstream changes.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit d73773eaa1)
2026-01-08 20:05:03 +00:00
Andrei Kvapil
3ab4707c6a fix(linstor): update skip-adjust-when-device-inaccessible patch
Add physical device path existence check to prevent race condition where
lsblk is called before kernel creates DRBD device node after drbdadm adjust.

This fixes issue where resources ended up in Unknown state after satellite
restart because:
- drbdadm adjust completes successfully (brings devices up)
- updateDiscGran() immediately tries lsblk
- /dev/drbd* doesn't exist yet (kernel hasn't created node)
- lsblk fails with "not a block device" exit code 32
- StorageException interrupts DeviceManager cycle
- Device remains in incomplete state

The patch now checks Files.exists(devicePath) before lsblk, allowing
the check to be retried in next cycle when device node is available.

Upstream: https://github.com/LINBIT/linstor-server/pull/471

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit a2a0747142)
2026-01-08 20:04:52 +00:00
Nikita
17966bfba1 [Backport release-0.39] [seaweedfs] Traffic locality (#1830)
# Description
Backport of #1748 to `release-0.39`.
2026-01-08 15:57:31 +03:00
nbykov0
a9de6bf07e [seaweed] Use upstream images
Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com>
(cherry picked from commit bcd4b8976a)
2026-01-08 12:56:44 +00:00
nbykov0
765cffdc66 [seaweed] Update to 4.05
Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com>
(cherry picked from commit aede1b9217)
2026-01-08 12:56:44 +00:00
nbykov0
48ebe418b2 [seaweedfs] Traffic locality for seaweedfs
Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com>
(cherry picked from commit 2a8a8a480f)
2026-01-08 12:56:44 +00:00
nbykov0
25f31022f0 [ingress] Add topology anti-affinities
Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com>
(cherry picked from commit 6ca8011dfa)
2026-01-08 12:56:44 +00:00
Andrei Kvapil
5be383610f [Backport release-0.39] [api, lineage] Tolerate all taints (#1827)
# Description
Backport of #1781 to `release-0.39`.
2026-01-07 16:45:11 +01:00
nbykov0
363c8cf066 [cozystack-api] Tolerate all taints
Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com>
(cherry picked from commit 4e602fd55d)
2026-01-07 15:02:50 +00:00
nbykov0
8c8ab1f1a0 [lineage-webhook] Tolerate all taints
Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com>
(cherry picked from commit aa66b8c0d3)
2026-01-07 15:02:50 +00:00
Andrei Kvapil
9405e05185 [Backport release-0.39] fix(linstor): prevent orphaned DRBD devices during toggle-disk retry (#1825)
# Description
Backport of #1823 to `release-0.39`.
2026-01-07 15:13:25 +01:00
Andrei Kvapil
0aee5b4dfa fix(linstor): prevent orphaned DRBD devices during toggle-disk retry
The previous retry logic in toggle-disk removed layer data from controller DB
and recreated it. However, removeLayerData() only deletes from the database
without calling drbdadm down on the satellite, leaving orphaned DRBD devices
in the kernel that occupy ports and block new operations.

This fix changes retry to simply repeat the operation with existing layer data,
allowing the satellite to handle it idempotently.

Upstream: https://github.com/LINBIT/linstor-server/pull/475

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit 8151e1e41a)
2026-01-07 14:00:22 +00:00
Andrei Kvapil
8373a8cb3f [Backport release-0.39] [linstor] Build linstor-server with custom patches (#1818)
# Description
Backport of #1726 to `release-0.39`.
2026-01-06 16:21:22 +01:00
Andrei Kvapil
cb0e457dda feat(linstor): build linstor-server with custom patches
Add patches for piraeus-server:
- adjust-on-resfile-change: handle resource file changes
- allow-toggle-disk-retry: enable disk operation retries
- force-metadata-check-on-disk-add: verify metadata on disk addition

Update plunger-satellite script and values.yaml for new build.

Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit daa91fd2f3)
2026-01-06 15:19:26 +00:00
Andrei Kvapil
c74b88ce66 [Backport release-0.39] [testing] Add aliases and autocomplete (#1809)
# Description
Backport of #1803 to `release-0.39`.
2026-01-05 20:04:26 +01:00
Timofei Larkin
07982b2646 [testing] Add aliases and autocomplete
## What this PR does

Adds a `k=kubectl` alias and bash completion for kubectl to the
e2e-testing sandbox container to maintainers have an easier time
exec'ing into the CI container when something needs to be debugged.

### Release note

```release-note
[testing] Add k=kubectl alias and enable kubectl completion in the CI
container.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
(cherry picked from commit bf1928c96f)
2026-01-05 18:48:14 +00:00
Andrei Kvapil
d80ea29dfa [Backport release-0.39] [kubernetes] Fix endpoints for cilium-gateway (#1808)
# Description
Backport of #1729 to `release-0.39`.
2026-01-05 18:12:34 +01:00
Andrei Kvapil
143da399ff [kubernetes] Fix endpoints for cilium-gateway
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit f59665208c)
2026-01-05 17:11:56 +00:00
Timofei Larkin
ad3bff6692 [kubeovn] Package from external repo (#1535)
Previously, all system charts have had their versions bumped with every
update of Cozystack, even if the underlying images and templates did not
change. Additionally, some components would have their docker images
rebuilt and get updated digests because of a changed docker context,
even though no relevant code had been changed. This would cause an
upgrade of several HelmReleases on every upgrade of Cozystack, even when
in practice nothing had changed. This is especially noticeable for
charts, such as kubeovn, which is a dependency for almost all other
system HelmReleases. By moving kubeovn out of the monorepo and
decoupling its release cycle from the release cycle of Cozystack, this
issue can be resolved and is another step towards decomposing Cozystack.

```release-note
[kubeovn] Decouple the release cycle of kubeovn from Cozystack's release
cycle, preventing unnecessary redeployments of all Helm releases, when
upgrading Cozystack.
```
2026-01-05 18:05:50 +01:00
Andrei Kvapil
f1852ca841 [Backport release-0.39] [kubevirt-operator] Revert incorrect case change in VM alerts (#1806)
# Description
Backport of #1804 to `release-0.39`.
2026-01-05 16:29:11 +01:00
Aleksei Sviridkin
5914515371 [kubevirt-operator] Revert incorrect case change in VM alerts
Revert PR #1770 which incorrectly changed status check from lowercase
to uppercase. The actual metrics use lowercase:
- kubevirt_vm_info uses status="running" (not "Running")
- kubevirt_vmi_info uses phase="running" (not "Running")

Verified by querying virt-controller metrics in instories cluster.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
(cherry picked from commit 36836fd84e)
2026-01-05 15:19:59 +00:00
Andrei Kvapil
5be4fc8f40 Release v0.39.2 (#1789)
This PR prepares the release `v0.39.2`.
2026-01-03 09:57:25 +01:00
cozystack-bot
f4ff44a18c Prepare release v0.39.2
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2026-01-03 01:38:23 +00:00
Andrei Kvapil
6b5e2e3e77 [Backport release-0.39] [system] Add resource requests and limits to etcd-defrag (#1786)
# Description
Backport of #1785 to `release-0.39`.
2026-01-02 19:13:11 +01:00
Matthieu ROBIN
fc84b80b70 Add resource requests and limits to etcd-defrag
Added resource requests and limits for etcd-defrag container.

Signed-off-by: Matthieu ROBIN <info@matthieurobin.com>
(cherry picked from commit 675eaa6178)
2026-01-02 18:12:42 +00:00
Andrei Kvapil
85a0003ffb [Backport release-0.39] [tenant] Run cleanup job from system namespace (#1777)
# Description
Backport of #1774 to `release-0.39`.
2025-12-30 12:05:45 +01:00
Timofei Larkin
7c766fc27d [tenant] Run cleanup job from system namespace
## What this PR does

The Helm hook that creates a job deleting all applications in a tenant
before deleting the tenant itself now runs this job from the cozy-system
namespace. This prevents conflicts with resource quotas (not enough
resources to run cleanup job) without temporary increases of the quota
or similar vulnerability-introducing hacks.

### Release note

```release-note
[tenant] Run cleanup job in system namespace to avoid conflicts on
resource quotas.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
(cherry picked from commit b94fc6c9c8)
2025-12-30 11:03:18 +00:00
Andrei Kvapil
46d81c806f [Backport release-0.39] [tenant] Allow egress to parent ingress pods (#1776)
# Description
Backport of #1765 to `release-0.39`.
2025-12-30 12:00:19 +01:00
Andrei Kvapil
9300309dfb [Backport release-0.39] [kubevirt-operator] Fix typo in VMNotRunningFor10Minutes alert (#1775)
# Description
Backport of #1770 to `release-0.39`.
2025-12-30 12:00:07 +01:00
Aleksei Sviridkin
d90a76bc25 fix(tenant): allow egress to parent ingress pods
Nested Kubernetes clusters with exposeMethod: Proxied cannot reach their
own external domains because the clusterwide egress policy blocks traffic
to ingress pods in parent namespaces.

This breaks cert-manager HTTP-01 self-check and any scenario where pods
need to access services exposed through parent ingress.

Add egress rule allowing traffic to ingress pods (cozystack.io/service:
ingress) in parent namespaces, following the same pattern as existing
vminsert and etcd rules.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
(cherry picked from commit 4dfdbfeb62)
2025-12-30 10:59:45 +00:00
Aleksei Sviridkin
13bfb72ea5 [kubevirt-operator] Fix typo in VMNotRunningFor10Minutes alert
Fix case sensitivity in status check: "running" → "Running".
The metric uses "Running" (capital R), so the lowercase check was
always triggering alerts even for running VMs.

Fixes: https://github.com/cozystack/cozystack/issues/1552

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
(cherry picked from commit 7fab919e07)
2025-12-30 10:58:23 +00:00
Andrei Kvapil
56ed29c7fc [Backport release-0.39] [vm] Always expose VMs with a service (#1751)
# Description
Backport of #1738 to `release-0.39`.
2025-12-24 14:38:37 +01:00
Timofei Larkin
1ffa1ef454 [vm] Always expose VMs with a service
## What this PR does

When VMs are created without a public IP, no service is created for them
and they have no in-cluster DNS name. This PR fixes this and resolves
#1731.

### Release note

```release-note
[vm] Always expose VMs with at least a ClusterIP service.
```

Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
(cherry picked from commit ba50d01877)
2025-12-24 13:10:14 +00:00
Andrei Kvapil
1826858bb0 Release v0.39.1 (#1749)
This PR prepares the release `v0.39.1`.
2025-12-24 09:16:37 +01:00
cozystack-bot
0e262838d2 Prepare release v0.39.1
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-12-23 23:26:06 +00:00
Andrei Kvapil
b610630592 [monitoring] Add SLACK_SEVERITY_FILTER field and VMAgent for tenant monitoring (#1712)
[monitoring] Add SLACK_SEVERITY_FILTER field and VMAgent for tenant
monitoring
What this PR does
This PR introduces the SLACK_SEVERITY_FILTER environment variable in the
Alerta deployment to enable
filtering of alert severities for Slack notifications based on the
disabledSeverity configuration.
Additionally, it adds a VMAgent resource template for scraping metrics
within tenant namespaces, improving
monitoring granularity and control.

```release-note
[monitoring] Add SLACK_SEVERITY_FILTER for filtering Slack alert severities and VMAgent configuration for
tenant-specific metrics scraping.
```

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

* **New Features**
  * Added configurable severity filtering for Telegram alerts.
  * Extended Slack severity filtering to accept lists of severities.

* **Bug Fixes / Behavior**
* Severity settings now accept arrays (multiple severities) instead of
single comma-separated strings.

* **Documentation**
* Updated configuration docs and examples to show list-style severity
settings.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-24 00:21:24 +01:00
Andrei Kvapil
357d9f95be Release v0.39.0 (#1747)
This PR prepares the release `v0.39.0`.
2025-12-23 19:07:54 +01:00
cozystack-bot
37a2e38798 Prepare release v0.39.0
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-12-23 16:27:57 +00:00
Nikita
da44a65ba9 [system/cilium] Enable topology-aware routing for services (#1734) 2025-12-23 19:17:03 +03:00
Nikita
26da347de8 [Backport release-0.38] [cilium] Enable automatic pod rollout on configmap updates (#1745)
# Description
Backport of #1728 to `release-0.38`.
2025-12-23 15:41:56 +03:00
Nikita
dd2798dbda [Backport release-0.38] [virtual-machine,vm-instance] Add nodeAffinity for Windows VMs based on scheduling config (#1744)
# Description
Backport of #1693 to `release-0.38`.
2025-12-23 15:41:33 +03:00
Andrei Kvapil
fb5b4da2b2 [cilium] Enable automatic pod rollout on configmap updates
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit 97e8d2aa49)
2025-12-23 12:41:25 +00:00
Andrei Kvapil
540e1c6e0d [virtual-machine,vm-instance] Add nodeAffinity for Windows VMs based on scheduling config
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit 3975da93c6)
2025-12-23 12:39:33 +00:00
Nikita
1dd8e00e17 [Backport release-0.38] Update SeaweedFS v4.02 (#1732)
# Description
Backport of #1725 to `release-0.38`.
2025-12-18 13:40:31 +03:00
Andrei Kvapil
79165ca2f9 Update SeaweedFS v4.02
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit 1805be3c48)
2025-12-18 10:39:52 +00:00
Andrei Kvapil
a8bd179f0d [Backport release-0.38] [apps] Refactor apiserver to use typed objects and fix UnstructuredList GVK (#1709)
# Description
Backport of #1679 to `release-0.38`.
2025-12-10 19:28:16 +01:00
Andrei Kvapil
1491535b35 [apps] Refactor apiserver to use typed objects and fix UnstructuredList GVK
This commit refactors the apiserver REST handlers to use typed objects
(appsv1alpha1.Application) instead of unstructured.Unstructured, eliminating
the need for runtime conversions and simplifying the codebase.

Additionally, it fixes an issue where UnstructuredList objects were using
the first registered kind from typeToGVK instead of the kind from the
object's field when multiple kinds are registered with the same Go type.

This is a more comprehensive fix for the problem addressed in
https://github.com/cozystack/cozystack/pull/1630, which was reverted in
https://github.com/cozystack/cozystack/pull/1677.

The fix includes the upstream fix from kubernetes/kubernetes#135537,
which enables short-circuit path for UnstructuredList similar to regular
Unstructured objects, using GVK from the object field instead of
typeToGVK.

Changes:
- Refactored rest.go handlers to use typed Application objects
- Removed unstructured.Unstructured conversions
- Fixed UnstructuredList GVK handling
- Updated dependencies in go.mod/go.sum
- Added e2e test for OpenAPI validation
- Updated Dockerfile

Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit ca29fc855a)
2025-12-10 12:22:57 +00:00
Andrei Kvapil
290c6be04b Release v0.38.4 (#1704)
This PR prepares the release `v0.38.4`.
2025-12-09 19:54:27 +01:00
cozystack-bot
03328dc4e4 Prepare release v0.38.4
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-12-09 16:23:14 +00:00
Andrei Kvapil
9311a9e547 [Backport release-0.38] [virtual-machine] Improve check for resizing job (#1701)
# Description
Backport of #1688 to `release-0.38`.
2025-12-09 17:10:14 +01:00
Andrei Kvapil
548b2c0ed3 [virtual-machine] Improve check for resizing job
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit 0bab895026)
2025-12-09 16:08:54 +00:00
Andrei Kvapil
202ff3433e [Backport release-0.38] [dashboard] Fix CustomFormsOverride schema to nest properties under spec.properties (#1700)
# Description
Backport of #1692 to `release-0.38`.
2025-12-09 17:05:22 +01:00
Andrei Kvapil
891195018f [dashboard] Fix CustomFormsOverride schema to nest properties under spec.properties
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit 578a810413)
2025-12-09 16:04:57 +00:00
Andrei Kvapil
d53861837f [Backport release-0.38] [linstor] Update piraeus-operator v2.10.2 (#1697)
# Description
Backport of #1689 to `release-0.38`.
2025-12-09 14:58:13 +01:00
Andrei Kvapil
f1a75ab864 [linstor] Update piraeus-operator v2.10.2
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit 58dd1f5881)
2025-12-09 13:43:46 +00:00
Nikita
2110534e63 Release v0.38.3 (#1686)
This PR prepares the release `v0.38.3`.
2025-12-04 20:07:33 +03:00
cozystack-bot
2e22a6579e Prepare release v0.38.3
Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com>
2025-12-04 16:19:17 +00:00
Nikita
35907dd474 [core:installer] Address buildx warnings (#1682)
## What this PR does
Buildx is worried about Dockerfile syntax, this pr fixes it.
```
 - FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 1)
 - FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 16)
```

### Release note
```release-note
Buildx warnings addressed.
```
2025-12-03 19:42:24 +03:00
Nikita
af56c105c2 [system:coredns] update coredns app labels to match Talos coredns labels (#1675)
## What this PR does
Updates coredns app labels to match Talos coredns labels

### Release note

```release-note
Coredns app labels updated to match Talos coredns labels.
```
2025-12-03 19:42:14 +03:00
Nikita
30e5b71e3f [system:monitoring-agents] rename coredns metrics service (#1676)
## What this PR does
Renames coredns metrics service

### Release note
```release-note
Renamed coredns metrics service not to interfere with coredns service used for name resolution in tenant k8s clusters.
```
2025-12-03 19:41:59 +03:00
135 changed files with 3919 additions and 951 deletions

View File

@@ -17,7 +17,7 @@ build: build-deps
make -C packages/system/cozystack-controller image
make -C packages/system/lineage-controller-webhook image
make -C packages/system/cilium image
make -C packages/system/kubeovn image
make -C packages/system/linstor image
make -C packages/system/kubeovn-webhook image
make -C packages/system/kubeovn-plunger image
make -C packages/system/dashboard image

11
go.mod
View File

@@ -6,11 +6,15 @@ go 1.23.0
require (
github.com/fluxcd/helm-controller/api v1.1.0
github.com/go-logr/logr v1.4.2
github.com/go-logr/zapr v1.3.0
github.com/google/gofuzz v1.2.0
github.com/onsi/ginkgo/v2 v2.19.0
github.com/onsi/gomega v1.33.1
github.com/prometheus/client_golang v1.19.1
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.9.0
go.uber.org/zap v1.27.0
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.31.2
k8s.io/apiextensions-apiserver v0.31.2
@@ -44,9 +48,7 @@ require (
github.com/fluxcd/pkg/apis/meta v1.6.1 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
@@ -74,7 +76,6 @@ require (
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
@@ -94,7 +95,6 @@ require (
go.opentelemetry.io/otel/trace v1.28.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/net v0.33.0 // indirect
@@ -119,3 +119,6 @@ require (
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)
// See: issues.k8s.io/135537
replace k8s.io/apimachinery => github.com/cozystack/apimachinery v0.0.0-20251201201312-18e522a87614

4
go.sum
View File

@@ -18,6 +18,8 @@ github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cozystack/apimachinery v0.0.0-20251201201312-18e522a87614 h1:jH9elECUvhiIs3IMv3oS5k1JgCLVsSK6oU4dmq5gyW8=
github.com/cozystack/apimachinery v0.0.0-20251201201312-18e522a87614/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -291,8 +293,6 @@ k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0=
k8s.io/apiextensions-apiserver v0.31.2/go.mod h1:i+Geh+nGCJEGiCGR3MlBDkS7koHIIKWVfWeRFiOsUcM=
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4=
k8s.io/apiserver v0.31.2/go.mod h1:o3nKZR7lPlJqkU5I3Ove+Zx3JuoFjQobGX1Gctw6XuE=
k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=

View File

@@ -21,14 +21,33 @@
}
@test "Test kinds" {
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/tenants | jq -r '.kind')
if [ "$val" != "TenantList" ]; then
echo "Expected kind to be TenantList, got $val"
exit 1
fi
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/tenants | jq -r '.items[0].kind')
if [ "$val" != "Tenant" ]; then
echo "Expected kind to be Tenant, got $val"
exit 1
fi
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/ingresses | jq -r '.kind')
if [ "$val" != "IngressList" ]; then
echo "Expected kind to be IngressList, got $val"
exit 1
fi
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/ingresses | jq -r '.items[0].kind')
if [ "$val" != "Ingress" ]; then
echo "Expected kind to be Ingress, got $val"
exit 1
fi
}
@test "Create and delete namespace" {
kubectl create ns cozy-test-create-and-delete-namespace --dry-run=client -o yaml | kubectl apply -f -
if ! kubectl delete ns cozy-test-create-and-delete-namespace; then
echo "Failed to delete namespace"
kubectl describe ns cozy-test-create-and-delete-namespace
exit 1
fi
}

View File

@@ -105,8 +105,26 @@ func buildMultilineStringSchema(openAPISchema string) (map[string]any, error) {
"properties": map[string]any{},
}
// Check if there's a spec property
specProp, ok := props["spec"].(map[string]any)
if !ok {
return map[string]any{}, nil
}
specProps, ok := specProp["properties"].(map[string]any)
if !ok {
return map[string]any{}, nil
}
// Create spec.properties structure in schema
schemaProps := schema["properties"].(map[string]any)
specSchema := map[string]any{
"properties": map[string]any{},
}
schemaProps["spec"] = specSchema
// Process spec properties recursively
processSpecProperties(props, schema["properties"].(map[string]any))
processSpecProperties(specProps, specSchema["properties"].(map[string]any))
return schema, nil
}

View File

@@ -9,41 +9,46 @@ func TestBuildMultilineStringSchema(t *testing.T) {
// Test OpenAPI schema with various field types
openAPISchema := `{
"properties": {
"simpleString": {
"type": "string",
"description": "A simple string field"
},
"stringWithEnum": {
"type": "string",
"enum": ["option1", "option2"],
"description": "String with enum should be skipped"
},
"numberField": {
"type": "number",
"description": "Number field should be skipped"
},
"nestedObject": {
"spec": {
"type": "object",
"properties": {
"nestedString": {
"simpleString": {
"type": "string",
"description": "Nested string should get multilineString"
"description": "A simple string field"
},
"nestedStringWithEnum": {
"stringWithEnum": {
"type": "string",
"enum": ["a", "b"],
"description": "Nested string with enum should be skipped"
}
}
},
"arrayOfObjects": {
"type": "array",
"items": {
"type": "object",
"properties": {
"itemString": {
"type": "string",
"description": "String in array item"
"enum": ["option1", "option2"],
"description": "String with enum should be skipped"
},
"numberField": {
"type": "number",
"description": "Number field should be skipped"
},
"nestedObject": {
"type": "object",
"properties": {
"nestedString": {
"type": "string",
"description": "Nested string should get multilineString"
},
"nestedStringWithEnum": {
"type": "string",
"enum": ["a", "b"],
"description": "Nested string with enum should be skipped"
}
}
},
"arrayOfObjects": {
"type": "array",
"items": {
"type": "object",
"properties": {
"itemString": {
"type": "string",
"description": "String in array item"
}
}
}
}
}
@@ -70,33 +75,44 @@ func TestBuildMultilineStringSchema(t *testing.T) {
t.Fatal("schema.properties is not a map")
}
// Check simpleString
simpleString, ok := props["simpleString"].(map[string]any)
// Check spec property exists
spec, ok := props["spec"].(map[string]any)
if !ok {
t.Fatal("simpleString not found in properties")
t.Fatal("spec not found in properties")
}
specProps, ok := spec["properties"].(map[string]any)
if !ok {
t.Fatal("spec.properties is not a map")
}
// Check simpleString
simpleString, ok := specProps["simpleString"].(map[string]any)
if !ok {
t.Fatal("simpleString not found in spec.properties")
}
if simpleString["type"] != "multilineString" {
t.Errorf("simpleString should have type multilineString, got %v", simpleString["type"])
}
// Check stringWithEnum should not be present (or should not have multilineString)
if stringWithEnum, ok := props["stringWithEnum"].(map[string]any); ok {
if stringWithEnum, ok := specProps["stringWithEnum"].(map[string]any); ok {
if stringWithEnum["type"] == "multilineString" {
t.Error("stringWithEnum should not have multilineString type")
}
}
// Check numberField should not be present
if numberField, ok := props["numberField"].(map[string]any); ok {
if numberField, ok := specProps["numberField"].(map[string]any); ok {
if numberField["type"] != nil {
t.Error("numberField should not have any type override")
}
}
// Check nested object
nestedObject, ok := props["nestedObject"].(map[string]any)
nestedObject, ok := specProps["nestedObject"].(map[string]any)
if !ok {
t.Fatal("nestedObject not found in properties")
t.Fatal("nestedObject not found in spec.properties")
}
nestedProps, ok := nestedObject["properties"].(map[string]any)
if !ok {
@@ -113,9 +129,9 @@ func TestBuildMultilineStringSchema(t *testing.T) {
}
// Check array of objects
arrayOfObjects, ok := props["arrayOfObjects"].(map[string]any)
arrayOfObjects, ok := specProps["arrayOfObjects"].(map[string]any)
if !ok {
t.Fatal("arrayOfObjects not found in properties")
t.Fatal("arrayOfObjects not found in spec.properties")
}
items, ok := arrayOfObjects["items"].(map[string]any)
if !ok {

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:e0a07082bb6fc6aeaae2315f335386f1705a646c72f9e0af512aebbca5cb2b15
ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:31ebc09cfa11d8b438d2bbb32fa61b133aaf4b48b1a1282c9e59b5c127af61c1

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.0.0@sha256:2d39989846c3579dd020b9f6c77e6e314cc81aa344eaac0f6d633e723c17196d
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.0.0@sha256:6f2b1d6b0b2bdc66f1cbb30c59393369cbf070cb8f5fec748f176952273483cc

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.0.0@sha256:5335c044313b69ee13b30ca4941687e509005e55f4ae25723861edbf2fbd6dd2
ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.0.0@sha256:dee69d15fa8616aa6a1e5a67fc76370e7698a7f58b25e30650eb39c9fb826de8

View File

@@ -1,5 +1,5 @@
diff --git a/pkg/controller/kubevirteps/kubevirteps_controller.go b/pkg/controller/kubevirteps/kubevirteps_controller.go
index 53388eb8e..28644236f 100644
index 53388eb8e..873060251 100644
--- a/pkg/controller/kubevirteps/kubevirteps_controller.go
+++ b/pkg/controller/kubevirteps/kubevirteps_controller.go
@@ -12,7 +12,6 @@ import (
@@ -10,12 +10,17 @@ index 53388eb8e..28644236f 100644
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -669,35 +668,50 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
@@ -666,38 +665,62 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
// for extracting the nodes it does not matter what type of address we are dealing with
// all nodes with an endpoint for a corresponding slice will be selected.
nodeSet := sets.Set[string]{}
+ hasEndpointsWithoutNodeName := false
for _, slice := range tenantSlices {
for _, endpoint := range slice.Endpoints {
// find all unique nodes that correspond to an endpoint in a tenant slice
+ if endpoint.NodeName == nil {
+ klog.Warningf("Skipping endpoint without NodeName in slice %s/%s", slice.Namespace, slice.Name)
+ hasEndpointsWithoutNodeName = true
+ continue
+ }
nodeSet.Insert(*endpoint.NodeName)
@@ -23,6 +28,13 @@ index 53388eb8e..28644236f 100644
}
- klog.Infof("Desired nodes for service %s in namespace %s: %v", service.Name, service.Namespace, sets.List(nodeSet))
+ // Fallback: if no endpoints with NodeName were found, but there are endpoints without NodeName,
+ // distribute traffic to all VMIs (similar to ExternalTrafficPolicy=Cluster behavior)
+ if nodeSet.Len() == 0 && hasEndpointsWithoutNodeName {
+ klog.Infof("No endpoints with NodeName found for service %s/%s, falling back to all VMIs", service.Namespace, service.Name)
+ return c.getAllVMIEndpoints()
+ }
+
+ klog.Infof("Desired nodes for service %s/%s: %v", service.Namespace, service.Name, sets.List(nodeSet))
for _, node := range sets.List(nodeSet) {
@@ -68,7 +80,7 @@ index 53388eb8e..28644236f 100644
desiredEndpoints = append(desiredEndpoints, &discovery.Endpoint{
Addresses: []string{i.IP},
Conditions: discovery.EndpointConditions{
@@ -705,9 +719,9 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
@@ -705,9 +728,9 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
Serving: &serving,
Terminating: &terminating,
},
@@ -80,6 +92,71 @@ index 53388eb8e..28644236f 100644
}
}
}
@@ -716,6 +739,64 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
return desiredEndpoints
}
+// getAllVMIEndpoints returns endpoints for all VMIs in the infra namespace.
+// This is used as a fallback when tenant endpoints don't have NodeName specified,
+// similar to ExternalTrafficPolicy=Cluster behavior where traffic is distributed to all nodes.
+func (c *Controller) getAllVMIEndpoints() []*discovery.Endpoint {
+ var endpoints []*discovery.Endpoint
+
+ // List all VMIs in the infra namespace
+ vmiList, err := c.infraDynamic.
+ Resource(kubevirtv1.VirtualMachineInstanceGroupVersionKind.GroupVersion().WithResource("virtualmachineinstances")).
+ Namespace(c.infraNamespace).
+ List(context.TODO(), metav1.ListOptions{})
+ if err != nil {
+ klog.Errorf("Failed to list VMIs in namespace %q: %v", c.infraNamespace, err)
+ return endpoints
+ }
+
+ for _, obj := range vmiList.Items {
+ vmi := &kubevirtv1.VirtualMachineInstance{}
+ err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, vmi)
+ if err != nil {
+ klog.Errorf("Failed to convert Unstructured to VirtualMachineInstance: %v", err)
+ continue
+ }
+
+ if vmi.Status.NodeName == "" {
+ klog.Warningf("Skipping VMI %s/%s: NodeName is empty", vmi.Namespace, vmi.Name)
+ continue
+ }
+ nodeNamePtr := &vmi.Status.NodeName
+
+ ready := vmi.Status.Phase == kubevirtv1.Running
+ serving := vmi.Status.Phase == kubevirtv1.Running
+ terminating := vmi.Status.Phase == kubevirtv1.Failed || vmi.Status.Phase == kubevirtv1.Succeeded
+
+ for _, i := range vmi.Status.Interfaces {
+ if i.Name == "default" {
+ if i.IP == "" {
+ klog.Warningf("VMI %s/%s interface %q has no IP, skipping", vmi.Namespace, vmi.Name, i.Name)
+ continue
+ }
+ endpoints = append(endpoints, &discovery.Endpoint{
+ Addresses: []string{i.IP},
+ Conditions: discovery.EndpointConditions{
+ Ready: &ready,
+ Serving: &serving,
+ Terminating: &terminating,
+ },
+ NodeName: nodeNamePtr,
+ })
+ break
+ }
+ }
+ }
+
+ klog.Infof("Fallback: created %d endpoints from all VMIs in namespace %s", len(endpoints), c.infraNamespace)
+ return endpoints
+}
+
func (c *Controller) ensureEndpointSliceLabels(slice *discovery.EndpointSlice, svc *v1.Service) (map[string]string, bool) {
labels := make(map[string]string)
labelsChanged := false
diff --git a/pkg/controller/kubevirteps/kubevirteps_controller_test.go b/pkg/controller/kubevirteps/kubevirteps_controller_test.go
index 1c97035b4..d205d0bed 100644
--- a/pkg/controller/kubevirteps/kubevirteps_controller_test.go

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:d5c836ba33cf5dbed7e6f866784f668f80ffe69179e7c75847b680111984eefb
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:b42c6af641ee0eadb7e0a42e368021b4759f443cb7b71b7e745a64f0fc8b752e

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:a09724a7f95283f9130b3da2a89d81c4c6051c6edf0392a81b6fc90f404b76b6
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:d25e567bc8b17b596e050f5ff410e36112c7966e33f4b372c752e7350bacc894

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:1c0beb1b23a109b0e13727b4c73d2c74830e11cede92858ab20101b66f45a858
ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:aca403030ff5d831415d72367866fdf291fab73ee2cfddbe4c93c2915a316ab1

View File

@@ -3,7 +3,7 @@ apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "tenant.name" . }}-cleanup
namespace: {{ include "tenant.name" . }}
namespace: cozy-system
annotations:
helm.sh/hook: pre-delete
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
@@ -39,13 +39,13 @@ roleRef:
subjects:
- kind: ServiceAccount
name: {{ include "tenant.name" . }}-cleanup
namespace: {{ include "tenant.name" . }}
namespace: cozy-system
---
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "tenant.name" . }}-cleanup
namespace: {{ include "tenant.name" . }}
namespace: cozy-system
annotations:
helm.sh/hook: pre-delete
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded

View File

@@ -67,6 +67,19 @@ spec:
{{- end }}
{{- end }}
{{- end }}
{{- if ne (include "tenant.name" .) "tenant-root" }}
- toEndpoints:
{{- if hasPrefix "tenant-" .Release.Namespace }}
{{- $parts := splitList "-" .Release.Namespace }}
{{- range $i, $v := $parts }}
{{- if ne $i 0 }}
- matchLabels:
cozystack.io/service: ingress
"k8s:io.kubernetes.pod.namespace": {{ join "-" (slice $parts 0 (add $i 1)) }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
---
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy

View File

@@ -69,3 +69,36 @@ Generate a stable UUID for cloud-init re-initialization upon upgrade.
{{- end }}
{{- $uuid }}
{{- end }}
{{/*
Node Affinity for Windows VMs
*/}}
{{- define "virtual-machine.nodeAffinity" -}}
{{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" -}}
{{- if $configMap -}}
{{- $dedicatedNodesForWindowsVMs := get $configMap.data "dedicatedNodesForWindowsVMs" -}}
{{- if eq $dedicatedNodesForWindowsVMs "true" -}}
{{- $isWindows := hasPrefix "windows" (toString .Values.instanceProfile) -}}
affinity:
nodeAffinity:
{{- if $isWindows }}
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: scheduling.cozystack.io/vm-windows
operator: In
values:
- "true"
{{- else }}
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: scheduling.cozystack.io/vm-windows
operator: NotIn
values:
- "true"
{{- end }}
{{- end -}}
{{- end -}}
{{- end -}}

View File

@@ -1,4 +1,3 @@
{{- if .Values.external }}
---
apiVersion: v1
kind: Service
@@ -7,17 +6,24 @@ metadata:
labels:
apps.cozystack.io/user-service: "true"
{{- include "virtual-machine.labels" . | nindent 4 }}
{{- if .Values.external }}
annotations:
networking.cozystack.io/wholeIP: "true"
{{- end }}
spec:
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
{{- if .Values.external }}
externalTrafficPolicy: Local
{{- if ((include "cozy-lib.network.disableLoadBalancerNodePorts" $) | fromYaml) }}
allocateLoadBalancerNodePorts: false
{{- end }}
{{- else }}
clusterIP: None
{{- end }}
selector:
{{- include "virtual-machine.selectorLabels" . | nindent 4 }}
ports:
{{- if .Values.external }}
{{- if and (eq .Values.externalMethod "WholeIP") (not .Values.externalPorts) }}
- port: 65535
{{- else }}
@@ -27,4 +33,6 @@ spec:
targetPort: {{ . }}
{{- end }}
{{- end }}
{{- else }}
- port: 65535
{{- end }}

View File

@@ -27,7 +27,11 @@
{{- if and $existingPVC $desiredStorage -}}
{{- $currentStorage := $existingPVC.spec.resources.requests.storage | toString -}}
{{- if not (eq $currentStorage $desiredStorage) -}}
{{- $needResizePVC = true -}}
{{- $oldSize := (include "cozy-lib.resources.toFloat" $currentStorage) | float64 -}}
{{- $newSize := (include "cozy-lib.resources.toFloat" $desiredStorage) | float64 -}}
{{- if gt $newSize $oldSize -}}
{{- $needResizePVC = true -}}
{{- end -}}
{{- end -}}
{{- end -}}

View File

@@ -124,6 +124,8 @@ spec:
terminationGracePeriodSeconds: 30
{{- include "virtual-machine.nodeAffinity" . | nindent 6 }}
volumes:
- name: systemdisk
dataVolume:

View File

@@ -1,5 +1,17 @@
{{- $existingPVC := lookup "v1" "PersistentVolumeClaim" .Release.Namespace .Release.Name }}
{{- if and $existingPVC (ne ($existingPVC.spec.resources.requests.storage | toString) .Values.storage) -}}
{{- $shouldResize := false -}}
{{- if and $existingPVC .Values.storage -}}
{{- $currentStorage := $existingPVC.spec.resources.requests.storage | toString -}}
{{- if ne $currentStorage .Values.storage -}}
{{- $oldSize := (include "cozy-lib.resources.toFloat" $currentStorage) | float64 -}}
{{- $newSize := (include "cozy-lib.resources.toFloat" .Values.storage) | float64 -}}
{{- if gt $newSize $oldSize -}}
{{- $shouldResize = true -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- if $shouldResize -}}
apiVersion: batch/v1
kind: Job
metadata:
@@ -23,6 +35,7 @@ spec:
command: ["sh", "-xec"]
args:
- |
echo "Resizing PVC to {{ .Values.storage }}..."
kubectl patch pvc {{ .Release.Name }} -p '{"spec":{"resources":{"requests":{"storage":"{{ .Values.storage }}"}}}}'
---
apiVersion: v1

View File

@@ -69,3 +69,36 @@ Generate a stable UUID for cloud-init re-initialization upon upgrade.
{{- end }}
{{- $uuid }}
{{- end }}
{{/*
Node Affinity for Windows VMs
*/}}
{{- define "virtual-machine.nodeAffinity" -}}
{{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" -}}
{{- if $configMap -}}
{{- $dedicatedNodesForWindowsVMs := get $configMap.data "dedicatedNodesForWindowsVMs" -}}
{{- if eq $dedicatedNodesForWindowsVMs "true" -}}
{{- $isWindows := hasPrefix "windows" (toString .Values.instanceProfile) -}}
affinity:
nodeAffinity:
{{- if $isWindows }}
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: scheduling.cozystack.io/vm-windows
operator: In
values:
- "true"
{{- else }}
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: scheduling.cozystack.io/vm-windows
operator: NotIn
values:
- "true"
{{- end }}
{{- end -}}
{{- end -}}
{{- end -}}

View File

@@ -1,4 +1,3 @@
{{- if .Values.external }}
---
apiVersion: v1
kind: Service
@@ -7,17 +6,24 @@ metadata:
labels:
apps.cozystack.io/user-service: "true"
{{- include "virtual-machine.labels" . | nindent 4 }}
{{- if .Values.external }}
annotations:
networking.cozystack.io/wholeIP: "true"
{{- end }}
spec:
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
{{- if .Values.external }}
externalTrafficPolicy: Local
{{- if ((include "cozy-lib.network.disableLoadBalancerNodePorts" $) | fromYaml) }}
allocateLoadBalancerNodePorts: false
{{- end }}
{{- else }}
clusterIP: None
{{- end }}
selector:
{{- include "virtual-machine.selectorLabels" . | nindent 4 }}
ports:
{{- if .Values.external }}
{{- if and (eq .Values.externalMethod "WholeIP") (not .Values.externalPorts) }}
- port: 65535
{{- else }}
@@ -27,4 +33,6 @@ spec:
targetPort: {{ . }}
{{- end }}
{{- end }}
{{- else }}
- port: 65535
{{- end }}

View File

@@ -95,6 +95,9 @@ spec:
noCloud: {}
{{- end }}
terminationGracePeriodSeconds: 30
{{- include "virtual-machine.nodeAffinity" . | nindent 6 }}
volumes:
{{- range .Values.disks }}
- name: disk-{{ .name }}

View File

@@ -1,4 +1,4 @@
FROM golang:1.24-alpine as k8s-await-election-builder
FROM golang:1.24-alpine AS k8s-await-election-builder
ARG K8S_AWAIT_ELECTION_GITREPO=https://github.com/LINBIT/k8s-await-election
ARG K8S_AWAIT_ELECTION_VERSION=0.4.1
@@ -13,7 +13,7 @@ RUN git clone ${K8S_AWAIT_ELECTION_GITREPO} /usr/local/go/k8s-await-election/ \
&& make \
&& mv ./out/k8s-await-election-${TARGETARCH} /k8s-await-election
FROM golang:1.24-alpine as builder
FROM golang:1.24-alpine AS builder
ARG TARGETOS
ARG TARGETARCH

View File

@@ -1,2 +1,2 @@
cozystack:
image: ghcr.io/cozystack/cozystack/installer:v0.38.2@sha256:9ff92b655de6f9bea3cba4cd42dcffabd9aace6966dcfb1cc02dda2420ea4a15
image: ghcr.io/cozystack/cozystack/installer:v0.39.5@sha256:0d1b48c61278b0c6214c50ca06f354cfbe6181cd282e9d6ef35c968456fcc951

View File

@@ -87,25 +87,25 @@ releases:
releaseName: cozystack
chart: cozy-cozy-proxy
namespace: cozy-system
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,multus]
- name: cert-manager-crds
releaseName: cert-manager-crds
chart: cozy-cert-manager-crds
namespace: cozy-cert-manager
dependsOn: [cilium, kubeovn]
dependsOn: []
- name: cozystack-api
releaseName: cozystack-api
chart: cozy-cozystack-api
namespace: cozy-system
dependsOn: [cilium,kubeovn,cozystack-controller]
dependsOn: [cilium,kubeovn,multus,cozystack-controller]
- name: cozystack-controller
releaseName: cozystack-controller
chart: cozy-cozystack-controller
namespace: cozy-system
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,multus]
{{- if eq (index $cozyConfig.data "telemetry-enabled") "false" }}
values:
cozystackController:
@@ -116,44 +116,44 @@ releases:
releaseName: lineage-controller-webhook
chart: cozy-lineage-controller-webhook
namespace: cozy-system
dependsOn: [cozystack-controller,cilium,kubeovn,cert-manager]
dependsOn: [cozystack-controller,cilium,kubeovn,multus,cert-manager]
- name: cozystack-resource-definition-crd
releaseName: cozystack-resource-definition-crd
chart: cozystack-resource-definition-crd
namespace: cozy-system
dependsOn: [cilium,kubeovn,cozystack-api,cozystack-controller]
dependsOn: [cilium,kubeovn,multus,cozystack-api,cozystack-controller]
- name: cozystack-resource-definitions
releaseName: cozystack-resource-definitions
chart: cozystack-resource-definitions
namespace: cozy-system
dependsOn: [cilium,kubeovn,cozystack-api,cozystack-controller,cozystack-resource-definition-crd]
dependsOn: [cilium,kubeovn,multus,cozystack-api,cozystack-controller,cozystack-resource-definition-crd]
- name: cert-manager
releaseName: cert-manager
chart: cozy-cert-manager
namespace: cozy-cert-manager
dependsOn: [cert-manager-crds]
dependsOn: [cilium,kubeovn,multus,cert-manager-crds]
- name: cert-manager-issuers
releaseName: cert-manager-issuers
chart: cozy-cert-manager-issuers
namespace: cozy-cert-manager
dependsOn: [cilium,kubeovn,cert-manager]
dependsOn: [cilium,kubeovn,multus,cert-manager]
- name: victoria-metrics-operator
releaseName: victoria-metrics-operator
chart: cozy-victoria-metrics-operator
namespace: cozy-victoria-metrics-operator
dependsOn: [cilium,kubeovn,cert-manager]
dependsOn: [cilium,kubeovn,multus,cert-manager]
- name: monitoring-agents
releaseName: monitoring-agents
chart: cozy-monitoring-agents
namespace: cozy-monitoring
privileged: true
dependsOn: [victoria-metrics-operator, vertical-pod-autoscaler-crds]
dependsOn: [victoria-metrics-operator,vertical-pod-autoscaler-crds]
values:
scrapeRules:
etcd:
@@ -163,14 +163,14 @@ releases:
releaseName: kubevirt-operator
chart: cozy-kubevirt-operator
namespace: cozy-kubevirt
dependsOn: [cilium,kubeovn,victoria-metrics-operator]
dependsOn: [cilium,kubeovn,multus,victoria-metrics-operator]
- name: kubevirt
releaseName: kubevirt
chart: cozy-kubevirt
namespace: cozy-kubevirt
privileged: true
dependsOn: [cilium,kubeovn,kubevirt-operator]
dependsOn: [cilium,kubeovn,multus,kubevirt-operator]
{{- $cpuAllocationRatio := index $cozyConfig.data "cpu-allocation-ratio" }}
{{- if $cpuAllocationRatio }}
values:
@@ -181,19 +181,19 @@ releases:
releaseName: kubevirt-instancetypes
chart: cozy-kubevirt-instancetypes
namespace: cozy-kubevirt
dependsOn: [cilium,kubeovn,kubevirt-operator,kubevirt]
dependsOn: [cilium,kubeovn,multus,kubevirt-operator,kubevirt]
- name: kubevirt-cdi-operator
releaseName: kubevirt-cdi-operator
chart: cozy-kubevirt-cdi-operator
namespace: cozy-kubevirt-cdi
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,multus]
- name: kubevirt-cdi
releaseName: kubevirt-cdi
chart: cozy-kubevirt-cdi
namespace: cozy-kubevirt-cdi
dependsOn: [cilium,kubeovn,kubevirt-cdi-operator]
dependsOn: [cilium,kubeovn,multus,kubevirt-cdi-operator]
- name: gpu-operator
releaseName: gpu-operator
@@ -201,7 +201,7 @@ releases:
namespace: cozy-gpu-operator
privileged: true
optional: true
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,multus]
valuesFiles:
- values.yaml
- values-talos.yaml
@@ -211,25 +211,25 @@ releases:
chart: cozy-metallb
namespace: cozy-metallb
privileged: true
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,multus]
- name: etcd-operator
releaseName: etcd-operator
chart: cozy-etcd-operator
namespace: cozy-etcd-operator
dependsOn: [cilium,kubeovn,cert-manager]
dependsOn: [cilium,kubeovn,multus,cert-manager]
- name: grafana-operator
releaseName: grafana-operator
chart: cozy-grafana-operator
namespace: cozy-grafana-operator
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,multus]
- name: mariadb-operator
releaseName: mariadb-operator
chart: cozy-mariadb-operator
namespace: cozy-mariadb-operator
dependsOn: [cilium,kubeovn,cert-manager,victoria-metrics-operator]
dependsOn: [cilium,kubeovn,multus,cert-manager,victoria-metrics-operator]
values:
mariadb-operator:
clusterName: {{ $clusterDomain }}
@@ -238,13 +238,13 @@ releases:
releaseName: postgres-operator
chart: cozy-postgres-operator
namespace: cozy-postgres-operator
dependsOn: [cilium,kubeovn,cert-manager]
dependsOn: [cilium,kubeovn,multus,cert-manager]
- name: kafka-operator
releaseName: kafka-operator
chart: cozy-kafka-operator
namespace: cozy-kafka-operator
dependsOn: [cilium,kubeovn,victoria-metrics-operator]
dependsOn: [cilium,kubeovn,multus,victoria-metrics-operator]
values:
strimzi-kafka-operator:
kubernetesServiceDnsDomain: {{ $clusterDomain }}
@@ -253,65 +253,65 @@ releases:
releaseName: clickhouse-operator
chart: cozy-clickhouse-operator
namespace: cozy-clickhouse-operator
dependsOn: [cilium,kubeovn,victoria-metrics-operator]
dependsOn: [cilium,kubeovn,multus,victoria-metrics-operator]
- name: foundationdb-operator
releaseName: foundationdb-operator
chart: cozy-foundationdb-operator
namespace: cozy-foundationdb-operator
dependsOn: [cilium,kubeovn,cert-manager]
dependsOn: [cilium,kubeovn,multus,cert-manager]
- name: rabbitmq-operator
releaseName: rabbitmq-operator
chart: cozy-rabbitmq-operator
namespace: cozy-rabbitmq-operator
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,multus]
- name: redis-operator
releaseName: redis-operator
chart: cozy-redis-operator
namespace: cozy-redis-operator
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,multus]
- name: piraeus-operator
releaseName: piraeus-operator
chart: cozy-piraeus-operator
namespace: cozy-linstor
dependsOn: [cilium,kubeovn,cert-manager,victoria-metrics-operator]
dependsOn: [cilium,kubeovn,multus,cert-manager,victoria-metrics-operator]
- name: linstor
releaseName: linstor
chart: cozy-linstor
namespace: cozy-linstor
privileged: true
dependsOn: [piraeus-operator,cilium,kubeovn,cert-manager,snapshot-controller]
dependsOn: [piraeus-operator,cilium,kubeovn,multus,cert-manager,snapshot-controller]
- name: nfs-driver
releaseName: nfs-driver
chart: cozy-nfs-driver
namespace: cozy-nfs-driver
privileged: true
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,multus]
optional: true
- name: snapshot-controller
releaseName: snapshot-controller
chart: cozy-snapshot-controller
namespace: cozy-snapshot-controller
dependsOn: [cilium,kubeovn,cert-manager-issuers]
dependsOn: [cilium,kubeovn,multus,cert-manager-issuers]
- name: objectstorage-controller
releaseName: objectstorage-controller
chart: cozy-objectstorage-controller
namespace: cozy-objectstorage-controller
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,multus]
- name: telepresence
releaseName: traffic-manager
chart: cozy-telepresence
namespace: cozy-telepresence
optional: true
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,multus]
- name: dashboard
releaseName: dashboard
@@ -324,6 +324,7 @@ releases:
dependsOn:
- cilium
- kubeovn
- multus
{{- if eq $oidcEnabled "true" }}
- keycloak-configure
{{- end }}
@@ -332,56 +333,56 @@ releases:
releaseName: kamaji
chart: cozy-kamaji
namespace: cozy-kamaji
dependsOn: [cilium,kubeovn,cert-manager]
dependsOn: [cilium,kubeovn,multus,cert-manager]
- name: capi-operator
releaseName: capi-operator
chart: cozy-capi-operator
namespace: cozy-cluster-api
privileged: true
dependsOn: [cilium,kubeovn,cert-manager]
dependsOn: [cilium,kubeovn,multus,cert-manager]
- name: capi-providers-bootstrap
releaseName: capi-providers-bootstrap
chart: cozy-capi-providers-bootstrap
namespace: cozy-cluster-api
privileged: true
dependsOn: [cilium,kubeovn,capi-operator]
dependsOn: [cilium,kubeovn,multus,capi-operator]
- name: capi-providers-core
releaseName: capi-providers-core
chart: cozy-capi-providers-core
namespace: cozy-cluster-api
privileged: true
dependsOn: [cilium,kubeovn,capi-operator]
dependsOn: [cilium,kubeovn,multus,capi-operator]
- name: capi-providers-cpprovider
releaseName: capi-providers-cpprovider
chart: cozy-capi-providers-cpprovider
namespace: cozy-cluster-api
privileged: true
dependsOn: [cilium,kubeovn,capi-operator]
dependsOn: [cilium,kubeovn,multus,capi-operator]
- name: capi-providers-infraprovider
releaseName: capi-providers-infraprovider
chart: cozy-capi-providers-infraprovider
namespace: cozy-cluster-api
privileged: true
dependsOn: [cilium,kubeovn,capi-operator]
dependsOn: [cilium,kubeovn,multus,capi-operator]
- name: external-dns
releaseName: external-dns
chart: cozy-external-dns
namespace: cozy-external-dns
optional: true
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,multus]
- name: external-secrets-operator
releaseName: external-secrets-operator
chart: cozy-external-secrets-operator
namespace: cozy-external-secrets-operator
optional: true
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,multus]
- name: bootbox
releaseName: bootbox
@@ -389,7 +390,7 @@ releases:
namespace: cozy-bootbox
privileged: true
optional: true
dependsOn: [cilium,kubeovn]
dependsOn: [cilium,kubeovn,multus]
{{- if $oidcEnabled }}
- name: keycloak
@@ -438,7 +439,7 @@ releases:
chart: cozy-vertical-pod-autoscaler-crds
namespace: cozy-vertical-pod-autoscaler
privileged: true
dependsOn: [cilium, kubeovn]
dependsOn: []
- name: reloader
releaseName: reloader

0
packages/core/testing/Chart.yaml Executable file → Normal file
View File

0
packages/core/testing/Makefile Executable file → Normal file
View File

10
packages/core/testing/images/e2e-sandbox/Dockerfile Executable file → Normal file
View File

@@ -9,7 +9,7 @@ ARG TARGETOS
ARG TARGETARCH
RUN apt update -q
RUN apt install -yq --no-install-recommends psmisc genisoimage ca-certificates qemu-kvm qemu-utils iproute2 iptables wget xz-utils netcat curl jq make git
RUN apt install -yq --no-install-recommends psmisc genisoimage ca-certificates qemu-kvm qemu-utils iproute2 iptables wget xz-utils netcat curl jq make git bash-completion
RUN curl -sSL "https://github.com/siderolabs/talos/releases/download/v${TALOSCTL_VERSION}/talosctl-${TARGETOS}-${TARGETARCH}" -o /usr/local/bin/talosctl \
&& chmod +x /usr/local/bin/talosctl
RUN curl -sSL "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/${TARGETOS}/${TARGETARCH}/kubectl" -o /usr/local/bin/kubectl \
@@ -21,5 +21,13 @@ RUN curl -sSL "https://fluxcd.io/install.sh" | bash
RUN curl -sSL "https://github.com/cozystack/cozypkg/raw/refs/heads/main/hack/install.sh" | sh -s -- -v "${COZYPKG_VERSION}"
RUN curl https://dl.min.io/client/mc/release/${TARGETOS}-${TARGETARCH}/mc --create-dirs -o /usr/local/bin/mc \
&& chmod +x /usr/local/bin/mc
RUN <<'EOF'
cat <<'EOT' >> /etc/bash.bashrc
. /etc/bash_completion
. <(kubectl completion bash)
alias k=kubectl
complete -F __start_kubectl k
EOT
EOF
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]

2
packages/core/testing/values.yaml Executable file → Normal file
View File

@@ -1,2 +1,2 @@
e2e:
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.38.2@sha256:84be9e42bc2c04b0765c8b89e0a9728c49ebf4676a92522b007af96ae9aec68d
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.39.5@sha256:3395a0e3c02e85878a44d3177cae3c37b087edeb4fab6547cb8227862b1de773

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/matchbox:v0.38.2@sha256:9cd7f46fcae119a3f8e35b428b018d0cb6da7b0cdd2ce764cc9fbf6dcd903f27
ghcr.io/cozystack/cozystack/matchbox:v0.39.5@sha256:7a0c29d75a0b5416e2409d4703131b2f3f09cdc39e55ee916803cabf0606f724

View File

@@ -12,6 +12,13 @@ spec:
containers:
- name: etcd-defrag
image: ghcr.io/ahrtr/etcd-defrag:v0.13.0
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
args:
- --endpoints={{ range $i, $e := until (int .Values.replicas) }}{{ if $i }},{{ end }}https://{{ $.Release.Name }}-{{ $i }}.{{ $.Release.Name }}-headless.{{ $.Release.Namespace }}.svc:2379{{ end }}
- --cacert=/etc/etcd/pki/client/cert/ca.crt

View File

@@ -55,25 +55,26 @@
### Alerta configuration
| Name | Description | Type | Value |
| ----------------------------------------- | ----------------------------------------------------------------- | ---------- | ------- |
| `alerta` | Configuration for the Alerta service. | `object` | `{}` |
| `alerta.storage` | Persistent volume size for the database. | `string` | `10Gi` |
| `alerta.storageClassName` | StorageClass used for the database. | `string` | `""` |
| `alerta.resources` | Resource configuration. | `object` | `{}` |
| `alerta.resources.requests` | Resource requests. | `object` | `{}` |
| `alerta.resources.requests.cpu` | CPU request. | `quantity` | `100m` |
| `alerta.resources.requests.memory` | Memory request. | `quantity` | `256Mi` |
| `alerta.resources.limits` | Resource limits. | `object` | `{}` |
| `alerta.resources.limits.cpu` | CPU limit. | `quantity` | `1` |
| `alerta.resources.limits.memory` | Memory limit. | `quantity` | `1Gi` |
| `alerta.alerts` | Alert routing configuration. | `object` | `{}` |
| `alerta.alerts.telegram` | Configuration for Telegram alerts. | `object` | `{}` |
| `alerta.alerts.telegram.token` | Telegram bot token. | `string` | `""` |
| `alerta.alerts.telegram.chatID` | Telegram chat ID(s), separated by commas. | `string` | `""` |
| `alerta.alerts.telegram.disabledSeverity` | List of severities without alerts (e.g. "informational,warning"). | `string` | `""` |
| `alerta.alerts.slack` | Configuration for Slack alerts. | `object` | `{}` |
| `alerta.alerts.slack.url` | Configuration uri for Slack alerts. | `string` | `""` |
| Name | Description | Type | Value |
| ----------------------------------------- | --------------------------------------------------------------------- | ---------- | ------- |
| `alerta` | Configuration for the Alerta service. | `object` | `{}` |
| `alerta.storage` | Persistent volume size for the database. | `string` | `10Gi` |
| `alerta.storageClassName` | StorageClass used for the database. | `string` | `""` |
| `alerta.resources` | Resource configuration. | `object` | `{}` |
| `alerta.resources.requests` | Resource requests. | `object` | `{}` |
| `alerta.resources.requests.cpu` | CPU request. | `quantity` | `100m` |
| `alerta.resources.requests.memory` | Memory request. | `quantity` | `256Mi` |
| `alerta.resources.limits` | Resource limits. | `object` | `{}` |
| `alerta.resources.limits.cpu` | CPU limit. | `quantity` | `1` |
| `alerta.resources.limits.memory` | Memory limit. | `quantity` | `1Gi` |
| `alerta.alerts` | Alert routing configuration. | `object` | `{}` |
| `alerta.alerts.telegram` | Configuration for Telegram alerts. | `object` | `{}` |
| `alerta.alerts.telegram.token` | Telegram bot token. | `string` | `""` |
| `alerta.alerts.telegram.chatID` | Telegram chat ID(s), separated by commas. | `string` | `""` |
| `alerta.alerts.telegram.disabledSeverity` | List of severities without alerts (e.g. ["informational","warning"]). | `[]string` | `[]` |
| `alerta.alerts.slack` | Configuration for Slack alerts. | `object` | `{}` |
| `alerta.alerts.slack.url` | Configuration uri for Slack alerts. | `string` | `""` |
| `alerta.alerts.slack.disabledSeverity` | List of severities without alerts (e.g. ["informational","warning"]). | `[]string` | `[]` |
### Grafana configuration

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/grafana:0.0.0@sha256:c63978e1ed0304e8518b31ddee56c4e8115541b997d8efbe1c0a74da57140399
ghcr.io/cozystack/cozystack/grafana:0.0.0@sha256:8ce0cd90c8f614cdabf5a41f8aa50b7dfbd02b31b9a0bd7897927e7f89968e07

View File

@@ -129,13 +129,19 @@ spec:
value: "{{ .Values.alerta.alerts.telegram.token }}"
- name: TELEGRAM_WEBHOOK_URL
value: "https://{{ printf "alerta.%s" (.Values.host | default $host) }}/api/webhooks/telegram?api-key={{ $apiKey }}"
{{- if .Values.alerta.alerts.telegram.disabledSeverity }}
- name: TELEGRAM_DISABLE_NOTIFICATION_SEVERITY
value: "{{ .Values.alerta.alerts.telegram.disabledSeverity }}"
value: {{ .Values.alerta.alerts.telegram.disabledSeverity | toJson | quote }}
{{- end }}
{{- end }}
{{- if .Values.alerta.alerts.slack.url }}
- name: "SLACK_WEBHOOK_URL"
value: "{{ .Values.alerta.alerts.slack.url }}"
{{- if .Values.alerta.alerts.slack.disabledSeverity }}
- name: SLACK_SEVERITY_FILTER
value: {{ .Values.alerta.alerts.slack.disabledSeverity | toJson | quote }}
{{- end }}
{{- end }}
ports:

View File

@@ -20,6 +20,14 @@
"url"
],
"properties": {
"disabledSeverity": {
"description": "List of severities without alerts (e.g. [\"informational\",\"warning\"]).",
"type": "array",
"default": [],
"items": {
"type": "string"
}
},
"url": {
"description": "Configuration uri for Slack alerts.",
"type": "string",
@@ -42,9 +50,12 @@
"default": ""
},
"disabledSeverity": {
"description": "List of severities without alerts (e.g. \"informational,warning\").",
"type": "string",
"default": ""
"description": "List of severities without alerts (e.g. [\"informational\",\"warning\"]).",
"type": "array",
"default": [],
"items": {
"type": "string"
}
},
"token": {
"description": "Telegram bot token.",

View File

@@ -99,10 +99,11 @@ logsStorages:
## @typedef {struct} TelegramAlerts - Telegram alert configuration.
## @field {string} token - Telegram bot token.
## @field {string} chatID - Telegram chat ID(s), separated by commas.
## @field {string} [disabledSeverity] - List of severities without alerts (e.g. "informational,warning").
## @field {[]string} [disabledSeverity] - List of severities without alerts (e.g. ["informational","warning"]).
## @typedef {struct} SlackAlerts - Slack alert configuration.
## @field {string} url - Configuration uri for Slack alerts.
## @field {[]string} [disabledSeverity] - List of severities without alerts (e.g. ["informational","warning"]).
## @typedef {struct} Alerts - Alert routing configuration.
## @field {TelegramAlerts} [telegram] - Configuration for Telegram alerts.
@@ -129,9 +130,10 @@ alerta:
telegram:
token: ""
chatID: ""
disabledSeverity: ""
disabledSeverity: []
slack:
url: ""
disabledSeverity: []
##
## @section Grafana configuration
##

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.38.2@sha256:ff3281fe53a97d2cd5cd94bd4c4d8ff08189508729869bb39b3f60c80da5f919
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.39.5@sha256:4bb47c8adb34543403a16d1ff61b307939850e8cf5fc365388469e30dfb9681b

View File

@@ -1,11 +1,13 @@
OUT=../../_out/repos/system
CHARTS := $(shell find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}')
CHARTS := $(shell grep -F 'version: 0.0.0' */Chart.yaml | cut -f1 -d/)
VERSIONED_CHARTS := $(shell grep '^version:' */Chart.yaml | grep -Fv '0.0.0' | cut -f1 -d/)
include ../../scripts/common-envs.mk
repo:
rm -rf "$(OUT)"
helm package -d "$(OUT)" $(CHARTS) --version $(COZYSTACK_VERSION)
helm package -d "$(OUT)" $(VERSIONED_CHARTS)
cd "$(OUT)" && helm repo index .
fix-charts:

View File

@@ -1 +1 @@
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:3825c9b4b6238f88f1b0de73bd18866a7e5f83f178d28fe2830f3bf24efb187d
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:ecb140d026ed72660306953a7eec140d7ac81e79544d5bbf1aba5f62aa5f8b69

View File

@@ -10,6 +10,7 @@ cilium:
enabled: true
loadBalancer:
algorithm: maglev
serviceTopology: true
ipam:
mode: "kubernetes"
image:
@@ -18,3 +19,6 @@ cilium:
digest: "sha256:81262986a41487bfa3d0465091d3a386def5bd1ab476350bd4af2fdee5846fe6"
envoy:
enabled: false
rollOutCiliumPods: true
operator:
rollOutPods: true

View File

@@ -3,3 +3,6 @@ coredns:
repository: registry.k8s.io/coredns/coredns
tag: v1.12.4
replicaCount: 2
k8sAppLabelOverride: kube-dns
service:
name: kube-dns

View File

@@ -21,6 +21,8 @@ spec:
labels:
app: cozystack-api
spec:
tolerations:
- operator: Exists
serviceAccountName: cozystack-api
{{- if .Values.cozystackAPI.localK8sAPIEndpoint.enabled }}
nodeSelector:

View File

@@ -1,5 +1,5 @@
cozystackAPI:
image: ghcr.io/cozystack/cozystack/cozystack-api:v0.38.2@sha256:d17f1c59658731e5a2063c3db348adbc03b5cd31720052016b68449164cf2f14
image: ghcr.io/cozystack/cozystack/cozystack-api:v0.39.5@sha256:51574c6bb61ae31e63193f84daf18c14ceb71580786e262191c4aa0ac44b1519
localK8sAPIEndpoint:
enabled: true
replicas: 2

View File

@@ -1,6 +1,6 @@
cozystackController:
image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.38.2@sha256:468b2eccbc0aa00bd3d72d56624a46e6ba178fa279cdd19248af74d32ea7d319
image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.39.5@sha256:dd6bb910842151ec60cea74f43b03ac35f461a03f6fe4370b12c7aaf48fee7b8
debug: false
disableTelemetry: false
cozystackVersion: "v0.38.2"
cozystackVersion: "v0.39.5"
cozystackAPIKind: "DaemonSet"

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
{{- $brandingConfig:= lookup "v1" "ConfigMap" "cozy-system" "cozystack-branding" }}
{{- $tenantText := "v0.38.2" }}
{{- $tenantText := "v0.39.5" }}
{{- $footerText := "Cozystack" }}
{{- $titleText := "Cozystack Dashboard" }}
{{- $logoText := "" }}

View File

@@ -1,6 +1,6 @@
openapiUI:
image: ghcr.io/cozystack/cozystack/openapi-ui:v0.38.2@sha256:5aafb6c864c5523418d021a9fe5b514990d36972b6f1de9c34a1cd41f9d8bf7e
image: ghcr.io/cozystack/cozystack/openapi-ui:v0.39.5@sha256:6c48044fd9bfaea65478c39fc9f3e7322d06aef46022211847621eeb6d46c51c
openapiUIK8sBff:
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v0.38.2@sha256:7ffd8ae7b9da73fec7ae61a71c9c821a718d89a1b1df0197e09fda57678e1220
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v0.39.5@sha256:fda379dce49c2cd8cb8d7d2a1d8ec6f7bedb3419c058c4355ecdece1c1e937f4
tokenProxy:
image: ghcr.io/cozystack/cozystack/token-proxy:v0.38.2@sha256:fad27112617bb17816702571e1f39d0ac3fe5283468d25eb12f79906cdab566b
image: ghcr.io/cozystack/cozystack/token-proxy:v0.39.5@sha256:4fc8a11f8a1a81aa0774ae2b1ed2e05d36d0b3ef1e37979cc4994e65114d93ae

View File

@@ -54,9 +54,45 @@ ingress-nginx:
requests:
cpu: 100m
memory: 90Mi
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 10
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- '{{ include "ingress-nginx.name" . }}'
- key: app.kubernetes.io/instance
operator: In
values:
- '{{ .Release.Name }}'
- key: app.kubernetes.io/component
operator: In
values:
- controller
topologyKey: kubernetes.io/hostname
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- '{{ include "ingress-nginx.name" . }}'
- key: app.kubernetes.io/instance
operator: In
values:
- '{{ .Release.Name }}'
- key: app.kubernetes.io/component
operator: In
values:
- controller
topologyKey: topology.kubernetes.io/zone
defaultBackend:
##
enabled: true
resources:
limits:

View File

@@ -3,7 +3,7 @@ kamaji:
deploy: false
image:
pullPolicy: IfNotPresent
tag: v0.38.2@sha256:13741b8f6dfede3ea0fd16d8bbebae810bc19254a81d7e5a139535efa17eabff
tag: v0.39.5@sha256:0bcfb2d376224b18a0627d7b18ba6202bb8a553f71796023e12740d9513740c7
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:v0.38.2@sha256:13741b8f6dfede3ea0fd16d8bbebae810bc19254a81d7e5a139535efa17eabff
- --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v0.39.5@sha256:0bcfb2d376224b18a0627d7b18ba6202bb8a553f71796023e12740d9513740c7

View File

@@ -1,4 +1,4 @@
portSecurity: true
routes: ""
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v0.38.2@sha256:76c8af24cbec0261718c13c0150aa81c238a956626d4fd7baa8970b47fb3a6f0
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v0.39.5@sha256:52b7764b06ca3d6e5a2e74ba3d6d68ecdcf6b71a227e50b1344a8f11bf693a23
ovnCentralName: ovn-central

View File

@@ -1,3 +1,3 @@
portSecurity: true
routes: ""
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.38.2@sha256:8e67b2971f8c079a8b0636be1d091a9545d6cb653d745ff222a5966f56f903bd
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.39.5@sha256:e6334c29d3aaf0dea766c88e3e05b53ad623d1bb497b3c836e6f76adade45b29

View File

@@ -1,3 +1,3 @@
apiVersion: v2
name: cozy-kubeovn
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process
version: 0.38.0

View File

@@ -1,4 +1,4 @@
KUBEOVN_TAG=$(shell awk '$$1 == "version:" {print $$2}' charts/kube-ovn/Chart.yaml)
KUBEOVN_TAG=v0.40.0
export NAME=kubeovn
export NAMESPACE=cozy-$(NAME)
@@ -7,28 +7,7 @@ include ../../../scripts/common-envs.mk
include ../../../scripts/package.mk
update:
rm -rf charts && mkdir -p charts/kube-ovn
tag=$$(git ls-remote --tags --sort="v:refname" https://github.com/kubeovn/kube-ovn | awk -F'[/^]' '{print $$3}' | grep '^v1\.14\.' | tail -n1 ) && \
curl -sSL https://github.com/kubeovn/kube-ovn/archive/refs/tags/$${tag}.tar.gz | \
tar xzvf - --strip 1 kube-ovn-$${tag#*v}/charts/kube-ovn
patch --no-backup-if-mismatch -p4 < patches/cozyconfig.diff
patch --no-backup-if-mismatch -p4 < patches/mtu.diff
version=$$(awk '$$1 == "version:" {print $$2}' charts/kube-ovn/Chart.yaml) && \
sed -i "s/ARG VERSION=.*/ARG VERSION=$${version}/" images/kubeovn/Dockerfile && \
sed -i "s/ARG TAG=.*/ARG TAG=$${version}/" images/kubeovn/Dockerfile
image:
docker buildx build images/kubeovn \
--tag $(REGISTRY)/kubeovn:$(call settag,$(KUBEOVN_TAG)) \
--tag $(REGISTRY)/kubeovn:$(call settag,$(KUBEOVN_TAG)-$(TAG)) \
--cache-from type=registry,ref=$(REGISTRY)/kubeovn:latest \
--cache-to type=inline \
--metadata-file images/kubeovn.json \
$(BUILDX_ARGS)
REGISTRY="$(REGISTRY)" \
yq -i '.global.registry.address = strenv(REGISTRY)' values.yaml
REPOSITORY="kubeovn" \
yq -i '.global.images.kubeovn.repository = strenv(REPOSITORY)' values.yaml
TAG="$(call settag,$(KUBEOVN_TAG))@$$(yq e '."containerimage.digest"' images/kubeovn.json -o json -r)" \
yq -i '.global.images.kubeovn.tag = strenv(TAG)' values.yaml
rm -f images/kubeovn.json
rm -rf charts values.yaml Chart.yaml
tag=$(KUBEOVN_TAG) && \
curl -sSL https://github.com/cozystack/kubeovn/archive/refs/tags/$${tag}.tar.gz | \
tar xzvf - --strip 2 kubeovn-$${tag#*v}/chart

View File

@@ -15,12 +15,12 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: v1.14.11
version: v1.14.25
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.14.11"
appVersion: "1.14.25"
kubeVersion: ">= 1.29.0-0"

View File

@@ -2,6 +2,18 @@
Currently supported version: 1.9
## Installing the Chart
### From OCI Registry
The Helm chart is available from GitHub Container Registry:
```bash
helm install kube-ovn oci://ghcr.io/kubeovn/charts/kube-ovn --version v1.15.0
```
### From Source
Installation :
```bash

View File

@@ -1,8 +1,10 @@
{{/*
Get IP-addresses of master nodes
Get IP-addresses of master nodes. If no nodes are returned, we assume this is
a dry-run/template call and return nothing.
*/}}
{{- define "kubeovn.nodeIPs" -}}
{{- $nodes := lookup "v1" "Node" "" "" -}}
{{- if $nodes -}}
{{- $ips := list -}}
{{- range $node := $nodes.items -}}
{{- $label := splitList "=" $.Values.MASTER_NODES_LABEL }}
@@ -25,6 +27,7 @@ Get IP-addresses of master nodes
{{- end -}}
{{ join "," $ips }}
{{- end -}}
{{- end -}}
{{/*
Number of master nodes

View File

@@ -39,7 +39,11 @@ spec:
topologyKey: kubernetes.io/hostname
priorityClassName: system-cluster-critical
serviceAccountName: ovn-ovs
automountServiceAccountToken: true
hostNetwork: true
securityContext:
seccompProfile:
type: RuntimeDefault
initContainers:
- name: hostpath-init
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}

View File

@@ -46,7 +46,11 @@ spec:
topologyKey: kubernetes.io/hostname
priorityClassName: system-cluster-critical
serviceAccountName: ovn
automountServiceAccountToken: true
hostNetwork: true
securityContext:
seccompProfile:
type: RuntimeDefault
initContainers:
- name: hostpath-init
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}

View File

@@ -40,7 +40,11 @@ spec:
topologyKey: kubernetes.io/hostname
priorityClassName: system-cluster-critical
serviceAccountName: ovn
automountServiceAccountToken: true
hostNetwork: true
securityContext:
seccompProfile:
type: RuntimeDefault
initContainers:
- name: hostpath-init
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}

View File

@@ -1200,6 +1200,52 @@ spec:
required:
- key
- operator
tolerations:
description: optional tolerations applied to the workload pods
items:
description: |-
The pod this Toleration is attached to tolerates any taint that matches
the triple <key,value,effect> using the matching operator <operator>.
properties:
effect:
description: |-
Effect indicates the taint effect to match. Empty means match all taint effects.
When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.
type: string
enum:
- NoSchedule
- PreferNoSchedule
- NoExecute
key:
description: |-
Key is the taint key that the toleration applies to. Empty means match all taint keys.
If the key is empty, operator must be Exists; this combination means to match all values and all keys.
type: string
operator:
description: |-
Operator represents a key's relationship to the value.
Valid operators are Exists and Equal. Defaults to Equal.
Exists is equivalent to wildcard for value, so that a pod can
tolerate all taints of a particular category.
type: string
enum:
- Exists
- Equal
tolerationSeconds:
description: |-
TolerationSeconds represents the period of time the toleration (which must be
of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default,
it is not set, which means tolerate the taint forever (do not evict). Zero and
negative values will be treated as 0 (evict immediately) by the system.
format: int64
type: integer
value:
description: |-
Value is the taint value the toleration matches to.
If the operator is Exists, the value should be empty, otherwise just a regular string.
type: string
type: object
type: array
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
@@ -2871,6 +2917,8 @@ spec:
type: array
items:
type: string
autoCreateVlanSubinterfaces:
type: boolean
required:
- defaultInterface
status:

View File

@@ -37,7 +37,11 @@ spec:
topologyKey: kubernetes.io/hostname
priorityClassName: system-cluster-critical
serviceAccountName: kube-ovn-app
automountServiceAccountToken: true
hostNetwork: true
securityContext:
seccompProfile:
type: RuntimeDefault
initContainers:
- name: hostpath-init
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}

View File

@@ -27,8 +27,12 @@ spec:
- operator: Exists
priorityClassName: system-node-critical
serviceAccountName: ovn-ovs
automountServiceAccountToken: true
hostNetwork: true
hostPID: true
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: openvswitch
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.DPDK_IMAGE_TAG }}

View File

@@ -3,6 +3,7 @@ kind: ServiceAccount
metadata:
name: ovn
namespace: {{ .Values.namespace }}
automountServiceAccountToken: false
{{- if .Values.global.registry.imagePullSecrets }}
imagePullSecrets:
{{- range $index, $secret := .Values.global.registry.imagePullSecrets }}
@@ -18,6 +19,7 @@ kind: ServiceAccount
metadata:
name: ovn-ovs
namespace: {{ .Values.namespace }}
automountServiceAccountToken: false
{{- if .Values.global.registry.imagePullSecrets }}
imagePullSecrets:
{{- range $index, $secret := .Values.global.registry.imagePullSecrets }}
@@ -33,6 +35,7 @@ kind: ServiceAccount
metadata:
name: kube-ovn-cni
namespace: {{ .Values.namespace }}
automountServiceAccountToken: false
{{- if .Values.global.registry.imagePullSecrets }}
imagePullSecrets:
{{- range $index, $secret := .Values.global.registry.imagePullSecrets }}
@@ -48,6 +51,7 @@ kind: ServiceAccount
metadata:
name: kube-ovn-app
namespace: {{ .Values.namespace }}
automountServiceAccountToken: false
{{- if .Values.global.registry.imagePullSecrets }}
imagePullSecrets:
{{- range $index, $secret := .Values.global.registry.imagePullSecrets }}

View File

@@ -26,8 +26,12 @@ spec:
operator: Exists
priorityClassName: system-node-critical
serviceAccountName: kube-ovn-cni
automountServiceAccountToken: true
hostNetwork: true
hostPID: true
securityContext:
seccompProfile:
type: RuntimeDefault
initContainers:
- name: hostpath-init
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}
@@ -35,7 +39,9 @@ spec:
command:
- sh
- -xec
- iptables -V
- |
chmod +t /usr/local/sbin
iptables -V
securityContext:
allowPrivilegeEscalation: true
capabilities:
@@ -60,16 +66,21 @@ spec:
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
- /kube-ovn/install-cni.sh
- --cni-conf-dir={{ .Values.cni_conf.CNI_CONF_DIR }}
- --cni-conf-dir={{ .Values.cni_conf.MOUNT_CNI_CONF_DIR }}
- --cni-conf-file={{ .Values.cni_conf.CNI_CONF_FILE }}
- --cni-conf-name={{- .Values.cni_conf.CNI_CONFIG_PRIORITY -}}-kube-ovn.conflist
env:
- name: POD_IPS
valueFrom:
fieldRef:
fieldPath: status.podIPs
securityContext:
runAsUser: 0
privileged: true
volumeMounts:
- mountPath: /opt/cni/bin
name: cni-bin
- mountPath: /etc/cni/net.d
- mountPath: {{ .Values.cni_conf.MOUNT_CNI_CONF_DIR }}
name: cni-conf
{{- if .Values.cni_conf.MOUNT_LOCAL_BIN_DIR }}
- mountPath: /usr/local/bin

View File

@@ -34,8 +34,12 @@ spec:
operator: Exists
priorityClassName: system-node-critical
serviceAccountName: ovn-ovs
automountServiceAccountToken: true
hostNetwork: true
hostPID: true
securityContext:
seccompProfile:
type: RuntimeDefault
initContainers:
- name: hostpath-init
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}
@@ -44,6 +48,7 @@ spec:
- sh
- -xec
- |
chmod +t /usr/local/sbin
chown -R nobody: /var/run/ovn /var/log/ovn /etc/openvswitch /var/run/openvswitch /var/log/openvswitch
iptables -V
{{- if not .Values.DISABLE_MODULES_MANAGEMENT }}

View File

@@ -28,7 +28,11 @@ spec:
- key: CriticalAddonsOnly
operator: Exists
serviceAccountName: kube-ovn-app
hostPID: true
automountServiceAccountToken: true
hostPID: false
securityContext:
seccompProfile:
type: RuntimeDefault
initContainers:
- name: hostpath-init
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}

View File

@@ -9,6 +9,7 @@ metadata:
"helm.sh/hook": post-delete
"helm.sh/hook-weight": "1"
"helm.sh/hook-delete-policy": hook-succeeded
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@@ -102,8 +103,11 @@ spec:
hostNetwork: true
nodeSelector:
kubernetes.io/os: "linux"
serviceAccount: kube-ovn-post-delete-hook
serviceAccountName: kube-ovn-post-delete-hook
automountServiceAccountToken: true
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: remove-subnet-finalizer
image: "{{ .Values.global.registry.address}}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}"

View File

@@ -11,6 +11,7 @@ metadata:
"helm.sh/hook": post-upgrade
"helm.sh/hook-weight": "1"
"helm.sh/hook-delete-policy": hook-succeeded
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@@ -133,8 +134,11 @@ spec:
hostNetwork: true
nodeSelector:
kubernetes.io/os: "linux"
serviceAccount: ovs-ovn-upgrade
serviceAccountName: ovs-ovn-upgrade
automountServiceAccountToken: true
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: ovs-ovn-upgrade
image: "{{ .Values.global.registry.address}}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}"

View File

@@ -9,7 +9,7 @@ global:
kubeovn:
repository: kube-ovn
vpcRepository: vpc-nat-gateway
tag: v1.14.11
tag: v1.14.25
support_arm: true
thirdparty: true
@@ -111,6 +111,7 @@ debug:
cni_conf:
CNI_CONFIG_PRIORITY: "01"
CNI_CONF_DIR: "/etc/cni/net.d"
MOUNT_CNI_CONF_DIR: "/etc/cni/net.d"
CNI_BIN_DIR: "/opt/cni/bin"
CNI_CONF_FILE: "/kube-ovn/01-kube-ovn.conflist"
LOCAL_BIN_DIR: "/usr/local/bin"

View File

@@ -1,47 +0,0 @@
# syntax = docker/dockerfile:experimental
ARG VERSION=v1.14.11
ARG BASE_TAG=$VERSION
FROM golang:1.25-bookworm as builder
ARG TAG=v1.14.11
RUN git clone --branch ${TAG} --depth 1 https://github.com/kubeovn/kube-ovn /source
WORKDIR /source
COPY patches /patches
RUN git apply /patches/*.diff
RUN make build-go
WORKDIR /source/dist/images
# imported from https://github.com/kubeovn/kube-ovn/blob/master/dist/images/Dockerfile
FROM kubeovn/kube-ovn-base:$BASE_TAG AS setcap
COPY --from=builder /source/dist/images/*.sh /kube-ovn/
COPY --from=builder /source/dist/images/kubectl-ko /kube-ovn/kubectl-ko
COPY --from=builder /source/dist/images/01-kube-ovn.conflist /kube-ovn/01-kube-ovn.conflist
COPY --from=builder /source/dist/images/kube-ovn /kube-ovn/kube-ovn
COPY --from=builder /source/dist/images/kube-ovn-cmd /kube-ovn/kube-ovn-cmd
COPY --from=builder /source/dist/images/kube-ovn-daemon /kube-ovn/kube-ovn-daemon
COPY --from=builder /source/dist/images/kube-ovn-controller /kube-ovn/kube-ovn-controller
RUN ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-monitor && \
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-speaker && \
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-webhook && \
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-leader-checker && \
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-ic-controller && \
ln -s /kube-ovn/kube-ovn-controller /kube-ovn/kube-ovn-pinger && \
setcap CAP_NET_BIND_SERVICE+eip /kube-ovn/kube-ovn-cmd && \
setcap CAP_NET_RAW,CAP_NET_BIND_SERVICE+eip /kube-ovn/kube-ovn-controller && \
setcap CAP_NET_ADMIN,CAP_NET_RAW,CAP_NET_BIND_SERVICE,CAP_SYS_ADMIN+eip /kube-ovn/kube-ovn-daemon
FROM kubeovn/kube-ovn-base:$BASE_TAG
COPY --chmod=0644 --from=builder /source/dist/images/logrotate/* /etc/logrotate.d/
COPY --from=builder /source/dist/images/grace_stop_ovn_controller /usr/share/ovn/scripts/grace_stop_ovn_controller
COPY --from=setcap /kube-ovn /kube-ovn
RUN /kube-ovn/iptables-wrapper-installer.sh --no-sanity-check
WORKDIR /kube-ovn

View File

@@ -1,109 +0,0 @@
diff --git a/pkg/ovn_leader_checker/ovn.go b/pkg/ovn_leader_checker/ovn.go
index 0f86a371d..8ddf7bca6 100755
--- a/pkg/ovn_leader_checker/ovn.go
+++ b/pkg/ovn_leader_checker/ovn.go
@@ -10,6 +10,7 @@ import (
"os/exec"
"strconv"
"strings"
+ "sync"
"syscall"
"time"
@@ -271,19 +272,56 @@ func checkNorthdSvcExist(cfg *Configuration, namespace, svcName string) bool {
return true
}
-func checkNorthdEpAvailable(ip string) bool {
- address := net.JoinHostPort(ip, OvnNorthdPort)
- conn, err := net.DialTimeout("tcp", address, 3*time.Second)
- if err != nil {
- klog.Errorf("failed to connect to northd leader %s, err: %v", ip, err)
- failCount++
- if failCount >= MaxFailCount {
- return false
- }
- } else {
+func checkNorthdEpAvailable(ips ...string) bool {
+ var wg sync.WaitGroup
+ success := make(chan struct{}, 1)
+ failure := make(chan struct{})
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ d := net.Dialer{Timeout: 3 * time.Second}
+ for _, ip := range ips {
+ address := net.JoinHostPort(ip, OvnNorthdPort)
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ conn, err := d.DialContext(ctx, "tcp", address)
+ if err != nil {
+ klog.Errorf("failed to connect to northd leader %s, err: %v", ip, err)
+ return
+ } else {
+ defer conn.Close()
+ select {
+ case success <- struct{}{}:
+ klog.V(5).Infof("succeed to connect to northd leader %s", ip)
+ cancel()
+ default:
+ // someone else already succeeded
+ }
+ }
+ }()
+ }
+ go func() {
+ wg.Wait()
+ close(failure)
+ }()
+ select {
+ case <-success:
failCount = 0
- klog.V(5).Infof("succeed to connect to northd leader %s", ip)
- _ = conn.Close()
+ return true
+ case <-failure:
+ // if the last groroutine is the one to succeed,
+ // there's a small chance that failure is selected,
+ // this is a guard for this
+ select {
+ case <-success:
+ failCount = 0
+ return true
+ default:
+ failCount++
+ if failCount >= MaxFailCount {
+ return false
+ }
+ }
}
return true
}
@@ -295,6 +333,8 @@ func checkNorthdEpAlive(cfg *Configuration, namespace, service string) bool {
return false
}
+ addresses := []string{}
+
for _, eps := range epsList.Items {
for _, ep := range eps.Endpoints {
if (ep.Conditions.Ready != nil && !*ep.Conditions.Ready) || len(ep.Addresses) == 0 {
@@ -303,12 +343,15 @@ func checkNorthdEpAlive(cfg *Configuration, namespace, service string) bool {
// Found an address, check its availability. We only need one.
klog.V(5).Infof("found address %s in endpoint slice %s/%s for service %s, checking availability", ep.Addresses[0], eps.Namespace, eps.Name, service)
- return checkNorthdEpAvailable(ep.Addresses[0])
+ addresses = append(addresses, ep.Addresses[0]) // Addresses are fungible by k8s API design
}
}
- klog.V(5).Infof("no address found in any endpoint slices for service %s/%s", namespace, service)
- return false
+ if len(addresses) == 0 {
+ klog.V(5).Infof("no address found in any endpoint slices for service %s/%s", namespace, service)
+ return false
+ }
+ return checkNorthdEpAvailable(addresses...)
}
func compactOvnDatabase(db string) {

View File

@@ -1,103 +0,0 @@
diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml
index d9a9a67..b2e12dd 100644
--- a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml
+++ b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml
@@ -51,18 +51,15 @@ spec:
- bash
- /kube-ovn/start-cniserver.sh
args:
+ {{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }}
- --enable-mirror={{- .Values.debug.ENABLE_MIRROR }}
- --mirror-iface={{- .Values.debug.MIRROR_IFACE }}
- --node-switch={{ .Values.networking.NODE_SUBNET }}
- --encap-checksum=true
- - --service-cluster-ip-range=
- {{- if eq .Values.networking.NET_STACK "dual_stack" -}}
- {{ .Values.dual_stack.SVC_CIDR }}
- {{- else if eq .Values.networking.NET_STACK "ipv4" -}}
- {{ .Values.ipv4.SVC_CIDR }}
- {{- else if eq .Values.networking.NET_STACK "ipv6" -}}
- {{ .Values.ipv6.SVC_CIDR }}
- {{- end }}
+ - --service-cluster-ip-range={{ index $cozyConfig.data "ipv4-svc-cidr" }}
+ {{- if .Values.global.logVerbosity }}
+ - --v={{ .Values.global.logVerbosity }}
+ {{- end }}
{{- if eq .Values.networking.NETWORK_TYPE "vlan" }}
- --iface=
{{- else}}
diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml
index 0e69494..756eb7c 100644
--- a/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml
+++ b/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml
@@ -52,46 +52,22 @@ spec:
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
args:
+ {{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }}
- /kube-ovn/start-controller.sh
- --default-ls={{ .Values.networking.DEFAULT_SUBNET }}
- - --default-cidr=
- {{- if eq .Values.networking.NET_STACK "dual_stack" -}}
- {{ .Values.dual_stack.POD_CIDR }}
- {{- else if eq .Values.networking.NET_STACK "ipv4" -}}
- {{ .Values.ipv4.POD_CIDR }}
- {{- else if eq .Values.networking.NET_STACK "ipv6" -}}
- {{ .Values.ipv6.POD_CIDR }}
- {{- end }}
- - --default-gateway=
- {{- if eq .Values.networking.NET_STACK "dual_stack" -}}
- {{ .Values.dual_stack.POD_GATEWAY }}
- {{- else if eq .Values.networking.NET_STACK "ipv4" -}}
- {{ .Values.ipv4.POD_GATEWAY }}
- {{- else if eq .Values.networking.NET_STACK "ipv6" -}}
- {{ .Values.ipv6.POD_GATEWAY }}
- {{- end }}
+ - --default-cidr={{ index $cozyConfig.data "ipv4-pod-cidr" }}
+ - --default-gateway={{ index $cozyConfig.data "ipv4-pod-gateway" }}
- --default-gateway-check={{- .Values.func.CHECK_GATEWAY }}
- --default-logical-gateway={{- .Values.func.LOGICAL_GATEWAY }}
- --default-u2o-interconnection={{- .Values.func.U2O_INTERCONNECTION }}
- --default-exclude-ips={{- .Values.networking.EXCLUDE_IPS }}
- --cluster-router={{ .Values.networking.DEFAULT_VPC }}
- --node-switch={{ .Values.networking.NODE_SUBNET }}
- - --node-switch-cidr=
- {{- if eq .Values.networking.NET_STACK "dual_stack" -}}
- {{ .Values.dual_stack.JOIN_CIDR }}
- {{- else if eq .Values.networking.NET_STACK "ipv4" -}}
- {{ .Values.ipv4.JOIN_CIDR }}
- {{- else if eq .Values.networking.NET_STACK "ipv6" -}}
- {{ .Values.ipv6.JOIN_CIDR }}
- {{- end }}
- - --service-cluster-ip-range=
- {{- if eq .Values.networking.NET_STACK "dual_stack" -}}
- {{ .Values.dual_stack.SVC_CIDR }}
- {{- else if eq .Values.networking.NET_STACK "ipv4" -}}
- {{ .Values.ipv4.SVC_CIDR }}
- {{- else if eq .Values.networking.NET_STACK "ipv6" -}}
- {{ .Values.ipv6.SVC_CIDR }}
- {{- end }}
+ - --node-switch-cidr={{ index $cozyConfig.data "ipv4-join-cidr" }}
+ - --service-cluster-ip-range={{ index $cozyConfig.data "ipv4-svc-cidr" }}
+ {{- if .Values.global.logVerbosity }}
+ - --v={{ .Values.global.logVerbosity }}
+ {{- end }}
- --network-type={{- .Values.networking.NETWORK_TYPE }}
- --default-provider-name={{ .Values.networking.vlan.PROVIDER_NAME }}
- --default-interface-name={{- .Values.networking.vlan.VLAN_INTERFACE_NAME }}
diff --git a/packages/system/kubeovn/charts/kube-ovn/values.yaml b/packages/system/kubeovn/charts/kube-ovn/values.yaml
index bfffc4d..b880749 100644
--- a/packages/system/kubeovn/charts/kube-ovn/values.yaml
+++ b/packages/system/kubeovn/charts/kube-ovn/values.yaml
@@ -70,10 +70,6 @@ func:
ENABLE_TPROXY: false
ipv4:
- POD_CIDR: "10.16.0.0/16"
- POD_GATEWAY: "10.16.0.1"
- SVC_CIDR: "10.96.0.0/12"
- JOIN_CIDR: "100.64.0.0/16"
PINGER_EXTERNAL_ADDRESS: "1.1.1.1"
PINGER_EXTERNAL_DOMAIN: "alauda.cn."

View File

@@ -1,14 +0,0 @@
diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml
index 63f4258..dafe1fd 100644
--- a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml
+++ b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml
@@ -112,6 +112,9 @@ spec:
- --secure-serving={{- .Values.func.SECURE_SERVING }}
- --enable-ovn-ipsec={{- .Values.func.ENABLE_OVN_IPSEC }}
- --set-vxlan-tx-off={{- .Values.func.SET_VXLAN_TX_OFF }}
+ {{- with .Values.mtu }}
+ - --mtu={{ . }}
+ {{- end }}
securityContext:
runAsUser: 0
privileged: false

View File

@@ -65,4 +65,4 @@ global:
images:
kubeovn:
repository: kubeovn
tag: v1.14.11@sha256:8e6cf216687b4a80c35fa7c60bb4d511dd6aaaaf19d1ec53321dfef98d343f51
tag: v1.14.25@sha256:d0b29daaf36e81cac0f9fb15d0ea6b1b49f1abba81a14c73b88a2e60ffcc5978

View File

@@ -1,3 +1,3 @@
storageClass: replicated
csiDriver:
image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:d5c836ba33cf5dbed7e6f866784f668f80ffe69179e7c75847b680111984eefb
image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:b42c6af641ee0eadb7e0a42e368021b4759f443cb7b71b7e745a64f0fc8b752e

View File

@@ -27,7 +27,7 @@ spec:
expr: |
max_over_time(
kubevirt_vmi_info{
phase!="Running",
phase!="running",
exported_namespace=~".+",
name=~".+"
}[10m]

View File

@@ -16,12 +16,7 @@ spec:
nodeSelector:
node-role.kubernetes.io/control-plane: ""
tolerations:
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
- operator: Exists
serviceAccountName: lineage-controller-webhook
containers:
- name: lineage-controller-webhook

View File

@@ -1,5 +1,5 @@
lineageControllerWebhook:
image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v0.38.2@sha256:a5c750a0f46e8e25329b3ee2110d5dfb077c73e473195f1ed768d28d6f43902c
image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v0.39.5@sha256:dc5c7c990e468b291390a54f5fb5970097a1ff65ba4be6e95317e3ee7bf4430d
debug: false
localK8sAPIEndpoint:
enabled: true

View File

@@ -1,4 +1,23 @@
export NAME=linstor
export NAMESPACE=cozy-$(NAME)
include ../../../scripts/common-envs.mk
include ../../../scripts/package.mk
LINSTOR_VERSION ?= 1.32.3
image:
docker buildx build images/piraeus-server \
--build-arg LINSTOR_VERSION=$(LINSTOR_VERSION) \
--build-arg K8S_AWAIT_ELECTION_VERSION=v0.4.2 \
--tag $(REGISTRY)/piraeus-server:$(call settag,$(LINSTOR_VERSION)) \
--tag $(REGISTRY)/piraeus-server:$(call settag,$(LINSTOR_VERSION)-$(TAG)) \
--cache-from type=registry,ref=$(REGISTRY)/piraeus-server:latest \
--cache-to type=inline \
--metadata-file images/piraeus-server.json \
$(BUILDX_ARGS)
REPOSITORY="$(REGISTRY)/piraeus-server" \
yq -i '.piraeusServer.image.repository = strenv(REPOSITORY)' values.yaml
TAG="$(call settag,$(LINSTOR_VERSION))@$$(yq e '."containerimage.digest"' images/piraeus-server.json -o json -r)" \
yq -i '.piraeusServer.image.tag = strenv(TAG)' values.yaml
rm -f images/piraeus-server.json

View File

@@ -10,10 +10,125 @@ trap terminate SIGINT SIGQUIT SIGTERM
echo "Starting Linstor per-satellite plunger"
INTERVAL_SEC="${INTERVAL_SEC:-30}"
STALL_ITERS="${STALL_ITERS:-4}"
STATE_FILE="${STATE_FILE:-/run/drbd-sync-watch.state}"
log() { printf '%s %s\n' "$(date -Is)" "$*" >&2; }
drbd_status_json() {
drbdsetup status --json 2>/dev/null || true
}
# Detect DRBD resources where resync is stuck:
# - at least one local device is Inconsistent
# - there is an active SyncTarget peer
# - there are other peers suspended with resync-suspended:dependency
# Output format: "<resource> <sync-peer> <percent-in-sync>"
drbd_stall_candidates() {
jq -r '
.[]?
| . as $r
| select(any($r.devices[]?; ."disk-state" == "Inconsistent"))
| (
[ $r.connections[]?
| . as $c
| $c.peer_devices[]?
| select(."replication-state" == "SyncTarget")
| { peer: $c.name, pct: (."percent-in-sync" // empty) }
] | .[0]?
) as $sync
| select($sync != null and ($sync.pct|tostring) != "")
| select(any($r.connections[]?.peer_devices[]?; ."resync-suspended" == "dependency"))
| "\($r.name) \($sync.peer) \($sync.pct)"
'
}
drbd_stall_load_state() {
[ -f "$STATE_FILE" ] && cat "$STATE_FILE" || true
}
drbd_stall_save_state() {
local tmp="${STATE_FILE}.tmp"
cat >"$tmp"
mv "$tmp" "$STATE_FILE"
}
# Break stalled resync by disconnecting the current SyncTarget peer.
# After reconnect, DRBD will typically pick another eligible peer and continue syncing.
drbd_stall_act() {
local res="$1"
local peer="$2"
local pct="$3"
log "STALL detected: res=$res sync_peer=$peer percent_in_sync=$pct -> disconnect/connect"
drbdadm disconnect "${res}:${peer}" && drbdadm connect "$res" || log "WARN: action failed for ${res}:${peer}"
}
# Track percent-in-sync progress across iterations.
# If progress does not change for STALL_ITERS loops, trigger reconnect.
drbd_fix_stalled_sync() {
local now prev json out
now="$(date +%s)"
prev="$(drbd_stall_load_state)"
json="$(drbd_status_json)"
[ -n "$json" ] || return 0
out="$(printf '%s' "$json" | drbd_stall_candidates)"
local new_state=""
local acts=""
while IFS= read -r line; do
[ -n "$line" ] || continue
set -- $line
local res="$1" peer="$2" pct="$3"
local key="${res} ${peer}"
local prev_line
prev_line="$(printf '%s\n' "$prev" | awk -v k="$key" '$1" "$2==k {print; exit}')"
local cnt last_act prev_pct prev_cnt prev_act
if [ -n "$prev_line" ]; then
set -- $prev_line
prev_pct="$3"
prev_cnt="$4"
prev_act="$5"
if [ "$pct" = "$prev_pct" ]; then
cnt=$((prev_cnt + 1))
else
cnt=1
fi
last_act="$prev_act"
else
cnt=1
last_act=0
fi
if [ "$cnt" -ge "$STALL_ITERS" ]; then
acts="${acts}${res} ${peer} ${pct}"$'\n'
cnt=0
last_act="$now"
fi
new_state="${new_state}${res} ${peer} ${pct} ${cnt} ${last_act}"$'\n'
done <<< "$out"
if [ -n "$acts" ]; then
while IFS= read -r a; do
[ -n "$a" ] || continue
set -- $a
drbd_stall_act "$1" "$2" "$3"
done <<< "$acts"
fi
printf '%s' "$new_state" | drbd_stall_save_state
}
while true; do
# timeout at the start of the loop to give a chance for the fresh linstor-satellite instance to cleanup itself
sleep 30 &
sleep "$INTERVAL_SEC" &
pid=$!
wait $pid
@@ -21,7 +136,7 @@ while true; do
# the `/` path could not be a backing file for a loop device, so it's a good indicator of a stuck loop device
# TODO describe the issue in more detail
# Using the direct /usr/sbin/losetup as the linstor-satellite image has own wrapper in /usr/local
stale_loopbacks=$(/usr/sbin/losetup --json | jq -r '.[][] | select(."back-file" == "/" or ."back-file" == "/ (deleted)").name' )
stale_loopbacks=$(/usr/sbin/losetup --json | jq -r '.[][] | select(."back-file" == "/" or ."back-file" == "/ (deleted)").name')
for stale_device in $stale_loopbacks; do (
echo "Detaching stuck loop device ${stale_device}"
set -x
@@ -39,4 +154,7 @@ while true; do
drbdadm up "${secondary}" || echo "Command failed"
); done
# Detect and fix stalled DRBD resync by switching SyncTarget peer
drbd_fix_stalled_sync || true
done

View File

@@ -0,0 +1,172 @@
ARG DISTRO=bookworm
ARG LINSTOR_VERSION
# ------------------------------------------------------------------------------
# Build linstor-server from source
FROM debian:bookworm AS builder
ARG LINSTOR_VERSION
ARG VERSION=${LINSTOR_VERSION}
ARG DISTRO
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get -y upgrade \
&& apt-get -y install build-essential git default-jdk-headless python3-all debhelper wget unzip && \
wget https://services.gradle.org/distributions/gradle-8.9-bin.zip -O /tmp/gradle.zip && \
unzip -d /opt /tmp/gradle.zip && \
rm /tmp/gradle.zip && \
ln -s /opt/gradle-8.9/bin/gradle /usr/local/bin/gradle
RUN git clone https://github.com/LINBIT/linstor-server.git /linstor-server
WORKDIR /linstor-server
RUN git checkout v${VERSION}
# Apply patches
COPY patches /patches
RUN git apply /patches/*.diff && \
git config user.email "build@cozystack.io" && \
git config user.name "Cozystack Builder" && \
git add -A && \
git commit -m "Apply patches"
# Initialize git submodules
RUN git submodule update --init --recursive || make check-submods
# Pre-download ALL dependencies before make tarball
# This ensures all transitive dependencies are cached, including optional ones like AWS SDK
RUN ./gradlew getProtoc
RUN ./gradlew generateJava
RUN ./gradlew --no-daemon --gradle-user-home .gradlehome downloadDependencies
# Manually create tarball without removing caches
# make tarball removes .gradlehome/caches/[0-9]* which deletes dependencies
# So we'll do the steps manually but keep the caches
RUN make check-submods versioninfo gen-java FORCE=1 VERSION=${VERSION}
RUN make server/jar.deps controller/jar.deps satellite/jar.deps jclcrypto/jar.deps FORCE=1 VERSION=${VERSION}
RUN make .filelist FORCE=1 VERSION=${VERSION} PRESERVE_DEBIAN=1
# Don't remove caches - we need them for offline build
RUN rm -Rf .gradlehome/wrapper .gradlehome/native .gradlehome/.tmp || true
RUN mkdir -p ./libs
RUN make tgz VERSION=${VERSION}
# Extract tarball and build DEB packages from it
RUN mv linstor-server-${VERSION}.tar.gz /linstor-server_${VERSION}.orig.tar.gz \
&& tar -C / -xvf /linstor-server_${VERSION}.orig.tar.gz
WORKDIR /linstor-server-${VERSION}
# Verify .gradlehome is present in extracted tarball
RUN test -d .gradlehome && echo ".gradlehome found in tarball" || (echo ".gradlehome not found in tarball!" && exit 1)
# 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
RUN LD_LIBRARY_PATH='' dpkg-buildpackage -rfakeroot -b -uc
# Copy built .deb packages to a location accessible from final image
# dpkg-buildpackage creates packages in parent directory
RUN mkdir -p /packages-output && \
find .. -maxdepth 1 -name "linstor-*.deb" -exec cp {} /packages-output/ \; && \
test -n "$(ls -A /packages-output)" || (echo "ERROR: No linstor .deb packages found after build." && exit 1)
# ------------------------------------------------------------------------------
# Final image
FROM debian:${DISTRO}
LABEL maintainer="Roland Kammerer <roland.kammerer@linbit.com>"
ARG LINSTOR_VERSION
ARG DISTRO
# Copy built .deb packages from builder stage
# dpkg-buildpackage creates packages in parent directory, we copied them to /packages-output
COPY --from=builder /packages-output/ /packages/
RUN { echo 'APT::Install-Recommends "false";' ; echo 'APT::Install-Suggests "false";' ; } > /etc/apt/apt.conf.d/99_piraeus
RUN --mount=type=cache,target=/var/cache,sharing=private \
--mount=type=cache,target=/var/lib/apt/lists,sharing=private \
--mount=type=tmpfs,target=/var/log \
# Install wget first for downloading keyring
apt-get update && apt-get install -y wget ca-certificates && \
# Enable contrib repos for zfsutils \
. /etc/os-release && \
sed -i -r 's/^Components: (.*)$/Components: \1 contrib/' /etc/apt/sources.list.d/debian.sources && \
echo "deb http://deb.debian.org/debian $VERSION_CODENAME-backports contrib" > /etc/apt/sources.list.d/backports.list && \
wget https://packages.linbit.com/public/linbit-keyring.deb -O /var/cache/linbit-keyring.deb && \
dpkg -i /var/cache/linbit-keyring.deb && \
echo "deb http://packages.linbit.com/public $VERSION_CODENAME misc" > /etc/apt/sources.list.d/linbit.list && \
apt-get update && \
# Install useful utilities and general dependencies
apt-get install -y udev drbd-utils jq net-tools iputils-ping iproute2 dnsutils netcat-traditional sysstat curl util-linux && \
# Install dependencies for optional features \
apt-get install -y \
# cryptsetup: luks layer
cryptsetup \
# e2fsprogs: LINSTOR can create file systems \
e2fsprogs \
# lsscsi: exos layer \
lsscsi \
# lvm2: manage lvm storage pools \
lvm2 \
# multipath-tools: exos layer \
multipath-tools \
# nvme-cli: nvme layer
nvme-cli \
# procps: used by LINSTOR to find orphaned send/receive processes \
procps \
# socat: used with thin-send-recv to send snapshots to another LINSTOR cluster
socat \
# thin-send-recv: used to send/receive snapshots of LVM thin volumes \
thin-send-recv \
# xfsprogs: LINSTOR can create file systems; xfs deps \
xfsprogs \
# zstd: used with thin-send-recv to send snapshots to another LINSTOR cluster \
zstd \
# zfsutils-linux: for zfs storage pools \
zfsutils-linux/$VERSION_CODENAME-backports \
&& \
# remove udev, no need for it in the container \
apt-get remove -y udev && \
# Install linstor packages from built .deb files and linstor-client from repository
apt-get install -y default-jre-headless python3-all python3-natsort linstor-client \
&& ls packages/*.deb >/dev/null && (dpkg -i packages/*.deb || apt-get install -f -y) \
&& rm -rf /packages \
&& sed -i 's/"-Djdk.tls.acknowledgeCloseNotify=true"//g' /usr/share/linstor-server/bin/Controller \
&& apt-get clean
# Log directory need to be group writable. OpenShift assigns random UID and GID, without extra RBAC changes we can only influence the GID.
RUN mkdir /var/log/linstor-controller && \
chown 0:1000 /var/log/linstor-controller && \
chmod -R 0775 /var/log/linstor-controller && \
# Ensure we log to files in containers, otherwise SOS reports won't show any logs at all
sed -i 's#<!-- <appender-ref ref="FILE" /> -->#<appender-ref ref="FILE" />#' /usr/share/linstor-server/lib/conf/logback.xml
RUN lvmconfig --type current --mergedconfig --config 'activation { udev_sync = 0 udev_rules = 0 monitoring = 0 } devices { global_filter = [ "r|^/dev/drbd|" ] obtain_device_list_from_udev = 0}' > /etc/lvm/lvm.conf.new && mv /etc/lvm/lvm.conf.new /etc/lvm/lvm.conf
RUN echo 'global { usage-count no; }' > /etc/drbd.d/global_common.conf
# controller
EXPOSE 3376/tcp 3377/tcp 3370/tcp 3371/tcp
# satellite
EXPOSE 3366/tcp 3367/tcp
RUN wget https://raw.githubusercontent.com/piraeusdatastore/piraeus/refs/heads/master/dockerfiles/piraeus-server/entry.sh -O /usr/bin/piraeus-entry.sh \
&& chmod +x /usr/bin/piraeus-entry.sh
ARG K8S_AWAIT_ELECTION_VERSION=v0.4.2
# TARGETARCH is a docker special variable: https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
ARG TARGETARCH
RUN wget https://github.com/LINBIT/k8s-await-election/releases/download/${K8S_AWAIT_ELECTION_VERSION}/k8s-await-election-${K8S_AWAIT_ELECTION_VERSION}-linux-${TARGETARCH}.tar.gz -O - | tar -xvz -C /usr/bin/
ARG LOSETUP_CONTAINER_VERSION=v1.0.1
RUN wget "https://github.com/LINBIT/losetup-container/releases/download/${LOSETUP_CONTAINER_VERSION}/losetup-container-$(uname -m)-unknown-linux-gnu.tar.gz" -O - | tar -xvz -C /usr/local/sbin && \
printf '#!/bin/sh\nLOSETUP_CONTAINER_ORIGINAL_LOSETUP=%s exec /usr/local/sbin/losetup-container "$@"\n' $(command -v losetup) > /usr/local/sbin/losetup && \
chmod +x /usr/local/sbin/losetup
RUN wget "https://dl.k8s.io/$(wget -O - https://dl.k8s.io/release/stable.txt)/bin/linux/${TARGETARCH}/kubectl" -O /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl
CMD ["startSatellite"]
ENTRYPOINT ["/usr/bin/k8s-await-election", "/usr/bin/piraeus-entry.sh"]

View File

@@ -0,0 +1,14 @@
# LINSTOR Server Patches
Custom patches for piraeus-server (linstor-server) v1.32.3.
- **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** — Prevent duplicate TCP ports after toggle-disk operations
- Upstream: [#476](https://github.com/LINBIT/linstor-server/pull/476)
- **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)

View File

@@ -0,0 +1,48 @@
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");

View File

@@ -0,0 +1,229 @@
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
--- 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
Resource rsc = ctrlApiDataLoader.loadRsc(nodeName, rscName, true);
+ // 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))
{
- 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
+ .<ApiCallRc>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
+ .<ApiCallRc>just(new ApiCallRcImpl())
+ .concatWith(updateAndAdjustDisk(nodeName, rscName, false, toggleIntoTiebreakerRef, context))
+ .concatWith(ctrlRscDfnApiCallHandler.get().updateProps(rsc.getResourceDefinition()));
}
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
+ errorReporter.logInfo(
+ "Toggle Disk cancel on %s/%s - cancelling failed DISK_REMOVE_REQUESTED",
+ nodeNameStr, rscNameStr);
+ unmarkDiskRemoveRequested(rsc);
+ ctrlTransactionHelper.commit();
+ return Flux.<ApiCallRc>just(
+ ApiCallRcImpl.singleApiCallRc(
+ ApiConsts.MODIFIED,
+ "Cancelled disk removal request"
+ )
+ );
+ }
+ // 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
+ .<ApiCallRc>just(new ApiCallRcImpl())
+ .concatWith(updateAndAdjustDisk(nodeName, rscName, true, toggleIntoTiebreakerRef, context))
+ .concatWith(ctrlRscDfnApiCallHandler.get().updateProps(rsc.getResourceDefinition()));
}
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<Resource> 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
+ .<ApiCallRc>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
}
}
+ private void unmarkDiskAddRequested(Resource rsc)
+ {
+ try
+ {
+ rsc.getStateFlags().disableFlags(apiCtx, Resource.Flags.DISK_ADD_REQUESTED);
+ }
+ catch (AccessDeniedException | DatabaseException exc)
+ {
+ throw new ImplementationError(exc);
+ }
+ }
+
+ private void unmarkDiskRemoveRequested(Resource rsc)
+ {
+ try
+ {
+ rsc.getStateFlags().disableFlags(apiCtx, Resource.Flags.DISK_REMOVE_REQUESTED);
+ }
+ catch (AccessDeniedException | DatabaseException exc)
+ {
+ throw new ImplementationError(exc);
+ }
+ }
+
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<Resource> layerDataRef)
+ {
+ boolean hasNonDiskless = false;
+ if (layerDataRef != null)
+ {
+ if (layerDataRef.getLayerKind() == DeviceLayerKind.STORAGE)
+ {
+ for (VlmProviderObject<Resource> vlmData : layerDataRef.getVlmLayerObjects().values())
+ {
+ if (vlmData.getProviderKind() != DeviceProviderKind.DISKLESS)
+ {
+ hasNonDiskless = true;
+ break;
+ }
+ }
+ }
+ if (!hasNonDiskless)
+ {
+ for (AbsRscLayerObject<Resource> 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);

View File

@@ -0,0 +1,87 @@
From 1250abe99d64a0501795e37d3b6af62410002239 Mon Sep 17 00:00:00 2001
From: Andrei Kvapil <kvapss@gmail.com>
Date: Mon, 12 Jan 2026 13:44:46 +0100
Subject: [PATCH] fix(drbd): prevent duplicate TCP ports after toggle-disk
operations
Remove redundant ensureStackDataExists() call with empty payload from
resetStoragePools() method that was causing TCP port conflicts after
toggle-disk operations.
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.
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.
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
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().
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
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
---
.../linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java | 2 --
1 file changed, 2 deletions(-)
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());
}
-
- ensureStackDataExists(rscRef, null, new LayerPayload());
}
catch (AccessDeniedException exc)
{
--
2.39.5 (Apple Git-154)

View File

@@ -0,0 +1,63 @@
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<Flags> 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<DrbdVlmData<Resource>> 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<Resource> drbdVlmData : checkMetaData)
@@ -988,8 +995,10 @@ public class DrbdLayer implements DeviceLayer
{
List<DrbdVlmData<Resource>> 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())
{

View File

@@ -0,0 +1,260 @@
From 6a556821b9a0996d34389a27b941694ce810a44c Mon Sep 17 00:00:00 2001
From: Andrei Kvapil <kvapss@gmail.com>
Date: Mon, 12 Jan 2026 14:26:57 +0100
Subject: [PATCH 1/3] Fix: Skip DRBD adjust/res file regeneration when child
layer device is inaccessible
When deleting encrypted (LUKS) resources, the LUKS layer may close its device
before the DRBD layer attempts to adjust the resource or regenerate the res
file. This causes 'Failed to adjust DRBD resource' errors.
This fix adds checks before regenerateResFile() and drbdUtils.adjust()
to verify that child layer devices are accessible. If a child device doesn't
exist or is not accessible (e.g., LUKS device is closed during resource
deletion), these operations are skipped and the adjustRequired flag is cleared,
allowing resource deletion to proceed successfully.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
---
.../linbit/linstor/layer/drbd/DrbdLayer.java | 72 ++++++++++++++++---
1 file changed, 61 insertions(+), 11 deletions(-)
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..871d830d1 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<Resource> dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA);
+ if (dataChild != null)
+ {
+ for (DrbdVlmData<Resource> drbdVlmData : drbdRscData.getVlmLayerObjects().values())
+ {
+ VlmProviderObject<Resource> 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<Resource> drbdVlmData : createMetaData)
@@ -766,19 +788,47 @@ 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;
+ if (!skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx))
{
- drbdUtils.adjust(
- drbdRscData,
- false,
- skipDisk,
- false
- );
+ AbsRscLayerObject<Resource> dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA);
+ if (dataChild != null)
+ {
+ for (DrbdVlmData<Resource> drbdVlmData : drbdRscData.getVlmLayerObjects().values())
+ {
+ VlmProviderObject<Resource> 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);
}
}
--
2.39.5 (Apple Git-154)
From afe51ea674c4a350c27d1f2cacfecf6fe42b8a7a Mon Sep 17 00:00:00 2001
From: Andrei Kvapil <kvapss@gmail.com>
Date: Mon, 12 Jan 2026 14:27:52 +0100
Subject: [PATCH 2/3] fix(satellite): skip lsblk when device path doesn't
physically exist
Add physical device path existence check before calling lsblk in updateDiscGran().
This prevents race condition when drbdadm adjust temporarily brings devices down/up
and the kernel hasn't created the device node yet.
Issue: After satellite restart with patched code, some DRBD resources ended up in
Unknown state because:
1. drbdadm adjust successfully completes (brings devices up)
2. updateDiscGran() immediately tries to check discard granularity
3. /dev/drbd* device node doesn't exist yet (kernel hasn't created it)
4. lsblk fails with exit code 32 "not a block device"
5. StorageException interrupts DeviceManager cycle
6. DRBD device remains in incomplete state
Solution: Check Files.exists(devicePath) before calling lsblk. If device doesn't
exist yet, skip the check - it will be retried in the next DeviceManager cycle
when the device node is available.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
---
.../linbit/linstor/core/devmgr/DeviceHandlerImpl.java | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
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..1c13cfc9d 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
@@ -68,6 +68,8 @@ import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -1645,8 +1647,11 @@ public class DeviceHandlerImpl implements DeviceHandler
private void updateDiscGran(VlmProviderObject<Resource> vlmData) throws DatabaseException, StorageException
{
- String devicePath = vlmData.getDevicePath();
- if (devicePath != null && vlmData.exists())
+ @Nullable String devicePath = vlmData.getDevicePath();
+ // 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)
{
--
2.39.5 (Apple Git-154)
From de1f22e7c008c5479f85a3b1ebdf8461944210f4 Mon Sep 17 00:00:00 2001
From: Andrei Kvapil <kvapss@gmail.com>
Date: Mon, 12 Jan 2026 14:28:23 +0100
Subject: [PATCH 3/3] fix(drbd): only check child devices when disk access is
actually needed
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The previous implementation blocked `drbdadm adjust` whenever child
device paths were unavailable, even for operations that don't require
disk access (like network reconnect from StandAlone to Connected).
After node reboot, DRBD resources often remain in StandAlone state
because:
1. updateResourceToCurrentDrbdState() correctly detects StandAlone
and sets adjustRequired=true
2. However, canAdjust check fails because child volumes may have
devicePath=null (due to INACTIVE flag, cloning state, or
initialization race)
3. adjust is skipped → adjustRequired=false → resources stay StandAlone
Root cause: The canAdjust check was added to protect LUKS deletion
scenarios but was applied to ALL cases, including network reconnect
which doesn't need disk access.
Fix: Check child device accessibility only when disk access is actually
required (volume creation, resize, metadata operations). Network
reconnect operations (StandAlone → Connected) now proceed without
checking child devices.
This ensures automatic DRBD reconnection after reboot while preserving
protection for LUKS deletion scenarios.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
---
.../linbit/linstor/layer/drbd/DrbdLayer.java | 27 ++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
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 871d830d1..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
@@ -792,7 +792,32 @@ public class DrbdLayer implements DeviceLayer
// This is important for encrypted resources (LUKS) where the device
// might be closed during deletion
boolean canAdjust = true;
- if (!skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx))
+
+ // 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<Resource> drbdVlmData : drbdRscData.getVlmLayerObjects().values())
+ {
+ Volume vlm = (Volume) drbdVlmData.getVolume();
+ StateFlags<Volume.Flags> 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<Resource> dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA);
if (dataChild != null)
--
2.39.5 (Apple Git-154)

View File

@@ -1,24 +0,0 @@
{{- define "cozy.linstor.version" -}}
{{- $piraeusConfigMap := lookup "v1" "ConfigMap" "cozy-linstor" "piraeus-operator-image-config"}}
{{- if not $piraeusConfigMap }}
{{- fail "Piraeus controller is not yet installed, ConfigMap cozy-linstor/piraeus-operator-image-config is missing" }}
{{- end }}
{{- $piraeusImagesConfig := $piraeusConfigMap | dig "data" "0_piraeus_datastore_images.yaml" nil | required "No image config" | fromYaml }}
base: {{ $piraeusImagesConfig.base | required "No image base in piraeus config" }}
controller:
image: {{ $piraeusImagesConfig | dig "components" "linstor-controller" "image" nil | required "No controller image" }}
tag: {{ $piraeusImagesConfig | dig "components" "linstor-controller" "tag" nil | required "No controller tag" }}
satellite:
image: {{ $piraeusImagesConfig | dig "components" "linstor-satellite" "image" nil | required "No satellite image" }}
tag: {{ $piraeusImagesConfig | dig "components" "linstor-satellite" "tag" nil | required "No satellite tag" }}
{{- end -}}
{{- define "cozy.linstor.version.controller" -}}
{{- $version := (include "cozy.linstor.version" .) | fromYaml }}
{{- printf "%s/%s:%s" $version.base $version.controller.image $version.controller.tag }}
{{- end -}}
{{- define "cozy.linstor.version.satellite" -}}
{{- $version := (include "cozy.linstor.version" .) | fromYaml }}
{{- printf "%s/%s:%s" $version.base $version.satellite.image $version.satellite.tag }}
{{- end -}}

View File

@@ -27,8 +27,10 @@ spec:
podTemplate:
spec:
containers:
- name: linstor-controller
image: {{ .Values.piraeusServer.image.repository }}:{{ .Values.piraeusServer.image.tag }}
- name: plunger
image: {{ include "cozy.linstor.version.controller" . }}
image: {{ .Values.piraeusServer.image.repository }}:{{ .Values.piraeusServer.image.tag }}
command:
- "/scripts/plunger-controller.sh"
securityContext:

View File

@@ -13,6 +13,7 @@ spec:
hostNetwork: true
containers:
- name: linstor-satellite
image: {{ .Values.piraeusServer.image.repository }}:{{ .Values.piraeusServer.image.tag }}
securityContext:
# real-world installations need some debugging from time to time
readOnlyRootFilesystem: false

View File

@@ -11,7 +11,7 @@ spec:
spec:
containers:
- name: plunger
image: {{ include "cozy.linstor.version.satellite" . }}
image: {{ .Values.piraeusServer.image.repository }}:{{ .Values.piraeusServer.image.tag }}
command:
- "/scripts/plunger-satellite.sh"
securityContext:
@@ -48,7 +48,7 @@ spec:
name: script-volume
readOnly: true
- name: drbd-logger
image: {{ include "cozy.linstor.version.satellite" . }}
image: {{ .Values.piraeusServer.image.repository }}:{{ .Values.piraeusServer.image.tag }}
command:
- "/scripts/plunger-drbd-logger.sh"
securityContext:

View File

@@ -1 +1,4 @@
piraeusServer:
image:
repository: ghcr.io/cozystack/cozystack/piraeus-server
tag: 1.32.3@sha256:1138c8dc0a117360ef70e2e2ab97bc2696419b63f46358f7668c7e01a96c419b

View File

@@ -4,8 +4,8 @@ metallb:
controller:
image:
repository: ghcr.io/cozystack/cozystack/metallb-controller
tag: v0.15.2@sha256:0e9080234fc8eedab78ad2831fb38df375c383e901a752d72b353c8d13b9605f
tag: v0.15.2@sha256:623ce74b5802bff6e29f29478ccab29ce4162a64148be006c69e16cc3207e289
speaker:
image:
repository: ghcr.io/cozystack/cozystack/metallb-speaker
tag: v0.15.2@sha256:e14d4c328c3ab91a6eadfeea90da96388503492d165e7e8582f291b1872e53b2
tag: v0.15.2@sha256:f264058afd9228452a260ab9c9dd1859404745627a2a38c2ba4671e27f3b3bb2

View File

@@ -2,7 +2,7 @@
apiVersion: v1
kind: Service
metadata:
name: coredns
name: coredns-metrics
namespace: kube-system
labels:
app: coredns
@@ -19,7 +19,7 @@ spec:
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMServiceScrape
metadata:
name: coredns
name: coredns-metrics
namespace: cozy-monitoring
spec:
selector:

View File

@@ -162,7 +162,6 @@ spec:
memory: "100Mi"
limits:
cpu: "100m"
memory: "300Mi"
securityContext:
privileged: true
terminationMessagePolicy: FallbackToLogsOnError

View File

@@ -1,3 +1,3 @@
objectstorage:
controller:
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.38.2@sha256:7d37495cce46d30d4613ecfacaa7b7f140e7ea8f3dbcc3e8c976e271de6cc71b"
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.39.5@sha256:e8f605a5ec4b801dfaf49aa7bce8d7a1ce28df578e23a0b859571f60f98efe8c"

View File

@@ -3,8 +3,8 @@ name: piraeus
description: |
The Piraeus Operator manages software defined storage clusters using LINSTOR in Kubernetes.
type: application
version: 2.10.1
appVersion: "v2.10.1"
version: 2.10.2
appVersion: "v2.10.2"
maintainers:
- name: Piraeus Datastore
url: https://piraeus.io

View File

@@ -23,10 +23,10 @@ data:
tag: v1.32.3
image: piraeus-server
linstor-csi:
tag: v1.10.2
tag: v1.10.3
image: piraeus-csi
nfs-server:
tag: v1.10.2
tag: v1.10.3
image: piraeus-csi-nfs-server
drbd-reactor:
tag: v1.10.0
@@ -44,7 +44,7 @@ data:
tag: v1.3.0
image: linstor-affinity-controller
drbd-module-loader:
tag: v9.2.15
tag: v9.2.16
# The special "match" attribute is used to select an image based on the node's reported OS.
# The operator will first check the k8s node's ".status.nodeInfo.osImage" field, and compare it against the list
# here. If one matches, that specific image name will be used instead of the fallback image.
@@ -99,7 +99,7 @@ data:
tag: v2.17.0
image: livenessprobe
csi-provisioner:
tag: v6.0.0
tag: v6.1.0
image: csi-provisioner
csi-snapshotter:
tag: v8.4.0

View File

@@ -993,6 +993,24 @@ spec:
- Retain
- Delete
type: string
evacuationStrategy:
description: EvacuationStrategy configures the evacuation of volumes
from a Satellite when DeletionPolicy "Evacuate" is used.
nullable: true
properties:
attachedVolumeReattachTimeout:
default: 5m
description: |-
AttachedVolumeReattachTimeout configures how long evacuation waits for attached volumes to reattach on
different nodes. Setting this to 0 disable this evacuation step.
type: string
unattachedVolumeAttachTimeout:
default: 5m
description: |-
UnattachedVolumeAttachTimeout configures how long evacuation waits for unattached volumes to attach on
different nodes. Setting this to 0 disable this evacuation step.
type: string
type: object
internalTLS:
description: |-
InternalTLS configures secure communication for the LINSTOR Satellite.
@@ -1683,6 +1701,23 @@ spec:
- Retain
- Delete
type: string
evacuationStrategy:
description: EvacuationStrategy configures the evacuation of volumes
from a Satellite when DeletionPolicy "Evacuate" is used.
properties:
attachedVolumeReattachTimeout:
default: 5m
description: |-
AttachedVolumeReattachTimeout configures how long evacuation waits for attached volumes to reattach on
different nodes. Setting this to 0 disable this evacuation step.
type: string
unattachedVolumeAttachTimeout:
default: 5m
description: |-
UnattachedVolumeAttachTimeout configures how long evacuation waits for unattached volumes to attach on
different nodes. Setting this to 0 disable this evacuation step.
type: string
type: object
internalTLS:
description: |-
InternalTLS configures secure communication for the LINSTOR Satellite.

Some files were not shown because too many files have changed in this diff Show More