Skip to content

[tenant][kubernetes] Introduce better cleanup logic - #1661

Merged
Andrei Kvapil (kvaps) merged 3 commits into
mainfrom
cleanup
Nov 27, 2025
Merged

[tenant][kubernetes] Introduce better cleanup logic#1661
Andrei Kvapil (kvaps) merged 3 commits into
mainfrom
cleanup

Conversation

@kvaps

@kvaps Andrei Kvapil (kvaps) commented Nov 26, 2025

Copy link
Copy Markdown
Member

Signed-off-by: Andrei Kvapil kvapss@gmail.com

What this PR does

Release note

[tenant][kubernetes] Introduce better cleanup logic

Summary by CodeRabbit

  • New Features

    • Added an automated pre-delete cleanup job for tenant namespaces to remove tenant-related releases during uninstall.
  • Improvements

    • Cleanup now runs early in the uninstall sequence with a clearer, stepwise orchestration for resource removal.
    • Expanded permissions and execution allowances to enable the cleanup workflow.
    • Deployment annotations now use a content checksum to better detect config changes.
  • Removals

    • Previous teardown sequence for certain release types was removed and replaced by the new workflow.

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

Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
@dosubot dosubot Bot added the size/L This PR changes 100-499 lines, ignoring generated files label Nov 26, 2025
@coderabbitai

coderabbitai Bot commented Nov 26, 2025

Copy link
Copy Markdown
Contributor

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Pre-delete hook timing added and reorganized: kubernetes delete job now runs as pre-delete with expanded RBAC and an ordered multi-step cleanup; helmreleases teardown template removed; a new tenant-scoped pre-delete cleanup job was introduced; two dashboard deployments receive checksum annotation changes.

Changes

Cohort / File(s) Change Summary
Kubernetes delete orchestration
packages/apps/kubernetes/templates/delete.yaml
Hook annotations moved from post-delete to pre-delete for Job, ServiceAccount, Role, RoleBinding. Job gained metadata.label policy.cozystack.io/allow-to-apiserver: "true", tolerations, and a multi-step shell script that suspends HelmReleases, deletes HelmReleases, Kamaji/Tenant control planes, DataVolumes, and LoadBalancer Services. Role rules expanded to include helmreleases, kamajicontrolplanes, tenantcontrolplanes (get/list/watch/patch/delete); datavolumes and services now include watch.
Removed Flux teardown template
packages/apps/kubernetes/templates/helmreleases/delete.yaml
Entire pre-delete teardown (Job, ServiceAccount, Role, RoleBinding) for Flux HelmReleases removed; orchestration consolidated into other templates.
Tenant-scoped cleanup job
packages/apps/tenant/templates/cleanup-job.yaml
New file: ServiceAccount, Role, RoleBinding, and Job annotated as pre-delete (hook-weight controlled). Job runs bitnami/kubectl script to delete HelmReleases in two cohorts using label selectors (applications vs tenant modules), uses --ignore-not-found/--wait, prints progress, and sets ttlSecondsAfterFinished: 300.
Dashboard checksum annotation updates
packages/system/dashboard/templates/nginx.yaml, packages/system/dashboard/templates/web.yaml
Replaced static/null annotations with dynamic checksum/config values computing sha256 of the relevant configmap/template content.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Helm as Helm Uninstall
    participant PreDeleteJob as Pre-delete Cleanup Job
    participant K8s as Kubernetes API
    note right of PreDeleteJob `#DDEBF7`: Kubernetes delete orchestration\n(suspend -> delete -> control planes -> datavolumes -> services)
    Helm->>PreDeleteJob: trigger pre-delete hooks
    PreDeleteJob->>K8s: patch (suspend) HelmReleases
    PreDeleteJob->>K8s: delete HelmReleases
    PreDeleteJob->>K8s: delete KamajiControlPlane / TenantControlPlane
    PreDeleteJob->>K8s: delete DataVolumes (cdi.kubevirt.io)
    PreDeleteJob->>K8s: delete LoadBalancer Services (core/v1)
    K8s-->>PreDeleteJob: responses / status
    PreDeleteJob-->>Helm: cleanup complete
Loading
sequenceDiagram
    autonumber
    participant TenantHelm as Tenant Uninstall
    participant TenantJob as Tenant Cleanup Job
    participant K8s as Kubernetes API
    note right of TenantJob `#F6F3E8`: Two cohorts: Applications\n(cozystack.io/ui=true, tenantmodule!=true)\nTenant Modules (tenantmodule=true)
    TenantHelm->>TenantJob: trigger pre-delete hook
    TenantJob->>K8s: delete HelmReleases (applications selector)
    K8s-->>TenantJob: deleted/ignored
    TenantJob->>K8s: delete HelmReleases (tenant modules selector)
    K8s-->>TenantJob: deleted/ignored
    TenantJob-->>TenantHelm: finished (TTL enforced)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Files span multiple charts and change hook timing, RBAC, job scripts, and remove an entire template — review must verify ordering, permissions, and that no teardown is orphaned.
  • Pay extra attention to:
    • Multi-step Job shell script sequencing and kubectl flags (--wait usage, label selectors)
    • RBAC expansions (ensure least privilege and correct API groups/resource names)
    • Hook weights and pre-delete timing implications (race conditions)
    • Removal of helmreleases/delete.yaml — ensure no dependent logic elsewhere
    • Checksum template changes reference correct configmap content paths

Possibly related PRs

Suggested reviewers

  • lllamnyp
  • nbykov0

Poem

🐇 I hopped in with scripts and a tidy plan,

Suspended releases with a careful hand.
Roles widened, jobs now run before goodbyes,
Tenants and apps waved under azure skies.
TTL set, echoes of deleted ties — hooray, cleanup done!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: introducing improved cleanup logic for tenant and Kubernetes resources, which aligns with the primary modifications across multiple cleanup-related templates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cleanup

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d46cccd and 0df528a.

📒 Files selected for processing (2)
  • packages/system/dashboard/templates/nginx.yaml (1 hunks)
  • packages/system/dashboard/templates/web.yaml (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (2)
packages/system/dashboard/templates/web.yaml (1)

18-19: Verification confirms the annotation change is correct and follows established patterns.

The configmap.yaml file exists at packages/system/dashboard/templates/configmap.yaml, and the Helm template syntax is correct. This checksum annotation pattern is consistently used throughout the codebase (18+ locations) and will properly trigger pod restarts when the configuration changes.

packages/system/dashboard/templates/nginx.yaml (1)

19-19: Verification confirms all claims in the review comment.

The annotation pattern is correctly implemented:

  • ✓ nginx-config.yaml exists in the template directory
  • ✓ Pattern matches web.yaml's approach (line 19 of web.yaml uses identical structure with configmap.yaml)
  • ✓ Annotation is properly placed in pod template metadata
  • ✓ Helm template syntax is correct and will dynamically compute checksums to trigger pod restarts on config changes

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.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello Andrei Kvapil (@kvaps), 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 significantly refactors and enhances the cleanup logic for Kubernetes resources managed by Helm. The core change involves shifting the main cleanup operations to a pre-delete hook, ensuring resources are properly deprovisioned before the Helm release is fully gone. It also generalizes the cleanup of HelmReleases and introduces dedicated cleanup for Kamaji and TenantControlPlane resources, alongside a new, specialized cleanup job for tenant-specific applications and modules. These changes aim to improve the robustness and completeness of resource teardown.

Highlights

  • Cleanup Hook Timing: The primary Kubernetes cleanup job has been reconfigured to run as a pre-delete Helm hook instead of post-delete. This ensures that critical resources are cleaned up before the Helm release is fully removed, preventing orphaned resources.
  • Expanded Kubernetes Cleanup Logic: The main cleanup script for Kubernetes resources has been significantly enhanced. It now includes steps to suspend and delete HelmReleases, and to delete KamajiControlPlane and TenantControlPlane resources, in addition to the existing DataVolumes and LoadBalancer Services cleanup.
  • Generalized HelmRelease Teardown: The previous dedicated helmreleases/delete.yaml file, which contained specific logic for suspending individual HelmReleases, has been removed. Its functionality has been generalized and integrated into the main cleanup job, using label-based selection for HelmReleases.
  • Dedicated Tenant Cleanup Job: A new cleanup-job.yaml has been introduced for tenant applications. This job specifically targets and deletes HelmReleases within a tenant's namespace, categorizing them into 'Applications' and 'Tenant Modules' based on their labels, ensuring proper deprovisioning of tenant-specific resources.
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 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 counter productive. 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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

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.

@kvaps Andrei Kvapil (kvaps) changed the title Introduce better cleanup logic [tenant][kubernetes] Introduce better cleanup logic Nov 26, 2025
@dosubot dosubot Bot added the kind/feature Categorizes issue or PR as related to a new feature label Nov 26, 2025

@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 introduces a more robust cleanup logic for both the kubernetes and tenant Helm charts by moving the cleanup hooks from post-delete to pre-delete. This ensures resources are cleaned up in a specific order before Helm removes the release. The kubernetes chart's cleanup is improved by using a label selector to dynamically find and manage HelmRelease resources, replacing a previous hardcoded list. A new cleanup job is also added for the tenant chart. These are positive changes, but I've found a few issues that could impact the reliability of the cleanup process. My review includes suggestions to improve error handling in the cleanup script, pin the version of a container image, and correct RBAC permissions to ensure the jobs can execute successfully.

Comment on lines +36 to +38
kubectl -n {{ .Release.Namespace }} patch "$hr" \
-p '{"spec": {"suspend": true}}' \
--type=merge --field-manager=flux-client-side-apply

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 kubectl patch command is not resilient to failures. Because set -e is active, if patching a single HelmRelease fails, the entire cleanup script will exit. This would prevent other HelmReleases from being suspended and block the subsequent cleanup steps. The previous implementation in the removed helmreleases/delete.yaml file handled this by appending || true to the patch command. To make the cleanup job more robust, consider adding similar resilience here.

                  kubectl -n {{ .Release.Namespace }} patch "$hr" \
                    -p '{"spec": {"suspend": true}}' \
                    --type=merge --field-manager=flux-client-side-apply || true

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Not relevant we need these errors

Comment thread packages/apps/tenant/templates/cleanup-job.yaml
Comment thread packages/apps/tenant/templates/cleanup-job.yaml

@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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/apps/kubernetes/templates/delete.yaml (1)

1-9: ---

Quote the annotation value on line 8 to fix the YAML syntax error.

The helm.sh/hook-delete-policy annotation value contains commas and must be quoted according to YAML spec. yamllint correctly reports a syntax error.

Change line 8 from:

    "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation,hook-failed

To:

    "helm.sh/hook-delete-policy": "hook-succeeded,before-hook-creation,hook-failed"

Note: This same pattern appears in other files (packages/apps/tenant/templates/cleanup-job.yaml and others), which also trigger the same yamllint error and should be corrected similarly.

🧹 Nitpick comments (4)
packages/apps/kubernetes/templates/delete.yaml (2)

32-40: Verify safety of the HelmRelease suspension loop.

The loop at lines 33-40 iterates over kubectl get ... -o name output. The inline check if [ -n "$hr" ]; then is redundant because get -o name only outputs names (or nothing if no matches). However, if the kubectl command itself fails, the script will exit (due to set -e) before any errors are logged. Add explicit error handling for the kubectl call itself.

- for hr in $(kubectl -n {{ .Release.Namespace }} get helmreleases.helm.toolkit.fluxcd.io -l "cozystack.io/target-cluster-name={{ .Release.Name }}" -o name 2>/dev/null || true); do
-   if [ -n "$hr" ]; then           
-     echo "  Suspending $hr"
-     kubectl -n {{ .Release.Namespace }} patch "$hr" \
-       -p '{"spec": {"suspend": true}}' \
-       --type=merge --field-manager=flux-client-side-apply
-   fi
- done
+ for hr in $(kubectl -n {{ .Release.Namespace }} get helmreleases.helm.toolkit.fluxcd.io -l "cozystack.io/target-cluster-name={{ .Release.Name }}" -o name 2>/dev/null || echo ""); do
+   echo "  Suspending $hr"
+   kubectl -n {{ .Release.Namespace }} patch "$hr" \
+     -p '{"spec": {"suspend": true}}' \
+     --type=merge --field-manager=flux-client-side-apply
+ done

Removes the redundant [ -n "$hr" ] check and simplifies the logic.


88-116: Verify that the 'watch' verb is necessary for HelmRelease patch operations.

The Role grants watch verb for datavolumes and services (newly added), and the HelmReleases rule includes patch verb (line 96). Typically, watch is needed for controller-managed operations, but for direct imperative patches, it may not be required. Confirm whether the watch verb is necessary, or if it should be removed to follow least-privilege principles.

Also, verify that patch verb with --type=merge --field-manager=flux-client-side-apply is the correct approach for Flux objects. This uses server-side apply semantics; ensure the field-manager alignment is intentional.

If watch is not needed for patch operations, consider removing it:

  - apiGroups:
      - "helm.toolkit.fluxcd.io"
    resources:
      - helmreleases
    verbs:
      - get
      - list
-     - watch
      - patch
      - delete
packages/apps/tenant/templates/cleanup-job.yaml (2)

70-85: Improve error handling and clarify the two-phase HelmRelease deletion strategy.

The cleanup script lacks explicit error handling and logging similar to the concern in file 1. Additionally, the script deletes HelmReleases in two cohorts (Applications and Tenant Modules). Clarify:

  1. Why are they split into two cohorts? (e.g., ordering dependency, different deletion semantics)
  2. Is there any risk of the second cohort failing if the first succeeds?
  3. Should both cohorts run even if the first fails, or should the script halt on first failure?

Consider adding error handling:

  - /bin/bash
  - -c
  - |
    set -e
    NAMESPACE="{{ include "tenant.name" . }}"
    
    echo "Cleaning up HelmReleases in namespace: $NAMESPACE"
    
    echo "Deleting Applications"
-   kubectl delete helmreleases.helm.toolkit.fluxcd.io -n "$NAMESPACE" \
+   kubectl delete helmreleases.helm.toolkit.fluxcd.io -n "$NAMESPACE" \
      -l 'cozystack.io/ui=true,internal.cozystack.io/tenantmodule!=true' \
-     --ignore-not-found=true --wait=true
+     --ignore-not-found=true --wait=true || echo "Warning: Failed to delete Applications"
    
    echo "Deleting Tenant Modules"
-   kubectl delete helmreleases.helm.toolkit.fluxcd.io -n "$NAMESPACE" \
+   kubectl delete helmreleases.helm.toolkit.fluxcd.io -n "$NAMESPACE" \
      -l 'cozystack.io/ui=true,internal.cozystack.io/tenantmodule=true' \
-     --ignore-not-found=true --wait=true
+     --ignore-not-found=true --wait=true || echo "Warning: Failed to delete Tenant Modules"
    
    echo "Cleanup completed successfully"

This logs warnings if either cohort deletion fails while still completing cleanup.


43-52: Verify pre-delete hook execution ordering across kubernetes and tenant cleanup jobs.

File 1 (kubernetes/delete.yaml) defines a pre-delete Job with weights: ServiceAccount (0), Role (5), RoleBinding (5), Job (10).

File 2 (tenant/cleanup-job.yaml) defines a pre-delete Job with weights: ServiceAccount/Role/RoleBinding (-5), Job (0).

Lower weights execute first in Helm hooks, so tenant cleanup (weight -5 Job) runs before kubernetes cleanup (weight 10 Job). Verify this ordering is intentional:

  • If tenant cleanup must complete before cluster cleanup, this is correct.
  • If cluster cleanup should occur first, adjust tenant Job weight to be higher than 10.
  • If there's no strict dependency, document this to prevent future confusion.

Also confirm that the tenant namespace is still accessible when kubernetes-level cleanup (which deletes control planes) runs.

Document the hook weight rationale in comments or a separate ADR explaining why tenant cleanup (weight 0) runs before kubernetes cleanup (weight 10).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa42845 and d46cccd.

📒 Files selected for processing (3)
  • packages/apps/kubernetes/templates/delete.yaml (4 hunks)
  • packages/apps/kubernetes/templates/helmreleases/delete.yaml (0 hunks)
  • packages/apps/tenant/templates/cleanup-job.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/apps/kubernetes/templates/helmreleases/delete.yaml
🧰 Additional context used
🪛 YAMLlint (1.37.1)
packages/apps/tenant/templates/cleanup-job.yaml

[error] 5-5: syntax error: expected , but found ''

(syntax)

packages/apps/kubernetes/templates/delete.yaml

[error] 9-9: syntax error: expected , but found ''

(syntax)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (1)
packages/apps/kubernetes/templates/delete.yaml (1)

12-14: The policy label correctly enables Cilium network policy for required API server access.

The policy.cozystack.io/allow-to-apiserver: "true" label controls a Cilium NetworkPolicy (defined in packages/apps/tenant/templates/networkpolicy.yaml lines 136-145) that permits egress to the kube-apiserver. This cleanup job legitimately requires API access to execute multiple kubectl operations: suspending/deleting HelmReleases, deleting KamajiControlPlane and TenantControlPlane resources, cleaning up DataVolumes, and removing LoadBalancer Services. The label aligns with the security posture by combining explicit RBAC permissions (Role with scoped verbs) with network-level access control (Cilium policy selector).

Comment on lines +30 to +66
- |
set -e
echo "Step 1: Suspending all HelmReleases with label cozystack.io/target-cluster-name={{ .Release.Name }}"
for hr in $(kubectl -n {{ .Release.Namespace }} get helmreleases.helm.toolkit.fluxcd.io -l "cozystack.io/target-cluster-name={{ .Release.Name }}" -o name 2>/dev/null || true); do
if [ -n "$hr" ]; then
echo " Suspending $hr"
kubectl -n {{ .Release.Namespace }} patch "$hr" \
-p '{"spec": {"suspend": true}}' \
--type=merge --field-manager=flux-client-side-apply
fi
done

kubectl -n {{ .Release.Namespace }} delete services
-l "cluster.x-k8s.io/cluster-name={{ .Release.Name }}"
--field-selector spec.type=LoadBalancer
--ignore-not-found=true
echo "Step 2: Deleting HelmReleases with label cozystack.io/target-cluster-name={{ .Release.Name }}"
kubectl -n {{ .Release.Namespace }} delete helmreleases.helm.toolkit.fluxcd.io \
-l "cozystack.io/target-cluster-name={{ .Release.Name }}" \
--ignore-not-found=true --wait=true

echo "Step 3: Deleting KamajiControlPlane {{ .Release.Name }}"
kubectl -n {{ .Release.Namespace }} delete kamajicontrolplanes.controlplane.cluster.x-k8s.io {{ .Release.Name }} \
--ignore-not-found=true

echo "Step 4: Deleting TenantControlPlane {{ .Release.Name }}"
kubectl -n {{ .Release.Namespace }} delete tenantcontrolplanes.kamaji.clastix.io {{ .Release.Name }} \
--ignore-not-found=true

echo "Step 5: Cleaning up DataVolumes"
kubectl -n {{ .Release.Namespace }} delete datavolumes \
-l "cluster.x-k8s.io/cluster-name={{ .Release.Name }}" \
--ignore-not-found=true

echo "Step 6: Cleaning up LoadBalancer Services"
kubectl -n {{ .Release.Namespace }} delete services \
-l "cluster.x-k8s.io/cluster-name={{ .Release.Name }}" \
--field-selector spec.type=LoadBalancer \
--ignore-not-found=true

echo "Cleanup completed successfully"

@coderabbitai coderabbitai Bot Nov 26, 2025

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.

🛠️ Refactor suggestion | 🟠 Major

Improve error handling and logging in the cleanup script.

The shell script uses set -e for fail-fast behavior, but lacks explicit error context. If any step fails mid-execution (e.g., during the HelmRelease suspension loop), subsequent steps are skipped without detailed indication of what failed. Add error handling to:

  • Capture and report the specific step/resource that failed
  • Ensure idempotence even if individual commands partially fail (some succeed, some don't)
  • Log which resources were successfully cleaned vs. which were skipped/failed

Consider using || echo "...failed (continuing)" || true for non-critical steps, or wrap with explicit error recovery logic.

Consider this refactored approach:

  - /bin/sh
  - -c
  - |
    set -e
    NAMESPACE="{{ .Release.Namespace }}"
    CLUSTER_NAME="{{ .Release.Name }}"
    FAILED=false
    
    echo "Step 1: Suspending all HelmReleases with label cozystack.io/target-cluster-name=$CLUSTER_NAME"
    if kubectl -n "$NAMESPACE" get helmreleases.helm.toolkit.fluxcd.io \
        -l "cozystack.io/target-cluster-name=$CLUSTER_NAME" -o name 2>/dev/null | grep . &>/dev/null; then
      kubectl -n "$NAMESPACE" get helmreleases.helm.toolkit.fluxcd.io \
        -l "cozystack.io/target-cluster-name=$CLUSTER_NAME" -o name | while read hr; do
        echo "  Suspending $hr"
        kubectl -n "$NAMESPACE" patch "$hr" \
          -p '{"spec": {"suspend": true}}' \
          --type=merge --field-manager=flux-client-side-apply || { echo "  Failed to suspend $hr"; FAILED=true; }
      done
    else
      echo "  No HelmReleases found"
    fi
    
    echo "Step 2: Deleting HelmReleases with label cozystack.io/target-cluster-name=$CLUSTER_NAME"
    kubectl -n "$NAMESPACE" delete helmreleases.helm.toolkit.fluxcd.io \
      -l "cozystack.io/target-cluster-name=$CLUSTER_NAME" \
      --ignore-not-found=true --wait=true || { echo "  Failed to delete HelmReleases"; FAILED=true; }
    
    # ... continue for remaining steps ...
    
    if [ "$FAILED" = true ]; then
      echo "Cleanup completed with errors"
      exit 1
    else
      echo "Cleanup completed successfully"
    fi

This approach:

  • Uses variables to avoid template rendering on every line
  • Checks if resources exist before attempting operations
  • Tracks failures but allows subsequent steps to execute
  • Provides clear per-step error reporting

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/apps/kubernetes/templates/delete.yaml around lines 30-66, the
Helm/cleanup shell block uses set -e and has no per-step error context or
recovery which aborts the whole cleanup on first failure and hides which
resources succeeded or failed; update the script to assign .Release.Name and
.Release.Namespace to local variables once, remove brittle global set -e, wrap
each logical step (suspend HelmReleases, delete HelmReleases, delete
KamajiControlPlane, delete TenantControlPlane, delete DataVolumes, delete
LoadBalancer Services) in a small handler that: checks for existence before
acting, runs the kubectl command but captures its exit code, logs a clear
success or failure message including resource type/name and step number, records
failures in a cumulative flag or list, and continues to the next step; at the
end, print a summary of succeeded vs failed steps and exit with non-zero only if
any critical failures recorded (or zero if all non-critical), ensuring
idempotence by using --ignore-not-found where appropriate and using || true only
for explicitly non-fatal commands.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Not relevant we need these errors

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.

Andrei Kvapil (@kvaps), understood! I'll keep the current fail-fast behavior with set -e. The cleanup will halt immediately on any error, ensuring failures are visible and require attention.

Comment thread packages/apps/tenant/templates/cleanup-job.yaml
restartPolicy: OnFailure
containers:
- name: cleanup
image: bitnami/kubectl:latest

@coderabbitai coderabbitai Bot Nov 26, 2025

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

Pin the container image to a specific version.

Line 65 uses bitnami/kubectl:latest, which is not reproducible and will pull different image versions across deployments. Replace with a specific version tag to ensure consistency.

-        image: bitnami/kubectl:latest
+        image: bitnami/kubectl:1.32.0

Verify the available versions and compatibility with your Kubernetes clusters. For pinning best practices, consider using image digests (SHA256) for maximum immutability, though version tags are also acceptable.

🤖 Prompt for AI Agents
In packages/apps/tenant/templates/cleanup-job.yaml around line 65, the container
image is pinned to the non-reproducible tag "bitnami/kubectl:latest"; change
this to a specific version tag or, ideally, an image digest (sha256) to ensure
consistent, immutable deployments; update the image field to a chosen stable tag
(e.g., bitnami/kubectl:<version>) or to the digest form
(bitnami/kubectl@sha256:<digest>), verify the tag/digest exists and is
compatible with your cluster, and update any documentation or deployment CI that
relied on "latest".

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

we use it in many cases like that, let's fix it in other PR

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.

Andrei Kvapil (@kvaps), understood! Since this pattern exists in multiple places across the codebase, would you like me to create a GitHub issue to track the work of pinning container image versions? That way it won't be forgotten and can be addressed systematically in a follow-up PR.

…nges

Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
@kvaps Andrei Kvapil (kvaps) added the backport Should change be backported on previous release label Nov 26, 2025
Comment thread packages/apps/kubernetes/templates/delete.yaml
…nges (#1662)

## What this PR does

This PR adds config hash annotations to the dashboard deployment
templates to ensure pods are automatically restarted when their
configuration changes.

Changes:
- Added `checksum/config` annotation to `nginx.yaml` deployment that
computes a hash of `nginx-config.yaml`
- Added `checksum/config` annotation to `web.yaml` deployment that
computes a hash of `configmap.yaml`

This replaces the hardcoded checksum in nginx.yaml with a dynamically
computed one, and adds a new checksum for the web deployment.

### Release note

```release-note
[dashboard] Add config hash annotations to automatically restart pods when configuration changes
```

@nbykov0 Nikita (nbykov0) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Nov 27, 2025
@kvaps
Andrei Kvapil (kvaps) merged commit aa8062c into main Nov 27, 2025
22 checks passed
@kvaps
Andrei Kvapil (kvaps) deleted the cleanup branch November 27, 2025 13:32
@github-actions

Copy link
Copy Markdown

Backport failed because this pull request contains merge commits. You can either backport this pull request manually, or configure the action to skip merge commits.

Andrei Kvapil (kvaps) added a commit that referenced this pull request Nov 27, 2025
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[tenant][kubernetes] Introduce better cleanup logic
```

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

* **New Features**
* Added an automated pre-delete cleanup job for tenant namespaces to
remove tenant-related releases during uninstall.

* **Improvements**
* Cleanup now runs early in the uninstall sequence with a clearer,
stepwise orchestration for resource removal.
* Expanded permissions and execution allowances to enable the cleanup
workflow.
* Deployment annotations now use a content checksum to better detect
config changes.

* **Removals**
* Previous teardown sequence for certain release types was removed and
replaced by the new workflow.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Andrei Kvapil (kvaps) added a commit that referenced this pull request Nov 27, 2025
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[tenant][kubernetes] Introduce better cleanup logic
```

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

* **New Features**
* Added an automated pre-delete cleanup job for tenant namespaces to
remove tenant-related releases during uninstall.

* **Improvements**
* Cleanup now runs early in the uninstall sequence with a clearer,
stepwise orchestration for resource removal.
* Expanded permissions and execution allowances to enable the cleanup
workflow.
* Deployment annotations now use a content checksum to better detect
config changes.

* **Removals**
* Previous teardown sequence for certain release types was removed and
replaced by the new workflow.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport Should change be backported on previous release kind/feature Categorizes issue or PR as related to a new feature lgtm This PR has been approved by a maintainer size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants