|
| 1 | +#!/usr/bin/env bats |
| 2 | +# ----------------------------------------------------------------------------- |
| 3 | +# Unit tests for the in-sandbox Talos image cache manifest split and the |
| 4 | +# importer-reachability contract it relies on. |
| 5 | +# |
| 6 | +# hack/e2e-talos-image-cache.yaml bundles four documents but they are applied in |
| 7 | +# two phases: hack/e2e-install-cozystack.bats applies everything EXCEPT the |
| 8 | +# CiliumClusterwideNetworkPolicy (its CRD does not exist before Cozystack is |
| 9 | +# installed), and hack/e2e-apps/talos-image-cache.sh applies ONLY that policy |
| 10 | +# later, once Cilium is up. If a future document is added to the manifest and |
| 11 | +# silently dropped from the pre-Cilium apply, or the Cilium document leaks into |
| 12 | +# it (and errors on the missing CRD), the mirror breaks and e2e falls back to |
| 13 | +# the flaky public factory. These tests pin that split. |
| 14 | +# |
| 15 | +# They also pin the load-bearing reachability invariant: the throwaway probe pod |
| 16 | +# is a faithful proxy for a real CDI importer only if it carries the exact label |
| 17 | +# and namespace the egress policy selects. If either side drifts, the probe can |
| 18 | +# pass while real importers stay blocked (a false positive that makes CI worse). |
| 19 | +# |
| 20 | +# Finally they cover the two pure fragments of the decision itself, sourced from |
| 21 | +# hack/e2e-apps/talos-image-cache.sh (sourcing it has no cluster side effects — it |
| 22 | +# only sets parameter defaults and defines functions): the strict 206 gate that |
| 23 | +# decides whether tenants are pointed at the mirror at all, and the --overrides JSON |
| 24 | +# builder whose silent breakage would drop every probe to the public factory. The |
| 25 | +# kubectl orchestration around them needs a live cluster and is covered by the e2e |
| 26 | +# run, matching how the other hack/ helpers are tested. |
| 27 | +# |
| 28 | +# cozytest.sh's awk parser recognizes only @test blocks and a bare `}` on its |
| 29 | +# own line; there is no bats `run` or `$status`, and setup()/teardown() are not |
| 30 | +# honored. Each test runs under `set -eu -x`; assertions are direct shell tests |
| 31 | +# that exit non-zero on failure. mikefarah yq prints `---` between matched |
| 32 | +# documents, so document streams are compared with those separators stripped. |
| 33 | +# |
| 34 | +# Run with: hack/cozytest.sh hack/talos-image-cache_test.bats |
| 35 | +# ----------------------------------------------------------------------------- |
| 36 | + |
| 37 | +@test "manifest documents partition into pre-Cilium apply plus the Cilium policy" { |
| 38 | + manifest=hack/e2e-talos-image-cache.yaml |
| 39 | + total=$(yq '.kind' "$manifest" | grep -vc '^---$') |
| 40 | + excluded=$(yq 'select(.kind != "CiliumClusterwideNetworkPolicy") | .kind' "$manifest" | grep -vc '^---$') |
| 41 | + selected=$(yq 'select(.kind == "CiliumClusterwideNetworkPolicy") | .kind' "$manifest" | grep -vc '^---$') |
| 42 | + [ "$total" -eq 4 ] |
| 43 | + [ "$excluded" -eq 3 ] |
| 44 | + [ "$selected" -eq 1 ] |
| 45 | + [ $((excluded + selected)) -eq "$total" ] |
| 46 | +} |
| 47 | + |
| 48 | +@test "pre-Cilium apply keeps Service, Deployment, ConfigMap and drops the Cilium policy" { |
| 49 | + manifest=hack/e2e-talos-image-cache.yaml |
| 50 | + kinds=$(yq 'select(.kind != "CiliumClusterwideNetworkPolicy") | .kind' "$manifest" | grep -v '^---$') |
| 51 | + for want in Service Deployment ConfigMap; do |
| 52 | + printf '%s\n' "$kinds" | grep -qx "$want" || { echo "pre-Cilium apply is missing $want" >&2; exit 1; } |
| 53 | + done |
| 54 | + if printf '%s\n' "$kinds" | grep -qx CiliumClusterwideNetworkPolicy; then |
| 55 | + echo "CiliumClusterwideNetworkPolicy leaked into the pre-Cilium apply" >&2 |
| 56 | + exit 1 |
| 57 | + fi |
| 58 | +} |
| 59 | + |
| 60 | +@test "point-of-use apply selects exactly the Cilium policy" { |
| 61 | + manifest=hack/e2e-talos-image-cache.yaml |
| 62 | + kinds=$(yq 'select(.kind == "CiliumClusterwideNetworkPolicy") | .kind' "$manifest" | grep -v '^---$') |
| 63 | + [ "$kinds" = "CiliumClusterwideNetworkPolicy" ] |
| 64 | +} |
| 65 | + |
| 66 | +@test "egress policy selects the importer label and namespace the probe pod uses" { |
| 67 | + manifest=hack/e2e-talos-image-cache.yaml |
| 68 | + helper=hack/e2e-apps/talos-image-cache.sh |
| 69 | + label=$(yq 'select(.kind == "CiliumClusterwideNetworkPolicy") | .spec.endpointSelector.matchLabels["k8s:cdi.kubevirt.io"]' "$manifest") |
| 70 | + ns=$(yq 'select(.kind == "CiliumClusterwideNetworkPolicy") | .spec.endpointSelector.matchLabels["k8s:io.kubernetes.pod.namespace"]' "$manifest") |
| 71 | + [ "$label" = "importer" ] |
| 72 | + [ "$ns" = "tenant-test" ] |
| 73 | + # The probe pod must carry the same label the policy selects, else it faces a |
| 74 | + # different egress than a real importer and the guarantee breaks. |
| 75 | + grep -q "cdi.kubevirt.io=importer" "$helper" || { echo "probe pod label drifted from the egress selector" >&2; exit 1; } |
| 76 | + grep -q "TALOS_IMAGE_CACHE_PROBE_NS:-tenant-test" "$helper" || { echo "probe namespace drifted from the egress selector" >&2; exit 1; } |
| 77 | +} |
| 78 | + |
| 79 | +@test "egress rule targets the mirror pod's own identity" { |
| 80 | + manifest=hack/e2e-talos-image-cache.yaml |
| 81 | + target=$(yq 'select(.kind == "CiliumClusterwideNetworkPolicy") | .spec.egress[0].toEndpoints[0].matchLabels["k8s:app.kubernetes.io/name"]' "$manifest") |
| 82 | + target_ns=$(yq 'select(.kind == "CiliumClusterwideNetworkPolicy") | .spec.egress[0].toEndpoints[0].matchLabels["k8s:io.kubernetes.pod.namespace"]' "$manifest") |
| 83 | + mirror=$(yq 'select(.kind == "Deployment") | .spec.template.metadata.labels["app.kubernetes.io/name"]' "$manifest") |
| 84 | + mirror_ns=$(yq 'select(.kind == "Deployment") | .metadata.namespace' "$manifest") |
| 85 | + [ "$target" = "talos-image-cache" ] |
| 86 | + [ "$target_ns" = "kube-system" ] |
| 87 | + # The allow's destination must equal the mirror Deployment's own pod label and |
| 88 | + # namespace, otherwise the hole is punched toward nothing: moving the mirror |
| 89 | + # would leave these tests green while importers could no longer reach it. |
| 90 | + [ "$target" = "$mirror" ] |
| 91 | + [ "$target_ns" = "$mirror_ns" ] |
| 92 | +} |
| 93 | + |
| 94 | +@test "strict 206 gate selects the mirror on a byte-range success" { |
| 95 | + . hack/e2e-apps/talos-image-cache.sh |
| 96 | + if ! _talos_image_cache_probe_succeeded "code=206"; then |
| 97 | + echo "expected a 206 probe result to select the mirror" >&2 |
| 98 | + exit 1 |
| 99 | + fi |
| 100 | +} |
| 101 | + |
| 102 | +@test "strict 206 gate finds the marker inside surrounding probe output" { |
| 103 | + . hack/e2e-apps/talos-image-cache.sh |
| 104 | + out=$(printf 'pod/talos-image-cache-probe created\ncode=206\npod "talos-image-cache-probe" deleted\n') |
| 105 | + if ! _talos_image_cache_probe_succeeded "$out"; then |
| 106 | + echo "expected 206 to be found in multi-line attach output" >&2 |
| 107 | + exit 1 |
| 108 | + fi |
| 109 | +} |
| 110 | + |
| 111 | +@test "strict 206 gate falls back on range-less, unreachable, empty and unrelated output" { |
| 112 | + . hack/e2e-apps/talos-image-cache.sh |
| 113 | + # A plain 200 means the mirror answered without range support; 000 means curl |
| 114 | + # never connected. Both fall back to the public factory, as does output that |
| 115 | + # carried no result marker at all (an attach stream lost with no logs to recover). |
| 116 | + for bad in "code=200" "code=000" "code=404" "" "error: pods not found"; do |
| 117 | + if _talos_image_cache_probe_succeeded "$bad"; then |
| 118 | + echo "expected non-206 output to fall back to the public factory: [$bad]" >&2 |
| 119 | + exit 1 |
| 120 | + fi |
| 121 | + done |
| 122 | +} |
| 123 | + |
| 124 | +@test "probe overrides builder emits valid JSON with the image, command and restricted PSA" { |
| 125 | + . hack/e2e-apps/talos-image-cache.sh |
| 126 | + img=example.invalid/img:tag |
| 127 | + cmd='curl -s -o /dev/null -w code=%{http_code} -r 0-0 http://talos-image-cache.kube-system.svc/x.raw.xz' |
| 128 | + json=$(_talos_image_cache_probe_overrides "$img" "$cmd") |
| 129 | + # Invalid JSON would make every probe fail and silently drop CI back to the |
| 130 | + # public factory, so parse it rather than pattern-match it. |
| 131 | + printf '%s' "$json" | yq -p=json -o=json '.' >/dev/null |
| 132 | + [ "$(printf '%s' "$json" | yq -p=json '.spec.containers[0].name')" = "c" ] |
| 133 | + [ "$(printf '%s' "$json" | yq -p=json '.spec.containers[0].image')" = "$img" ] |
| 134 | + [ "$(printf '%s' "$json" | yq -p=json '.spec.containers[0].command[2]')" = "$cmd" ] |
| 135 | + [ "$(printf '%s' "$json" | yq -p=json '.spec.securityContext.runAsNonRoot')" = "true" ] |
| 136 | + [ "$(printf '%s' "$json" | yq -p=json '.spec.containers[0].securityContext.allowPrivilegeEscalation')" = "false" ] |
| 137 | +} |
0 commit comments