diff --git a/defaults.inc b/defaults.inc index a85c975..00ed7d8 100755 --- a/defaults.inc +++ b/defaults.inc @@ -3,7 +3,7 @@ action="${1:-}" FOLDER='/opt/idssys/ta-proxmenu' -VERS='2026.7.25-49' +VERS='2026.7.25-50' noupdate=' ' diff --git a/inc/deploy-pulse-lxc.sh b/inc/deploy-pulse-lxc.sh index 029c5a3..eeed006 100644 --- a/inc/deploy-pulse-lxc.sh +++ b/inc/deploy-pulse-lxc.sh @@ -26,6 +26,10 @@ TAPM_PULSE_VALID_POSITIVE_INTEGER() { [[ "${1:-}" =~ ^[1-9][0-9]*$ ]] } +TAPM_PULSE_VALID_NONNEGATIVE_INTEGER() { + [[ "${1:-}" =~ ^[0-9]+$ ]] +} + TAPM_PULSE_VALID_PORT() { [[ "${1:-}" =~ ^[0-9]+$ ]] && (( 10#$1 >= 1 && 10#$1 <= 65535 )) } @@ -46,6 +50,23 @@ TAPM_PULSE_VALID_IPV4_CIDR() { done } +TAPM_PULSE_VALID_IPV4() { + local value="${1:-}" + + [[ "$value" != */* ]] && TAPM_PULSE_VALID_IPV4_CIDR "${value}/32" +} + +TAPM_PULSE_VALID_OPTIONAL_IPV4_CIDR() { + [[ -z "${1:-}" ]] || TAPM_PULSE_VALID_IPV4_CIDR "$1" +} + +TAPM_PULSE_VALID_OPTIONAL_VLAN() { + local value="${1:-}" + + [[ -z "$value" ]] || + { [[ "$value" =~ ^[0-9]+$ ]] && (( 10#$value >= 1 && 10#$value <= 4094 )); } +} + TAPM_PULSE_ARCH() { case "${1:-}" in x86_64|amd64) printf 'amd64\n';; @@ -147,11 +168,57 @@ TAPM_PULSE_PROMPT() { fi } +TAPM_PULSE_PROMPT_UNTIL_VALID() { + local variable="$1" + local label="$2" + local default_value="$3" + local validator="$4" + local error_message="$5" + local candidate + + while true; do + TAPM_PULSE_PROMPT candidate "$label" "$default_value" + if "$validator" "$candidate"; then + printf -v "$variable" '%s' "$candidate" + return 0 + fi + TAPM_PULSE_FAIL "$error_message" + done +} + TAPM_PULSE_FAIL() { echo -e "\n${idsCL[LightRed]}$1${idsCL[Default]}" return 1 } +TAPM_PULSE_CONFIRM_CREDENTIALS_SAVED() { + local pulse_url="$1" + local admin_username="$2" + local admin_password="$3" + local primary_api_token="$4" + local installed="$5" + local failed="$6" + local acknowledgement + + while true; do + echo + echo -e "${idsCL[LightYellow]}============================================================================${idsCL[Default]}" + echo -e "${idsCL[LightYellow]} IMPORTANT — SAVE THESE PULSE CREDENTIALS NOW${idsCL[Default]}" + echo -e "${idsCL[LightYellow]} They are generated for this installation and will not be displayed again.${idsCL[Default]}" + echo -e "${idsCL[LightYellow]}============================================================================${idsCL[Default]}" + echo -e " Pulse URL: ${idsCL[LightCyan]}${pulse_url}${idsCL[Default]}" + echo -e " Username: ${idsCL[White]}${admin_username}${idsCL[Default]}" + echo -e " Password: ${idsCL[LightGreen]}${admin_password}${idsCL[Default]}" + echo -e " API token: ${idsCL[LightGreen]}${primary_api_token}${idsCL[Default]}" + echo " Agents: ${installed} installed, ${failed} failed or offline" + echo -e "${idsCL[LightYellow]}============================================================================${idsCL[Default]}" + echo + read -r -p " Type saved after recording the password and API token: " acknowledgement + [[ "$acknowledgement" =~ ^[Ss][Aa][Vv][Ee][Dd]$ ]] && return 0 + echo -e "\n${idsCL[LightYellow]}The credentials remain above. Save them before continuing.${idsCL[Default]}" + done +} + TAPM_PULSE_SET_BRIDGE_FROM_SELECTION() { local variable="$1" local selection="${2:-}" @@ -632,35 +699,38 @@ TAPM_DEPLOY_PULSE_LXC() { { TAPM_PULSE_FAIL "Configured Pulse port '${pulse_port}' is invalid."; return 1; } default_ctid="$(pvesh get /cluster/nextid 2>/dev/null || true)" - TAPM_PULSE_PROMPT ctid "Container ID" "$default_ctid" - TAPM_PULSE_VALID_CTID "$ctid" || - { TAPM_PULSE_FAIL "The container ID is invalid."; return 1; } - if pct status "$ctid" >/dev/null 2>&1; then - TAPM_PULSE_FAIL "Container ${ctid} already exists; no changes were made." - return 1 - fi + while true; do + TAPM_PULSE_PROMPT_UNTIL_VALID ctid "Container ID" "$default_ctid" \ + TAPM_PULSE_VALID_CTID "The container ID must be a whole number of at least three digits." + if ! pct status "$ctid" >/dev/null 2>&1; then + break + fi + TAPM_PULSE_FAIL "Container ${ctid} already exists. Choose another container ID." + default_ctid="$(pvesh get /cluster/nextid 2>/dev/null || true)" + done - TAPM_PULSE_PROMPT hostname "Container hostname" "Pulse-Monitor" - TAPM_PULSE_VALID_HOSTNAME "$hostname" || - { TAPM_PULSE_FAIL "The hostname is invalid."; return 1; } + TAPM_PULSE_PROMPT_UNTIL_VALID hostname "Container hostname" "Pulse-Monitor" \ + TAPM_PULSE_VALID_HOSTNAME \ + "The hostname must contain only letters, numbers, periods, and hyphens." - read -r -p " Customize CPU, memory, disk, or swap? [y/N] " choice + while true; do + read -r -p " Customize CPU, memory, disk, or swap? [y/N] " choice + [[ -z "$choice" || "$choice" =~ ^[YyNn]$ ]] && break + TAPM_PULSE_FAIL "Enter y or n." + done if [[ "$choice" =~ ^[Yy]$ ]]; then - TAPM_PULSE_PROMPT memory "Memory in MiB" "$memory" - TAPM_PULSE_VALID_POSITIVE_INTEGER "$memory" || - { TAPM_PULSE_FAIL "Memory must be a positive whole number."; return 1; } - TAPM_PULSE_PROMPT disk "Root disk size in GiB" "$disk" - TAPM_PULSE_VALID_POSITIVE_INTEGER "$disk" || - { TAPM_PULSE_FAIL "Disk size must be a positive whole number."; return 1; } - TAPM_PULSE_PROMPT cores "CPU cores" "$cores" - TAPM_PULSE_VALID_POSITIVE_INTEGER "$cores" || - { TAPM_PULSE_FAIL "CPU cores must be a positive whole number."; return 1; } - TAPM_PULSE_PROMPT cpulimit "CPU limit (0 for unlimited)" "$cpulimit" - [[ "$cpulimit" =~ ^[0-9]+$ ]] || - { TAPM_PULSE_FAIL "CPU limit must be zero or a positive whole number."; return 1; } - TAPM_PULSE_PROMPT swap "Swap in MiB" "$swap" - [[ "$swap" =~ ^[0-9]+$ ]] || - { TAPM_PULSE_FAIL "Swap must be zero or a positive whole number."; return 1; } + TAPM_PULSE_PROMPT_UNTIL_VALID memory "Memory in MiB" "$memory" \ + TAPM_PULSE_VALID_POSITIVE_INTEGER "Memory must be a positive whole number." + TAPM_PULSE_PROMPT_UNTIL_VALID disk "Root disk size in GiB" "$disk" \ + TAPM_PULSE_VALID_POSITIVE_INTEGER "Disk size must be a positive whole number." + TAPM_PULSE_PROMPT_UNTIL_VALID cores "CPU cores" "$cores" \ + TAPM_PULSE_VALID_POSITIVE_INTEGER "CPU cores must be a positive whole number." + TAPM_PULSE_PROMPT_UNTIL_VALID cpulimit "CPU limit (0 for unlimited)" "$cpulimit" \ + TAPM_PULSE_VALID_NONNEGATIVE_INTEGER \ + "CPU limit must be zero or a positive whole number." + TAPM_PULSE_PROMPT_UNTIL_VALID swap "Swap in MiB" "$swap" \ + TAPM_PULSE_VALID_NONNEGATIVE_INTEGER \ + "Swap must be zero or a positive whole number." fi echo @@ -670,23 +740,20 @@ TAPM_DEPLOY_PULSE_LXC() { )" [[ -n "$default_bridge" ]] || default_bridge='vmbr0' TAPM_PULSE_SELECT_BRIDGE bridge "$default_bridge" || return 1 - TAPM_PULSE_PROMPT address_cidr \ - "Static IPv4 address with prefix (leave blank for DHCP)" + TAPM_PULSE_PROMPT_UNTIL_VALID address_cidr \ + "Static IPv4 address with prefix (leave blank for DHCP)" '' \ + TAPM_PULSE_VALID_OPTIONAL_IPV4_CIDR \ + "Enter a valid IPv4 CIDR such as 10.10.2.30/16, or leave it blank for DHCP." if [[ -n "$address_cidr" ]]; then - TAPM_PULSE_VALID_IPV4_CIDR "$address_cidr" || - { TAPM_PULSE_FAIL "The static IPv4 address is invalid."; return 1; } - TAPM_PULSE_PROMPT gateway "IPv4 gateway" - TAPM_PULSE_VALID_IPV4_CIDR "${gateway}/32" || - { TAPM_PULSE_FAIL "The IPv4 gateway is invalid."; return 1; } + TAPM_PULSE_PROMPT_UNTIL_VALID gateway "IPv4 gateway" '' \ + TAPM_PULSE_VALID_IPV4 "Enter a valid IPv4 gateway." fi TAPM_PULSE_PROMPT nameserver \ "DNS servers, space-separated (leave blank to inherit host settings)" - TAPM_PULSE_PROMPT vlan_id "VLAN ID (leave blank for untagged)" - if [[ -n "$vlan_id" ]] && - { [[ ! "$vlan_id" =~ ^[0-9]+$ ]] || (( vlan_id < 1 || vlan_id > 4094 )); }; then - TAPM_PULSE_FAIL "The VLAN ID must be between 1 and 4094." - return 1 - fi + TAPM_PULSE_PROMPT_UNTIL_VALID vlan_id \ + "VLAN ID (leave blank for untagged)" '' \ + TAPM_PULSE_VALID_OPTIONAL_VLAN \ + "The VLAN ID must be between 1 and 4094, or blank for untagged." TAPM_PULSE_CLUSTER_HA_ENABLED && add_ha='yes' @@ -930,11 +997,7 @@ TAPM_DEPLOY_PULSE_LXC() { echo echo -e "${idsCL[Green]}Pulse ${release} was installed and its service is active.${idsCL[Default]}" - echo -e " Open: ${idsCL[LightCyan]}${pulse_url}${idsCL[Default]}" - echo " Username: ${admin_username}" - echo " Password: ${admin_password}" - echo " API token: ${primary_api_token}" - echo " Agents: ${TAPM_PULSE_AGENTS_INSTALLED} installed, ${TAPM_PULSE_AGENTS_FAILED} failed or offline" - echo - echo -e "${idsCL[LightYellow]}Save the generated password and API token now; they are only shown once.${idsCL[Default]}" + TAPM_PULSE_CONFIRM_CREDENTIALS_SAVED \ + "$pulse_url" "$admin_username" "$admin_password" "$primary_api_token" \ + "$TAPM_PULSE_AGENTS_INSTALLED" "$TAPM_PULSE_AGENTS_FAILED" } diff --git a/tests/test-pulse.sh b/tests/test-pulse.sh index 3235392..5c7213b 100644 --- a/tests/test-pulse.sh +++ b/tests/test-pulse.sh @@ -63,10 +63,20 @@ assert_success "valid Pulse hostname" TAPM_PULSE_VALID_HOSTNAME pulse-monitor assert_failure "invalid Pulse hostname" TAPM_PULSE_VALID_HOSTNAME 'pulse monitor' assert_success "valid positive integer" TAPM_PULSE_VALID_POSITIVE_INTEGER 1024 assert_failure "zero is not positive" TAPM_PULSE_VALID_POSITIVE_INTEGER 0 +assert_success "zero is a valid nonnegative integer" \ + TAPM_PULSE_VALID_NONNEGATIVE_INTEGER 0 +assert_failure "negative integer rejected" TAPM_PULSE_VALID_NONNEGATIVE_INTEGER -1 assert_success "valid Pulse port" TAPM_PULSE_VALID_PORT 7655 assert_failure "Pulse port too high" TAPM_PULSE_VALID_PORT 65536 assert_success "valid Pulse IPv4 CIDR" TAPM_PULSE_VALID_IPV4_CIDR 10.10.1.50/24 assert_failure "invalid Pulse IPv4 CIDR" TAPM_PULSE_VALID_IPV4_CIDR 10.10.1.500/24 +assert_success "valid Pulse gateway" TAPM_PULSE_VALID_IPV4 10.10.0.1 +assert_failure "gateway CIDR rejected" TAPM_PULSE_VALID_IPV4 10.10.0.1/16 +assert_success "blank optional Pulse address accepted" \ + TAPM_PULSE_VALID_OPTIONAL_IPV4_CIDR '' +assert_success "valid Pulse VLAN" TAPM_PULSE_VALID_OPTIONAL_VLAN 4094 +assert_failure "Pulse VLAN zero rejected" TAPM_PULSE_VALID_OPTIONAL_VLAN 0 +assert_success "blank Pulse VLAN accepted" TAPM_PULSE_VALID_OPTIONAL_VLAN '' resources='[ {"type":"lxc","name":"pulse-a","tags":"tapm;pulse"},