From 415df93ea055d083d14528c74a9dad030462eefb Mon Sep 17 00:00:00 2001 From: A <258483684+la14-1@users.noreply.github.com> Date: Fri, 13 Feb 2026 15:05:18 -0800 Subject: [PATCH] refactor: decompose latitude and contabo create_server into focused helpers (#1022) Extract validation, error handling, and response parsing from create_server into dedicated helpers following the pattern from PR #1016. Latitude helpers: _latitude_validate_inputs, _latitude_check_create_error, _latitude_extract_server_id Contabo helpers: _contabo_validate_inputs, _contabo_check_create_error, _contabo_extract_instance_id Agent: complexity-hunter Co-authored-by: A <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) --- contabo/lib/common.sh | 66 ++++++++++++++++++++++++++--------------- latitude/lib/common.sh | 67 +++++++++++++++++++++++++++--------------- 2 files changed, 86 insertions(+), 47 deletions(-) diff --git a/contabo/lib/common.sh b/contabo/lib/common.sh index 7072c663..ac36c700 100644 --- a/contabo/lib/common.sh +++ b/contabo/lib/common.sh @@ -200,24 +200,56 @@ _contabo_wait_for_instance() { CONTABO_SERVER_IP "Instance" 60 } +# Validate Contabo instance creation inputs +# Usage: _contabo_validate_inputs PRODUCT_ID REGION IMAGE_ID PERIOD +_contabo_validate_inputs() { + validate_resource_name "$1" || { log_error "Invalid CONTABO_PRODUCT_ID"; return 1; } + validate_region_name "$2" || { log_error "Invalid CONTABO_REGION"; return 1; } + validate_resource_name "$3" || { log_error "Invalid CONTABO_IMAGE_ID"; return 1; } + if [[ ! "$4" =~ ^[0-9]+$ ]]; then + log_error "Invalid CONTABO_PERIOD: must be a positive integer" + return 1 + fi +} + +# Check instance creation response for errors and report failure details +# Usage: _contabo_check_create_error RESPONSE +# Returns 0 if there IS an error (caller should return 1), 1 if response is OK +_contabo_check_create_error() { + local response="$1" + if ! echo "$response" | grep -q '"error"' && echo "$response" | grep -q '"instanceId"'; then + return 1 + fi + log_error "Failed to create Contabo instance" + log_error "API Error: $(extract_api_error_message "$response" "$response")" + log_error "" + log_error "Common issues:" + log_error " - Insufficient account balance" + log_error " - Product/region unavailable" + log_error " - Account limits reached" + return 0 +} + +# Extract instance ID from creation response +# Sets: CONTABO_INSTANCE_ID on success +# Usage: _contabo_extract_instance_id RESPONSE +_contabo_extract_instance_id() { + local response="$1" + CONTABO_INSTANCE_ID=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('data',[{}])[0].get('instanceId',''))") + export CONTABO_INSTANCE_ID + log_info "Instance created: ID=$CONTABO_INSTANCE_ID" +} + # Create a Contabo instance with cloud-init create_server() { local name="$1" - # Use env vars or defaults local region="${CONTABO_REGION:-EU}" local product_id="${CONTABO_PRODUCT_ID:-V45}" # VPS S SSD (2 vCPU, 8 GB RAM) local image_id="${CONTABO_IMAGE_ID:-ubuntu-24.04}" local period="${CONTABO_PERIOD:-1}" # 1 month - # Validate inputs to prevent injection into Python code - validate_resource_name "$product_id" || { log_error "Invalid CONTABO_PRODUCT_ID"; return 1; } - validate_region_name "$region" || { log_error "Invalid CONTABO_REGION"; return 1; } - validate_resource_name "$image_id" || { log_error "Invalid CONTABO_IMAGE_ID"; return 1; } - if [[ ! "$period" =~ ^[0-9]+$ ]]; then - log_error "Invalid CONTABO_PERIOD: must be a positive integer" - return 1 - fi + _contabo_validate_inputs "$product_id" "$region" "$image_id" "$period" || return 1 log_step "Creating Contabo instance '$name' (product: $product_id, region: $region)..." @@ -230,23 +262,11 @@ create_server() { local response response=$(contabo_api POST "/compute/instances" "$body") - # Check for errors - if echo "$response" | grep -q '"error"' || ! echo "$response" | grep -q '"instanceId"'; then - log_error "Failed to create Contabo instance" - log_error "API Error: $(extract_api_error_message "$response" "$response")" - log_error "" - log_error "Common issues:" - log_error " - Insufficient account balance" - log_error " - Product/region unavailable" - log_error " - Account limits reached" + if _contabo_check_create_error "$response"; then return 1 fi - # Extract instance ID - CONTABO_INSTANCE_ID=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('data',[{}])[0].get('instanceId',''))") - export CONTABO_INSTANCE_ID - - log_info "Instance created: ID=$CONTABO_INSTANCE_ID" + _contabo_extract_instance_id "$response" log_step "Waiting for instance to be provisioned..." _contabo_wait_for_instance "$CONTABO_INSTANCE_ID" diff --git a/latitude/lib/common.sh b/latitude/lib/common.sh index 555dda16..35522e2a 100644 --- a/latitude/lib/common.sh +++ b/latitude/lib/common.sh @@ -207,6 +207,46 @@ get_server_name() { get_validated_server_name "LATITUDE_SERVER_NAME" "Enter server name: " } +# Validate Latitude.sh server creation inputs +# Usage: _latitude_validate_inputs PLAN SITE OS +_latitude_validate_inputs() { + validate_resource_name "$1" || { log_error "Invalid LATITUDE_PLAN"; return 1; } + validate_region_name "$2" || { log_error "Invalid LATITUDE_SITE"; return 1; } + validate_resource_name "$3" || { log_error "Invalid LATITUDE_OS"; return 1; } +} + +# Check server creation response for errors and report failure details +# Usage: _latitude_check_create_error RESPONSE +# Returns 0 if there IS an error (caller should return 1), 1 if response is OK +_latitude_check_create_error() { + local response="$1" + if echo "$response" | python3 -c "import json,sys; d=json.loads(sys.stdin.read()); sys.exit(0 if 'data' in d else 1)" 2>/dev/null; then + return 1 + fi + log_error "Failed to create Latitude.sh server" + local error_msg + error_msg=$(echo "$response" | _latitude_extract_error) + log_error "API Error: $error_msg" + log_error "" + log_error "Common issues:" + log_error " - Insufficient account balance or payment method required" + log_error " - Plan/site unavailable (try different LATITUDE_PLAN or LATITUDE_SITE)" + log_error " - Server limit reached for your account" + log_error "" + log_error "Check your account status: https://www.latitude.sh/dashboard" + return 0 +} + +# Extract server ID from creation response or report failure +# Sets: LATITUDE_SERVER_ID on success +# Usage: _latitude_extract_server_id RESPONSE +_latitude_extract_server_id() { + local response="$1" + LATITUDE_SERVER_ID=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['data']['id'])") + export LATITUDE_SERVER_ID + log_info "Server created: ID=$LATITUDE_SERVER_ID" +} + # Create a Latitude.sh server create_server() { local hostname="$1" @@ -214,18 +254,13 @@ create_server() { local site="${LATITUDE_SITE:-DAL2}" local os="${LATITUDE_OS:-ubuntu_24_04_x64_lts}" - # Validate env var inputs to prevent injection into Python code - validate_resource_name "$plan" || { log_error "Invalid LATITUDE_PLAN"; return 1; } - validate_region_name "$site" || { log_error "Invalid LATITUDE_SITE"; return 1; } - validate_resource_name "$os" || { log_error "Invalid LATITUDE_OS"; return 1; } + _latitude_validate_inputs "$plan" "$site" "$os" || return 1 log_step "Creating Latitude.sh server '$hostname' (plan: $plan, site: $site)..." - # Get project ID local project_id project_id=$(get_latitude_project_id) || return 1 - # Get all SSH key IDs local ssh_key_ids ssh_key_ids=$(_latitude_get_ssh_key_ids) @@ -235,27 +270,11 @@ create_server() { local response response=$(latitude_api POST "/servers" "$body") - # Check for errors - if ! echo "$response" | python3 -c "import json,sys; d=json.loads(sys.stdin.read()); sys.exit(0 if 'data' in d else 1)" 2>/dev/null; then - log_error "Failed to create Latitude.sh server" - local error_msg - error_msg=$(echo "$response" | _latitude_extract_error) - log_error "API Error: $error_msg" - log_error "" - log_error "Common issues:" - log_error " - Insufficient account balance or payment method required" - log_error " - Plan/site unavailable (try different LATITUDE_PLAN or LATITUDE_SITE)" - log_error " - Server limit reached for your account" - log_error "" - log_error "Check your account status: https://www.latitude.sh/dashboard" + if _latitude_check_create_error "$response"; then return 1 fi - # Extract server ID - LATITUDE_SERVER_ID=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['data']['id'])") - export LATITUDE_SERVER_ID - - log_info "Server created: ID=$LATITUDE_SERVER_ID" + _latitude_extract_server_id "$response" log_step "Waiting for server provisioning (this may take a few minutes for bare metal)..." }