diff --git a/binarylane/lib/common.sh b/binarylane/lib/common.sh index fcdbe4bb..6f96b50b 100644 --- a/binarylane/lib/common.sh +++ b/binarylane/lib/common.sh @@ -167,6 +167,57 @@ _binarylane_wait_for_active() { return 1 } +# Build JSON request body for BinaryLane server creation +# Usage: _binarylane_build_server_body NAME REGION SIZE IMAGE SSH_KEY_IDS +_binarylane_build_server_body() { + local name="$1" region="$2" size="$3" image="$4" ssh_key_ids="$5" + + local userdata + userdata=$(get_cloud_init_userdata) + local json_userdata + json_userdata=$(json_escape "$userdata") + + python3 -c " +import json, sys +userdata = json.loads(sys.stdin.read()) +body = { + 'name': '$name', + 'region': '$region', + 'size': '$size', + 'image': '$image', + 'ssh_keys': $ssh_key_ids, + 'user_data': userdata, + 'backups': False +} +print(json.dumps(body)) +" <<< "$json_userdata" +} + +# Parse server ID from create response, or log error and return 1 +# Sets BINARYLANE_SERVER_ID on success +_binarylane_handle_create_response() { + local response="$1" + + if echo "$response" | grep -q '"server"'; then + BINARYLANE_SERVER_ID=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['server']['id'])") + export BINARYLANE_SERVER_ID + log_info "Server created: ID=$BINARYLANE_SERVER_ID" + return 0 + fi + + log_error "Failed to create BinaryLane server" + local error_msg + error_msg=$(echo "$response" | python3 -c "import json,sys; d=json.loads(sys.stdin.read()); print(d.get('message','Unknown error'))" 2>/dev/null || echo "$response") + log_error "API Error: $error_msg" + log_warn "Common issues:" + log_warn " - Insufficient account balance" + log_warn " - Size/region/image unavailable (try different BINARYLANE_SIZE, BINARYLANE_REGION, or BINARYLANE_IMAGE)" + log_warn " - Server limit reached" + log_warn " - Invalid cloud-init userdata" + log_warn "Remediation: Check https://home.binarylane.com.au/" + return 1 +} + create_server() { local name="$1" local size="${BINARYLANE_SIZE:-std-1vcpu}" @@ -186,52 +237,14 @@ create_server() { local ssh_key_ids ssh_key_ids=$(extract_ssh_key_ids "$ssh_keys_response" "ssh_keys") - local userdata - userdata=$(get_cloud_init_userdata) - - # Pass userdata safely via stdin to avoid triple-quote injection - local json_userdata - json_userdata=$(json_escape "$userdata") - + # Build request body and create server local body - body=$(python3 -c " -import json, sys -userdata = json.loads(sys.stdin.read()) -body = { - 'name': '$name', - 'region': '$region', - 'size': '$size', - 'image': '$image', - 'ssh_keys': $ssh_key_ids, - 'user_data': userdata, - 'backups': False -} -print(json.dumps(body)) -" <<< "$json_userdata") + body=$(_binarylane_build_server_body "$name" "$region" "$size" "$image" "$ssh_key_ids") local response response=$(binarylane_api POST "/servers" "$body") - if echo "$response" | grep -q '"server"'; then - BINARYLANE_SERVER_ID=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['server']['id'])") - export BINARYLANE_SERVER_ID - log_info "Server created: ID=$BINARYLANE_SERVER_ID" - else - log_error "Failed to create BinaryLane server" - - # Parse error details - local error_msg - error_msg=$(echo "$response" | python3 -c "import json,sys; d=json.loads(sys.stdin.read()); print(d.get('message','Unknown error'))" 2>/dev/null || echo "$response") - log_error "API Error: $error_msg" - - log_warn "Common issues:" - log_warn " - Insufficient account balance" - log_warn " - Size/region/image unavailable (try different BINARYLANE_SIZE, BINARYLANE_REGION, or BINARYLANE_IMAGE)" - log_warn " - Server limit reached" - log_warn " - Invalid cloud-init userdata" - log_warn "Remediation: Check https://home.binarylane.com.au/" - return 1 - fi + _binarylane_handle_create_response "$response" || return 1 # Wait for server to become active with IP _binarylane_wait_for_active diff --git a/ramnode/lib/common.sh b/ramnode/lib/common.sh index 1d56d4b6..18541b69 100755 --- a/ramnode/lib/common.sh +++ b/ramnode/lib/common.sh @@ -404,6 +404,29 @@ for net_name, addrs in addresses.items(): return 1 } +# Parse server ID from create response, or log error and return 1 +# Sets RAMNODE_SERVER_ID on success +_ramnode_handle_create_response() { + local response="$1" + + if echo "$response" | grep -q '"error"'; then + log_error "Failed to create RamNode server" + local error_msg + error_msg=$(echo "$response" | python3 -c "import json,sys; d=json.loads(sys.stdin.read()); print(d.get('error',{}).get('message','Unknown error'))" 2>/dev/null || echo "$response") + log_error "API Error: $error_msg" + log_error "" + log_error "Common issues:" + log_error " - Insufficient cloud credit (minimum \$3 required)" + log_error " - Flavor not available" + log_error " - SSH key not found" + return 1 + fi + + RAMNODE_SERVER_ID=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['server']['id'])") + export RAMNODE_SERVER_ID + log_info "Server created: ID=$RAMNODE_SERVER_ID" +} + # Create a RamNode server create_server() { local name="$1" @@ -416,7 +439,6 @@ create_server() { log_info "Fetching Ubuntu 24.04 image..." local image_id image_id=$(_list_images) - if [[ -z "$image_id" ]]; then log_error "Could not find Ubuntu 24.04 image" return 1 @@ -441,23 +463,7 @@ create_server() { local response response=$(ramnode_compute_api POST "/servers" "$body") - if echo "$response" | grep -q '"error"'; then - log_error "Failed to create RamNode server" - local error_msg - error_msg=$(echo "$response" | python3 -c "import json,sys; d=json.loads(sys.stdin.read()); print(d.get('error',{}).get('message','Unknown error'))" 2>/dev/null || echo "$response") - log_error "API Error: $error_msg" - log_error "" - log_error "Common issues:" - log_error " - Insufficient cloud credit (minimum \$3 required)" - log_error " - Flavor not available" - log_error " - SSH key not found" - return 1 - fi - - # Extract server ID - RAMNODE_SERVER_ID=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['server']['id'])") - export RAMNODE_SERVER_ID - log_info "Server created: ID=$RAMNODE_SERVER_ID" + _ramnode_handle_create_response "$response" || return 1 # Wait for IP assignment _ramnode_wait_for_ip diff --git a/upcloud/lib/common.sh b/upcloud/lib/common.sh index d486258f..f9c5c527 100644 --- a/upcloud/lib/common.sh +++ b/upcloud/lib/common.sh @@ -227,6 +227,29 @@ print(json.dumps(body)) " <<< "$json_ssh_key" } +# Parse server UUID from create response, or log error and return 1 +# Sets UPCLOUD_SERVER_UUID on success +_upcloud_handle_create_response() { + local response="$1" + + if echo "$response" | grep -q '"error"'; then + log_error "Failed to create UpCloud server" + local error_msg + error_msg=$(echo "$response" | python3 -c "import json,sys; d=json.loads(sys.stdin.read()); print(d.get('error',{}).get('error_message','Unknown error'))" 2>/dev/null || echo "$response") + log_error "API Error: $error_msg" + log_warn "Common issues:" + log_warn " - Insufficient account balance" + log_warn " - Plan not available in zone (try different UPCLOUD_PLAN or UPCLOUD_ZONE)" + log_warn " - Server limit reached" + log_warn "Remediation: Check https://hub.upcloud.com/" + return 1 + fi + + UPCLOUD_SERVER_UUID=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['server']['uuid'])") + export UPCLOUD_SERVER_UUID + log_info "Server created: UUID=$UPCLOUD_SERVER_UUID" +} + # Create an UpCloud server create_server() { local name="$1" @@ -263,24 +286,7 @@ create_server() { local response response=$(upcloud_api POST "/server" "$body") - # Check for errors - if echo "$response" | grep -q '"error"'; then - log_error "Failed to create UpCloud server" - local error_msg - error_msg=$(echo "$response" | python3 -c "import json,sys; d=json.loads(sys.stdin.read()); print(d.get('error',{}).get('error_message','Unknown error'))" 2>/dev/null || echo "$response") - log_error "API Error: $error_msg" - log_warn "Common issues:" - log_warn " - Insufficient account balance" - log_warn " - Plan not available in zone (try different UPCLOUD_PLAN or UPCLOUD_ZONE)" - log_warn " - Server limit reached" - log_warn "Remediation: Check https://hub.upcloud.com/" - return 1 - fi - - # Extract server UUID - UPCLOUD_SERVER_UUID=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['server']['uuid'])") - export UPCLOUD_SERVER_UUID - log_info "Server created: UUID=$UPCLOUD_SERVER_UUID" + _upcloud_handle_create_response "$response" || return 1 _wait_for_upcloud_server_ip "$UPCLOUD_SERVER_UUID" }