From 807808ed00354053505da0dc5924cdf415fc992b Mon Sep 17 00:00:00 2001
From: Kiara Grouwstra <kiara@procolix.eu>
Date: Wed, 19 Feb 2025 09:43:19 +0100
Subject: [PATCH 1/5] bash scripts: snake-case variables, deduplicate $RANDOM,
 satisfy LSP
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Nicolas “Niols” Jeannerod <nicolas.jeannerod@moduscreate.com>
---
 deployment/proxmox/provision.sh | 126 ++++++++++++++++----------------
 deployment/proxmox/remove.sh    |  60 ++++++++-------
 2 files changed, 97 insertions(+), 89 deletions(-)

diff --git a/deployment/proxmox/provision.sh b/deployment/proxmox/provision.sh
index 61156c6d..89b15364 100755
--- a/deployment/proxmox/provision.sh
+++ b/deployment/proxmox/provision.sh
@@ -1,17 +1,17 @@
-#!/usr/bin/env sh
+#!/usr/bin/env bash
 set -euC
 
 ################################################################################
 ## Constants
 
-readonly apiurl=https://192.168.51.81:8006/api2/json
+readonly api_url=https://192.168.51.81:8006/api2/json
 
 ## FIXME: There seems to be a problem with file upload where the task is
 ## registered to `node051` no matter what node we are actually uploading to? For
 ## now, let us just use `node051` everywhere.
 readonly node=node051
 
-readonly tmpdir=/tmp/proxmox-provision-$RANDOM$RANDOM
+readonly tmpdir=/tmp/proxmox-provision-$RANDOM
 mkdir $tmpdir
 
 ################################################################################
@@ -22,7 +22,7 @@ password=
 sockets=1
 cores=1
 memory=2048
-vmids=
+vm_ids=
 
 help () {
   cat <<EOF
@@ -46,15 +46,17 @@ Others:
 EOF
 }
 
+# shellcheck disable=SC2059
 die () { printf '\033[31m'; printf "$@"; printf '\033[0m\n'; exit 2; }
+# shellcheck disable=SC2059
 die_with_help () { printf '\033[31m'; printf "$@"; printf '\033[0m\n'; help; exit 2; }
 
 while [ $# -gt 0 ]; do
   argument=$1
   shift
   case $argument in
-    --username) readonly username=$1; shift ;;
-    --password) readonly password=$1; shift ;;
+    --username) readonly username="$1"; shift ;;
+    --password) readonly password="$1"; shift ;;
 
     --sockets) sockets=$1; shift ;;
     --cores) cores=$1; shift ;;
@@ -62,17 +64,17 @@ while [ $# -gt 0 ]; do
 
     -h|-\?|--help) help; exit 0 ;;
 
-    -*) die_with_help 'Unknown argument: `%s`.' "$argument" ;;
+    -*) die_with_help "Unknown argument: '%s'." "$argument" ;;
 
-    *) vmids="$vmids $argument" ;;
+    *) vm_ids="$vm_ids $argument" ;;
   esac
 done
 
 if [ -z "$username" ] || [ -z "$password" ]; then
   if [ -f .proxmox ]; then
-    { read username; read password; } < .proxmox
+    { read -r username; read -r password; } < .proxmox
   else
-    die_with_help 'Required: `--username` and `--password`.\n'
+    die_with_help "Required: '--username' and '--password'.\n"
   fi
 fi
 
@@ -83,9 +85,9 @@ readonly memory
 ## FIXME: When we figure out how to use other nodes than node051.
 # if [ -z "$node" ]; then
 #   printf 'Picking random node...'
-#   proxmox GET $apiurl/nodes
+#   proxmox GET $api_url/nodes
 #   node=$(from_response .data[].node | sort -R | head -n 1)
-#   printf ' done. Picked `%s`.\n' "$node"
+#   printf " done. Picked '%s'.\n" "$node"
 # fi
 # readonly node
 
@@ -96,19 +98,21 @@ printf 'Authenticating...'
 response=$(
     http \
         --verify no \
-        POST $apiurl/access/ticket \
+        POST $api_url/access/ticket \
         "username=$username" \
         "password=$password"
     )
-readonly ticket=$(echo "$response" | jq -r .data.ticket)
-readonly csrfToken=$(echo "$response" | jq -r .data.CSRFPreventionToken)
+ticket=$(echo "$response" | jq -r .data.ticket)
+readonly ticket
+csrf_token=$(echo "$response" | jq -r .data.CSRFPreventionToken)
+readonly csrf_token
 printf ' done.\n'
 
 acquire_lock () {
-  until mkdir $tmpdir/lock-$1 2>/dev/null; do sleep 1; done
+  until mkdir "$tmpdir/lock-$1" 2>/dev/null; do sleep 1; done
 }
 release_lock () {
-  rmdir $tmpdir/lock-$1
+  rmdir "$tmpdir/lock-$1"
 }
 
 proxmox () {
@@ -119,7 +123,7 @@ proxmox () {
     --ignore-stdin \
     "$@" \
     "Cookie:PVEAuthCookie=$ticket" \
-    "CSRFPreventionToken:$csrfToken"
+    "CSRFPreventionToken:$csrf_token"
   release_lock proxmox
 }
 
@@ -130,13 +134,13 @@ proxmox_sync () (
   upid=$(echo "$response" | jq -r .data)
 
   while :; do
-    response=$(proxmox GET $apiurl/nodes/$node/tasks/$upid/status)
+    response=$(proxmox GET "$api_url/nodes/$node/tasks/$upid/status")
     status=$(echo "$response" | jq -r .data.status)
 
     case $status in
       running) sleep 1 ;;
       stopped) break ;;
-      *) die 'unexpected status: `%s`' "$status" ;;
+      *) die "unexpected status: '%s'" "$status" ;;
     esac
   done
 )
@@ -146,16 +150,16 @@ proxmox_sync () (
 
 build_iso () {
   acquire_lock build
-  printf 'Building ISO for VM %d...\n' $1
+  printf 'Building ISO for VM %d...\n' "$1"
 
   nix build \
-    .#isoInstallers.provisioning.fedi$1 \
+    ".#isoInstallers.provisioning.fedi$1" \
     --log-format raw --quiet \
-    --out-link $tmpdir/installer-fedi$1
+    --out-link "$tmpdir/installer-fedi$1"
 
-  ln -sf $tmpdir/installer-fedi$1/iso/installer.iso $tmpdir/installer-fedi$1.iso
+  ln -sf "$tmpdir/installer-fedi$1/iso/installer.iso" "$tmpdir/installer-fedi$1.iso"
 
-  printf 'done building ISO for VM %d.\n' $1
+  printf 'done building ISO for VM %d.\n' "$1"
   release_lock build
 }
 
@@ -164,13 +168,13 @@ build_iso () {
 
 upload_iso () {
   acquire_lock upload
-  printf 'Uploading ISO for VM %d...\n' $1
+  printf 'Uploading ISO for VM %d...\n' "$1"
 
-  proxmox_sync POST $apiurl/nodes/$node/storage/local/upload \
-    filename@$tmpdir/installer-fedi$1.iso \
+  proxmox_sync POST"$api_url/nodes/$node/storage/local/upload" \
+    "filename@$tmpdir/installer-fedi$1.iso" \
     content==iso
 
-  printf 'done uploading ISO for VM %d.\n' $1
+  printf 'done uploading ISO for VM %d.\n' "$1"
   release_lock upload
 }
 
@@ -178,22 +182,22 @@ upload_iso () {
 ## Remove ISO
 
 remove_iso () {
-  printf 'Removing ISO for VM %d...\n' $1
+  printf 'Removing ISO for VM %d...\n' "$1"
 
-  proxmox_sync DELETE $apiurl/nodes/$node/storage/local/content/local:iso/installer-fedi$1.iso
+  proxmox_sync DELETE "$api_url/nodes/$node/storage/local/content/local:iso/installer-fedi$1.iso"
 
-  printf 'done removing ISO for VM %d.\n' $1
+  printf 'done removing ISO for VM %d.\n' "$1"
 }
 
 ################################################################################
 ## Create VM
 
 create_vm () {
-  printf 'Creating VM %d...\n' $1
+  printf 'Creating VM %d...\n' "$1"
 
-  proxmox_sync POST $apiurl/nodes/$node/qemu \
+  proxmox_sync POST "$api_url/nodes/$node/qemu" \
     \
-    vmid==$1 \
+    vm_id=="$1" \
     name=="fedi$1" \
     pool==Fediversity \
     \
@@ -207,77 +211,77 @@ create_vm () {
     scsihw==virtio-scsi-single \
     scsi0=='linstor_storage:32,discard=on,ssd=on,iothread=on' \
     \
-    sockets==$sockets \
-    cores==$cores \
+    sockets=="$sockets" \
+    cores=="$cores" \
     cpu==x86-64-v2-AES \
     numa==1 \
     \
-    memory==$memory \
+    memory=="$memory" \
     \
     net0=='virtio,bridge=vnet1306'
 
-  printf 'done creating VM %d.\n' $1
+  printf 'done creating VM %d.\n' "$1"
 }
 
 ################################################################################
 ## Install VM
 
 install_vm () (
-  printf 'Installing VM %d...\n' $1
+  printf 'Installing VM %d...\n' "$1"
 
-  proxmox_sync POST $apiurl/nodes/$node/qemu/$1/status/start
+  proxmox_sync POST "$api_url/nodes/$node/qemu/$1/status/start"
 
   while :; do
-    response=$(proxmox GET $apiurl/nodes/$node/qemu/$1/status/current)
+    response=$(proxmox GET "$api_url/nodes/$node/qemu/$1/status/current")
     status=$(echo "$response" | jq -r .data.status)
     case $status in
       running) sleep 1 ;;
       stopped) break ;;
-      *) printf ' unexpected status: `%s`\n' "$status"; exit 2 ;;
+      *) printf " unexpected status: '%s'\n" "$status"; exit 2 ;;
     esac
   done
 
-  printf 'done installing VM %d.\n' $1
+  printf 'done installing VM %d.\n' "$1"
 )
 
 ################################################################################
 ## Start VM
 
 start_vm () {
-  printf 'Starting VM %d...\n' $1
+  printf 'Starting VM %d...\n' "$1"
 
-  proxmox_sync POST $apiurl/nodes/$node/qemu/$1/config \
+  proxmox_sync POST "$api_url/nodes/$node/qemu/$1/config" \
     ide2=='none,media=cdrom' \
     net0=='virtio,bridge=vnet1305'
 
-  proxmox_sync POST $apiurl/nodes/$node/qemu/$1/status/start
+  proxmox_sync POST "$api_url/nodes/$node/qemu/$1/status/start"
 
-  printf 'done starting VM %d.\n' $1
+  printf 'done starting VM %d.\n' "$1"
 }
 
 ################################################################################
 ## Main loop
 
-printf 'Provisioning VMs%s with:\n' "$vmids"
-printf '  sockets: %d\n' $sockets
-printf '  cores: %d\n' $cores
-printf '  memory: %d\n' $memory
+printf 'Provisioning VMs%s with:\n' "$vm_ids"
+printf '  sockets: %d\n' "$sockets"
+printf '  cores: %d\n' "$cores"
+printf '  memory: %d\n' "$memory"
 
 provision_vm () {
-  build_iso $1
-  upload_iso $1
-  create_vm $1
-  install_vm $1
-  start_vm $1
-  remove_iso $1
+  build_iso "$1"
+  upload_iso "$1"
+  create_vm "$1"
+  install_vm "$1"
+  start_vm "$1"
+  remove_iso "$1"
 }
 
-for vmid in $vmids; do
-  provision_vm $vmid &
+for vm_id in $vm_ids; do
+  provision_vm "$vm_id" &
 done
 wait
 
-printf 'done provisioning VMs%s.\n' "$vmids"
+printf 'done provisioning VMs%s.\n' "$vm_ids"
 
 ################################################################################
 ## Cleanup
diff --git a/deployment/proxmox/remove.sh b/deployment/proxmox/remove.sh
index 9564fae6..b84323cf 100755
--- a/deployment/proxmox/remove.sh
+++ b/deployment/proxmox/remove.sh
@@ -1,17 +1,17 @@
-#!/usr/bin/env sh
+#!/usr/bin/env bash
 set -euC
 
 ################################################################################
 ## Constants
 
-readonly apiurl=https://192.168.51.81:8006/api2/json
+readonly api_url=https://192.168.51.81:8006/api2/json
 
 ## FIXME: There seems to be a problem with file upload where the task is
 ## registered to `node051` no matter what node we are actually uploading to? For
 ## now, let us just use `node051` everywhere.
 readonly node=node051
 
-readonly tmpdir=/tmp/proxmox-provision-$RANDOM$RANDOM
+readonly tmpdir=/tmp/proxmox-provision-$RANDOM
 mkdir $tmpdir
 
 ################################################################################
@@ -19,7 +19,7 @@ mkdir $tmpdir
 
 username=
 password=
-vmids=
+vm_ids=
 
 help () {
   cat <<EOF
@@ -38,7 +38,9 @@ Others:
 EOF
 }
 
+# shellcheck disable=SC2059
 die () { printf '\033[31m'; printf "$@"; printf '\033[0m\n'; exit 2; }
+# shellcheck disable=SC2059
 die_with_help () { printf '\033[31m'; printf "$@"; printf '\033[0m\n'; help; exit 2; }
 
 while [ $# -gt 0 ]; do
@@ -50,17 +52,17 @@ while [ $# -gt 0 ]; do
 
     -h|-\?|--help) help; exit 0 ;;
 
-    -*) die_with_help 'Unknown argument: `%s`.' "$argument" ;;
+    -*) die_with_help "Unknown argument: '%s'." "$argument" ;;
 
-    *) vmids="$vmids $argument" ;;
+    *) vm_ids="$vm_ids $argument" ;;
   esac
 done
 
 if [ -z "$username" ] || [ -z "$password" ]; then
   if [ -f .proxmox ]; then
-    { read username; read password; } < .proxmox
+    { read -r username; read -r password; } < .proxmox
   else
-    die_with_help 'Required: `--username` and `--password`.\n'
+    die_with_help "Required: '--username' and '--password'.\n"
   fi
 fi
 
@@ -71,19 +73,21 @@ printf 'Authenticating...'
 response=$(
     http \
         --verify no \
-        POST $apiurl/access/ticket \
+        POST $api_url/access/ticket \
         "username=$username" \
         "password=$password"
     )
-readonly ticket=$(echo "$response" | jq -r .data.ticket)
-readonly csrfToken=$(echo "$response" | jq -r .data.CSRFPreventionToken)
+ticket=$(echo "$response" | jq -r .data.ticket)
+readonly ticket
+csrf_token=$(echo "$response" | jq -r .data.CSRFPreventionToken)
+readonly csrf_token
 printf ' done.\n'
 
 acquire_lock () {
-  until mkdir $tmpdir/lock-$1 2>/dev/null; do sleep 1; done
+  until mkdir "$tmpdir/lock-$1" 2>/dev/null; do sleep 1; done
 }
 release_lock () {
-  rmdir $tmpdir/lock-$1
+  rmdir "$tmpdir/lock-$1"
 }
 
 proxmox () {
@@ -93,7 +97,7 @@ proxmox () {
     --form \
     "$@" \
     "Cookie:PVEAuthCookie=$ticket" \
-    "CSRFPreventionToken:$csrfToken"
+    "CSRFPreventionToken:$csrf_token"
   release_lock proxmox
 }
 
@@ -104,13 +108,13 @@ proxmox_sync () (
   upid=$(echo "$response" | jq -r .data)
 
   while :; do
-    response=$(proxmox GET $apiurl/nodes/$node/tasks/$upid/status)
+    response=$(proxmox GET "$api_url/nodes/$node/tasks/$upid/status")
     status=$(echo "$response" | jq -r .data.status)
 
     case $status in
       running) sleep 1 ;;
       stopped) break ;;
-      *) die 'unexpected status: `%s`' "$status" ;;
+      *) die "unexpected status: '%s'" "$status" ;;
     esac
   done
 )
@@ -119,43 +123,43 @@ proxmox_sync () (
 ## Stop VM
 
 stop_vm () {
-  printf 'Stopping VM %d...\n' $1
+  printf 'Stopping VM %d...\n' "$1"
 
-  proxmox_sync POST $apiurl/nodes/$node/qemu/$1/status/stop \
+  proxmox_sync POST "$api_url/nodes/$node/qemu/$1/status/stop" \
     'overrule-shutdown'==1
 
-  printf 'done stopping VM %d.\n' $1
+  printf 'done stopping VM %d.\n' "$1"
 }
 
 ################################################################################
 ## Delete VM
 
 delete_vm () {
-  printf 'Deleting VM %d...\n' $1
+  printf 'Deleting VM %d...\n' "$1"
 
-  proxmox_sync DELETE $apiurl/nodes/$node/qemu/$1 \
+  proxmox_sync DELETE "$api_url/nodes/$node/qemu/$1" \
     'destroy-unreferenced-disks'==1 \
     'purge'==1
 
-  printf 'done deleting VM %d.\n' $1
+  printf 'done deleting VM %d.\n' "$1"
 }
 
 ################################################################################
 ## Main loop
 
-printf 'Removing VMs%s...\n' "$vmids"
+printf 'Removing VMs%s...\n' "$vm_ids"
 
 remove_vm () {
-  stop_vm $1
-  delete_vm $1
+  stop_vm "$1"
+  delete_vm "$1"
 }
 
-for vmid in $vmids; do
-  remove_vm $vmid &
+for vm_id in $vm_ids; do
+  remove_vm "$vm_id" &
 done
 wait
 
-printf 'done removing VMs%s.\n' "$vmids"
+printf 'done removing VMs%s.\n' "$vm_ids"
 
 ################################################################################
 ## Cleanup

From 253a5ad8fac2abf55f355059da3a4ee525e63a8d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?=
 <nicolas.jeannerod@moduscreate.com>
Date: Wed, 19 Feb 2025 17:55:31 +0100
Subject: [PATCH 2/5] Fix missing space, add missing quotes

---
 deployment/proxmox/provision.sh | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/deployment/proxmox/provision.sh b/deployment/proxmox/provision.sh
index 89b15364..2930d304 100755
--- a/deployment/proxmox/provision.sh
+++ b/deployment/proxmox/provision.sh
@@ -85,7 +85,7 @@ readonly memory
 ## FIXME: When we figure out how to use other nodes than node051.
 # if [ -z "$node" ]; then
 #   printf 'Picking random node...'
-#   proxmox GET $api_url/nodes
+#   proxmox GET "$api_url/nodes"
 #   node=$(from_response .data[].node | sort -R | head -n 1)
 #   printf " done. Picked '%s'.\n" "$node"
 # fi
@@ -98,7 +98,7 @@ printf 'Authenticating...'
 response=$(
     http \
         --verify no \
-        POST $api_url/access/ticket \
+        POST "$api_url/access/ticket" \
         "username=$username" \
         "password=$password"
     )
@@ -170,7 +170,7 @@ upload_iso () {
   acquire_lock upload
   printf 'Uploading ISO for VM %d...\n' "$1"
 
-  proxmox_sync POST"$api_url/nodes/$node/storage/local/upload" \
+  proxmox_sync POST "$api_url/nodes/$node/storage/local/upload" \
     "filename@$tmpdir/installer-fedi$1.iso" \
     content==iso
 

From 5f2938877650008693cfcda53b1c1a772d9133ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?=
 <nicolas.jeannerod@moduscreate.com>
Date: Wed, 19 Feb 2025 18:09:25 +0100
Subject: [PATCH 3/5] printf + exit -> die

---
 deployment/proxmox/provision.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/deployment/proxmox/provision.sh b/deployment/proxmox/provision.sh
index 2930d304..9ad2cd40 100755
--- a/deployment/proxmox/provision.sh
+++ b/deployment/proxmox/provision.sh
@@ -237,7 +237,7 @@ install_vm () (
     case $status in
       running) sleep 1 ;;
       stopped) break ;;
-      *) printf " unexpected status: '%s'\n" "$status"; exit 2 ;;
+      *) die " unexpected status: '%s'\n" "$status" ;;
     esac
   done
 

From 797ce362bdf3a1591ea56f07912a66838e47d28e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?=
 <nicolas.jeannerod@moduscreate.com>
Date: Wed, 19 Feb 2025 18:17:59 +0100
Subject: [PATCH 4/5] Add debug mode to provisioning script

---
 deployment/proxmox/provision.sh | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/deployment/proxmox/provision.sh b/deployment/proxmox/provision.sh
index 9ad2cd40..0b38bfb0 100755
--- a/deployment/proxmox/provision.sh
+++ b/deployment/proxmox/provision.sh
@@ -24,6 +24,8 @@ cores=1
 memory=2048
 vm_ids=
 
+debug=false
+
 help () {
   cat <<EOF
 Usage: $0 [OPTION...] [ID...]
@@ -40,6 +42,7 @@ Other options:
   --sockets INT     Number of sockets (default: $sockets)
   --cores INT       Number of cores (default: $cores)
   --memory INT      Memory (default: $memory)
+  --debug           Run this script in debug mode
 
 Others:
   -h|-?|--help      Show this help and exit
@@ -51,6 +54,9 @@ die () { printf '\033[31m'; printf "$@"; printf '\033[0m\n'; exit 2; }
 # shellcheck disable=SC2059
 die_with_help () { printf '\033[31m'; printf "$@"; printf '\033[0m\n'; help; exit 2; }
 
+# shellcheck disable=SC2059
+debug () { if $debug; then printf >&2 '\033[37m'; printf >&2 "$@"; printf >&2 '\033[0m\n'; fi }
+
 while [ $# -gt 0 ]; do
   argument=$1
   shift
@@ -62,6 +68,8 @@ while [ $# -gt 0 ]; do
     --cores) cores=$1; shift ;;
     --memory) memory=$1; shift ;;
 
+    --debug) debug=true ;;
+
     -h|-\?|--help) help; exit 0 ;;
 
     -*) die_with_help "Unknown argument: '%s'." "$argument" ;;
@@ -91,6 +99,8 @@ readonly memory
 # fi
 # readonly node
 
+readonly debug
+
 ################################################################################
 ## Getting started
 
@@ -117,14 +127,19 @@ release_lock () {
 
 proxmox () {
   acquire_lock proxmox
-  http \
-    --form \
-    --verify no \
-    --ignore-stdin \
-    "$@" \
-    "Cookie:PVEAuthCookie=$ticket" \
-    "CSRFPreventionToken:$csrf_token"
+  debug 'request %s' "$*"
+  response=$(
+    http \
+      --form \
+      --verify no \
+      --ignore-stdin \
+      "$@" \
+      "Cookie:PVEAuthCookie=$ticket" \
+      "CSRFPreventionToken:$csrf_token"
+  )
+  debug 'response to request %s:\n  %s' "$*" "$response"
   release_lock proxmox
+  echo "$response"
 }
 
 ## Synchronous variant for when the `proxmox` function would just respond an

From 18a14d29abf03f15f26a751c02074df3029cedd1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicolas=20=E2=80=9CNiols=E2=80=9D=20Jeannerod?=
 <nicolas.jeannerod@moduscreate.com>
Date: Wed, 19 Feb 2025 18:20:08 +0100
Subject: [PATCH 5/5] Fix `vm_id` -> `vmid`

It probably was changed by mistake beforehand.
---
 deployment/proxmox/provision.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/deployment/proxmox/provision.sh b/deployment/proxmox/provision.sh
index 0b38bfb0..dba60b37 100755
--- a/deployment/proxmox/provision.sh
+++ b/deployment/proxmox/provision.sh
@@ -212,7 +212,7 @@ create_vm () {
 
   proxmox_sync POST "$api_url/nodes/$node/qemu" \
     \
-    vm_id=="$1" \
+    vmid=="$1" \
     name=="fedi$1" \
     pool==Fediversity \
     \