Skip to content

Cleanup Old Review Namespaces #167

Cleanup Old Review Namespaces

Cleanup Old Review Namespaces #167

name: Cleanup Old Review Namespaces
on:
schedule:
- cron: "0 4 * * *"
workflow_dispatch: {}
concurrency:
group: cleanup-old-review-namespaces
cancel-in-progress: false
jobs:
cleanup_old_namespaces:
name: "Delete review namespaces older than 3 weeks + DBs"
runs-on: self-hosted
env:
KUBE_CONFIG_BASE64_DATA: ${{ secrets.TEST_CLUSTER_KUBE_CONFIG_BASE64_DATA }}
TEST_CLUSTER_DB_CREDENTIALS: ${{ secrets.TEST_CLUSTER_DB_CREDENTIALS }}
RETENTION_DAYS: "21"
NS_PREFIX: "review-"
IGNORE_NAMESPACES: ${{ vars.IGNORE_NAMESPACES }}
steps:
- name: Compute cutoff timestamp
id: cutoff
shell: bash
run: |
CUTOFF_TS=$(date -u -d "${RETENTION_DAYS} days ago" +%Y-%m-%dT%H:%M:%SZ)
echo "CUTOFF_TS=$CUTOFF_TS" >> "$GITHUB_ENV"
echo "Cutoff: $CUTOFF_TS"
- name: List candidate namespaces older than cutoff
id: list_old
shell: bash
run: |
set -euo pipefail
# Build ignore list from Actions Variable (supports commas or newlines)
printf '%s\n' "${IGNORE_NAMESPACES:-}" \
| tr ',' '\n' \
| sed -E 's/^[[:space:]]+|[[:space:]]+$//g' \
| sed '/^[[:space:]]*$/d' \
| sort -u > ignore_ns.txt || true
echo "Exact ignore entries:"
if [[ -s ignore_ns.txt ]]; then cat ignore_ns.txt; else echo "(none)"; fi
echo
# Gather candidates older than cutoff
kubectl --kubeconfig <(echo "$KUBE_CONFIG_BASE64_DATA" | base64 --decode) \
get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.creationTimestamp}{"\n"}{end}' \
| awk -v prefix="$NS_PREFIX" -v cutoff="$CUTOFF_TS" '
$1 ~ ("^" prefix) && $2 < cutoff { print $1 }
' \
| sort -u > candidates.txt || true
echo "Candidates before ignores:"
if [[ -s candidates.txt ]]; then cat candidates.txt; else echo "(none)"; fi
echo
# Drop exact matches from candidates
: > ignored_exact.txt
if [[ -s ignore_ns.txt && -s candidates.txt ]]; then
comm -12 <(sort -u candidates.txt) <(sort -u ignore_ns.txt) > ignored_exact.txt || true
grep -F -x -v -f ignore_ns.txt candidates.txt > tmp.txt || true
mv tmp.txt candidates.txt || true
fi
echo "Ignored (exact matches):"
if [[ -s ignored_exact.txt ]]; then cat ignored_exact.txt; else echo "(none)"; fi
echo
# Final list to delete
mv candidates.txt namespaces_to_delete.txt || true
echo "Namespaces to delete:"
if [[ -s namespaces_to_delete.txt ]]; then cat namespaces_to_delete.txt; else echo "(none)"; fi
NS_LIST=$(paste -sd, namespaces_to_delete.txt 2>/dev/null || true)
echo "ns_list=$NS_LIST" >> "$GITHUB_OUTPUT"
- name: Delete old namespaces
if: steps.list_old.outputs.ns_list != ''
shell: bash
run: |
set -euo pipefail
while IFS= read -r ns; do
[[ -z "$ns" ]] && continue
echo "Deleting namespace: $ns"
kubectl --kubeconfig <(echo "$KUBE_CONFIG_BASE64_DATA" | base64 --decode) \
delete namespace "$ns" --wait=false || {
echo "Warning: failed to delete $ns (continuing)"; continue;
}
done < namespaces_to_delete.txt
- name: Drop corresponding review databases
if: steps.list_old.outputs.ns_list != ''
shell: bash
env:
PSQL_DSN: ${{ secrets.TEST_CLUSTER_DB_CREDENTIALS }}
run: |
set -euo pipefail
psql --version
TMP_SQL="$(mktemp)"
echo "-- Autogenerated DROP DATABASE statements" > "$TMP_SQL"
while IFS= read -r ns; do
[[ -z "$ns" ]] && continue
raw="${ns#review-}"
sanitized="$(echo "$raw" | sed 's/[^a-zA-Z0-9_-]//g; s/_/-/g' | cut -c -30)"
if [[ -z "$sanitized" ]]; then
echo "Skipping DB cleanup for $ns: empty derived prefix"
continue
fi
echo "-- From namespace: $ns -> prefix: $sanitized" >> "$TMP_SQL"
echo "WITH to_drop AS (SELECT datname FROM pg_database WHERE datname LIKE '${sanitized}%' AND datname NOT IN ('postgres','template0','template1')) SELECT 'DROP DATABASE ' || quote_ident(datname) || ' WITH (FORCE);' FROM to_drop;" >> "$TMP_SQL"
done < namespaces_to_delete.txt
echo "Planned DROP statements:"
# Show the generated DROP statements (do not execute yet)
psql -d "$PSQL_DSN" -v ON_ERROR_STOP=1 -Atqc "\i $TMP_SQL" | sed 's/^/ /' || true
echo "Executing drops..."
# Generate the DROP statements, then pipe them into a second psql for execution
psql -d "$PSQL_DSN" -v ON_ERROR_STOP=1 -Atqc "\i $TMP_SQL" | psql -d "$PSQL_DSN" -v ON_ERROR_STOP=1
echo "Database cleanup finished."
- name: Summary
shell: bash
run: |
echo "Cleanup completed."
echo "Retention: $RETENTION_DAYS days"
echo "Cutoff: $CUTOFF_TS"