From 03426fbd718029e40d6680f299189e97f915a614 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Thu, 16 Apr 2026 21:13:06 +0300 Subject: [PATCH] test(hack): pin HelmRelease v2 status shape used by remediation guard run-kubernetes.sh extracts .status.installFailures and .status.upgradeFailures via kubectl -o jsonpath. If a future flux release renames the counters, kubectl returns empty, the guard reports no cycle, and e2e silently misses real remediation loops. Add a bats unit test that feeds a pinned HelmRelease v2 status snippet through the same jsonpath and asserts the extraction still yields the expected values. Also leave a pointer comment in run-kubernetes.sh so a future flux bump surfaces the version coupling in review. Assisted-By: Claude Signed-off-by: Aleksei Sviridkin --- hack/e2e-apps/run-kubernetes.sh | 7 ++++++ hack/remediation-guard.bats | 44 +++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/hack/e2e-apps/run-kubernetes.sh b/hack/e2e-apps/run-kubernetes.sh index 811f42a7..e6002b53 100644 --- a/hack/e2e-apps/run-kubernetes.sh +++ b/hack/e2e-apps/run-kubernetes.sh @@ -326,6 +326,13 @@ EOF # A non-zero installFailures/upgradeFailures indicates the helm-wait budget expired while # admin-kubeconfig was still being provisioned, which would trigger uninstall remediation # and churn the Cluster CR. + # Flux helm-controller v2 status shape: .status.installFailures and + # .status.upgradeFailures are counters populated by the controller on + # every failed install/upgrade. If a future flux release renames them, + # kubectl returns the empty string and the guard silently passes. The + # shape is pinned by hack/remediation-guard.bats (see that file for + # details), and the vendored API types live under + # vendor/github.com/fluxcd/helm-controller/api/v2. install_failures=$(kubectl get hr -n tenant-test "kubernetes-${test_name}" -ojsonpath='{.status.installFailures}') upgrade_failures=$(kubectl get hr -n tenant-test "kubernetes-${test_name}" -ojsonpath='{.status.upgradeFailures}') if helmrelease_has_remediation_cycle "${install_failures}" "${upgrade_failures}"; then diff --git a/hack/remediation-guard.bats b/hack/remediation-guard.bats index 64aa83af..9c33452a 100644 --- a/hack/remediation-guard.bats +++ b/hack/remediation-guard.bats @@ -85,3 +85,47 @@ helmrelease_has_remediation_cycle "3" "5" || rc=$? [ "$rc" -eq 0 ] } + +@test "installFailures and upgradeFailures extraction pins HR v2 status shape" { + # Pins the Flux HelmRelease v2 status shape that run-kubernetes.sh relies + # on. If a future Flux version renames .status.installFailures (or + # .status.upgradeFailures), kubectl get -o jsonpath returns an empty + # string, the guard quietly says "no cycle", and real remediation loops + # slip past the e2e assertion. + # + # This test uses yq to read the exact path used in the e2e script. yq + # evaluates the same json-ish jsonpath against a pinned HR snippet, so + # the test fails loudly if the field ever disappears or moves. Cross + # reference: vendor/github.com/fluxcd/helm-controller/api/v2/ status + # struct field tags. + tmp=$(mktemp -d) + trap 'rm -rf "$tmp"' EXIT + + cat > "$tmp/hr.yaml" <<'YAML' +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: kubernetes-test + namespace: tenant-test +spec: + interval: 5m +status: + installFailures: 2 + upgradeFailures: 0 + conditions: + - type: Ready + status: "False" + reason: UninstallSucceeded +YAML + + install_failures=$(yq '.status.installFailures' "$tmp/hr.yaml") + upgrade_failures=$(yq '.status.upgradeFailures' "$tmp/hr.yaml") + + [ "$install_failures" = "2" ] + [ "$upgrade_failures" = "0" ] + + . hack/e2e-apps/remediation-guard.sh + rc=0 + helmrelease_has_remediation_cycle "$install_failures" "$upgrade_failures" || rc=$? + [ "$rc" -eq 0 ] +}