|
1 | 1 | #!/usr/bin/env bats |
2 | 2 | # ----------------------------------------------------------------------------- |
3 | | -# Unit tests for cozy_guard_addon_helmreleases in |
| 3 | +# Unit tests for cozy_guard_all_helmreleases and cozy_guard_addon_helmreleases in |
4 | 4 | # hack/e2e-chainsaw/_lib/run-kubernetes.sh |
5 | 5 | # |
6 | 6 | # cozy_guard_helmrelease judges one release; cozy_guard_addon_helmreleases runs |
@@ -32,7 +32,8 @@ load test_helper |
32 | 32 | # created. The stub answers the shapes the guard issues: the namespace listing |
33 | 33 | # (from ALL_HR), one release's install strategy and Ready condition together |
34 | 34 | # (<name>.strategy and <name>.ready, joined the way the guard's jsonpath joins |
35 | | -# them), and its history statuses (<name>.history). A missing fixture file |
| 35 | +# them), its history statuses (<name>.history), and whether it exists at all |
| 36 | +# (`-o name`, which an ABSENT marker turns into NotFound). A missing fixture file |
36 | 37 | # stands for a field the object does not carry, which is what kubectl prints for |
37 | 38 | # an absent jsonpath - nothing. |
38 | 39 | # |
@@ -96,6 +97,7 @@ for a in "$@"; do |
96 | 97 | describe) mode=describe ;; |
97 | 98 | *'status.history'*) mode=history ;; |
98 | 99 | *'install.strategy.name'*) mode=probe ;; |
| 100 | + name) mode=exists ;; |
99 | 101 | kubernetes-*) name=$a ;; |
100 | 102 | esac |
101 | 103 | done |
@@ -145,6 +147,15 @@ case "$mode" in |
145 | 147 | "$(cat "$FIXTURES/$name.strategy" 2>/dev/null || true)" \ |
146 | 148 | "$(cat "$FIXTURES/$name.ready" 2>/dev/null || true)" |
147 | 149 | ;; |
| 150 | + exists) |
| 151 | + # `get hr <name> -o name`: NotFound is a non-zero exit with the message on |
| 152 | + # stderr, which an ABSENT marker reproduces; anything else exists. |
| 153 | + if [ -f "$FIXTURES/$name.ABSENT" ]; then |
| 154 | + echo "Error from server (NotFound): helmreleases.helm.toolkit.fluxcd.io \"$name\" not found" >&2 |
| 155 | + exit 1 |
| 156 | + fi |
| 157 | + echo "helmrelease.helm.toolkit.fluxcd.io/$name" |
| 158 | + ;; |
148 | 159 | describe) echo "stub describe of $name" ;; |
149 | 160 | esac |
150 | 161 | exit 0 |
@@ -252,6 +263,154 @@ STUB |
252 | 263 | rm -rf "$tmp" |
253 | 264 | } |
254 | 265 |
|
| 266 | +@test "the pool release is read by the full guard, and its footprint fails the run" { |
| 267 | + # The worker pool is kubernetes-nodes-<test>-md0, an Application HelmRelease |
| 268 | + # the apiserver stamps like the parent, on the node-join path through the |
| 269 | + # talos-reconcile Job it renders. Its name does not start with the addon |
| 270 | + # prefix kubernetes-<test>-, so the addon listing never selects it, and |
| 271 | + # before this read a failed action on it was invisible to the guard on every |
| 272 | + # path. Parent and addon are clean here; the pool alone carries the finding. |
| 273 | + . hack/e2e-chainsaw/_lib/run-kubernetes.sh |
| 274 | + tmp=$(mktemp -d) |
| 275 | + cozy_make_kubectl_stub "$tmp" |
| 276 | + export FIXTURES="$tmp/fixtures" |
| 277 | + printf 'kubernetes-test-latest-version-coredns\n' > "$FIXTURES/ALL_HR" |
| 278 | + printf 'RetryOnFailure' > "$FIXTURES/kubernetes-test-latest-version.strategy" |
| 279 | + printf 'deployed\n' > "$FIXTURES/kubernetes-test-latest-version.history" |
| 280 | + printf 'deployed\n' > "$FIXTURES/kubernetes-test-latest-version-coredns.history" |
| 281 | + printf 'RetryOnFailure' > "$FIXTURES/kubernetes-nodes-test-latest-version-md0.strategy" |
| 282 | + printf 'failed\ndeployed\n' > "$FIXTURES/kubernetes-nodes-test-latest-version-md0.history" |
| 283 | + |
| 284 | + rc=0 |
| 285 | + out=$(PATH="$tmp/bin:$PATH" cozy_guard_all_helmreleases tenant-test kubernetes-test-latest-version 2>&1) || rc=$? |
| 286 | + if [ "$rc" -eq 0 ]; then |
| 287 | + echo "expected the pool release footprint to fail the run, got: $out" >&2 |
| 288 | + exit 1 |
| 289 | + fi |
| 290 | + if ! printf '%s\n' "$out" | grep -q -F 'HelmRelease kubernetes-nodes-test-latest-version-md0 carries a failed or uninstalled Snapshot, and its install strategy is RetryOnFailure'; then |
| 291 | + echo "expected the pool release to be named in the verdict, got: $out" >&2 |
| 292 | + exit 1 |
| 293 | + fi |
| 294 | + if ! grep -q -F 'kubernetes-nodes-test-latest-version-md0' "$FIXTURES/CALLS"; then |
| 295 | + echo "expected a read of the pool release, calls were: $(cat "$FIXTURES/CALLS")" >&2 |
| 296 | + exit 1 |
| 297 | + fi |
| 298 | + |
| 299 | + rm -rf "$tmp" |
| 300 | +} |
| 301 | + |
| 302 | +@test "a clean pool release leaves the full guard green" { |
| 303 | + # The pin for the passing path: with every release clean the composite |
| 304 | + # returns zero, so the pool read above is a read and not an unconditional |
| 305 | + # verdict. The pool name is derived from the parent, the way the addon |
| 306 | + # prefix is, which this fixture spells out. |
| 307 | + . hack/e2e-chainsaw/_lib/run-kubernetes.sh |
| 308 | + tmp=$(mktemp -d) |
| 309 | + cozy_make_kubectl_stub "$tmp" |
| 310 | + export FIXTURES="$tmp/fixtures" |
| 311 | + printf 'kubernetes-test-latest-version-coredns\n' > "$FIXTURES/ALL_HR" |
| 312 | + printf 'RetryOnFailure' > "$FIXTURES/kubernetes-test-latest-version.strategy" |
| 313 | + printf 'deployed\n' > "$FIXTURES/kubernetes-test-latest-version.history" |
| 314 | + printf 'deployed\n' > "$FIXTURES/kubernetes-test-latest-version-coredns.history" |
| 315 | + printf 'RetryOnFailure' > "$FIXTURES/kubernetes-nodes-test-latest-version-md0.strategy" |
| 316 | + printf 'deployed\n' > "$FIXTURES/kubernetes-nodes-test-latest-version-md0.history" |
| 317 | + |
| 318 | + PATH="$tmp/bin:$PATH" cozy_guard_all_helmreleases tenant-test kubernetes-test-latest-version |
| 319 | + [ "$(cozy_pool_helmrelease_name kubernetes-test-latest-version)" = kubernetes-nodes-test-latest-version-md0 ] |
| 320 | + |
| 321 | + rm -rf "$tmp" |
| 322 | +} |
| 323 | + |
| 324 | +@test "the pool release is held to a stamped install strategy like the parent" { |
| 325 | + # The pool is an Application HelmRelease, so an empty strategy there is the |
| 326 | + # read and never the object, the same as on the parent. Without this the |
| 327 | + # jsonpath rename the parent check exists for would leave the pool's |
| 328 | + # footprint on the lenient branch while the parent's check still passed. |
| 329 | + . hack/e2e-chainsaw/_lib/run-kubernetes.sh |
| 330 | + tmp=$(mktemp -d) |
| 331 | + cozy_make_kubectl_stub "$tmp" |
| 332 | + export FIXTURES="$tmp/fixtures" |
| 333 | + printf 'kubernetes-test-latest-version-coredns\n' > "$FIXTURES/ALL_HR" |
| 334 | + printf 'RetryOnFailure' > "$FIXTURES/kubernetes-test-latest-version.strategy" |
| 335 | + printf 'deployed\n' > "$FIXTURES/kubernetes-test-latest-version.history" |
| 336 | + printf 'deployed\n' > "$FIXTURES/kubernetes-test-latest-version-coredns.history" |
| 337 | + printf 'True' > "$FIXTURES/kubernetes-nodes-test-latest-version-md0.ready" |
| 338 | + printf 'deployed\n' > "$FIXTURES/kubernetes-nodes-test-latest-version-md0.history" |
| 339 | + |
| 340 | + rc=0 |
| 341 | + out=$(PATH="$tmp/bin:$PATH" cozy_guard_all_helmreleases tenant-test kubernetes-test-latest-version 2>&1) || rc=$? |
| 342 | + if [ "$rc" -eq 0 ]; then |
| 343 | + echo "expected an empty install strategy on the pool release to fail the run, got: $out" >&2 |
| 344 | + exit 1 |
| 345 | + fi |
| 346 | + if ! printf '%s\n' "$out" | grep -q -F 'spec.install.strategy.name of kubernetes-nodes-test-latest-version-md0 came back empty'; then |
| 347 | + echo "expected the failure to name the pool release, got: $out" >&2 |
| 348 | + exit 1 |
| 349 | + fi |
| 350 | + |
| 351 | + rm -rf "$tmp" |
| 352 | +} |
| 353 | + |
| 354 | +@test "the failure-path report classifies the pool release too" { |
| 355 | + # Same reading as the passing path, from the exit handler: a pool release |
| 356 | + # that failed an action is named, and the report still returns zero. |
| 357 | + . hack/e2e-chainsaw/_lib/run-kubernetes.sh |
| 358 | + tmp=$(mktemp -d) |
| 359 | + cozy_make_kubectl_stub "$tmp" |
| 360 | + export FIXTURES="$tmp/fixtures" |
| 361 | + printf 'kubernetes-test-latest-version-coredns\n' > "$FIXTURES/ALL_HR" |
| 362 | + printf 'RetryOnFailure' > "$FIXTURES/kubernetes-test-latest-version.strategy" |
| 363 | + printf 'deployed\n' > "$FIXTURES/kubernetes-test-latest-version.history" |
| 364 | + printf 'deployed\n' > "$FIXTURES/kubernetes-test-latest-version-coredns.history" |
| 365 | + printf 'RetryOnFailure' > "$FIXTURES/kubernetes-nodes-test-latest-version-md0.strategy" |
| 366 | + printf 'failed\ndeployed\n' > "$FIXTURES/kubernetes-nodes-test-latest-version-md0.history" |
| 367 | + |
| 368 | + rc=0 |
| 369 | + out=$(PATH="$tmp/bin:$PATH" cozy_report_helmrelease_remediation tenant-test kubernetes-test-latest-version 2>&1) || rc=$? |
| 370 | + if [ "$rc" -ne 0 ]; then |
| 371 | + echo "expected the report to return zero, got rc=$rc: $out" >&2 |
| 372 | + exit 1 |
| 373 | + fi |
| 374 | + if ! printf '%s\n' "$out" | grep -q -F 'HelmRelease kubernetes-nodes-test-latest-version-md0 carries a failed or uninstalled Snapshot'; then |
| 375 | + echo "expected the pool release footprint to be classified, got: $out" >&2 |
| 376 | + exit 1 |
| 377 | + fi |
| 378 | + |
| 379 | + rm -rf "$tmp" |
| 380 | +} |
| 381 | + |
| 382 | +@test "the failure-path report says when the pool release was never created" { |
| 383 | + # The pool is applied after the parent, so a run that failed between the two |
| 384 | + # has no pool release. That is a fact about where the run stopped, and the |
| 385 | + # report says so instead of reporting a failed read of a missing object. |
| 386 | + . hack/e2e-chainsaw/_lib/run-kubernetes.sh |
| 387 | + tmp=$(mktemp -d) |
| 388 | + cozy_make_kubectl_stub "$tmp" |
| 389 | + export FIXTURES="$tmp/fixtures" |
| 390 | + printf 'kubernetes-test-latest-version-coredns\n' > "$FIXTURES/ALL_HR" |
| 391 | + printf 'RetryOnFailure' > "$FIXTURES/kubernetes-test-latest-version.strategy" |
| 392 | + printf 'deployed\n' > "$FIXTURES/kubernetes-test-latest-version.history" |
| 393 | + printf 'deployed\n' > "$FIXTURES/kubernetes-test-latest-version-coredns.history" |
| 394 | + : > "$FIXTURES/kubernetes-nodes-test-latest-version-md0.ABSENT" |
| 395 | + |
| 396 | + rc=0 |
| 397 | + out=$(PATH="$tmp/bin:$PATH" cozy_report_helmrelease_remediation tenant-test kubernetes-test-latest-version 2>&1) || rc=$? |
| 398 | + if [ "$rc" -ne 0 ]; then |
| 399 | + echo "expected the report to return zero, got rc=$rc: $out" >&2 |
| 400 | + exit 1 |
| 401 | + fi |
| 402 | + if ! printf '%s\n' "$out" | grep -q -F 'kubernetes-nodes-test-latest-version-md0 does not exist'; then |
| 403 | + echo "expected the report to say the pool release does not exist, got: $out" >&2 |
| 404 | + exit 1 |
| 405 | + fi |
| 406 | + if printf '%s\n' "$out" | grep -q -F 'Reading .spec.install.strategy.name and the Ready condition of kubernetes-nodes-test-latest-version-md0 failed'; then |
| 407 | + echo "expected no failed read of the absent pool release, got: $out" >&2 |
| 408 | + exit 1 |
| 409 | + fi |
| 410 | + |
| 411 | + rm -rf "$tmp" |
| 412 | +} |
| 413 | + |
255 | 414 | @test "single-release guard fails a RetryOnFailure release that failed and recovered" { |
256 | 415 | # The entry point the parent kubernetes-<test> release goes through, and the |
257 | 416 | # shape the rule exists for. The apiserver stamps RetryOnFailure on every |
|
0 commit comments