3247 Commits
Author SHA1 Message Date
myasnikovdaniil 9e007c3982 chore(release): cut v1.3.4 (#2674)
This PR prepares the release `v1.3.4`.
v1.3.4
2026-05-19 15:50:15 +05:00
cozystack-ci[bot] e72d6fcb26 Prepare release v1.3.4
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com>
2026-05-19 10:45:20 +00:00
myasnikovdaniil 9182c01eb5 fix(platform,dashboard): wire OpenSearch into PaaS bundle and form overrides (#2649)
## What this PR does

This is the release-1.3 counterpart of #2648.

OpenSearch has shipped as a complete package set in 1.3 —
`packages/apps/opensearch/`, `packages/system/opensearch-operator/`,
`packages/system/opensearch-rd/`, plus the `cozystack.opensearch-*`
PackageSources — but the PaaS bundle template never references the
OpenSearch PackageSources. On any cluster with
`bundles.paas.enabled=true`:

- `opensearch-operator` is not deployed → no `OpenSearchCluster` CRD on
cluster
- `opensearch-rd` release is not deployed → no
`ApplicationDefinition/opensearch` → the dashboard catalog has no
OpenSearch entry and tenants cannot create
`opensearches.apps.cozystack.io`

Two changes in this PR:

1. **`packages/core/platform/templates/bundles/paas.yaml`** — add
`cozystack.opensearch-operator` and `cozystack.opensearch-application`
to the bundle, matching every other DB application.
2. **`internal/controller/dashboard/customformsoverride.go` + test** —
add `OpenSearch` to the StorageClass listInput override case so the
create-form `storageClass` field renders as a dropdown in the legacy
openapi-ui that ships with 1.3. (The new cozystack-ui in 1.4 derives
this widget client-side from the schema, so the form-override change is
not needed on main — see #2648.)

### Release note
```release-note
Fix: OpenSearch is now installed by the PaaS bundle (`bundles.paas.enabled=true`); previously the operator and ApplicationDefinition were defined but never referenced from the bundle, so OpenSearch did not appear in the dashboard catalog. The StorageClass field in the OpenSearch create form now renders as a dropdown.
```
api/apps/v1alpha1/v1.3.4
2026-05-19 15:27:45 +05:00
myasnikovdaniil d71a2e92cc [Backport release-1.3] fix(harbor): drive bucket-secret.yaml from values, gate HR on BucketInfo (#2673)
# Description
Backport of #2528 to `release-1.3`.
2026-05-18 20:22:33 +05:00
Myasnikov Daniil 4c5319c426 test(harbor): add helm-unittest coverage for bucket-secret guards
`hack/helm-unit-tests.sh` auto-discovers any package whose Makefile has
a `test` target. `packages/system/harbor` had none, so the conditional
rendering of `bucket-secret.yaml` (the path this PR actively reshapes)
was un-asserted in CI and the bucket-secret guard would not be pinned
against future refactors.

Add the canonical `test: helm unittest .` target, matching every other
`packages/system/*` package that ships a `tests/` directory
(linstor-scheduler, linstor-gui, cozystack-scheduler, nfs-driver).

Add `tests/bucket_secret_test.yaml` with seven cases covering: spec
absent, spec empty, secretS3 absent, secretS3 empty, secretS3 partial
(only one inner field), bucketName missing, and fully populated. The
first six assert no Secret is rendered; the last asserts the four
expected `stringData` entries plus the static region. 7/7 pass locally
(`bash hack/helm-unit-tests.sh`).

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 4ecb8fb0d6)
2026-05-18 15:15:17 +00:00
Myasnikov Daniil 6528c4221c fix(harbor): guard registry-s3 render against partial BucketInfo
The COSI BucketAccess controller is expected to populate `BucketInfo`
atomically, but the prior guard (`with .Values.spec` / `with .secretS3`)
only short-circuits on missing or empty inputs. A partially populated
`secretS3` would still emit the registry-s3 Secret with empty
`accessSecretKey`/`endpoint` values, and a missing `bucketName` was
not guarded at all — silently breaking Harbor's S3 storage backend.

Tighten the inner guard to require all four required fields
(accessKeyID, accessSecretKey, endpoint, bucketName) before the Secret
is emitted, so partial input falls into the no-render path alongside
unset/empty. The body now uses dot-access on `.secretS3` for
consistency with the new gate. Behaviour for fully populated input is
unchanged.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 5fb337de19)
2026-05-18 15:15:17 +00:00
Myasnikov Daniil a304e145ad fix(harbor): merge BucketInfo at values root, drop targetPath
The previous fix (a21c18f9) sourced BucketInfo via Flux valuesFrom with
`targetPath: bucket.bucketInfo`. Flux runs values with `targetPath`
through Helm's `strvals.ParseInto`, which splits the value on commas as
list separators. The COSI BucketInfo JSON is comma-rich, so values
resolution bailed:

  could not resolve Secret chart values reference
  'tenant-X/harbor-X-registry-bucket' with key 'BucketInfo':
  key "\"spec\":{\"bucketName\":\"bucket-1785...\""
  has no value (cannot end with ,)

Drop `targetPath`. With only `valuesKey: BucketInfo`, helm-controller
unmarshals the value as YAML and merges at the chart's values root, so
JSON commas stay nested instead of being split. The system chart's
bucket-secret.yaml now reads `.Values.spec.bucketName` /
`.Values.spec.secretS3.{accessKeyID,accessSecretKey,endpoint}`,
guarded by nested `with` blocks so the registry-s3 Secret renders only
when secretS3 has been populated.

Gating semantics from the previous fix are preserved: with the default
`optional: false`, helm-controller still refuses to compose values
until the COSI BucketAccess controller writes `BucketInfo` into the
Secret, and the value's content still drives the HR config-digest.

Verified via `helm template` against the system chart with three
scenarios: missing `spec` and `spec` without `secretS3` render
nothing; full BucketInfo renders all five S3 env keys correctly.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Assisted-By: Claude <noreply@anthropic.com>
(cherry picked from commit 2555a9ac08)
2026-05-18 15:15:17 +00:00
Myasnikov Daniil 9179dbd940 fix(harbor): drive bucket-secret.yaml from values, gate HR on BucketInfo
Two bugs caused the harbor E2E to fail (CI #25049445125):

1. cozy-harbor's bucket-secret.yaml called `index $existingSecret.data
   "BucketInfo"` unconditionally. During first-time install the COSI
   BucketAccess controller may create the credentials Secret as a
   placeholder before populating it, so `.data` is nil and the chart
   render crashes with `index of untyped nil`.

2. The downstream `<release>-system` HelmRelease starts reconciling
   immediately, in parallel with bucket provisioning, hitting bug #1
   on every retry within the test's 5-minute window.

The previous shape relied on `lookup` to read the Secret at render
time. helm-controller's upgrade trigger is digest-based over composed
values + chart artifact, so a `lookup` returning new data on a later
reconcile is not enough on its own to force an upgrade — the rendered
output may diverge but the digest does not.

Switch to a values-driven shape:

- `bucket-secret.yaml` now reads `.Values.bucket.bucketInfo` (a JSON
  string) and uses `dig` for safe access; if the value is empty or the
  expected nested fields are missing, no `*-registry-s3` Secret is
  rendered.
- The `<release>-system` HR sources `BucketInfo` via `valuesFrom` with
  `targetPath: bucket.bucketInfo`. With the default `optional: false`,
  helm-controller will refuse to compose values until the key exists,
  which both gates initial reconciliation on the BucketAccess Secret
  being populated and forces a config-digest change (and thus a helm
  upgrade) when its contents change.
- `bucket.secretName` is removed from the system chart's values and
  from the apps chart's `values:` block; the only consumer (the
  `lookup` call) is gone.

Verified four scenarios via `helm template` against the system chart:
unset bucketInfo, empty string, empty JSON object `{}`, and a
fully-populated BucketInfo — only the last renders the registry-s3
Secret; the others render nothing without erroring.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 8daf495286)
2026-05-18 15:15:17 +00:00
Myasnikov Daniil fc5e6e17dc fix(platform,dashboard): wire OpenSearch into PaaS bundle and form overrides
The opensearch chart, operator chart, ApplicationDefinition and
PackageSources have shipped in the repo since the original OpenSearch
series, but the PaaS bundle template never references the
opensearch-operator / opensearch-application PackageSources. On isp-*
variants with bundles.paas.enabled=true, neither the operator nor the
ApplicationDefinition is ever installed, so the dashboard catalog has
no OpenSearch entry and tenants cannot create
opensearches.apps.cozystack.io resources.

This patch:
  - adds opensearch-operator and opensearch-application to the PaaS
    bundle template, alongside every other DB app
  - adds OpenSearch to the StorageClass listInput override case in
    customformsoverride.go (with matching test entry), so the form's
    storageClass field renders as a StorageClass dropdown in the
    release-1.3 (legacy openapi-ui) dashboard, matching every other
    DB app

Note: only the bundle half is needed on main (release-1.4) because the
new cozystack-ui derives the StorageClass widget client-side from the
schema and ignores CustomFormsOverride; this combined patch targets
release-1.3 directly.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-05-14 10:33:35 +05:00
myasnikovdaniil 705a221096 chore(release): cut v1.3.3 (#2593)
This PR prepares the release `v1.3.3`.
v1.3.3
2026-05-07 16:37:39 +05:00
cozystack-ci[bot] 7cc761a9a3 Prepare release v1.3.3
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com>
2026-05-07 10:30:21 +00:00
myasnikovdaniil 40055fdfa3 [Backport release-1.3] fix(kafka): create LoadBalancer service when external flag is enabled (#2592)
# Description
Backport of #2578 to `release-1.3`.
api/apps/v1alpha1/v1.3.3
2026-05-07 15:17:19 +05:00
IvanHunters 48df89a7a4 fix(kafka): create LoadBalancer service when external flag is enabled
When `external: true` was set, the Kafka chart relied on Strimzi to
create a LoadBalancer service via listener configuration, but the
listener was unconditionally defined (as `type: internal` when disabled).
Additionally, the dashboard RBAC did not include the Strimzi-managed
external bootstrap service, making it invisible to tenant users.

- Make the external listener conditional: only add it to the Kafka CR
  when `external: true` is set
- Add `<release>-kafka-external-bootstrap` to dashboard RBAC rules
  when `external: true`, so the LB service is accessible from the
  tenant dashboard
- Add helm-unittest tests for both kafka listeners and dashboard RBAC
  covering the external/internal paths

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
(cherry picked from commit 418e3da527)
2026-05-07 10:16:15 +00:00
myasnikovdaniil 8888f6b0a8 [Backport release-1.3] fix(mongodb): expose replica-set members per pod for external access (#2514) (#2590)
# Description
Backport of #2522 to `release-1.3`.
2026-05-07 13:55:37 +05:00
Myasnikov Daniil 416989b24d fix(mongodb): expose replica-set members per pod for external access (#2514)
A single LoadBalancer selecting all replset members round-robined writes
across primary and secondaries, so ~⅔ of writes failed with "not primary".
Drivers couldn't auto-recover either: `isMaster` returned in-cluster DNS
names that don't resolve from outside.

Switch to the Percona operator's native `replsets[].expose` so it creates
one LoadBalancer per pod and rewrites `rs.conf` with the external IPs;
the driver then discovers the primary correctly via the standard replica
set connection string. The manual `external-svc.yaml` is now scoped to
sharded mode (mongos front-end) only.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 8358c04ce9)
2026-05-07 08:55:22 +00:00
myasnikovdaniil 1005e12d02 [Backport release-1.3] feat(postgres): add serverName parameter for backup recovery (#2362) (#2588)
## What this PR does

Backport of #2362 to release-1.3.

Adds optional `bootstrap.serverName` field to support backup recovery
when the Barman server name in `backup.info` differs from the Kubernetes
cluster name.

This fixes "no target backup found" errors during CloudNativePG recovery
operations.

### Release note

```release-note
feat(postgres): add optional bootstrap.serverName parameter to fix recovery failures when the original cluster's Barman server name differs from its Kubernetes resource name
```
2026-05-07 13:52:37 +05:00
myasnikovdaniil f93a1e315e [Backport release-1.3] fix(kafka): bump default resourcesPreset to medium (#2589)
# Description
Backport of #2537 to `release-1.3`.
2026-05-07 13:11:47 +05:00
Ivan Okhotnikov 10d0dd6455 fix(kafka): bump default resourcesPreset to medium
The `small` preset (512Mi) is not sufficient for the Strimzi 0.45 /
Kafka 3.9 image baseline. With the default Strimzi heap formula
(Kafka 50%, ZooKeeper 75% of container memory limit), the remaining
non-heap budget (Metaspace, code cache, direct buffers, JMX
javaagent, embedded Jetty admin server, KRaft migration endpoints,
thread stacks) consistently overflows the 512Mi cgroup limit at
startup, leading to OOMKilled (exit 137) and CrashLoopBackOff for
both Kafka and ZooKeeper pods.

Bump the default `resourcesPreset` for kafka and zookeeper from
`small` (512Mi) to `medium` (1Gi). The `small` preset stays in the
enum so existing deployments that pin it are unaffected; only the
default changes.

Signed-off-by: Ivan Okhotnikov <ivan.okhotnikov@aenix.io>
(cherry picked from commit c2884eac17)
2026-05-07 08:11:13 +00:00
IvanHunters 1c584e5e85 docs(postgres): clarify bootstrap field descriptions
Update oldName and serverName field descriptions based on code review
feedback to avoid confusion about their actual roles:

- oldName: Remove misleading "(matches serverName in backup.info)"
  text. This field represents the Kubernetes cluster resource name,
  not the Barman server name.

- serverName: Provide clearer explanation that it's the S3 path prefix
  (barmanObjectStore.serverName) used by the original cluster, and should
  only be set when it differs from the Kubernetes resource name.

Updated in:
- values.yaml (source of truth for field documentation)
- types.go (Go API type comments)
- values.schema.json (JSON schema for validation)
- postgres.yaml (CRD with embedded OpenAPI schema)

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-05-07 10:50:43 +03:00
IvanHunters e3033f6aef feat(postgres): add serverName parameter for backup recovery
Add serverName field to bootstrap configuration to explicitly specify
Barman server name from backup.info. This fixes "no target backup found"
errors when server_name in backup.info differs from Kubernetes cluster name.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-05-07 10:50:43 +03:00
myasnikovdaniil 235da6827d Release v1.3.2 (#2549)
This PR prepares the release `v1.3.2`.
v1.3.2
2026-05-07 12:15:13 +05:00
cozystack-ci[bot] 4232fc4b5c Prepare release v1.3.2
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com>
2026-05-07 01:39:27 +00:00
myasnikovdaniil 8d4cd88fc0 [Backport release-1.3] fix(linstor): plunger demotes idle-Primary DRBD resources (#2580)
# Description
Backport of #2539 to `release-1.3`.
api/apps/v1alpha1/v1.3.2
2026-05-06 16:01:07 +05:00
myasnikovdaniil d07ced441a [Backport release-1.3] fix(linstor): retry drbdadm secondary after mkfs on transient EBUSY (#2579)
# Description
Backport of #2538 to `release-1.3`.
2026-05-06 15:54:18 +05:00
Andrei Kvapil 44f9e49556 fix(linstor): plunger demotes idle-Primary DRBD resources
DRBD auto-promotes a resource to Primary during the provisioning path
(e.g. mkfs.ext4 against a freshly created replica), but auto-demote
sometimes fails to fire on close. The resource then stays Primary on
a node that has no consumer, and CSI on the legitimate target node
fails NodePublishVolume with "failed to set source device readwrite".
The only recovery is an explicit drbdadm secondary on the stuck node.

Add a per-satellite plunger handler that detects local Primaries
where every device has open == false and demotes them. Both the
detection and the action are local to the satellite, so no peer
coordination is needed. open:no makes drbdadm secondary safe; if a
real consumer is about to arrive, auto-promote will redo it.

Upstream tracker: piraeusdatastore/piraeus-operator#942

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit a6ac0485a8)
2026-05-06 09:50:18 +00:00
IvanHunters 972e0701d1 fix(linstor): retry drbdadm secondary after mkfs on transient EBUSY
LINSTOR satellite aborts resource initialization with "Device is held open
by someone" (errno -12) when an external probe (Talos block-controller,
udev, multipathd) opens the freshly-promoted DRBD device just before the
post-mkfs demotion. The controller view then stays in SyncTarget /
Inconsistent / DELETING indefinitely, blocking cleanup of orphan PVs in
Released phase.

Add a retry wrapper secondaryAfterMkfs() with exponential backoff
(7 attempts, 250ms -> 2s) that triggers only on this specific error
message. DrbdPrimary.close() now uses the wrapper so the retry only
applies to the post-mkfs demotion path; the existing public secondary()
contract is untouched.

Related: LINBIT/linstor-server#268, LINBIT/drbd#74
Signed-off-by: IvanHunters <ivan.okhotnikov@aenix.io>
(cherry picked from commit 90fbb16f88)
2026-05-06 09:48:39 +00:00
myasnikovdaniil 313a1ac16f [Backport release-1.3] fix(seaweedfs): pass filer.replicas and s3.replicas to HelmRelease (#2551)
# Description
Backport of #2533 to `release-1.3`.
2026-05-06 10:31:10 +05:00
myasnikovdaniil 6ca7d98794 [Backport to release 1.3] fix(ci): generate changelog from release tag, not from main (#2574)
Backport #2530 to release-1.3
2026-05-06 10:30:49 +05:00
myasnikovdaniil 0380b40be4 [Backport release-1.3] fix(ci): pass app token to Publish draft release step (#2572)
# Description
Backport of #2532 to `release-1.3`.
2026-05-06 10:30:18 +05:00
Myasnikov Daniil 5d1300c037 docs(agents,ci): finish "tag-not-HEAD" sweep and fix changelog-exists gate
- tags.yaml: the check_changelog step now looks at origin/main rather than
  the working tree. After Round 2 we check out the release tag, which never
  has the changelog file (the PR opened by this job merges to main after
  the tag is cut), so the previous `[ -f ]` test always returned false and
  the gate was effectively dead on reruns.
- changelog.md: Step 6's `RELEASE_END=$(git log -1 --format=%ai HEAD)`
  (lines 179, 222) was missed in the previous "never HEAD" sweep — now
  uses v<new_version>, matching Step 5.
- changelog.md: Step 2 ("on main branch") was incompatible with the CI flow
  which now runs detached at the release tag. Relaxed it to list both
  configurations (interactive on main; CI detached at v<version>), while
  the "Scope and boundaries" section continues to forbid the agent from
  switching branches.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 39605f213f)
2026-05-06 10:25:59 +05:00
Myasnikov Daniil e50affa58d docs(agents): fix new-contributor detection and tags.yaml/doc drift
- tags.yaml: drop "(or HEAD)" from the changelog prompt; the doc says
  "never HEAD" and the workflow now matches.
- docs/agents/changelog.md: exclude the current release file from the
  "previous contributors" set used for first-time-contributor detection.
  The previous globbing on `docs/changelogs/v*.md` swept up the file
  being generated, so `comm -23` would never report any newcomers.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit ce63bfcdb0)
2026-05-06 10:25:59 +05:00
Myasnikov Daniil dbf6168083 docs(agents): address changelog rule review feedback
- Drop the "orphan backport, drop entry" rule: a backport whose original is on
  main but hasn't shipped in a stable tag is the first delivery and must stay
  in the changelog.
- Add `v` prefix to `<previous_version>`/`<new_version>` placeholders in all
  four `git log` invocations for consistency with the rest of the doc.
- Apply the bot/CI account filter to the new-contributors detection (and the
  standalone previous-contributors extraction) so bot accounts can no longer
  leak into the human Contributors list.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 0f9198d510)
2026-05-06 10:25:59 +05:00
Myasnikov Daniil 7a9b451a71 fix(ci): generate changelog from the release tag, not from main
The generate-changelog job in .github/workflows/tags.yaml previously
checked out main and ran the AI agent there. The agent followed
docs/agents/changelog.md, which instructed it to compute the release
range with `git log <previous_version>..HEAD`. For minor releases
(vX.Y.0) cut from main this happens to give the right answer; for
patch releases (vX.Y.Z, Z>0) cut from a release-X.Y branch, HEAD-on-
main is a strict superset of the release tag and contains commits
that were merged to main both before and after the tag.

The v1.3.1 changelog generated by this workflow (#2480) demonstrated
the failure mode: it included 8 PRs merged to main but never shipped
in v1.3.1, 6 backport PRs that landed on release-1.3 *after* v1.3.1
was tagged, both originals and their backports as separate entries,
a hallucinated 2024 PR, and the cozystack-ci bot in the contributors
list. The corrected v1.3.1 changelog contains the single fix that
v1.3.1 actually ships.

This PR fixes the root cause and tightens the agent guardrails:

* tags.yaml: check out the release tag commit (`ref: ${{ steps.tag.
  outputs.tag }}`) instead of `main`, so HEAD == the release commit
  and `git log v<prev>..HEAD` corresponds to the actual release. The
  Create-changelog-branch step still creates the PR branch from
  origin/main, so PRs continue to merge cleanly into main.
* tags.yaml: update the AI prompt to state explicitly that HEAD is
  the release commit and that the upper bound for the release range
  is the new tag, not main.
* docs/agents/changelog.md: replace `..HEAD` with `..v<new_version>`
  in every example so the instruction is unambiguous regardless of
  which branch is checked out.
* docs/agents/changelog.md: add a hard rule that backport PRs MUST
  be merged into the original entry (single line, two PR numbers),
  never listed as a second entry. Document the edge case where the
  original PR is outside the range — drop the entry.
* docs/agents/changelog.md: forbid using the brief description
  verbatim as the detailed description, and forbid inventing entries
  for PRs that are not in the release range.
* docs/agents/changelog.md: filter bot/CI accounts (`app/*`,
  `*[bot]`, cozystack-ci, github-actions, dependabot, renovate) out
  of the human Contributors list. Bot attribution on individual
  entries is still valid.

The corresponding correction for the v1.3.1 changelog itself is in
PR #2480 on the changelog-v1.3.1 branch.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 2420d8949b)
2026-05-06 10:25:57 +05:00
Myasnikov Daniil 70e8f84361 fix(ci): pass app token to Publish draft release step
The Publish draft release step used the workflow's default GITHUB_TOKEN,
which does not return draft releases from listReleases — so the script
failed with "Draft release for vX.Y.Z not found" and every release tag
had to be undrafted manually.

This same bug was fixed once before in 4db55ac5eb ([ci] Add Github
token to fetch draft releases) by adding github-token: GH_PAT to the
listing step. It was lost during the refactor in 66a756b6 that combined
the separate listing/flags/publish steps into a single Publish step,
and not restored when f96b2da1 migrated remaining github-token usages
from GH_PAT to the cozystack-ci App token.

Add github-token: app-token.outputs.token to the merged step so it
shares the same auth as the rest of the workflow.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 7d2fe2f7d1)
2026-05-06 05:25:42 +00:00
Aleksei Sviridkin d3ad21aa5a fix(seaweedfs): pass filer.replicas and s3.replicas to HelmRelease
The filer and s3 blocks in the seaweedfs HelmRelease values omitted the
replicas: field, so user-supplied .Values.filer.replicas and
.Values.s3.replicas were silently ignored and the upstream chart defaults
took over. This also caused the WorkloadMonitor resources for filer and
s3 (which read the same values) to diverge from the actual replica count
and trigger spurious alerts when users set non-default values.

Mirror the master/volume/db pattern and interpolate replicas: into both
blocks.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
(cherry picked from commit 400da5ee2b)
2026-05-01 11:16:08 +00:00
Andrei Kvapil c4dca4352d [Backport release-1.3] fix(talos): bump Talos to v1.12.7 (CVE-2026-31431) (#2547)
# Description
Backport of #2545 to `release-1.3`.
2026-05-01 12:56:15 +02:00
Andrei Kvapil d6b8eecae9 fix(talos): bump Talos to v1.12.7 (CVE-2026-31431)
Bumps Talos Linux from v1.12.6 to v1.12.7, which includes the kernel
fix for CVE-2026-31431 ("Copy Fail") — an unprivileged-to-root local
privilege escalation in the Linux kernel crypto subsystem (algif_aead).

Also refreshes the bundled system extension digests (amd-ucode, amdgpu,
bnx2-bnx2x, i915, intel-ice-firmware, qlogic-firmware, drbd, zfs) to
their 1.12.7-aligned builds.

Generated via `make update` in packages/core/talos.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
(cherry picked from commit 8549f3b9c3)
2026-05-01 10:55:42 +00:00
myasnikovdaniil 4f309823d2 Release v1.3.1 (#2479)
This PR prepares the release `v1.3.1`.
v1.3.1
2026-04-29 15:02:54 +05:00
cozystack-ci[bot] c0004e5c5a Prepare release v1.3.1
Signed-off-by: cozystack-ci[bot] <274107086+cozystack-ci[bot]@users.noreply.github.com>
2026-04-29 04:26:30 +00:00
myasnikovdaniil 09edf16b85 [Backport release-1.3] fix(api): prevent IDOR in TenantNamespace Get and Watch handlers (#2524)
# Description
Backport of #2471 to `release-1.3`.
api/apps/v1alpha1/v1.3.1
2026-04-28 22:41:54 +05:00
Myasnikov Daniil 5c68447763 fix(api): address review feedback on TenantNamespace watch path
- Hoist user identity extraction out of the Watch goroutine; reuse a
  cached username and groups map across events instead of re-fetching
  them per event. Watch now returns Unauthorized up front when no user
  is present in the context, rather than failing silently per event.
- Switch the per-event access-check error log to structured klog.ErrorS
  to comply with the project Go style guide.
- Strengthen TestGet_WithAccess to assert the concrete *TenantNamespace
  type plus Name, Kind, and APIVersion, so type or metadata regressions
  fail fast.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 61ed7ad89c)
2026-04-28 14:07:29 +00:00
IvanHunters e1f66927cb fix(api): address review feedback on TenantNamespace RBAC
Address review feedback from sircthulhu and CodeRabbit:

1. Return Forbidden instead of NotFound for unauthorized access
   - Get() now returns 403 Forbidden to follow standard K8s RBAC behavior
   - Previously returned 404 NotFound for security-by-obscurity
   - Updated test expectations to match new behavior

2. Propagate field and label selectors in Watch handler
   - Pass opts.FieldSelector and opts.LabelSelector to upstream Watch
   - Add defensive filtering before authorization to prevent RBAC bypass
   - Fixes potential issue with resourceNames restrictions

3. Refactor subject-matching logic to eliminate duplication
   - Extract matchesSubject() helper for Group/User/ServiceAccount checks
   - Remove duplicated code from filterAccessible and hasAccessToNamespace
   - Consolidates ServiceAccount namespace fallback logic

All tests pass successfully.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
(cherry picked from commit 7257b6aed4)
2026-04-28 14:07:29 +00:00
IvanHunters 79c23d895a test(api): add security tests for TenantNamespace IDOR fix
Added comprehensive unit tests for authorization logic:

- hasAccessToNamespace() tests:
  - User subject access (positive and negative cases)
  - Group subject access
  - ServiceAccount subject access
  - ServiceAccount with empty namespace (defaults to RoleBinding ns)
  - Privileged groups (system:masters, cozystack-cluster-admin)

- Get() handler tests:
  - Returns namespace when user has access
  - Returns NotFound when user lacks access (not Forbidden)
  - Returns NotFound for non-tenant namespaces

All tests verify that authorization correctly enforces RoleBinding-based
access control and prevents IDOR vulnerabilities.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
(cherry picked from commit 5b2501db91)
2026-04-28 14:07:29 +00:00
IvanHunters 82fb7990d5 fix(api): prevent IDOR in TenantNamespace Get and Watch handlers
Fixed two IDOR vulnerabilities allowing authenticated users to access
metadata of any tenant namespace without proper authorization.

Changes:
- Added hasAccessToNamespace() for efficient single-namespace access checks
- Get() now verifies access before returning namespace metadata
- Watch() filters events per-namespace with proper authorization
- Returns NotFound (not Forbidden) to prevent tenant enumeration

Performance optimization:
- hasAccessToNamespace() lists RoleBindings only in target namespace
  instead of listing all cluster RoleBindings (order of magnitude faster)
- Watch handler logs authorization errors for security audit

Additional fixes:
- Handle ServiceAccount subjects with empty namespace correctly
- Add klog error logging for failed authorization checks

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
(cherry picked from commit 64a3edff01)
2026-04-28 14:07:29 +00:00
myasnikovdaniil 5eb52f00b0 [Backport release-1.3] build(linstor): include linstor-gui in root image build target (#2518)
# Description
Backport of #2498 to `release-1.3`.
2026-04-28 15:57:04 +05:00
Myasnikov Daniil fe577a26c6 build(linstor): use shared BUILDX_ARGS for linstor-gui image build
The per-package Makefile added in #2382 hardcoded buildx flags
(--provenance, --builder, --platform=linux/amd64,linux/arm64,
--push, --load, --label) instead of using the shared $(BUILDX_ARGS)
macro from hack/common-envs.mk.

This broke CI: the runner's default docker driver does not support
multi-platform builds, and the hardcoded multi-arch platform list
crashed `make build` with "Multi-platform build is not supported for
the docker driver."

Replace the hardcoded flags with $(BUILDX_ARGS) to match every other
package (e.g. linstor, dashboard, cilium). $(BUILDX_ARGS) injects
--push, --load, --label, --provenance=false, and only sets --builder
or --platform when the operator explicitly exports BUILDER/PLATFORM.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit a9a66bf066)
2026-04-28 10:55:42 +00:00
Myasnikov Daniil f6da822cd0 build(linstor): include linstor-gui in root image build target
The linstor-gui package was added in #2382 with its own per-package
Makefile and Dockerfile, but the root Makefile's `build:` target was
not updated to invoke it. As a result `ghcr.io/cozystack/cozystack/
linstor-gui` has never been published (registry returns NAME_UNKNOWN)
and the chart's `image.tag` was never digest-pinned. Any cluster
deploying the chart hits ImagePullBackOff.

Wire the package into the root build alongside the other system
images. The next CI build will publish the image and the per-package
Makefile will rewrite values.yaml `image.repository`/`image.tag` to a
digest-pinned reference automatically.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit 7443e22345)
2026-04-28 10:55:42 +00:00
myasnikovdaniil 3c2aeb133e [Backport release-1.3] fix(kamaji): increase memory limits and add startup probe (#2491)
# Description
Backport of #2421 to `release-1.3`.
2026-04-28 15:46:45 +05:00
myasnikovdaniil c131acc8c6 [Backport release-1.3] fix(etcd): remove destructive post-upgrade cert-regeneration hook (#2511)
# Description
Backport of #2462 to `release-1.3`.
2026-04-28 15:46:34 +05:00
Myasnikov Daniil cbc1606c41 fix(etcd): remove destructive post-upgrade cert-regeneration hook
The etcd chart shipped a `post-upgrade` Helm hook that `kubectl delete`d the
etcd TLS chain (`etcd-{ca,peer-ca,client,peer,server}-tls`) and then deleted
etcd pods on every chart upgrade, gated by a semver compare of an
`etcd-deployed-version` ConfigMap against `2.6.1`.

The hook was added as a one-shot migration for the chart `2.6.0 -> 2.6.1`
transition. Since commit f871fbdb ("Remove versions_map logic") all chart
versions are stamped as `0.0.0+<git-hash>`, which per semver is always
`< 2.6.1`. The gate therefore always resolves to "update certs", firing the
destructive hook on every etcd upgrade. On clusters running Kamaji-managed
tenant control planes this wipes the etcd CA, cert-manager re-issues it, and
tenant kube-apiservers hit `x509: certificate signed by unknown authority`
against `etcd.<ns>.svc:2379` until each tenant DataStore is manually
re-reconciled.

Commit 47d81f70 ("Disabled private key rotation in CA certs") already fixed
the underlying `rotationPolicy: Always` issue the migration was papering
over, so the hook has no remaining use. Remove the hook Job, its RBAC, the
version ConfigMap it read, and add a helm-unittest suite under
`packages/extra/etcd/tests/` that guards against re-introducing the hook
or the version ConfigMap.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit c1508940bd)
2026-04-28 09:37:16 +00:00