mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-01 10:22:08 +00:00
Merge pull request #457 from f3rs3n/fix/cloud-init-helper-launcher
fix(vm): repair Cloud-Init helper links and download checks
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
"""Exercise the Cloud-Init menu without network access or guest creation."""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
SELECTOR = ROOT / "scripts/vm/select_linux_iso.sh"
|
||||
|
||||
|
||||
class CloudInitLauncherTests(unittest.TestCase):
|
||||
def run_menu(self, selection="2", body=None, curl_exit=0, dialog_exit=0,
|
||||
installer_exit=0, stdin="\n"):
|
||||
source = SELECTOR.read_text(encoding="utf-8")
|
||||
match = re.search(r"(?ms)^function select_linux_cloudinit\(\) \{.*?^\}", source)
|
||||
assert match is not None, "Cloud-Init menu function not found"
|
||||
with tempfile.TemporaryDirectory(prefix="cloudinit-launcher-") as temporary:
|
||||
work = Path(temporary)
|
||||
(work / "menus").mkdir()
|
||||
(work / "menus/create_vm_menu.sh").write_text(
|
||||
'printf "menu-returned\\n" >> "$ACTION_LOG"\n', encoding="utf-8")
|
||||
environment = os.environ.copy()
|
||||
environment.update({
|
||||
"LOCAL_SCRIPTS": str(work), "SELECTION": selection,
|
||||
"DIALOG_EXIT": str(dialog_exit), "CURL_EXIT": str(curl_exit),
|
||||
"INSTALLER_EXIT": str(installer_exit),
|
||||
"CURL_BODY": body if body is not None else
|
||||
'printf "helper-executed\\n" >> "$ACTION_LOG"\nexit "$INSTALLER_EXIT"\n',
|
||||
"ACTION_LOG": str(work / "actions"),
|
||||
"CURL_ARGS": str(work / "curl-args"),
|
||||
"DIALOG_LOG": str(work / "dialog-args"),
|
||||
})
|
||||
prelude = r'''
|
||||
translate() { printf '%s' "$1"; }
|
||||
msg_success() { printf 'continue-prompt\n' >> "$ACTION_LOG"; }
|
||||
whiptail() { printf 'website-prompt\n' >> "$ACTION_LOG"; }
|
||||
dialog() {
|
||||
printf '%s\n' "$@" >> "$DIALOG_LOG"
|
||||
if [[ " $* " == *" --menu "* ]]; then
|
||||
printf '%s' "$SELECTION" >&2
|
||||
return "$DIALOG_EXIT"
|
||||
fi
|
||||
}
|
||||
curl() {
|
||||
printf '%s\n' "$@" > "$CURL_ARGS"
|
||||
printf '%s' "$CURL_BODY"
|
||||
return "$CURL_EXIT"
|
||||
}
|
||||
'''
|
||||
result = subprocess.run(
|
||||
["bash", "--noprofile", "--norc", "-c",
|
||||
prelude + match.group() + "\nselect_linux_cloudinit\n"],
|
||||
input=stdin, capture_output=True, text=True, env=environment,
|
||||
timeout=5)
|
||||
def contents(name):
|
||||
path = work / name
|
||||
return path.read_text(encoding="utf-8") if path.exists() else ""
|
||||
return result, contents("curl-args"), contents("dialog-args"), contents("actions")
|
||||
|
||||
def test_debian_uses_the_maintained_generic_installer(self):
|
||||
result, curl_args, _, actions = self.run_menu()
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(curl_args.splitlines()[-1],
|
||||
"https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-vm.sh")
|
||||
self.assertIn("helper-executed", actions)
|
||||
|
||||
|
||||
def test_ubuntu_uses_one_generic_entry_without_obsolete_release_labels(self):
|
||||
result, curl_args, menu, actions = self.run_menu(selection="3")
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(curl_args.splitlines()[-1],
|
||||
"https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu-vm.sh")
|
||||
self.assertIn("helper-executed", actions)
|
||||
self.assertEqual(menu.count("(Cloud-Init automated)"), 3)
|
||||
for obsolete in ("Debian 13", "Debian 12", "Ubuntu 22.04", "Ubuntu 24.04",
|
||||
"Ubuntu 24.10", "Ubuntu 25.04"):
|
||||
self.assertNotIn(obsolete, menu)
|
||||
|
||||
|
||||
def test_failed_or_empty_download_never_runs_a_script(self):
|
||||
for body, exit_code in (("404: Not Found", 22),
|
||||
('printf "helper-executed\\n" >> "$ACTION_LOG"', 28),
|
||||
("", 0), (" \n\t", 0)):
|
||||
with self.subTest(body=body, exit_code=exit_code):
|
||||
result, _, messages, actions = self.run_menu(body=body, curl_exit=exit_code)
|
||||
self.assertNotIn("helper-executed", actions)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("Error: Failed to download the script.", messages)
|
||||
self.assertNotIn("menu-returned", actions)
|
||||
|
||||
|
||||
def test_installer_failure_returns_its_status_without_success_navigation(self):
|
||||
result, _, _, actions = self.run_menu(installer_exit=17)
|
||||
self.assertEqual(result.returncode, 17, result.stderr)
|
||||
self.assertIn("helper-executed", actions)
|
||||
self.assertNotIn("continue-prompt", actions)
|
||||
self.assertNotIn("website-prompt", actions)
|
||||
self.assertNotIn("menu-returned", actions)
|
||||
|
||||
|
||||
def test_arch_and_successful_menu_navigation_are_preserved(self):
|
||||
result, curl_args, _, actions = self.run_menu(selection="1")
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(curl_args.splitlines()[-1],
|
||||
"https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/archlinux-vm.sh")
|
||||
self.assertEqual(actions.splitlines(),
|
||||
["helper-executed", "continue-prompt", "website-prompt", "menu-returned"])
|
||||
arguments = curl_args.splitlines()
|
||||
self.assertIn("-fsSL", arguments)
|
||||
self.assertEqual(arguments[arguments.index("--connect-timeout") + 1], "5")
|
||||
self.assertEqual(arguments[arguments.index("--max-time") + 1], "60")
|
||||
|
||||
def test_return_and_cancel_do_not_download_or_execute(self):
|
||||
for selection, status, expected in (("4", 0, 1), ("2", 1, 0), ("2", 255, 0)):
|
||||
with self.subTest(selection=selection, status=status):
|
||||
result, curl_args, _, actions = self.run_menu(
|
||||
selection=selection, dialog_exit=status)
|
||||
self.assertEqual(result.returncode, expected, result.stderr)
|
||||
self.assertEqual(curl_args, "")
|
||||
self.assertEqual(actions, "")
|
||||
|
||||
def test_unknown_selection_is_rejected(self):
|
||||
result, curl_args, _, actions = self.run_menu(selection="999")
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertEqual(curl_args, "")
|
||||
self.assertEqual(actions, "")
|
||||
|
||||
def test_installer_keeps_interactive_standard_input(self):
|
||||
body = 'IFS= read -r answer; printf "input:%s\\n" "$answer" >> "$ACTION_LOG"\n'
|
||||
result, _, _, actions = self.run_menu(body=body, stdin="answer with spaces\n\n")
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn("input:answer with spaces\n", actions)
|
||||
self.assertIn("menu-returned", actions)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -170,18 +170,15 @@ function select_linux_iso_official() {
|
||||
|
||||
|
||||
function select_linux_cloudinit() {
|
||||
# The maintained Debian and Ubuntu helpers select their own release.
|
||||
local CLOUDINIT_OPTIONS=(
|
||||
"1" "Arch Linux (Cloud-Init automated) │ Helper-Scripts"
|
||||
"2" "Debian 13 (Cloud-Init automated) │ Helper-Scripts"
|
||||
"3" "Debian 12 (Cloud-Init automated) │ Helper-Scripts"
|
||||
"4" "Ubuntu 22.04 (Cloud-Init automated) │ Helper-Scripts"
|
||||
"5" "Ubuntu 24.04 (Cloud-Init automated) │ Helper-Scripts"
|
||||
"6" "Ubuntu 24.10 (Cloud-Init automated) │ Helper-Scripts"
|
||||
"7" "Ubuntu 25.04 (Cloud-Init automated) │ Helper-Scripts"
|
||||
"8" "$(translate "Return to Main Menu")"
|
||||
"1" "Arch Linux (Cloud-Init automated) │ Helper-Scripts"
|
||||
"2" "Debian (Cloud-Init automated) │ Helper-Scripts"
|
||||
"3" "Ubuntu (Cloud-Init automated) │ Helper-Scripts"
|
||||
"4" "$(translate "Return to Main Menu")"
|
||||
)
|
||||
|
||||
local script_selection
|
||||
local script_selection script_url script_content
|
||||
script_selection=$(dialog --backtitle "ProxMenux" --title "$(translate "Cloud-Init Automated Installers")" \
|
||||
--menu "\n$(translate "Select a pre-configured Linux VM script to execute:")" 20 78 10 \
|
||||
"${CLOUDINIT_OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
@@ -190,34 +187,29 @@ function select_linux_cloudinit() {
|
||||
|
||||
case "$script_selection" in
|
||||
1)
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/archlinux-vm.sh")
|
||||
script_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/archlinux-vm.sh"
|
||||
;;
|
||||
2)
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-13-vm.sh")
|
||||
script_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-vm.sh"
|
||||
;;
|
||||
3)
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-vm.sh")
|
||||
script_url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu-vm.sh"
|
||||
;;
|
||||
4)
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2204-vm.sh")
|
||||
;;
|
||||
5)
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2404-vm.sh")
|
||||
;;
|
||||
6)
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2410-vm.sh")
|
||||
;;
|
||||
7)
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2504-vm.sh")
|
||||
echo -e
|
||||
echo -e "after installation, checkout:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/272"
|
||||
echo -e
|
||||
;;
|
||||
8)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Do not execute a failed or partial download through process substitution.
|
||||
if ! script_content=$(curl -fsSL --connect-timeout 5 --max-time 60 "$script_url") || [[ -z "${script_content//[[:space:]]/}" ]]; then
|
||||
dialog --title "Helper Scripts" --msgbox "$(translate "Error: Failed to download the script.")" 8 70
|
||||
return 1
|
||||
fi
|
||||
bash -c "$script_content" || return $?
|
||||
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
|
||||
|
||||
Reference in New Issue
Block a user