#!/bin/sh
# Migration 55 --> 56
# Remove the tenant-cluster FluxCD addon (packages/system/fluxcd-operator and
# packages/system/fluxcd).
#
# Those charts were the last vendored copyleft-licensed content in the
# repository and only served the optional `addons.fluxcd` toggle of the
# Kubernetes app, which deployed a Flux Operator + FluxInstance into a tenant
# cluster through a pair of HelmReleases living in the management cluster. The
# addon is dropped as a breaking change.
#
# For any tenant that had it enabled, orphan those HelmReleases rather than let
# them be pruned: suspend reconciliation, drop the Flux finalizer so the delete
# does NOT trigger a Helm uninstall inside the tenant cluster, then delete the
# now-inert object from the management cluster. The Flux release already running
# inside the tenant keeps running untouched. This mirrors the orphan idiom used
# by migration 29.
#
# Runs as a pre-upgrade hook, i.e. before the new (addon-less) Kubernetes chart
# reaches the tenant HelmReleases, so the finalizer is removed before any
# pruning can fire.
#
# Fail-closed. Dropping the finalizer is what this script relies on to keep the
# delete below from triggering a Helm uninstall inside the tenant. (On
# helm-controller v1.5.0 the suspend above also blocks that uninstall, but the
# fence lives in the controller, not here, so we do not lean on it.) Migrations
# never re-run, so a swallowed error would stamp the version on a half-orphaned
# fleet and never look back. Every step therefore runs under `set -euo pipefail`
# with no `|| true`, and the delete is gated on the finalizer actually being
# gone — verified with a read that aborts on any API error, not one that treats
# a failed read as "gone". If anything fails the migration stops (recoverable)
# rather than deleting a still-finalized object (not).

set -euo pipefail

# shellcheck source=lib/cozystack-version.sh
. "$(dirname "$0")/lib/cozystack-version.sh"

# The addon HelmReleases are the only ones referencing these ExternalArtifact
# charts, so match on chartRef.name to select them across every tenant namespace.
# No `|| true`: a failed list must stop the migration, not silently proceed to
# stamp the version on a fleet we never actually inspected.
hrs=$(kubectl get helmreleases.helm.toolkit.fluxcd.io --all-namespaces -o json \
  | jq -r '.items[]
      | select(.spec.chartRef.name == "cozystack-kubernetes-application-kubevirt-kubernetes-fluxcd"
            or .spec.chartRef.name == "cozystack-kubernetes-application-kubevirt-kubernetes-fluxcd-operator")
      | "\(.metadata.namespace) \(.metadata.name)"')

# Feed the loop with a heredoc, not a pipe, so it runs in this shell and an
# `exit 1` below aborts the whole migration instead of only its subshell.
if [ -n "$hrs" ]; then
  while read -r ns name; do
    [ -n "$ns" ] || continue
    echo "Orphaning FluxCD addon HelmRelease ${ns}/${name}"
    # Suspend so Flux stops reconciling while we detach the finalizer.
    kubectl -n "$ns" patch helmreleases.helm.toolkit.fluxcd.io "$name" --type=merge \
      -p '{"spec":{"suspend":true}}'
    # Drop the Flux finalizer. Not best-effort: this is what makes the delete
    # inert, so a failure here must abort (set -e), never fall through to delete.
    kubectl -n "$ns" patch helmreleases.helm.toolkit.fluxcd.io "$name" --type=merge \
      -p '{"metadata":{"finalizers":null}}'
    # Verify the finalizer is actually gone before deleting. If the patch was
    # rejected (webhook, RBAC, conflict, transient apiserver error) the object
    # still carries it, and deleting now would set a deletionTimestamp that makes
    # helm-controller run the very uninstall this migration exists to avoid.
    # --ignore-not-found, not `2>/dev/null || true`: a concurrently-deleted
    # object reads empty with rc 0, but a real API error aborts under set -e
    # instead of passing the gate on an object whose state was never read.
    remaining=$(kubectl -n "$ns" get helmreleases.helm.toolkit.fluxcd.io "$name" \
      --ignore-not-found -o jsonpath='{.metadata.finalizers}')
    if [ -n "$remaining" ]; then
      echo "ERROR: finalizer still present on ${ns}/${name} after patch (${remaining}); refusing to delete" >&2
      exit 1
    fi
    kubectl -n "$ns" delete helmreleases.helm.toolkit.fluxcd.io "$name" --ignore-not-found --wait=false
  done <<EOF
$hrs
EOF
fi

# Stamp version
stamp_cozystack_version 56
