From 6b5d7d16c57e5105f9ff8e2b5fca80e7c75cdafe Mon Sep 17 00:00:00 2001 From: pat-s Date: Mon, 31 Aug 2026 09:07:41 +0000 Subject: [PATCH] fix(cdn): resolve a pull zone when the API answers with a bare array The zone lookup used `.Items // .` to accept both response shapes. It accepts neither: indexing an array with a string is an error in jq, not a null, so `//` never substitutes and the whole expression aborts with "Cannot index array with string". The listing endpoint answers with a bare array for this account, so every purge of cran.allianceswisspass.devxy.io has been failing. Reindex pipelines reported failure at the purge step while the reindex itself had succeeded, and that zone kept serving objects the rebuild replaced. Also surfaced by the same failure: - The listing response was used without checking the HTTP status, so an auth or rate-limit error was reported as a missing hostname. - A hostname matching several zones silently purged only the first. Verified against stubbed array-shaped and object-shaped responses; the previous script fails the array case. --- scripts/purge_cdn_zone.sh | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/scripts/purge_cdn_zone.sh b/scripts/purge_cdn_zone.sh index 648dfc3..6c07eef 100755 --- a/scripts/purge_cdn_zone.sh +++ b/scripts/purge_cdn_zone.sh @@ -47,12 +47,31 @@ resolve_zone_id() { fi response_file=$(mktemp) - curl -sS -o "${response_file}" \ - -H "AccessKey: ${api_key}" \ - "https://api.bunny.net/pullzone" + local status + status=$( + curl -sS -o "${response_file}" -w '%{http_code}' \ + -H "AccessKey: ${api_key}" \ + "https://api.bunny.net/pullzone?perPage=1000" + ) + + if [[ "${status}" != "200" ]]; then + echo "Listing BunnyCDN pull zones failed with HTTP ${status}:" >&2 + head -c 500 "${response_file}" >&2 + echo >&2 + rm -f "${response_file}" + exit 1 + fi + + # The endpoint answers with a bare array on some accounts and a paginated + # object on others. `.Items // .` looks like it covers both but does not: + # indexing an array with a string is an *error*, and `//` only substitutes + # for null, so the array case aborted with + # "Cannot index array with string" and the zone was never purged. zone_id=$( jq -r --arg hostname "${zone}" \ - '(.Items // .)[] | select(any(.Hostnames[]?; .Value == $hostname)) | .Id' \ + '(if type == "object" then (.Items // []) else . end)[] + | select(any(.Hostnames[]?; .Value == $hostname)) + | .Id' \ "${response_file}" ) rm -f "${response_file}" @@ -62,6 +81,12 @@ resolve_zone_id() { exit 1 fi + # Two zones sharing a hostname would purge only whichever jq emitted first. + if [[ $(wc -l <<<"${zone_id}") -gt 1 ]]; then + echo "Hostname ${zone} matched multiple pull zones: ${zone_id//$'\n'/ }" >&2 + exit 1 + fi + echo "${zone_id}" } -- 2.54.0