mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-07-18 03:45:06 +02:00
feat(tools.func): add retry logic and debug mode for stability
New helper functions: - curl_with_retry: Robust curl wrapper with retry logic (3 attempts) - curl_api_with_retry: API calls with HTTP status handling - download_gpg_key: GPG key download with retry and dearmor support - debug_log: Conditional debug output when TOOLS_DEBUG=true Replaced critical curl calls: - MongoDB GPG key download - NodeSource GPG key download - PostgreSQL GPG key download - PHP (Sury) keyring download - MySQL GPG key download - setup_deb822_repo GPG import Benefits: - Automatic retry on transient network failures - Configurable timeouts (CURL_TIMEOUT, CURL_CONNECT_TO) - Debug mode for troubleshooting (TOOLS_DEBUG=true) - Consistent error handling across all GPG key imports
This commit is contained in:
+202
-22
@@ -13,6 +13,7 @@
|
|||||||
# - Legacy installation cleanup (nvm, rbenv, rustup)
|
# - Legacy installation cleanup (nvm, rbenv, rustup)
|
||||||
# - OS-upgrade-safe repository preparation
|
# - OS-upgrade-safe repository preparation
|
||||||
# - Service pattern matching for multi-version tools
|
# - Service pattern matching for multi-version tools
|
||||||
|
# - Debug mode for troubleshooting (TOOLS_DEBUG=true)
|
||||||
#
|
#
|
||||||
# Usage in install scripts:
|
# Usage in install scripts:
|
||||||
# source /dev/stdin <<< "$FUNCTIONS" # Load from build.func
|
# source /dev/stdin <<< "$FUNCTIONS" # Load from build.func
|
||||||
@@ -27,9 +28,195 @@
|
|||||||
# prepare_repository_setup() - Cleanup repos + keyrings + validate APT
|
# prepare_repository_setup() - Cleanup repos + keyrings + validate APT
|
||||||
# install_packages_with_retry() - Install with 3 retries and APT refresh
|
# install_packages_with_retry() - Install with 3 retries and APT refresh
|
||||||
# upgrade_packages_with_retry() - Upgrade with 3 retries and APT refresh
|
# upgrade_packages_with_retry() - Upgrade with 3 retries and APT refresh
|
||||||
|
# curl_with_retry() - Curl with retry logic and timeouts
|
||||||
|
#
|
||||||
|
# Debug Mode:
|
||||||
|
# TOOLS_DEBUG=true ./script.sh - Enable verbose output for troubleshooting
|
||||||
#
|
#
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Debug helper - outputs to stderr when TOOLS_DEBUG is enabled
|
||||||
|
# Usage: debug_log "message"
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
debug_log() {
|
||||||
|
if [[ "${TOOLS_DEBUG:-false}" == "true" || "${TOOLS_DEBUG:-0}" == "1" ]]; then
|
||||||
|
echo "[DEBUG] $*" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Robust curl wrapper with retry logic, timeouts, and error handling
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# curl_with_retry "https://example.com/file" "/tmp/output"
|
||||||
|
# curl_with_retry "https://api.github.com/..." "-" | jq .
|
||||||
|
# CURL_RETRIES=5 curl_with_retry "https://slow.server/file" "/tmp/out"
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1 - URL to download
|
||||||
|
# $2 - Output file path (use "-" for stdout)
|
||||||
|
# $3 - (optional) Additional curl options as string
|
||||||
|
#
|
||||||
|
# Variables:
|
||||||
|
# CURL_RETRIES - Number of retries (default: 3)
|
||||||
|
# CURL_TIMEOUT - Max time per attempt in seconds (default: 60)
|
||||||
|
# CURL_CONNECT_TO - Connection timeout in seconds (default: 10)
|
||||||
|
#
|
||||||
|
# Returns: 0 on success, 1 on failure after all retries
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
curl_with_retry() {
|
||||||
|
local url="$1"
|
||||||
|
local output="${2:--}"
|
||||||
|
local extra_opts="${3:-}"
|
||||||
|
local retries="${CURL_RETRIES:-3}"
|
||||||
|
local timeout="${CURL_TIMEOUT:-60}"
|
||||||
|
local connect_timeout="${CURL_CONNECT_TO:-10}"
|
||||||
|
|
||||||
|
local attempt=1
|
||||||
|
local success=false
|
||||||
|
|
||||||
|
while [[ $attempt -le $retries ]]; do
|
||||||
|
debug_log "curl attempt $attempt/$retries: $url"
|
||||||
|
|
||||||
|
local curl_cmd="curl -fsSL --connect-timeout $connect_timeout --max-time $timeout"
|
||||||
|
[[ -n "$extra_opts" ]] && curl_cmd="$curl_cmd $extra_opts"
|
||||||
|
|
||||||
|
if [[ "$output" == "-" ]]; then
|
||||||
|
if $curl_cmd "$url"; then
|
||||||
|
success=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if $curl_cmd -o "$output" "$url"; then
|
||||||
|
success=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
debug_log "curl attempt $attempt failed, waiting ${attempt}s before retry..."
|
||||||
|
sleep "$attempt"
|
||||||
|
((attempt++))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$success" == "true" ]]; then
|
||||||
|
debug_log "curl successful: $url"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
debug_log "curl FAILED after $retries attempts: $url"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Robust curl wrapper for API calls (returns HTTP code + body)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# response=$(curl_api_with_retry "https://api.github.com/repos/owner/repo/releases/latest")
|
||||||
|
# http_code=$(curl_api_with_retry "https://api.github.com/..." "/tmp/body.json")
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1 - URL to call
|
||||||
|
# $2 - (optional) Output file for body (default: stdout)
|
||||||
|
# $3 - (optional) Additional curl options as string
|
||||||
|
#
|
||||||
|
# Returns: HTTP status code, body in file or stdout
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
curl_api_with_retry() {
|
||||||
|
local url="$1"
|
||||||
|
local body_file="${2:-}"
|
||||||
|
local extra_opts="${3:-}"
|
||||||
|
local retries="${CURL_RETRIES:-3}"
|
||||||
|
local timeout="${CURL_TIMEOUT:-60}"
|
||||||
|
local connect_timeout="${CURL_CONNECT_TO:-10}"
|
||||||
|
|
||||||
|
local attempt=1
|
||||||
|
local http_code=""
|
||||||
|
|
||||||
|
while [[ $attempt -le $retries ]]; do
|
||||||
|
debug_log "curl API attempt $attempt/$retries: $url"
|
||||||
|
|
||||||
|
local curl_cmd="curl -fsSL --connect-timeout $connect_timeout --max-time $timeout -w '%{http_code}'"
|
||||||
|
[[ -n "$extra_opts" ]] && curl_cmd="$curl_cmd $extra_opts"
|
||||||
|
|
||||||
|
if [[ -n "$body_file" ]]; then
|
||||||
|
http_code=$($curl_cmd -o "$body_file" "$url" 2>/dev/null) || true
|
||||||
|
else
|
||||||
|
# Capture body and http_code separately
|
||||||
|
local tmp_body="/tmp/curl_api_body_$$"
|
||||||
|
http_code=$($curl_cmd -o "$tmp_body" "$url" 2>/dev/null) || true
|
||||||
|
if [[ -f "$tmp_body" ]]; then
|
||||||
|
cat "$tmp_body"
|
||||||
|
rm -f "$tmp_body"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Success on 2xx codes
|
||||||
|
if [[ "$http_code" =~ ^2[0-9]{2}$ ]]; then
|
||||||
|
debug_log "curl API successful: $url (HTTP $http_code)"
|
||||||
|
echo "$http_code"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
debug_log "curl API attempt $attempt failed (HTTP $http_code), waiting ${attempt}s..."
|
||||||
|
sleep "$attempt"
|
||||||
|
((attempt++))
|
||||||
|
done
|
||||||
|
|
||||||
|
debug_log "curl API FAILED after $retries attempts: $url"
|
||||||
|
echo "$http_code"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Download and install GPG key with retry logic
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# download_gpg_key "https://example.com/key.gpg" "/etc/apt/keyrings/example.gpg"
|
||||||
|
# download_gpg_key "https://example.com/key.asc" "/etc/apt/keyrings/example.gpg" "dearmor"
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1 - URL to GPG key
|
||||||
|
# $2 - Output path for keyring file
|
||||||
|
# $3 - (optional) "dearmor" to convert ASCII-armored key to binary
|
||||||
|
#
|
||||||
|
# Returns: 0 on success, 1 on failure
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
download_gpg_key() {
|
||||||
|
local url="$1"
|
||||||
|
local output="$2"
|
||||||
|
local mode="${3:-}"
|
||||||
|
local retries="${CURL_RETRIES:-3}"
|
||||||
|
local timeout="${CURL_TIMEOUT:-30}"
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$output")"
|
||||||
|
|
||||||
|
local attempt=1
|
||||||
|
while [[ $attempt -le $retries ]]; do
|
||||||
|
debug_log "GPG key download attempt $attempt/$retries: $url"
|
||||||
|
|
||||||
|
if [[ "$mode" == "dearmor" ]]; then
|
||||||
|
if curl -fsSL --connect-timeout 10 --max-time "$timeout" "$url" 2>/dev/null | \
|
||||||
|
gpg --dearmor --yes -o "$output" 2>/dev/null; then
|
||||||
|
debug_log "GPG key installed: $output"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if curl -fsSL --connect-timeout 10 --max-time "$timeout" -o "$output" "$url" 2>/dev/null; then
|
||||||
|
debug_log "GPG key downloaded: $output"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
debug_log "GPG key download attempt $attempt failed, waiting ${attempt}s..."
|
||||||
|
sleep "$attempt"
|
||||||
|
((attempt++))
|
||||||
|
done
|
||||||
|
|
||||||
|
debug_log "GPG key download FAILED after $retries attempts: $url"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Cache installed version to avoid repeated checks
|
# Cache installed version to avoid repeated checks
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@@ -453,9 +640,8 @@ manage_tool_repository() {
|
|||||||
# Clean old repos first
|
# Clean old repos first
|
||||||
cleanup_old_repo_files "mongodb"
|
cleanup_old_repo_files "mongodb"
|
||||||
|
|
||||||
# Import GPG key
|
# Import GPG key with retry logic
|
||||||
mkdir -p /etc/apt/keyrings
|
if ! download_gpg_key "$gpg_key_url" "/etc/apt/keyrings/mongodb-server-${version}.gpg" "dearmor"; then
|
||||||
if ! curl -fsSL "$gpg_key_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/mongodb-server-${version}.gpg" 2>/dev/null; then
|
|
||||||
msg_error "Failed to download MongoDB GPG key"
|
msg_error "Failed to download MongoDB GPG key"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@@ -535,14 +721,11 @@ EOF
|
|||||||
local distro_codename
|
local distro_codename
|
||||||
distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)
|
distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)
|
||||||
|
|
||||||
# Create keyring directory first
|
# Download GPG key from NodeSource with retry logic
|
||||||
mkdir -p /etc/apt/keyrings
|
if ! download_gpg_key "$gpg_key_url" "/etc/apt/keyrings/nodesource.gpg" "dearmor"; then
|
||||||
|
|
||||||
# Download GPG key from NodeSource
|
|
||||||
curl -fsSL "$gpg_key_url" | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg || {
|
|
||||||
msg_error "Failed to import NodeSource GPG key"
|
msg_error "Failed to import NodeSource GPG key"
|
||||||
return 1
|
return 1
|
||||||
}
|
fi
|
||||||
|
|
||||||
cat <<EOF >/etc/apt/sources.list.d/nodesource.sources
|
cat <<EOF >/etc/apt/sources.list.d/nodesource.sources
|
||||||
Types: deb
|
Types: deb
|
||||||
@@ -563,11 +746,11 @@ EOF
|
|||||||
|
|
||||||
cleanup_old_repo_files "php"
|
cleanup_old_repo_files "php"
|
||||||
|
|
||||||
# Download and install keyring
|
# Download and install keyring with retry logic
|
||||||
curl -fsSLo /tmp/debsuryorg-archive-keyring.deb "$gpg_key_url" || {
|
if ! curl_with_retry "$gpg_key_url" "/tmp/debsuryorg-archive-keyring.deb"; then
|
||||||
msg_error "Failed to download PHP keyring"
|
msg_error "Failed to download PHP keyring"
|
||||||
return 1
|
return 1
|
||||||
}
|
fi
|
||||||
dpkg -i /tmp/debsuryorg-archive-keyring.deb >/dev/null 2>&1 || {
|
dpkg -i /tmp/debsuryorg-archive-keyring.deb >/dev/null 2>&1 || {
|
||||||
msg_error "Failed to install PHP keyring"
|
msg_error "Failed to install PHP keyring"
|
||||||
rm -f /tmp/debsuryorg-archive-keyring.deb
|
rm -f /tmp/debsuryorg-archive-keyring.deb
|
||||||
@@ -597,14 +780,11 @@ EOF
|
|||||||
|
|
||||||
cleanup_old_repo_files "postgresql"
|
cleanup_old_repo_files "postgresql"
|
||||||
|
|
||||||
# Create keyring directory first
|
# Import PostgreSQL key with retry logic
|
||||||
mkdir -p /etc/apt/keyrings
|
if ! download_gpg_key "$gpg_key_url" "/etc/apt/keyrings/postgresql.gpg" "dearmor"; then
|
||||||
|
|
||||||
# Import PostgreSQL key
|
|
||||||
curl -fsSL "$gpg_key_url" | gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg || {
|
|
||||||
msg_error "Failed to import PostgreSQL GPG key"
|
msg_error "Failed to import PostgreSQL GPG key"
|
||||||
return 1
|
return 1
|
||||||
}
|
fi
|
||||||
|
|
||||||
# Setup repository
|
# Setup repository
|
||||||
local distro_codename
|
local distro_codename
|
||||||
@@ -1239,11 +1419,11 @@ setup_deb822_repo() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Import GPG
|
# Import GPG key with retry logic
|
||||||
curl -fsSL "$gpg_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" || {
|
if ! download_gpg_key "$gpg_url" "/etc/apt/keyrings/${name}.gpg" "dearmor"; then
|
||||||
msg_error "Failed to import GPG key for ${name}"
|
msg_error "Failed to import GPG key for ${name}"
|
||||||
return 1
|
return 1
|
||||||
}
|
fi
|
||||||
|
|
||||||
# Write deb822
|
# Write deb822
|
||||||
{
|
{
|
||||||
@@ -3654,7 +3834,7 @@ function setup_mysql() {
|
|||||||
if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then
|
if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then
|
||||||
msg_info "Debian ${DISTRO_CODENAME} detected → using MySQL 8.4 LTS (libaio1t64 compatible)"
|
msg_info "Debian ${DISTRO_CODENAME} detected → using MySQL 8.4 LTS (libaio1t64 compatible)"
|
||||||
|
|
||||||
if ! curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --dearmor -o /etc/apt/keyrings/mysql.gpg 2>/dev/null; then
|
if ! download_gpg_key "https://repo.mysql.com/RPM-GPG-KEY-mysql-2023" "/etc/apt/keyrings/mysql.gpg" "dearmor"; then
|
||||||
msg_error "Failed to import MySQL GPG key"
|
msg_error "Failed to import MySQL GPG key"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user