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) <noreply@anthropic.com>
This commit is contained in:
A 2026-02-13 15:05:18 -08:00 committed by GitHub
parent 5412b9891f
commit 415df93ea0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 86 additions and 47 deletions

View file

@ -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"

View file

@ -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)..."
}