fix: clean up DNS zones

Add DNS-zone cleanup for Hetzner and OVH, best-effort Scaleway external-domain cleanup, and update the OVH block volume command to the current storage block CLI path.
This commit is contained in:
2026-06-29 01:40:57 +02:00
parent 06d1ff8085
commit d41f6d5958
+119 -3
View File
@@ -19,7 +19,7 @@ usage() {
cat <<'USAGE'
Usage: scripts/cleanup-cloud-resources [OPTIONS]
Delete cloud resources from the provider accounts used by supertest.
Delete cloud resources and DNS zones from the provider accounts used by supertest.
This does not read, modify, or remove Legion state or supertest artifacts.
Options:
@@ -64,6 +64,12 @@ is_test_name() {
[[ "$name" == st-* || "$name" == *tribes-supertest* || "$name" == *legion-supertest* ]]
}
normalize_dns_name() {
local name="${1:-}"
name="${name%.}"
printf '%s' "${name,,}"
}
json_array() {
jq -c 'if type == "array" then . elif . == null then [] else [.] end'
}
@@ -82,6 +88,64 @@ run_delete() {
"$@"
}
run_ovh_api_post() {
local path="$1"
if (( dry_run )); then
printf '+ ovh-api POST %q\n' "$path"
return 0
fi
printf '+ ovh-api POST %q\n' "$path"
node - "$path" <<'NODE'
const crypto = require("node:crypto")
async function main() {
const path = process.argv[2]
const endpoint = (process.env.OVH_ENDPOINT || "ovh-eu").trim()
const appKey = (process.env.OVH_APPLICATION_KEY || process.env.OVH_APP_KEY || "").trim()
const appSecret = (process.env.OVH_APPLICATION_SECRET || process.env.OVH_APP_SECRET || "").trim()
const consumerKey = (process.env.OVH_CONSUMER_KEY || "").trim()
const host = endpoint === "ovh-ca" ? "ca.api.ovh.com" : "eu.api.ovh.com"
const baseUrl = `https://${host}/1.0`
if (!appKey || !appSecret || !consumerKey) {
throw new Error("OVH application key/secret or consumer key is not set.")
}
const timeResponse = await fetch(`${baseUrl}/auth/time`, {
headers: { "X-Ovh-Application": appKey }
})
if (!timeResponse.ok) {
throw new Error(`OVH auth time failed: ${timeResponse.status} ${await timeResponse.text()}`)
}
const timestamp = (await timeResponse.text()).trim()
const method = "POST"
const body = ""
const url = `${baseUrl}${path}`
const signaturePayload = [appSecret, consumerKey, method, url, body, timestamp].join("+")
const signature = `$1$${crypto.createHash("sha1").update(signaturePayload).digest("hex")}`
const response = await fetch(url, {
method,
headers: {
"X-Ovh-Application": appKey,
"X-Ovh-Consumer": consumerKey,
"X-Ovh-Signature": signature,
"X-Ovh-Timestamp": timestamp
}
})
if (!response.ok) {
throw new Error(`OVH API POST ${path} failed: ${response.status} ${await response.text()}`)
}
}
main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error))
process.exit(1)
})
NODE
}
delete_hcloud_collection() {
local resource="$1"
local label="$2"
@@ -117,6 +181,7 @@ cleanup_hetzner() {
delete_hcloud_collection primary-ip "primary IP"
delete_hcloud_collection firewall "firewall"
delete_hcloud_collection network "network"
delete_hcloud_collection zone "DNS zone"
local list
if ! list="$(hcloud ssh-key list -o json)"; then
@@ -229,6 +294,36 @@ cleanup_scaleway_zone() {
fi
}
cleanup_scaleway_dns_zones() {
local domain
domain="$(normalize_dns_name "${SUPERTEST_DNS_DOMAIN:-}")"
if [[ -z "$domain" ]]; then
warn "skipping Scaleway DNS zone cleanup; Scaleway external-domain API has no list command and SUPERTEST_DNS_DOMAIN is not set"
return
fi
log "Deleting Scaleway external DNS zone: $domain"
if (( dry_run )); then
run_delete scw domain external-domain delete domain="$domain"
return
fi
local output
if output="$(run_delete scw domain external-domain delete domain="$domain" 2>&1)"; then
printf '%s\n' "$output"
return
fi
if grep -qi 'domain not found' <<<"$output"; then
log "Scaleway external DNS zone not found: $domain"
return
fi
printf '%s\n' "$output" >&2
fail "failed to delete Scaleway external DNS zone $domain"
}
cleanup_scaleway() {
if [[ -z "${SCW_ACCESS_KEY:-}" || -z "${SCW_SECRET_KEY:-}" || -z "${SCW_DEFAULT_PROJECT_ID:-}" ]]; then
warn "skipping Scaleway cleanup; SCW_ACCESS_KEY, SCW_SECRET_KEY, or SCW_DEFAULT_PROJECT_ID is not set"
@@ -243,6 +338,7 @@ cleanup_scaleway() {
for zone in "${scaleway_zones[@]}"; do
cleanup_scaleway_zone "$zone"
done
cleanup_scaleway_dns_zones
}
ovh_project_ids() {
@@ -294,11 +390,11 @@ cleanup_ovh_project() {
warn "failed to list OVH instance snapshots for project $project"
fi
if list="$(ovh_list_project "$project" storage-block)"; then
if list="$(ovh_list_project "$project" storage block)"; then
while IFS=$'\t' read -r id name; do
[[ -n "${id:-}" ]] || continue
log "Deleting OVH block volume: ${name:-$id} ($id)"
run_delete ovhcloud cloud storage-block delete "$id" --cloud-project "$project" ||
run_delete ovhcloud cloud storage block delete "$id" --cloud-project "$project" ||
fail "failed to delete OVH block volume $id"
done < <(printf '%s\n' "$list" | jq -r '.[] | [.id, (.name // "")] | @tsv')
else
@@ -336,6 +432,25 @@ cleanup_ovh_project() {
fi
}
cleanup_ovh_dns_zones() {
local list
if ! list="$(ovhcloud domain-zone list -o json 2>/dev/null | json_array)"; then
fail "failed to list OVH DNS zones"
return
fi
while IFS= read -r zone; do
[[ -n "$zone" ]] || continue
local encoded_zone
encoded_zone="$(jq -rn --arg value "$zone" '$value | @uri')"
log "Requesting OVH DNS zone termination: $zone"
run_ovh_api_post "/domain/zone/${encoded_zone}/terminate" ||
fail "failed to request OVH DNS zone termination for $zone"
log "OVH requires email or Manager confirmation before DNS zone $zone is permanently deleted."
done < <(printf '%s\n' "$list" | jq -r '.[] | .name // .zoneName // .id // empty')
}
cleanup_ovh() {
if [[ -z "${OVH_APP_KEY:-}${OVH_APPLICATION_KEY:-}" ||
-z "${OVH_APP_SECRET:-}${OVH_APPLICATION_SECRET:-}" ||
@@ -358,6 +473,7 @@ cleanup_ovh() {
[[ -n "$project" ]] || continue
cleanup_ovh_project "$project"
done <<<"$projects"
cleanup_ovh_dns_zones
}
selected_providers=()