Skip to content

refactor(seaweedfs): split seaweedfs-system into seaweedfs-db + seaweedfs-system - #2601

Merged
myasnikovdaniil merged 4 commits into
flux-bump-v048from
split-seaweedfs-db
May 28, 2026
Merged

refactor(seaweedfs): split seaweedfs-system into seaweedfs-db + seaweedfs-system#2601
myasnikovdaniil merged 4 commits into
flux-bump-v048from
split-seaweedfs-db

Conversation

@myasnikovdaniil

@myasnikovdaniil myasnikovdaniil commented May 10, 2026

Copy link
Copy Markdown
Contributor

What this PR does

Splits the seaweedfs-system HelmRelease into two: the CNPG Cluster/seaweedfs-db moves to its own HR (seaweedfs-db), and the application HR (seaweedfs-system) dependsOn it. The new HR uses Flux v2 HelmRelease.spec.healthCheckExprs with a CEL expression on Cluster.status.conditions[type=Ready] plus waitStrategy.name: poller, so its Ready=True only flips after the postgres primary is actually serving connections — not just after helm install applied the Cluster CR.

This eliminates the seaweedfs-filer CrashLoopBackOff race on a fresh tenant install. With Cilium kubeProxyReplacement: true, socket-LB returns EPERM from connect(2) to ClusterIPs with no Ready endpoints. Pre-split, the filer StatefulSet scheduled concurrently with the CNPG bootstrap (~55–70s of unavailable postgres), each connect() failed EPERM, kubelet exponential restart backoff pushed past the e2e bats kubectl wait hr/seaweedfs-system --timeout=2m window, and the "Configure Tenant and wait for applications" test failed.

Stacked on #2602

This PR depends on #2602 (Flux v2.8.0 upgrade) — healthCheckExprs is a helm-controller v1.5.0 feature. GitHub base set to flux-bump-v048 so the diff cleanly shows only this PR's changes; will auto-rebase onto main once #2602 merges.

Commits

  1. refactor(seaweedfs) — the split itself. Squash of the three iterative commits from the consolidated branch (ci: consolidate CI fixes (#2602 flux 2.8 + #2601 seaweedfs-db + #2612 kubevirt TPM + #2615 e2e trace) #2619): the initial split, a dependsOn merge-collision fix (collapses unconditional seaweedfs-db dependency with the namespace-guarded ingress dependency into one list), and the healthCheckExprs three-predicate form (route CNPG's ClusterIsNotReady bootstrap condition to inProgress, reserve failed for other Ready=False reasons, lean on the HR timeout: 10m as the real backstop).
  2. fix(tenant) — bump parent HR spec.timeout from 5m → 15m so the serialized seaweedfs-db → seaweedfs-system → seaweedfs chain has enough wall-clock budget. Two complementary changes: the static tenant-root HR (created by cozystack-basics) gets its spec.timeout bumped directly; sub-tenants (Tenant CRs whose HR is built by cozystack-api from the tenant ApplicationDefinition) get the release.cozystack.io/helm-install-timeout=15m annotation that kubernetes-rd already uses to opt into the longer budget.
  3. test(e2e) — consolidate two redundant child-HR waits in hack/e2e-install-cozystack.bats into one budgeted assert. The second kubectl wait (hr/monitoring hr/seaweedfs-system --timeout=2m) was a residue of a flux-reconcile-force workaround removed in feat(operator): expose HelmRelease generation knobs (interval, retry-interval, timeouts, max-history) #2509; monitoring's Ready is already implied by hr/tenant-root Ready and seaweedfs-system's by hr/seaweedfs Ready, so the line was tautologically true by the time it ran.

Migration

packages/core/platform/images/migrations/migrations/42 adopts existing Cluster/seaweedfs-db resources into the new release on upgrade — rewrites meta.helm.sh/release-name: seaweedfs-system → seaweedfs-db and stamps helm.sh/resource-policy: keep so the seaweedfs-system upgrade (which no longer renders the Cluster) does not delete it during the transition. Renumbered from the consolidated branch's slot 40 to fit fresh main (last migration is 41). targetVersion bumped 42 → 43.

The new HR's upgrade.force: true was dropped to match #2602's platform-wide cleanup (Helm v4 in helm-controller v1.5.0 rejects combining --force-replace with SSA's --force-conflicts).

Verification

  • helm template renders seaweedfs-db HR cleanly with healthCheckExprs, waitStrategy: poller, and upgrade: { remediation: { retries: -1 } } (no force: true).
  • Migration 42 syntax-checks under sh -n.
  • All four PR commits GPG-signed + DCO-signed-off.

Release note

NONE

Summary by CodeRabbit

  • New Features

    • SeaweedFS database is now available as a separate, independently configurable component with customizable replicas, storage size, and storage class options.
  • Chores

    • Restructured database configuration from the main application into a dedicated component for improved modularity and initialization order.

Review Change Stack

@github-actions github-actions Bot added area/storage Issues or PRs related to storage (linstor, seaweedfs, bucket, velero, harbor) kind/cleanup Categorizes issue or PR as related to cleanup of code, process, or technical debt size/XXL This PR changes 1000+ lines, ignoring generated files labels May 10, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a race condition during fresh tenant installations where seaweedfs-filer pods would crashloop due to the database not being ready. By decoupling the database infrastructure from the application components and utilizing Flux's dependency management and health check expressions, we ensure a reliable deployment order. This change requires Flux 2.8.x and includes a migration path for existing deployments.

Highlights

  • Architecture Refactor: Split the seaweedfs-system HelmRelease into two separate releases: seaweedfs-db (for the CNPG Cluster) and seaweedfs-system (for the application).
  • Race Condition Fix: Implemented Flux HelmRelease healthCheckExprs and waitStrategy: poller to ensure the application only deploys after the database is fully ready, preventing EPERM crashloops.
  • Migration Strategy: Added a migration script (migration 39) to re-annotate existing CNPG Cluster resources, allowing the new seaweedfs-db release to adopt them without disruption.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment Gemini (@gemini-code-assist) Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai

coderabbitai Bot commented May 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cccdd3f9-248e-4595-987c-fc35495faffd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request splits the SeaweedFS system into two coordinated HelmReleases to eliminate a startup race condition. The database release (seaweedfs-db) is now provisioned first with CNPG readiness gating via healthCheckExprs, and the workload release (seaweedfs-system) depends on successful database deployment. A migration script handles existing deployments.

Changes

SeaweedFS System Split

Layer / File(s) Summary
Design & Planning
docs/superpowers/plans/2026-05-10-split-seaweedfs-system.md, docs/superpowers/specs/2026-05-10-split-seaweedfs-system-design.md
Comprehensive plan and design spec detailing the EPERM crash problem, architecture with Flux dependsOn and CNPG healthCheck gating, step-by-step implementation tasks, migration strategy, error handling, and verification checkpoints.
New Chart Definition
packages/system/seaweedfs-db/Chart.yaml, packages/system/seaweedfs-db/Makefile, packages/system/seaweedfs-db/README.md, packages/system/seaweedfs-db/values.schema.json, packages/system/seaweedfs-db/values.yaml
New seaweedfs-db Helm chart with API v2 metadata, build scaffolding, database-specific values schema (replicas, size, storageClass), and configuration documentation.
Source Registration
packages/core/platform/sources/seaweedfs-application.yaml
Registers seaweedfs-db component pointing to system/seaweedfs-db in the PackageSource variants, enabling chart discovery and artifact packaging.
HelmRelease Wiring
packages/extra/seaweedfs/templates/seaweedfs-db.yaml, packages/extra/seaweedfs/templates/seaweedfs.yaml, packages/system/seaweedfs/values.yaml
New seaweedfs-db.yaml template with poller waitStrategy and CEL healthCheckExprs gating on CNPG Ready condition; updates seaweedfs.yaml with dependsOn on seaweedfs-db, reduces reconciliation interval to 30s, removes db values pass-through; deletes db config from seaweedfs values.yaml.
Migration for Adoption
packages/core/platform/images/migrations/migrations/39
Shell migration script that iterates namespaces, re-annotates existing Cluster/seaweedfs-db from seaweedfs-system to seaweedfs-db ownership, sets helm.sh/resource-policy=keep, and updates cozystack-version ConfigMap to v39.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A database splits from the system so fine,
With readiness gates in a poller design,
Flux waits for the CNPG to say it's all set,
Before filer pods start—no EPERM regret!
Two HelmReleases dance in dependency's line.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'refactor(seaweedfs): split seaweedfs-system into seaweedfs-db + seaweedfs-system' accurately and concisely summarizes the primary change—splitting a single HelmRelease into two separate releases—which is the core objective of the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch split-seaweedfs-db

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@dosubot dosubot Bot added the area/platform Issues or PRs related to platform infrastructure (bundle, flux, talos, installer) label May 10, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request splits the seaweedfs-system HelmRelease into seaweedfs-db and seaweedfs-system to ensure the database is ready before the application starts, resolving an EPERM crashloop issue. The changes introduce a new system chart, a migration script for existing clusters, and utilize FluxCD's healthCheckExprs for readiness gating. Feedback identifies a shell compatibility issue in the migration script, missing resource fields in the database values schema, and the presence of local absolute paths in the implementation plan.

@@ -0,0 +1,32 @@
#!/bin/sh

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The script uses set -o pipefail (on line 13), which is a bash-specific option and not supported by POSIX-compliant shells like dash (the default /bin/sh on many systems). To ensure the script executes correctly with this option, the shebang should be changed to #!/bin/bash.

Suggested change
#!/bin/sh
#!/bin/bash

Comment on lines +9 to +12
## @typedef {struct} DB - Database configuration.
## @field {int} [replicas] - Number of database replicas.
## @field {quantity} [size] - Persistent Volume size.
## @field {string} [storageClass] - StorageClass used to store the data.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The DB typedef is missing the resources and resourcesPreset fields. These are being passed by the wrapper template in packages/extra/seaweedfs/templates/seaweedfs-db.yaml. Adding them here ensures they are included in the generated JSON schema and documentation. Note that the database.yaml template in this chart also needs to be updated to use these values instead of hardcoded ones.

## @typedef {struct} DB - Database configuration.
## @field {int} [replicas] - Number of database replicas.
## @field {quantity} [size] - Persistent Volume size.
## @field {string} [storageClass] - StorageClass used to store the data.
## @field {string} [resourcesPreset] - Resource preset.
## @field {Resources} [resources] - Resource configuration.

Comment on lines +15 to +18
db:
replicas: 2
size: 10Gi
storageClass: ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Include default values for resourcesPreset and resources in the db block to align with the updated typedef and provide defaults for the schema.

db:
  replicas: 2
  size: 10Gi
  storageClass: ""
  resourcesPreset: "nanosmall"
  resources: {}


- [ ] **Step 3: Verify directory structure**

Run: `ls /home/daniil/aenix/cozystack-split-seaweedfs/packages/system/seaweedfs-db/`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

This document contains absolute paths specific to the author's local environment (e.g., /home/daniil/aenix/cozystack-split-seaweedfs/). These should be replaced with relative paths or placeholders to ensure the documentation is portable and clean. This applies to multiple locations throughout the file.

Suggested change
Run: `ls /home/daniil/aenix/cozystack-split-seaweedfs/packages/system/seaweedfs-db/`
Run: ls packages/system/seaweedfs-db/

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
packages/system/seaweedfs-db/Chart.yaml (1)

1-3: 💤 Low value

Consider adding a description field.

While not strictly required, adding a description field to the Chart.yaml improves discoverability and follows Helm best practices. The sibling charts in this repository may provide examples.

📝 Suggested addition
 apiVersion: v2
 name: cozy-seaweedfs-db
+description: SeaweedFS Database (CNPG Cluster) for Cozystack
 version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/system/seaweedfs-db/Chart.yaml` around lines 1 - 3, Add a
descriptive "description" field to the Helm chart metadata in Chart.yaml for
cozy-seaweedfs-db: update the Chart.yaml (the top-level apiVersion/name/version
block) to include a concise human-readable description string explaining the
chart purpose (e.g., what cozy-seaweedfs-db deploys and any notable behavior),
matching style of sibling charts so discoverability and Helm best practices are
followed.
docs/superpowers/specs/2026-05-10-split-seaweedfs-system-design.md (1)

7-9: 💤 Low value

Add language specifier to fenced code block.

The code block showing the error message should specify a language for proper syntax highlighting and to satisfy the markdown linter.

📝 Suggested fix
-```
+```text
 dial tcp <ClusterIP>:5432 (seaweedfs-db-rw): connect: operation not permitted

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @docs/superpowers/specs/2026-05-10-split-seaweedfs-system-design.md around
lines 7 - 9, The fenced code block containing the error message in the document
should include a language specifier for proper highlighting and linting; update
the fenced block around the line with the text "dial tcp :5432
(seaweedfs-db-rw): connect: operation not permitted" (the existing
triple-backtick block) to use a language tag such as text (i.e., change ``` to

docs/superpowers/plans/2026-05-10-split-seaweedfs-system.md (1)

169-175: 💤 Low value

Add language specifier to fenced code block.

For consistency with the rest of the plan and to satisfy the markdown linter, add a language specifier to this code block.

📝 Suggested fix
-```
+```yaml
 ---
 apiVersion: postgresql.cnpg.io/v1
 kind: Cluster
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/plans/2026-05-10-split-seaweedfs-system.md` around lines 169
- 175, The fenced code block containing the Kubernetes manifest (starting with
"apiVersion: postgresql.cnpg.io/v1", "kind: Cluster", and "metadata: name:
seaweedfs-db") needs a language specifier; update the opening fence from ``` to
```yaml so the block becomes a YAML code block to satisfy the markdown linter
and maintain consistency with other plan examples.
packages/system/seaweedfs-db/values.yaml (1)

5-7: 💤 Low value

Unused typedef: Resources is documented but not referenced.

The Resources typedef defines cpu and memory fields, but these are not used anywhere in the db configuration below. This may be a copy-paste artifact from another values file.

♻️ Suggested cleanup

If the Resources typedef is not needed:

 ##
 ## `@section` Database parameters
 ##
 
-## `@typedef` {struct} Resources - Resource configuration.
-## `@field` {quantity} [cpu] - Number of CPU cores allocated.
-## `@field` {quantity} [memory] - Amount of memory allocated.
-
 ## `@typedef` {struct} DB - Database configuration.
 ## `@field` {int} [replicas] - Number of database replicas.
 ## `@field` {quantity} [size] - Persistent Volume size.
 ## `@field` {string} [storageClass] - StorageClass used to store the data.

However, if resources will be added to the db configuration in the future, keeping the typedef is fine.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/system/seaweedfs-db/values.yaml` around lines 5 - 7, The typedef
"Resources" (struct Resources with cpu and memory) is declared but not
referenced by the db configuration; either remove this unused typedef block or
wire it into the db values by adding a resources entry that consumes cpu and
memory (e.g., ensure the db chart's values structure includes a resources object
and any templates reference values.db.resources.cpu/memory or similar). Locate
the "Resources" typedef and either delete it or add a values key (e.g.,
db.resources) and update relevant templates/deployment specs to read
values.db.resources.cpu and values.db.resources.memory so the fields are
actually used.
packages/extra/seaweedfs/templates/seaweedfs.yaml (1)

111-113: ⚡ Quick win

Document the rationale for the 10× faster reconciliation interval.

The interval was reduced from 5m to 30s, which increases reconciliation frequency by 10×. This could impact cluster load, especially with many tenants. Please clarify whether this aggressive interval is necessary for quick recovery after the DB becomes ready, or if a more moderate value (e.g., 1m or 2m) would suffice.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/extra/seaweedfs/templates/seaweedfs.yaml` around lines 111 - 113,
Add a short in-file comment above the interval setting that explains why the
reconciliation interval for the SeaweedFS resource was reduced from 5m to 30s
(or change the value) — reference the interval key and the dependsOn entry ({{
.Release.Name }}-db) and state whether the 30s is required for fast recovery
after the DB becomes ready or if a moderate value like 1m/2m is preferred to
reduce load; update the interval to the chosen moderate value if you decide not
to keep 30s and ensure the rationale comment documents the trade-offs for
cluster load and tenant scale.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/specs/2026-05-10-split-seaweedfs-system-design.md`:
- Around line 68-82: This PR adds HelmRelease.spec.healthCheckExprs (see
HelmRelease.spec.healthCheckExprs in
packages/extra/seaweedfs/templates/seaweedfs-db.yaml) which requires
helm-controller v1.5.0+/Flux 2.8.x; because the repo is pinned to Flux 2.7.x
(helm-controller v1.4.3) you must not merge this change until the Flux upgrade
lands—either (a) remove or revert the healthCheckExprs usage and instead use a
supported wait strategy/timeout fallback, or (b) gate this PR on the Flux
upgrade by confirming the Flux 2.8.x upgrade PR exists and will merge first and
add a note in this PR linking that upgrade and/or add CI gating that blocks
merge until the upgrade is merged.

In `@packages/extra/seaweedfs/templates/seaweedfs-db.yaml`:
- Around line 27-31: Add a comment immediately above the healthCheckExprs block
documenting that this field requires Flux v2.8.x (helm-controller v1.5.0+) and
that on older Flux (e.g., v2.7.x) the HelmRelease will either fail to parse or
silently ignore healthCheckExprs, removing the readiness check; update the
comment to mention the minimum Flux and helm-controller versions and the
resulting behavior so maintainers know to upgrade Flux before relying on the
healthCheckExprs in this HelmRelease.

---

Nitpick comments:
In `@docs/superpowers/plans/2026-05-10-split-seaweedfs-system.md`:
- Around line 169-175: The fenced code block containing the Kubernetes manifest
(starting with "apiVersion: postgresql.cnpg.io/v1", "kind: Cluster", and
"metadata: name: seaweedfs-db") needs a language specifier; update the opening
fence from ``` to ```yaml so the block becomes a YAML code block to satisfy the
markdown linter and maintain consistency with other plan examples.

In `@docs/superpowers/specs/2026-05-10-split-seaweedfs-system-design.md`:
- Around line 7-9: The fenced code block containing the error message in the
document should include a language specifier for proper highlighting and
linting; update the fenced block around the line with the text "dial tcp
<ClusterIP>:5432 (seaweedfs-db-rw): connect: operation not permitted" (the
existing triple-backtick block) to use a language tag such as text (i.e., change
``` to ```text) so the markdown linter and renderers treat it as plain text.

In `@packages/extra/seaweedfs/templates/seaweedfs.yaml`:
- Around line 111-113: Add a short in-file comment above the interval setting
that explains why the reconciliation interval for the SeaweedFS resource was
reduced from 5m to 30s (or change the value) — reference the interval key and
the dependsOn entry ({{ .Release.Name }}-db) and state whether the 30s is
required for fast recovery after the DB becomes ready or if a moderate value
like 1m/2m is preferred to reduce load; update the interval to the chosen
moderate value if you decide not to keep 30s and ensure the rationale comment
documents the trade-offs for cluster load and tenant scale.

In `@packages/system/seaweedfs-db/Chart.yaml`:
- Around line 1-3: Add a descriptive "description" field to the Helm chart
metadata in Chart.yaml for cozy-seaweedfs-db: update the Chart.yaml (the
top-level apiVersion/name/version block) to include a concise human-readable
description string explaining the chart purpose (e.g., what cozy-seaweedfs-db
deploys and any notable behavior), matching style of sibling charts so
discoverability and Helm best practices are followed.

In `@packages/system/seaweedfs-db/values.yaml`:
- Around line 5-7: The typedef "Resources" (struct Resources with cpu and
memory) is declared but not referenced by the db configuration; either remove
this unused typedef block or wire it into the db values by adding a resources
entry that consumes cpu and memory (e.g., ensure the db chart's values structure
includes a resources object and any templates reference
values.db.resources.cpu/memory or similar). Locate the "Resources" typedef and
either delete it or add a values key (e.g., db.resources) and update relevant
templates/deployment specs to read values.db.resources.cpu and
values.db.resources.memory so the fields are actually used.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d9aed729-becb-4727-a3d1-b0a9ea155b00

📥 Commits

Reviewing files that changed from the base of the PR and between 99e7070 and 28b02d5.

📒 Files selected for processing (13)
  • docs/superpowers/plans/2026-05-10-split-seaweedfs-system.md
  • docs/superpowers/specs/2026-05-10-split-seaweedfs-system-design.md
  • packages/core/platform/images/migrations/migrations/39
  • packages/core/platform/sources/seaweedfs-application.yaml
  • packages/extra/seaweedfs/templates/seaweedfs-db.yaml
  • packages/extra/seaweedfs/templates/seaweedfs.yaml
  • packages/system/seaweedfs-db/Chart.yaml
  • packages/system/seaweedfs-db/Makefile
  • packages/system/seaweedfs-db/README.md
  • packages/system/seaweedfs-db/templates/database.yaml
  • packages/system/seaweedfs-db/values.schema.json
  • packages/system/seaweedfs-db/values.yaml
  • packages/system/seaweedfs/values.yaml
💤 Files with no reviewable changes (1)
  • packages/system/seaweedfs/values.yaml

Comment on lines +68 to +82
```yaml
spec:
waitStrategy:
name: poller # required for healthCheckExprs to be evaluated
healthCheckExprs:
- apiVersion: postgresql.cnpg.io/v1
kind: Cluster
current: has(status.conditions) && status.conditions.exists(e, e.type == 'Ready' && e.status == 'True')
failed: has(status.conditions) && status.conditions.exists(e, e.type == 'Ready' && e.status == 'False')
```

`exists` requires at least one matching condition element, so a missing `status.conditions` evaluates to `false` and the wait keeps polling — important early in the lifecycle when the CR has been applied but CNPG hasn't yet stamped any conditions.

The expression matches every `postgresql.cnpg.io/v1 Cluster` resource managed by the release (only `seaweedfs-db` here). The HR's `wait` phase blocks until `current` returns `true`, which only happens once CNPG sets `Ready=True` on the Cluster. Helm's existing `timeout: 10m` is the outer ceiling — if CNPG never reaches Ready, the HR fails with a clear message instead of silently letting the dependent HR proceed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check current Flux version in the repository
echo "Searching for Flux version references..."
rg -n "flux.*version|fluxcd.*v2\." --type=yaml -C2 -g '!docs/**'

Repository: cozystack/cozystack

Length of output: 523


🏁 Script executed:

# Search for Flux version pins more broadly
find . -type f \( -name "*.yaml" -o -name "*.yml" -o -name "*.md" -o -name "*.json" \) ! -path "*/.*" | head -50 | xargs grep -l "flux.*2\." 2>/dev/null | head -20

Repository: cozystack/cozystack

Length of output: 45


🏁 Script executed:

# Look for version information in common locations
echo "=== Checking for FluxCD version in various config files ==="
for file in fluxcd.yaml flux-release.yaml Chart.yaml kustomization.yaml values.yaml release.yaml; do
  find . -name "$file" -type f ! -path "*/.*" -exec grep -H "version" {} \; 2>/dev/null
done

Repository: cozystack/cozystack

Length of output: 50375


🏁 Script executed:

# Search the docs directory for healthCheckExprs references
echo "=== Searching for healthCheckExprs feature documentation ==="
rg -n "healthCheckExprs" --type=yaml --type=markdown

Repository: cozystack/cozystack

Length of output: 6096


🏁 Script executed:

# Check for Flux version in the main README or installation files
echo "=== Checking README and main docs ==="
rg -n "flux.*2\.[0-9]" --type=markdown -C 2 docs/ 2>/dev/null | head -40

Repository: cozystack/cozystack

Length of output: 701


🏁 Script executed:

# Look at the actual seaweedfs-db template to see current implementation
head -50 ./packages/extra/seaweedfs/templates/seaweedfs-db.yaml

Repository: cozystack/cozystack

Length of output: 1451


🏁 Script executed:

# Search for any version requirements or comments about healthCheckExprs
rg -i "healthcheckexprs|helm.*controller|v1.5.0" --type=yaml --type=markdown -B 2 -A 2

Repository: cozystack/cozystack

Length of output: 50377


🏁 Script executed:

# Check Flux release notes or documentation in the repo
find . -type f -name "*.md" ! -path "*/.*" -exec rg -l "helm.controller|healthCheckExprs.*version|2\.8" {} \;

Repository: cozystack/cozystack

Length of output: 407


🌐 Web query:

Flux 2.8.0 helm-controller v1.5.0 healthCheckExprs feature release

💡 Result:

Flux 2.8.0 (feature release) shipped with helm-controller v1.5.0 on 2026-02-24, and the helm-controller v1.5.0 release introduced the HelmRelease.spec.healthCheckExprs feature (custom health checks via CEL expressions) [1][2]. What changed (healthCheckExprs / CEL health checks): - helm-controller v1.5.0 added support for custom health checks on HelmRelease using CEL expressions via the.spec.healthCheckExprs field [2][3]. - The helm-controller API docs specify that.spec.healthCheckExprs evaluates CEL expressions for custom resources (with per-entry apiVersion/kind, and optional inProgress/failed and required current) and that evaluation order is inProgress → failed → current; the first expression that returns true determines the custom resource’s health status [4]. - The feature is tied to the Helm v4 “kstatus” based health checking flow: the GitHub issue notes it is only available for kstatus health checks implemented in Helm 4 (i.e., the waiter WaitStrategy), and the legacy WaitStrategy won’t be able to use it [5]. Release timing / versions: - Flux v2.8.0 release tag is dated 2026-02-24 and explicitly lists helm-controller v1.5.0 as part of the Flux 2.8.0 component versions [1]. - helm-controller v1.5.0 changelog shows release date 2026-02-20 and documents the defaults/behavior changes plus the addition of.spec.healthCheckExprs support [2].

Citations:


🏁 Script executed:

# Check for any recent Flux version changes or upgrade PRs
rg -n "flux.*2\.8|helm.*controller.*v1\.5" --type=markdown --type=yaml -C 3 | head -60

Repository: cozystack/cozystack

Length of output: 45


🏁 Script executed:

# Look for any issue or PR references about Flux upgrades
find . -type f \( -name "*.md" -o -name "*.yaml" \) ! -path "*/.*" -exec grep -l "flux.*upgrade\|2\.8\.0\|upgrade.*flux" {} \; 2>/dev/null

Repository: cozystack/cozystack

Length of output: 232


🏁 Script executed:

# Check the git history for recent Flux-related changes
git log --oneline --all -30 2>/dev/null | grep -i "flux\|helm.controller"

Repository: cozystack/cozystack

Length of output: 45


🏁 Script executed:

# Check the v1.2.0 changelog for Flux upgrade information
grep -A 5 -B 5 "flux\|2\.8" docs/changelogs/v1.2.0.md | head -40

Repository: cozystack/cozystack

Length of output: 3265


🏁 Script executed:

# Check if there are any recent version bumps in values
rg "version.*2\.[78]" packages/system/fluxcd/ packages/core/flux-aio/ -A 2 -B 2

Repository: cozystack/cozystack

Length of output: 343


🏁 Script executed:

# Look at the fluxcd values to see what version is specified
cat packages/system/fluxcd/values.yaml | grep -A 10 -B 10 "version"

Repository: cozystack/cozystack

Length of output: 522


Block merge until Flux is upgraded to 2.8.x.

The PR uses HelmRelease.spec.healthCheckExprs (in packages/extra/seaweedfs/templates/seaweedfs-db.yaml, lines 27–30) which requires helm-controller v1.5.0+, shipped with Flux 2.8.0. The repository is currently pinned to Flux 2.7.x with helm-controller v1.4.3, which does not support this feature. Verify that a Flux upgrade PR exists and will merge first, or gate this PR on the Flux upgrade completing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/specs/2026-05-10-split-seaweedfs-system-design.md` around
lines 68 - 82, This PR adds HelmRelease.spec.healthCheckExprs (see
HelmRelease.spec.healthCheckExprs in
packages/extra/seaweedfs/templates/seaweedfs-db.yaml) which requires
helm-controller v1.5.0+/Flux 2.8.x; because the repo is pinned to Flux 2.7.x
(helm-controller v1.4.3) you must not merge this change until the Flux upgrade
lands—either (a) remove or revert the healthCheckExprs usage and instead use a
supported wait strategy/timeout fallback, or (b) gate this PR on the Flux
upgrade by confirming the Flux 2.8.x upgrade PR exists and will merge first and
add a note in this PR linking that upgrade and/or add CI gating that blocks
merge until the upgrade is merged.

Comment on lines +27 to +31
healthCheckExprs:
- apiVersion: postgresql.cnpg.io/v1
kind: Cluster
current: has(status.conditions) && status.conditions.exists(e, e.type == 'Ready' && e.status == 'True')
failed: has(status.conditions) && status.conditions.exists(e, e.type == 'Ready' && e.status == 'False')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check deployed Flux version for helm-controller

kubectl get deployment -n flux-system helm-controller -o jsonpath='{.spec.template.spec.containers[0].image}' 2>/dev/null || echo "Flux not found"

Repository: cozystack/cozystack

Length of output: 78


🏁 Script executed:

# First, let's examine the seaweedfs-db.yaml file
cat -n packages/extra/seaweedfs/templates/seaweedfs-db.yaml

Repository: cozystack/cozystack

Length of output: 1752


🏁 Script executed:

# Search for any existing documentation or validation about Flux version in the seaweedfs package
rg -i "flux|helm-controller|version|2\.8" packages/extra/seaweedfs/ -A 2 -B 2

Repository: cozystack/cozystack

Length of output: 38057


🏁 Script executed:

# Check if there are any migration or bootstrap scripts that validate Flux version
find packages/extra/seaweedfs -type f \( -name "*.yaml" -o -name "*.sh" -o -name "*.md" \) -exec grep -l "flux\|Flux\|validation\|preflight" {} \;

Repository: cozystack/cozystack

Length of output: 166


🏁 Script executed:

# Verify the file structure to ensure umbrella pattern compliance
tree -L 3 packages/extra/seaweedfs/ 2>/dev/null || find packages/extra/seaweedfs -type f -o -type d | head -20

Repository: cozystack/cozystack

Length of output: 695


🌐 Web query:

Flux helm-controller healthCheckExprs version requirement 2.8.0

💡 Result:

Flux’s HelmRelease .spec.healthCheckExprs (CEL-based custom health checks) requires helm-controller v1.5.0+; Flux 2.8.0 uses helm-controller v1.5.0 and introduces this support, so it is available starting with Flux 2.8.0 and any helm-controller >=1.5.0.[1][2] What you asked (“Flux helm-controller healthCheckExprs version requirement 2.8.0”): if your Flux version is 2.8.0 (released with helm-controller v1.5.0), you can use .spec.healthCheckExprs on HelmRelease resources.[1][3] Notes/constraints from the official docs: - .spec.healthCheckExprs is evaluated only when Helm wait is enabled (i.e., .spec.disableWait is false) and when the poller wait strategy is used (.spec.waitStrategy.name is poller).[3] - It is implemented via CEL expressions with current (required) and optional inProgress/failed.[3] - The implementation is only available for the kstatus-based health checks in Helm 4 (i.e., the poller wait strategy); users using the legacy wait strategy can’t use the feature.[4] Examples and field semantics are documented here: the HelmRelease “Health check expressions” section.[3]

Citations:


🌐 Web query:

Flux HelmRelease healthCheckExprs supported version helm-controller

💡 Result:

Flux HelmRelease supports custom health check expressions via the .spec.healthCheckExprs field (CEL expressions), and this feature is tied to the Helm 4 + waitStrategy: poller flow using kstatus readers. [1][2] Supported Helm-controller version (where this feature landed): fluxcd/helm-controller v1.5.0 (released 2026-02-20), which includes “Add support for custom health checks via CEL expressions” and notes that “custom health checks via CEL expressions” are supported via .spec.healthCheckExprs. [2] Implementation/usage notes (to ensure the expressions actually evaluate): - .spec.healthCheckExprs is evaluated only when Helm action wait is enabled (.spec.disableWait is false) and when the poller wait strategy is used (.spec.waitStrategy.name is poller). [1] - The controller evaluates inProgress first, then failed, then current; the first expression that evaluates to true determines health status. [1]

Citations:


Document the minimum Flux version requirement for healthCheckExprs.

The healthCheckExprs field on line 27 requires Flux 2.8.x (helm-controller v1.5.0+). If Flux is still on 2.7.x, the HelmRelease will either fail to parse or silently ignore the healthCheckExprs field, defeating the purpose of this readiness check.

Add a comment documenting this dependency:

Proposed documentation addition
+  # REQUIRES: Flux 2.8.x+ (helm-controller v1.5.0+) for healthCheckExprs support
   # `poller` waitStrategy is required for healthCheckExprs to be evaluated.
   # Without it, the HR flips Ready as soon as helm install applies the Cluster CR,
   # before CNPG has bootstrapped postgres.
   waitStrategy:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/extra/seaweedfs/templates/seaweedfs-db.yaml` around lines 27 - 31,
Add a comment immediately above the healthCheckExprs block documenting that this
field requires Flux v2.8.x (helm-controller v1.5.0+) and that on older Flux
(e.g., v2.7.x) the HelmRelease will either fail to parse or silently ignore
healthCheckExprs, removing the readiness check; update the comment to mention
the minimum Flux and helm-controller versions and the resulting behavior so
maintainers know to upgrade Flux before relying on the healthCheckExprs in this
HelmRelease.

@myasnikovdaniil
myasnikovdaniil marked this pull request as draft May 12, 2026 03:45
myasnikovdaniil added a commit that referenced this pull request May 12, 2026
…collision

Main has a conditional dependsOn:[ingress-nginx-system] in the application
HR (added after #2601 branched). PR #2601 added an unconditional
dependsOn:[seaweedfs-db]. The merge produced two `dependsOn:` keys in the
same HelmRelease spec, which the Helm post-render rejected:

  Helm install failed for release tenant-root/seaweedfs ...:
  error while running post render on files: map[string]interface {}(nil):
  yaml: unmarshal errors

Collapse into one list: the db dependency is unconditional; the ingress
dependency stays inside the existing `if eq $ingress .Release.Namespace`
guard so sub-tenants that inherit ingress from a parent namespace don't
deadlock on a non-existent local HR.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
@github-actions github-actions Bot added size/L This PR changes 100-499 lines, ignoring generated files and removed size/XXL This PR changes 1000+ lines, ignoring generated files labels May 13, 2026
myasnikovdaniil added a commit that referenced this pull request May 13, 2026
…collision

Main has a conditional dependsOn:[ingress-nginx-system] in the application
HR (added after #2601 branched). PR #2601 added an unconditional
dependsOn:[seaweedfs-db]. The merge produced two `dependsOn:` keys in the
same HelmRelease spec, which the Helm post-render rejected:

  Helm install failed for release tenant-root/seaweedfs ...:
  error while running post render on files: map[string]interface {}(nil):
  yaml: unmarshal errors

Collapse into one list: the db dependency is unconditional; the ingress
dependency stays inside the existing `if eq $ingress .Release.Namespace`
guard so sub-tenants that inherit ingress from a parent namespace don't
deadlock on a non-existent local HR.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
myasnikovdaniil added a commit that referenced this pull request May 19, 2026
…collision

Main has a conditional dependsOn:[ingress-nginx-system] in the application
HR (added after #2601 branched). PR #2601 added an unconditional
dependsOn:[seaweedfs-db]. The merge produced two `dependsOn:` keys in the
same HelmRelease spec, which the Helm post-render rejected:

  Helm install failed for release tenant-root/seaweedfs ...:
  error while running post render on files: map[string]interface {}(nil):
  yaml: unmarshal errors

Collapse into one list: the db dependency is unconditional; the ingress
dependency stays inside the existing `if eq $ingress .Release.Namespace`
guard so sub-tenants that inherit ingress from a parent namespace don't
deadlock on a non-existent local HR.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
@github-actions github-actions Bot added size/XXL This PR changes 1000+ lines, ignoring generated files and removed size/L This PR changes 100-499 lines, ignoring generated files labels May 25, 2026
@myasnikovdaniil
myasnikovdaniil changed the base branch from main to flux-bump-v048 May 25, 2026 06:39
@myasnikovdaniil
myasnikovdaniil marked this pull request as ready for review May 25, 2026 13:24
@dosubot dosubot Bot added the area/testing Issues or PRs related to testing (e2e, bats, unit tests) label May 25, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the SeaweedFS deployment by extracting the database component into a dedicated seaweedfs-db package and introducing a serial installation chain. It includes a migration script to transition existing clusters to the new Helm release and increases installation timeouts to 15 minutes to account for the extended bootstrap time. Review feedback highlights a portability issue in the migration script's shebang when using pipefail, an inconsistency regarding the upgrade.force flag in the SeaweedFS template, and the need to make resource limits configurable in the new database package.

@@ -0,0 +1,32 @@
#!/bin/sh

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The script uses set -o pipefail (line 13), which is a non-POSIX feature not supported by the standard /bin/sh (often dash on Debian/Ubuntu systems). As noted in the E2E tests in this repository, pipefail is unavailable in the default shell. To ensure the script handles pipe failures correctly and remains portable, please change the shebang to #!/bin/bash.

Suggested change
#!/bin/sh
#!/bin/bash

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holding off on this one. The script runs in the migrations container which is alpine:3.22 (see packages/core/platform/images/migrations/Dockerfile:1), where /bin/sh is BusyBox ash. BusyBox ash has supported pipefail since 1.30 (2018) and Alpine 3.22 ships 1.36+, so the dash-on-Debian concern doesn't apply here.

Repo precedent is also consistent: the orchestrator run-migrations.sh and 22 of the existing migrations (19 through 41) all use the same #!/bin/sh + set -euo pipefail pattern. Switching #42 alone to #!/bin/bash would introduce inconsistency without changing behavior.

Comment on lines 127 to 129
force: true
remediation:
retries: -1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The pull request description states that upgrade.force: true was dropped to align with a platform-wide cleanup and avoid conflicts with Flux's Server-Side Apply (SSA) mode. However, it is being explicitly added here. If this component is moving towards SSA or if you want to maintain consistency with the "platform-wide cleanup" mentioned, this line should be removed.

    remediation:
      retries: -1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is pre-existing on main and untouched by this PR — git show main:packages/extra/seaweedfs/templates/seaweedfs.yaml has the same upgrade: { force: true } at the same location. The PR diff doesn't add it.

The PR body's "The new HR's upgrade.force: true was dropped…" refers to the new seaweedfs-db HR, not the pre-existing seaweedfs-system HR. The new HR at packages/extra/seaweedfs/templates/seaweedfs-db.yaml correctly has only remediation.retries: -1 under upgrade:, no force. The platform-wide cleanup of force: true from seaweedfs-system is out of scope here and worth its own PR.

Comment on lines +9 to +18
## @typedef {struct} DB - Database configuration.
## @field {int} [replicas] - Number of database replicas.
## @field {quantity} [size] - Persistent Volume size.
## @field {string} [storageClass] - StorageClass used to store the data.

## @param {DB} db - Database configuration.
db:
replicas: 2
size: 10Gi
storageClass: ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Resources typedef (defined on lines 5-7) is currently unused in the DB struct definition. Additionally, the database.yaml template in this package has hardcoded resource limits. To make the database resources configurable (as attempted by the wrapper chart in packages/extra/seaweedfs/templates/seaweedfs-db.yaml), please add the resources field to the DB typedef and the db block. Note that you should also update packages/system/seaweedfs-db/templates/database.yaml to use .Values.db.resources.

## @typedef {struct} DB - Database configuration.
## @field {int} [replicas] - Number of database replicas.
## @field {quantity} [size] - Persistent Volume size.
## @field {Resources} [resources] - Resource requests and limits.
## @field {string} [storageClass] - StorageClass used to store the data.

## @param {DB} db - Database configuration.
db:
  replicas: 2
  size: 10Gi
  storageClass: ""
  resources:
    limits:
      cpu: "1"
      memory: 2048Mi
    requests:
      cpu: 100m
      memory: 512Mi

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
hack/e2e-install-cozystack.bats (1)

21-40: 💤 Low value

Consider trap-based cleanup for temporary files.

The current approach leaves temporary files in /tmp if the test fails before reaching line 40. While acceptable in a test environment, using a trap would ensure cleanup on both success and failure paths.

♻️ Optional improvement using trap
  local kubeovn_yaml linstor_yaml images_list
  kubeovn_yaml=$(mktemp)
  linstor_yaml=$(mktemp)
  images_list=$(mktemp)
+ trap 'rm -f "$kubeovn_yaml" "$linstor_yaml" "$images_list"' EXIT
  helm template packages/system/kubeovn > "$kubeovn_yaml"
  helm template packages/system/linstor > "$linstor_yaml"
  yq -N '
      (..|select(has("containers"))|.containers[]|.image),
      (..|select(has("initContainers"))|.initContainers[]|.image)
    ' "$kubeovn_yaml" "$linstor_yaml" > "$images_list"
  hack/e2e-prepull-images.sh < "$images_list"
- rm -f "$kubeovn_yaml" "$linstor_yaml" "$images_list"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/e2e-install-cozystack.bats` around lines 21 - 40, Add an EXIT trap to
ensure temporary files are removed even if the script fails: after creating the
temp filenames (kubeovn_yaml, linstor_yaml, images_list) register a trap that
deletes those files on EXIT (and unset or no-op if variables are empty), then
you can keep or remove the final rm -f "$kubeovn_yaml" "$linstor_yaml"
"$images_list" (it will be redundant but safe); ensure the trap references the
same variable names (kubeovn_yaml, linstor_yaml, images_list) and runs before
any commands that may fail (i.e., immediately after mktemp assignments).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@hack/e2e-install-cozystack.bats`:
- Around line 21-40: Add an EXIT trap to ensure temporary files are removed even
if the script fails: after creating the temp filenames (kubeovn_yaml,
linstor_yaml, images_list) register a trap that deletes those files on EXIT (and
unset or no-op if variables are empty), then you can keep or remove the final rm
-f "$kubeovn_yaml" "$linstor_yaml" "$images_list" (it will be redundant but
safe); ensure the trap references the same variable names (kubeovn_yaml,
linstor_yaml, images_list) and runs before any commands that may fail (i.e.,
immediately after mktemp assignments).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9ba14897-c627-4ebd-acb3-c4ef1619713f

📥 Commits

Reviewing files that changed from the base of the PR and between 28b02d5 and a7a0611.

📒 Files selected for processing (1)
  • hack/e2e-install-cozystack.bats

@github-actions github-actions Bot added size/L This PR changes 100-499 lines, ignoring generated files and removed size/XXL This PR changes 1000+ lines, ignoring generated files labels May 26, 2026
myasnikovdaniil added a commit that referenced this pull request May 27, 2026
Empty commit to fire a synchronize event against the new base
(split-seaweedfs-db) after rebasing onto PR #2601; the base-change
edited event does not trigger the Pull Request workflow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
myasnikovdaniil and others added 3 commits May 28, 2026 12:46
…edfs-system

Splits the seaweedfs-system HelmRelease in two so the CNPG
Cluster/seaweedfs-db lives in its own HR (seaweedfs-db), and the
application HR (seaweedfs-system) dependsOn it. The new HR uses Flux v2
HelmRelease.spec.healthCheckExprs with a CEL expression on
Cluster.status.conditions[type=Ready] plus waitStrategy.name: poller, so
its Ready=True only flips after the postgres primary is actually serving
connections — not just after helm install applied the Cluster CR.

This eliminates the seaweedfs-filer CrashLoopBackOff race on a fresh
tenant install. With Cilium kubeProxyReplacement: true, socket-LB
returns EPERM from connect(2) to ClusterIPs with no Ready endpoints.
Pre-split, the filer StatefulSet scheduled concurrently with the CNPG
bootstrap (~55–70s of unavailable postgres), each connect() failed
EPERM, kubelet exponential restart backoff pushed past the e2e bats
'kubectl wait hr/seaweedfs-system --timeout=2m' window, and the
'Configure Tenant and wait for applications' test failed.

healthCheckExprs uses Flux's three-predicate form: route
ClusterIsNotReady (the CNPG bootstrap condition) to `inProgress` so the
HR keeps polling, keep `failed` for other Ready=False reasons, and rely
on the HR `timeout: 10m` as the real backstop for a genuinely stuck
cluster. A plain Ready=False predicate would flag the HR failed within
seconds of creating the Cluster CR.

Migration 42 adopts existing Cluster/seaweedfs-db resources into the new
release on upgrade by rewriting meta.helm.sh/release-name and stamping
helm.sh/resource-policy: keep so the seaweedfs-system upgrade (which no
longer renders the Cluster) does not delete it during the transition.

The application HR's dependsOn collapses the db dependency (unconditional)
with the existing ingress dependency (guarded by `if eq $ingress
.Release.Namespace` so sub-tenants that inherit ingress from a parent
namespace don't deadlock on a non-existent local HR) into one list.

Requires Flux v2.8.x (helm-controller v1.5.0+) for healthCheckExprs.
Provided by the parent PR #2602 in this chain.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
PR #2619 split seaweedfs into a serialized chain
(seaweedfs-db -> seaweedfs-system -> seaweedfs) that needs ~5 min of
wall-clock to install: CNPG bootstrap, master quorum, then the wrapper.
With the prior 5m wait budget on the parent tenant HR, helm-controller
would time out before the children reach Ready, rollback would delete
the child HRs, and the upgrade would retry in a loop. The e2e bats step
that patches the Tenant CR and waits 60s for child HRs to exist landed
in the rollback gap and timed out.

Two complementary changes:

- tenant-root is created statically by cozystack-basics, so its
  HelmRelease spec.timeout is bumped 5m -> 15m directly.
- Sub-tenants (Tenant CRs) get their HR built by cozystack-api from
  the tenant ApplicationDefinition. Add the
  release.cozystack.io/helm-install-timeout=15m annotation that
  kubernetes-rd already uses to opt the kind into the longer budget.

Keeps both values in lockstep and documents the coupling inline.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
(cherry picked from commit cc9da3a)
values.yaml declared an unused Resources typedef while
templates/database.yaml hardcoded cpu/memory limits and requests. The
wrapper chart at packages/extra/seaweedfs/templates/seaweedfs-db.yaml
already passes db.resources through cozy-lib.resources.defaultingSanitize
into the sub-chart values, so the user-facing db.resources and
db.resourcesPreset on the outer chart silently dropped on the way in.

Drop the unused typedef, accept .Values.db.resources as a free-form
object (the wrapper already sanitizes it into the K8s nested
limits/requests shape that CNPG Cluster expects), and wire it through
to the Cluster manifest. Defaults are preserved verbatim so helm
template output is byte-identical for a no-override install.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
myasnikovdaniil added a commit that referenced this pull request May 28, 2026
Empty commit to fire a synchronize event against the new base
(split-seaweedfs-db) after rebasing onto PR #2601; the base-change
edited event does not trigger the Pull Request workflow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
The flux-bump-v048 base branch removed upgrade.force from every other
HelmRelease in 7f94182 because helm-controller v0.48 refuses to
combine Helm's --force replace with server-side apply's --force-conflicts
("cannot use force conflicts and force replace together"). This refactor
re-introduced the field on the seaweedfs-system HR by accident; CI E2E
fails with the same error in a loop until the seaweedfs subtree times
out the tenant install. Drop the line so the upgrade path matches every
other HR on the base branch.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
@myasnikovdaniil
myasnikovdaniil merged commit 0e8b46d into flux-bump-v048 May 28, 2026
9 checks passed
@myasnikovdaniil
myasnikovdaniil deleted the split-seaweedfs-db branch May 28, 2026 14:06
myasnikovdaniil added a commit that referenced this pull request May 29, 2026
Empty commit to fire a synchronize event against the new base
(split-seaweedfs-db) after rebasing onto PR #2601; the base-change
edited event does not trigger the Pull Request workflow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
myasnikovdaniil added a commit that referenced this pull request Jun 2, 2026
…(folds #2612) (#2602)

## What this PR does

Upgrades Flux v2.7.3 → v2.8.0 across both the vendored tenant chart and
the embedded management-cluster manifests, and folds in the chart fixes
that become hard errors under the new strict helm-controller v1.5.

Flux v2.8's helm-controller v1.5.0 ships:
- **Server-Side Apply with `--force-conflicts`** — strict CRD schema
enforcement; misplaced fields (silently dropped on v2.7) now fail the
apply.
- **kstatus-based health checking** by default — parent HR waits for
every applied resource (including child HRs) to be Ready before flipping
its own Ready, surfacing latent ordering deadlocks.
- **Helm v4 embedded** — `upgrade.force: true` is deprecated and now
conflicts with SSA.
- **`HelmRelease.spec.healthCheckExprs`** — prerequisite for proper
readiness gating (used in PR #2601 split).

Folds in PR #2612 (kubevirt-instancetypes null TPM fix) since the same
Flux upgrade triggers it.

## Commits

**Flux upgrade itself:**
- `feat(fluxcd)`: bump `flux-operator` / `flux-instance` vendored charts
to v0.48.0; web UI opt-in.
- `feat(flux)`: regenerate embedded management-cluster manifests via
`make update` in `packages/core/flux-aio` (timoni bundle build).

**Chart fixes for strict SSA — fields the chart sent that v2.7 silently
dropped, v2.8 rejects:**
- `fix(kubevirt-instancetypes)`: drop persistent strip that produced
null `preferredTPM` (folds #2612).
- `fix(foundationdb)`: relocate `faultDomain`, `imageType`, `labels`,
`minimumUptimeSecondsForBounce` from inside `automationOptions` to
direct children of `spec`.
- `fix(kafka)`: place `enableServiceLinks` under `template.pod`, not a
phantom `template.spec`.
- `fix(vm-instance)`: emit `disk: {}` (not `disk:`/null) when no bus is
set.
- `fix(platform)`: drop deprecated `upgrade.force: true` from
HelmReleases; fix `kafka` WorkloadMonitor `replicas` paths.

**Ordering / deadlock fixes under v2.8 kstatus:**
- `fix(vpa)`: break circular wait between parent install and nested
`vpa-for-vpa` HR.
- `fix(kubernetes)`: drop lookup-guarded parent-HR `dependsOn` on tenant
addon child HRs (parent waits on child via kstatus, child waited on
parent — deadlock).

**E2E waits for v2.8 kstatus timing:**
- `test(e2e)`: bump app HR-Ready waits to 5m (was 20s–100s under v2.7's
faster dispatch).
- `test(e2e)`: wait for parent HR Ready before downstream asserts in
`run-kubernetes.sh` and `vminstance.bats`.

## Scope discipline

This PR is part of the split of #2619 (the consolidated CI fixes branch)
into review-friendly pieces. Companion PRs:
- **PR #2601** (seaweedfs split) — folded into this PR (commits
`29c6afc8`, `0e8b46d7`, `7157158c`, `dccdeb52`, `f880b324`): the
seaweedfs-system → seaweedfs-db + seaweedfs-system split, its adoption
migration 43 (targetVersion 44), and the configurable db resources all
land here, because the strict-SSA `upgrade.force` removal and the
kstatus parent-HR timeout bump only make sense together with the split.
#2601 is superseded.
- **PR #2558** (drop 3× retry on `Run E2E` + `Install Cozystack`) —
independent, lands separately.
- Several smaller standalone fixes lifted out of #2619 (startup probes,
cert-manager `dependsOn`, prepull machinery, CSI HR timeout, NFS/OIDC
test improvements) — opened as separate PRs.

## Verification

- `helm template` renders cleanly for both `fluxcd` and
`fluxcd-operator` packages with `web.enabled=false` (default) and
`web.enabled=true`.
- Embedded `cmd/cozystack-operator` binary contains the v1.5.0 / v1.8.0
/ v2.1.0 controller image strings.
- No references to the v0.39-removed `--disable-wait-interruption` flag
anywhere in `packages/` or `internal/`.

### Release note

```release-note
Flux upgraded to v2.8.0 (helm-controller v1.5 — Helm v4 Server-Side Apply with --force-conflicts, kstatus health checking).

When upgrading existing clusters:
- Kubernetes 1.33+ is now required for the platform (management) cluster, and for any tenant cluster that enables the optional (default-off) Flux addon — that addon ships the bumped Flux too.
- HelmReleases no longer set `upgrade.force: true`. Helm v4 SSA resolves field-ownership conflicts automatically (`--force-conflicts`), but that is not the old client-side replace: immutable-field changes (e.g. StatefulSet volumeClaimTemplates/serviceName) no longer self-heal and require manual recreation — delete the object (e.g. `kubectl delete sts <name> --cascade=orphan`) and let Flux recreate it.
- KubeVirt: persistent TPM/EFI is re-enabled for the Windows 11/2k22/2k25 preferences (KubeVirt 1.8 VMPersistentState); each affected VM provisions an extra RWO backend-storage PVC from the default StorageClass.
- KubeVirt: the EOL centos.7*/centos.stream8* preferences are retained as deprecated, hidden aliases (`tags: hidden`, `instancetype.kubevirt.io/deprecated: "true"`) — existing VMInstances on these profiles keep rendering and need no action on upgrade, but the profiles are no longer offered for new VMs; repoint to centos.stream9/10 when convenient. The gn1.* GPU instancetypes are likewise retained.
- FoundationDB: imageType now reaches the operator (silently dropped pre-SSA); it is pinned to `split` to match the value existing clusters effectively ran, so upgrades stay non-disruptive. Set `imageType: unified` to migrate deliberately.
```


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

* **New Features**
* Flux Status web UI: optional web server, config secret, service port,
HTTPRoute/Ingress, network policy, and RBAC roles.
  * New SeaweedFS DB Helm chart and optional managed DB release.

* **Improvements**
* CRD/schema enhancements: new provider kinds, validations, variant
option, and external checksum refs.
* Raised Kubernetes prerequisite to 1.30+; extended e2e timeouts for
reliability.

* **Chores**
* Bumped Flux Operator and Flux versions; documentation links updated to
fluxoperator.dev.
* **Bug Fixes**
  * Removed aggressive HelmRelease force-upgrade/install flags.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2602?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
myasnikovdaniil added a commit that referenced this pull request Jun 5, 2026
Empty commit to fire a synchronize event against the new base
(split-seaweedfs-db) after rebasing onto PR #2601; the base-change
edited event does not trigger the Pull Request workflow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Aleksei Sviridkin (lexfrei) added a commit that referenced this pull request Jun 9, 2026
…ay.bats tenant teardown (#2558)

## What this PR does

Drops the 3× retry loop on `Run E2E tests` and `Install Cozystack into
sandbox`. `Prepare environment` keeps its 3× retry — that step is pure
infrastructure (Talos image download, sandbox VM boot, network) where
transient runner hiccups warrant a retry.

On failure, the test step now captures `kubectl get hr -A -o wide` and
`kubectl get events -A` under a collapsible group so triage starts with
the actual broken-state snapshot.

> [!NOTE]
> An earlier revision of this PR also doubled every bats timeout. That
commit was dropped in a rebase and is intentionally **not restored**:
the timeout class that actually matters (per-app HR-Ready waits) has
since been standardized at 5m on `main` (7b9f286), making a blanket 2×
redundant.

**Fixes gateway.bats teardown leakage.** The nested-tenant tests deleted
tenants fire-and-forget, parent and child back-to-back. The leftover
uninstalls (each blocked on a cleanup Job, parents wedged on
still-terminating child namespaces) plus one mid-install child HR
occupied exactly 5 workers on the `--concurrent=5` tenants
helm-controller shard, starving whichever app test ran next — observed
as the harbor HR sitting unreconciled for its whole 5m HR-Ready budget
in [run
27020081550](https://github.com/cozystack/cozystack/actions/runs/27020081550),
surfaced by this PR's own retry removal + diagnostics dump. Teardown now
deletes child→parent with hard `wait hr --for=delete` between, so a
wedged tenant uninstall fails gateway.bats itself, not an innocent
neighbor.

## Why

Audit of 30 successful PR runs found that across 5 sampled failure
attempts, **25/25 retries** on `Run E2E tests` failed — the retry loop
never recovered a flake, only stretched deterministic failures and
tripled diagnostic wall-time. Same data shape on `Install Cozystack`.

Beyond wasted CI time, the retry was hiding ~10 deterministic bugs (Helm
namespace-ownership conflict, seaweedfs HR timeout, harbor BucketInfo
wiring, vminstance disk race, etc.). Each failure looked like a "flake"
because the retry sometimes coincided with whatever transient state had
cleared — the retry never fixed the bug, just delayed surfacing.

## Dependencies

The deterministic bugs the retry was masking are now fixed on `main`:
- ✅ **#2508** — installer namespace bootstrap (Helm namespace-ownership
conflict on cold install) — merged
- ✅ **#2509** — operator HelmRelease config knobs (`seaweedfs-system`
2-min wait race within Flux's 5-min reconcile windows) — merged
- ✅ **#2528** — harbor bucket-secret + BucketInfo gating (harbor
`ValuesError` on first install) — merged
- ✅ **#2529** — objectstorage-controller BucketAccess conflict retry —
merged

Companion PRs in the #2619 split (independent of this PR,
ordering-wise):
- **#2602** — Flux v2.8.0 + chart fixes
- **#2601** — seaweedfs-system split

This PR does NOT depend on #2602/#2601 — it now touches only the
workflow file and gateway.bats teardown, both on top of fresh `main`.

Surfaced from #2500.

### Release note

```release-note
NONE
```


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

## Summary by CodeRabbit

* **Chores**
* CI prepare-environment step now reports plain attempt counts with
clear success/failure messages.
* Install and per-app test steps no longer retry; each runs once and
fails immediately on error. Failed apps log diagnostics and job proceeds
to remaining apps while overall job fails.
* **Tests**
* End-to-end tests and install/prepare flows use longer, more tolerant
timeouts and added existence polling to reduce flakiness and improve
diagnostics.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/cozystack/cozystack/pull/2558?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
myasnikovdaniil added a commit that referenced this pull request Jul 20, 2026
…st the default

Migration 43 performs the v1.5.0 db-split hand-over: it re-owns Cluster/seaweedfs-db
from <name>-system to the new <name>-db release and stamps
helm.sh/resource-policy: keep, so the <name>-system upgrade — whose post-split
chart no longer renders the Cluster — does not prune it as a removed resource.

It compared the owning release name against the literal "seaweedfs-system".
SeaweedFS is a user-creatable kind, so an instance named `foo` is owned by
`foo-system` and was silently skipped: no re-own, no keep. The <name>-system
upgrade then deleted the Cluster, CNPG took its PVC with it, and the filer
metadata — the index for every object in that tenant's S3 — was destroyed. Data
loss, not an outage: the volume PVCs still hold the bytes, nothing can find them.

Verified live on the upgrade stand, which is a natural control group. All five
tenants sit in the SAME failed-upgrade loop (<name>-system last-deployed is
rev 1, a pre-split revision whose manifest still contains the Cluster, so every
retry recomputes the same deletion):

  tenant-root/dsplit/l   instance `seaweedfs`  owner=seaweedfs-db  keep   Cluster alive
  tenant-named           instance `foo`        no Cluster at all          Cluster GONE

The only difference is that migration 43 fired for the default-named instances
and skipped `foo`. That also answers whether `keep` is sufficient: three tenants
survive the identical prune loop with it.

Correction to the reported mechanism: this is the 1.4->1.5 db split (PR #2601,
v1.5.0), not a 1.5->1.6 change — v1.5.3 and main are identical on every
db-split file. And the split does protect the object; it just only protected the
instances someone happened to name `seaweedfs`.

- lib/seaweedfs-db-adopt.sh: shared hand-over matching the `-system` SUFFIX, so
  `foo-system` -> `foo-db` exactly as `seaweedfs-system` -> `seaweedfs-db`.
  Idempotent, and refuses a release named literally `-system` rather than
  annotating an owner of `-db` that no release would claim.
- Migration 43 sources it: covers clusters upgrading from before 43 (1.4.x).
- Migration 53 (new) re-runs it: migrations never re-run, so any cluster already
  at >= 44 ran the hardcoded version and is still exposed. As a pre-upgrade hook
  it lands before <name>-system re-renders, closing the window on THIS upgrade.
  This is the one that matters for 1.5.x -> 1.6.
- targetVersion 53 -> 54.

Tests fail on unfixed code: the non-default-name case produces no ANNOTATE at
all against the hardcoded 43, while the default-name case passes — which is
precisely the bug's shape. They drive the real migration scripts against a fake
kubectl, following hack/migration-50-etcd-adopt.bats.

LIMIT, stated plainly: a Cluster that is already deleted cannot be recovered by
either migration — there is nothing left to re-annotate and the PVC went with
it. Such a tenant needs seaweedfs-db restored from a backup. Runbook Step 0 adds
the ownership audit that distinguishes at-risk from already-lost, and records
that <name>-db can report Ready while its Cluster is gone (it rendered fine; a
later <name>-system prune removed it and Flux has not re-checked) — so the
audit trusts `kubectl get cluster`, not the HelmRelease status.

Review round 2 — two ways this still lost the database:

- OWNERSHIP IS NOT SAFETY. The helper skipped any Cluster already owned by
  <name>-db, treating ownership as proof the hand-over was done. It is not: where
  the hand-over was skipped, <name>-system prunes the Cluster and <name>-db
  RECREATES it under its own ownership with NO keep, while <name>-system's prune
  baseline still lists it — so the next reconcile deletes it again. That is the
  observed delete/recreate loop, and the skip walked straight past it. Verified on
  the stand: tenant-l and tenant-root are seaweedfs-db-owned AND their
  <name>-system deployed revision (rev 1) still contains the Cluster, so only keep
  saves them; tenant-fresh is seaweedfs-db-owned without keep but was installed
  after the split, so its baseline never had one. Telling those apart needs the
  release's deployed manifest, which this script cannot read cheaply or reliably.
  The costs are asymmetric — a needless keep leaves an orphan on delete (now
  reclaimed by the cleanup hook), a missing one loses the database — so keep is
  stamped on every Cluster owned by either side of the split.

- FAIL OPEN, THEN STAMP. `set -euo pipefail` does not abort on a failing command
  substitution in a `for` word-list: the loop ran zero times, the script continued,
  and stamp_cozystack_version ran anyway. Migrations never re-run, so one throttle,
  RBAC hiccup, or not-yet-established CNPG apiservice behind the pre-upgrade hook
  permanently left every at-risk tenant exposed with no later migration to catch
  them. Reproduced: against the old helper a failing fleet scan still logged
  STAMP 54. Now every kubectl failure is fatal EXCEPT the two that genuinely mean
  "nothing to do" — the CNPG resource type not being served (the fail-open that IS
  load-bearing: a cluster without CNPG must still upgrade) and a Cluster vanishing
  between scan and read. An unreadable annotation is no longer indistinguishable
  from an absent one, and the unowned case now warns instead of passing silently.

- ORPHAN ON DELETE. `keep` is permanent (removal deferred to the 1.7 batch
  migrations) and also survives the <name>-db release's own uninstall, so
  `kubectl delete seaweedfs <name>` left the CNPG Cluster, its Postgres pods and
  its PVCs behind — the cleanup hook selects
  app.kubernetes.io/instance=<name>-system, which the db PVCs
  (cnpg.io/cluster=seaweedfs-db) do not carry. Migration 53 widened that from
  pre-1.5 default-named tenants to every instance name, so the hook now deletes
  the Cluster explicitly; its PVCs carry ownerReferences to it and follow.

- Tests assert the stamped VERSION (44 / 54), not a bare "STAMP": a wrong number
  would loop run-migrations.sh forever and the old assertion could not tell.

Review round 3 — the reclaim introduced a data-loss path of its own:

- DELETING ONE APP DELETED ANOTHER APP'S DATABASE. The db chart hardcodes the
  Cluster name `seaweedfs-db` for every instance, and the reclaim was scoped by
  that name in the namespace rather than by what the release owns.
  templates/seaweedfs-db.yaml gates the <name>-db HelmRelease on
  `topology != "Client"`; the cleanup hook had no such gate. Verified by render:
  `helm template s3-remote . --set topology=Client` emits ZERO s3-remote-db
  HelmReleases yet still emitted the Role granting delete on clusters/seaweedfs-db
  AND `kubectl delete cluster.postgresql.cnpg.io -n tenant-root seaweedfs-db`. So
  in a namespace running a server instance plus a Client, deleting the CLIENT
  destroyed the SERVER's filer metadata — CNPG takes the PVC via ownerReferences —
  for an app the operator never touched. Reachable the same way via a second Simple
  instance, whose <name>-db loses Helm's ownership check but which still installs
  and still carries the hook. main's hook was instance-scoped; this was new, and
  mine.
  Now gated exactly as seaweedfs-db.yaml is, and the Job re-checks
  meta.helm.sh/release-name == <release>-db at run time before deleting. The gate
  stops the permission being granted where it can never be legitimate; the runtime
  check stops the delete where it can.

- `|| echo` turned any API/authz/admission failure into exit zero, so backoffLimit
  never retried and hook-delete-policy removed the only cleanup Job while the
  keep-protected Cluster stayed orphaned. A failed reclaim now fails the Job.

- TEST SHELL. The migrations run under /bin/sh = busybox ash (the image is FROM
  alpine, run-migrations.sh is #!/bin/sh), but the tests drove them with bash — so
  the fail-closed guarantees were asserted in a shell that never runs them, which
  matters more than usual given the bug they cover was a fail-open. The scripts are
  now invoked via `sh` and the fake kubectl is POSIX sh. Verified directly against
  the production base image (busybox 1.37.0 / alpine:3.24): `set -o pipefail` is
  supported, every script passes `ash -n`, and the failing-fleet-scan path exits
  non-zero without stamping under real ash. The recipe is in the bats header.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/platform Issues or PRs related to platform infrastructure (bundle, flux, talos, installer) area/storage Issues or PRs related to storage (linstor, seaweedfs, bucket, velero, harbor) area/testing Issues or PRs related to testing (e2e, bats, unit tests) kind/cleanup Categorizes issue or PR as related to cleanup of code, process, or technical debt size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant