Archived
Fix --allow-duplicate-host creating real duplicates in create-proxmox-resource.sh
Check NixOS configurations / eval-hosts (pull_request) Failing after 11m24s
Check NixOS configurations / eval-hosts (pull_request) Failing after 11m24s
--allow-duplicate-host previously just skipped the existing-resource check entirely, so re-running e.g. --type lxc --host docker while an lxc-docker container already existed created a second container sharing the same hostname/identity instead of replacing it -- both then fight over DNS/DHCP for that hostname, and it's easy to end up testing the stale one without realizing. Now splits matches into "exact" (same --type as the one being created, e.g. another lxc-docker) and "cross-type" (a different platform sharing this host identity, e.g. a proxmox-docker VM alongside an lxc-docker container -- a deliberate, valid coexistence this script has never managed and still won't). Only an exact match is destroyed and replaced, after typing the hostname back to confirm; a cross-type match is always left untouched. Without --allow-duplicate-host, both cases still refuse to run exactly as before. Verified live against pve.sweet.home: correctly split VMID 103 (a stopped proxmox-docker VM, cross-type -- left untouched) from VMID 105 (the running lxc-docker container, exact-type -- flagged for destroy+replace), and confirmed the destroy prompt safely aborts on a non-matching confirmation, leaving both resources untouched.
This commit is contained in:
@@ -13,12 +13,21 @@
|
|||||||
# refuses to run if the target VMID already exists on the node, or if
|
# refuses to run if the target VMID already exists on the node, or if
|
||||||
# a VM/CT identified as --host already exists under any other VMID
|
# a VM/CT identified as --host already exists under any other VMID
|
||||||
# (checked live against the node; --allow-duplicate-host overrides).
|
# (checked live against the node; --allow-duplicate-host overrides).
|
||||||
|
# - --allow-duplicate-host distinguishes an exact match (same --type
|
||||||
|
# *and* --host, e.g. re-running --type lxc --host docker while an
|
||||||
|
# lxc-docker container already exists -- almost always a redeploy of
|
||||||
|
# the same target to pick up a rebuilt image) from a cross-type match
|
||||||
|
# (a different platform sharing the same host identity, e.g. a
|
||||||
|
# proxmox-docker VM coexisting with lxc-docker). Only the exact match
|
||||||
|
# is destroyed and replaced, after typing the hostname back to
|
||||||
|
# confirm (outside --dry-run) -- a cross-type match is always left
|
||||||
|
# untouched, matching-or-not.
|
||||||
# - --modify only ever touches a resource you name explicitly via
|
# - --modify only ever touches a resource you name explicitly via
|
||||||
# --vmid, shows exactly what will change first, and (outside
|
# --vmid, shows exactly what will change first, and (outside
|
||||||
# --dry-run) always requires typing that VMID back to confirm before
|
# --dry-run) always requires typing that VMID back to confirm before
|
||||||
# anything is sent to the node. There is no bulk/implicit modify.
|
# anything is sent to the node. There is no bulk/implicit modify.
|
||||||
# - Neither mode can start/stop/delete a resource. Not implemented on
|
# - Outside of --allow-duplicate-host's exact-match replace above,
|
||||||
# purpose -- ask before adding it.
|
# neither mode can start/stop/delete a resource.
|
||||||
#
|
#
|
||||||
# See --help for the full option list.
|
# See --help for the full option list.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -64,7 +73,13 @@ Create mode (default):
|
|||||||
already exists on the node (checked live via
|
already exists on the node (checked live via
|
||||||
qm/pct, not any file in this repo) --
|
qm/pct, not any file in this repo) --
|
||||||
otherwise refused, since it'd share that
|
otherwise refused, since it'd share that
|
||||||
host's hostName/hostId.
|
host's hostName/hostId. An existing resource
|
||||||
|
of this *same* --type (e.g. re-running --type
|
||||||
|
lxc --host docker over an existing lxc-docker)
|
||||||
|
is destroyed and replaced, after confirming --
|
||||||
|
a different --type sharing the same --host
|
||||||
|
(e.g. a proxmox-docker VM) is always left
|
||||||
|
untouched.
|
||||||
|
|
||||||
Modify mode (reconfigure an EXISTING resource -- requires --modify):
|
Modify mode (reconfigure an EXISTING resource -- requires --modify):
|
||||||
--modify Switch to modify mode.
|
--modify Switch to modify mode.
|
||||||
@@ -304,12 +319,13 @@ fi
|
|||||||
# this script itself always uses unless --name is overridden) -- a guest
|
# this script itself always uses unless --name is overridden) -- a guest
|
||||||
# manually renamed on the node afterwards wouldn't match, but nothing here
|
# manually renamed on the node afterwards wouldn't match, but nothing here
|
||||||
# creates guests that way.
|
# creates guests that way.
|
||||||
if [[ "$allow_duplicate_host" -eq 1 ]]; then
|
if [[ "$dry_run" -eq 1 ]]; then
|
||||||
echo
|
|
||||||
echo "--allow-duplicate-host: skipping the check for an existing '${host}' on ${node}."
|
|
||||||
elif [[ "$dry_run" -eq 1 ]]; then
|
|
||||||
echo
|
echo
|
||||||
echo "[dry-run] would check ${node} for an existing VM/CT identified as '${host}'"
|
echo "[dry-run] would check ${node} for an existing VM/CT identified as '${host}'"
|
||||||
|
if [[ "$allow_duplicate_host" -eq 1 ]]; then
|
||||||
|
echo "[dry-run] --allow-duplicate-host: an existing ${type} named '${host}' would be" \
|
||||||
|
"destroyed and replaced; a different-type match would be left untouched"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo
|
echo
|
||||||
echo "==> Checking ${node} for an existing VM/CT identified as '${host}'..."
|
echo "==> Checking ${node} for an existing VM/CT identified as '${host}'..."
|
||||||
@@ -334,17 +350,90 @@ REMOTE_SCRIPT
|
|||||||
echo "check entirely)." >&2
|
echo "check entirely)." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Split into "exact" (same resource kind as --type -- i.e. literally this
|
||||||
|
# same host+platform combo already exists, almost always a redeploy of
|
||||||
|
# the same target to test a rebuilt image) vs "cross-type" (a different
|
||||||
|
# platform sharing this host identity, e.g. a stopped proxmox-docker VM
|
||||||
|
# coexisting with an lxc-docker container -- a deliberate, valid setup
|
||||||
|
# this script has never managed and still won't). Read via a herestring
|
||||||
|
# (not a pipe) so the appends below survive outside the loop.
|
||||||
|
this_kind="$type"
|
||||||
|
exact_matches=""
|
||||||
|
cross_matches=""
|
||||||
if [[ -n "$existing" ]]; then
|
if [[ -n "$existing" ]]; then
|
||||||
echo "ERROR: '${host}' already exists on ${node}:" >&2
|
while read -r kind id n; do
|
||||||
echo "$existing" | while read -r kind id n; do
|
[[ -z "$kind" ]] && continue
|
||||||
|
if [[ "$kind" == "$this_kind" ]]; then
|
||||||
|
exact_matches+="${kind} ${id} ${n}"$'\n'
|
||||||
|
else
|
||||||
|
cross_matches+="${kind} ${id} ${n}"$'\n'
|
||||||
|
fi
|
||||||
|
done <<<"$existing"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$exact_matches" && "$allow_duplicate_host" -ne 1 ]]; then
|
||||||
|
echo "ERROR: '${host}' already exists on ${node} as this same resource type:" >&2
|
||||||
|
echo "$exact_matches" | while read -r kind id n; do
|
||||||
|
[[ -z "$kind" ]] && continue
|
||||||
|
echo " - ${kind} VMID ${id} (${n})" >&2
|
||||||
|
done
|
||||||
|
echo "Refusing to create a second ${this_kind} sharing this identity. Pass" >&2
|
||||||
|
echo "--allow-duplicate-host to destroy it and create a fresh one in its place" >&2
|
||||||
|
echo "(after confirming), or use --modify to reconfigure the existing one instead." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$cross_matches" && "$allow_duplicate_host" -ne 1 ]]; then
|
||||||
|
echo "ERROR: '${host}' already exists on ${node} as a different resource type:" >&2
|
||||||
|
echo "$cross_matches" | while read -r kind id n; do
|
||||||
|
[[ -z "$kind" ]] && continue
|
||||||
echo " - ${kind} VMID ${id} (${n})" >&2
|
echo " - ${kind} VMID ${id} (${n})" >&2
|
||||||
done
|
done
|
||||||
echo "Refusing to create a second resource sharing this identity. Pass" >&2
|
echo "Refusing to create a second resource sharing this identity. Pass" >&2
|
||||||
echo "--allow-duplicate-host to create one anyway (it gets its own distinct" >&2
|
echo "--allow-duplicate-host to create one anyway (it gets its own distinct" >&2
|
||||||
echo "sops key and VMID -- the existing resource(s) above are left untouched)," >&2
|
echo "sops key and VMID -- the existing resource above is left untouched)," >&2
|
||||||
echo "or use --modify to reconfigure the existing one instead." >&2
|
echo "or use --modify to reconfigure the existing one instead." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$cross_matches" ]]; then
|
||||||
|
echo "--allow-duplicate-host: '${host}' also exists on ${node} as a different resource" \
|
||||||
|
"type -- leaving it untouched:"
|
||||||
|
echo "$cross_matches" | while read -r kind id n; do
|
||||||
|
[[ -z "$kind" ]] && continue
|
||||||
|
echo " - ${kind} VMID ${id} (${n})"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$exact_matches" ]]; then
|
||||||
|
echo "--allow-duplicate-host: '${host}' already exists on ${node} as this same resource" \
|
||||||
|
"type -- it will be destroyed and replaced:"
|
||||||
|
echo "$exact_matches" | while read -r kind id n; do
|
||||||
|
[[ -z "$kind" ]] && continue
|
||||||
|
echo " - ${kind} VMID ${id} (${n})"
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
read -rp "Type the hostname (${host}) to confirm destroying the above and replacing it: " confirm
|
||||||
|
if [[ "$confirm" != "$host" ]]; then
|
||||||
|
echo "Cancelled -- input didn't match ${host}." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "$exact_matches" | while read -r kind id n; do
|
||||||
|
[[ -z "$kind" ]] && continue
|
||||||
|
echo "==> Destroying ${kind} VMID ${id} (${n})..."
|
||||||
|
if [[ "$kind" == "vm" ]]; then
|
||||||
|
# qm destroy has no --force to stop-then-destroy in one call (pct's
|
||||||
|
# does) -- stop explicitly first if it's running.
|
||||||
|
if ssh "$ssh_target" "qm status ${id}" 2>/dev/null | grep -q running; then
|
||||||
|
ssh "$ssh_target" "qm stop ${id}"
|
||||||
|
fi
|
||||||
|
ssh "$ssh_target" "qm destroy ${id} --purge 1"
|
||||||
|
else
|
||||||
|
ssh "$ssh_target" "pct destroy ${id} --force 1 --purge 1"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Target: ${flake_target} (host=${host}, type=${type}) -> Proxmox resource '${name}'"
|
echo "Target: ${flake_target} (host=${host}, type=${type}) -> Proxmox resource '${name}'"
|
||||||
|
|||||||
Reference in New Issue
Block a user