banger/rm.sh
Thales Maciel 9191b7e370
Improve VM lifecycle tooling
Make spawned VMs easier to use and restore from the host.

Add shared DNS and runtime helpers, publish <vm-name>.vm records through mapdns, and teach run/customize/interactive/restore to persist the metadata needed for SSH, DNS cleanup, and clean restores.

Seed per-VM /home and /var disks from the rootfs snapshot so package state is present on first boot, add an interactive customization entrypoint plus ssh.sh and human-friendly list output, and let stop/kill/rm operate on multiple VM identifiers.

Tear down stale TAP, dm, and loop state when VMs stop so restore can recreate them safely, and validate the updated scripts with bash -n plus targeted dry-run harnesses for teardown and restore paths.
2026-03-15 17:48:47 -03:00

113 lines
2.8 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
log() {
printf '[rm] %s\n' "$*"
}
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$DIR/dns.sh"
STATE="$DIR/state"
VM_ROOT="$STATE/vms"
usage() {
cat <<'EOF'
Usage: ./rm.sh <id-or-name-prefix>...
Removes VM artifacts from state/ and cleans up TAP and mapped devices.
EOF
}
find_vm_json() {
local query="$1"
local vm_json match_count=0 match=""
for vm_json in "$VM_ROOT"/*/vm.json; do
[[ -f "$vm_json" ]] || continue
local id name
id="$(jq -r '.meta.id // empty' "$vm_json")"
name="$(jq -r '.meta.name // empty' "$vm_json")"
if [[ "$id" == "$query"* || "$name" == "$query"* ]]; then
match="$vm_json"
match_count=$((match_count + 1))
fi
done
if (( match_count == 0 )); then
log "no VM found for prefix: $query"
exit 1
fi
if (( match_count > 1 )); then
log "multiple VMs found for prefix: $query"
exit 1
fi
printf '%s' "$match"
}
QUERIES=("$@")
if (( ${#QUERIES[@]} == 1 )) && [[ "${QUERIES[0]}" == "-h" || "${QUERIES[0]}" == "--help" ]]; then
usage
exit 0
fi
if (( ${#QUERIES[@]} == 0 )); then
usage
exit 1
fi
VM_JSONS=()
for query in "${QUERIES[@]}"; do
VM_JSON="$(find_vm_json "$query")"
already_added=0
for existing_vm_json in "${VM_JSONS[@]}"; do
if [[ "$existing_vm_json" == "$VM_JSON" ]]; then
already_added=1
break
fi
done
if (( already_added == 0 )); then
VM_JSONS+=("$VM_JSON")
fi
done
for vm_json in "${VM_JSONS[@]}"; do
vm_dir="$(dirname "$vm_json")"
vm_id="$(jq -r '.meta.id // empty' "$vm_json")"
vm_name="$(jq -r '.meta.name // empty' "$vm_json")"
pid="$(jq -r '.meta.pid // empty' "$vm_json")"
tap="$(jq -r '.meta.tap // empty' "$vm_json")"
api_sock="$(jq -r '.meta.api_sock // empty' "$vm_json")"
base_loop="$(jq -r '.meta.base_loop // empty' "$vm_json")"
cow_loop="$(jq -r '.meta.cow_loop // empty' "$vm_json")"
dm_dev="$(jq -r '.meta.dm_dev // empty' "$vm_json")"
dm_name="$(jq -r '.meta.dm_name // empty' "$vm_json")"
dns_name="$(jq -r '.meta.dns_name // empty' "$vm_json")"
if [[ -z "$dns_name" ]]; then
dns_name="$(banger_dns_name "$vm_name")"
fi
if [[ -n "$pid" ]]; then
sudo kill "$pid" 2>/dev/null || true
fi
if [[ -n "$tap" ]]; then
sudo ip link del "$tap" 2>/dev/null || true
fi
if [[ -n "$api_sock" ]]; then
rm -f "$api_sock"
fi
banger_dns_remove_record_name "$dns_name"
if [[ -n "$dm_dev" || -n "$dm_name" ]]; then
sudo dmsetup remove "${dm_name:-$dm_dev}" 2>/dev/null || true
fi
if [[ -n "$cow_loop" ]]; then
sudo losetup -d "$cow_loop" 2>/dev/null || true
fi
if [[ -n "$base_loop" ]]; then
sudo losetup -d "$base_loop" 2>/dev/null || true
fi
if [[ -d "$vm_dir" ]]; then
rm -rf "$vm_dir"
fi
log "removed ${vm_name:-$vm_dir}"
done