mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-22 11:24:18 +00:00
refactor: extract helpers from create_server() in 4 cloud providers (#423)
Extract wait-for-IP polling loops and JSON body builders from the largest create_server() functions (ramnode 105->59, netcup 95->50, cherry 80->57, binarylane 92->70 lines), following the pattern already established in ionos/lib/common.sh. Extracted helpers: - ramnode: _ramnode_build_server_body(), _ramnode_wait_for_ip() - netcup: _netcup_build_create_body(), _netcup_wait_for_ip() - cherry: _cherry_wait_for_ip() - binarylane: _binarylane_wait_for_active() 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:
parent
0181b73506
commit
de19996360
4 changed files with 180 additions and 150 deletions
|
|
@ -139,6 +139,34 @@ get_server_name() {
|
|||
|
||||
# get_cloud_init_userdata is now defined in shared/common.sh
|
||||
|
||||
# Poll the BinaryLane API until the server becomes active and has an IP
|
||||
# Sets BINARYLANE_SERVER_IP on success
|
||||
_binarylane_wait_for_active() {
|
||||
log_warn "Waiting for server to become active..."
|
||||
local max_attempts=60
|
||||
local attempt=1
|
||||
while [[ "$attempt" -le "$max_attempts" ]]; do
|
||||
local status_response
|
||||
status_response=$(binarylane_api GET "/servers/$BINARYLANE_SERVER_ID")
|
||||
local status
|
||||
status=$(echo "$status_response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['server']['status'])")
|
||||
|
||||
if [[ "$status" == "active" ]]; then
|
||||
BINARYLANE_SERVER_IP=$(echo "$status_response" | python3 -c "import json,sys; networks = json.loads(sys.stdin.read())['server']['networks']['v4']; print([n['ip_address'] for n in networks if n['type'] == 'public'][0])")
|
||||
export BINARYLANE_SERVER_IP
|
||||
log_info "Server active: IP=$BINARYLANE_SERVER_IP"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_warn "Server status: $status ($attempt/$max_attempts)"
|
||||
sleep "${INSTANCE_STATUS_POLL_DELAY}"
|
||||
attempt=$((attempt + 1))
|
||||
done
|
||||
|
||||
log_error "Server did not become active in time"
|
||||
return 1
|
||||
}
|
||||
|
||||
create_server() {
|
||||
local name="$1"
|
||||
local size="${BINARYLANE_SIZE:-std-1vcpu}"
|
||||
|
|
@ -205,30 +233,8 @@ print(json.dumps(body))
|
|||
return 1
|
||||
fi
|
||||
|
||||
# Wait for server to get an IP
|
||||
log_warn "Waiting for server to become active..."
|
||||
local max_attempts=60
|
||||
local attempt=1
|
||||
while [[ "$attempt" -le "$max_attempts" ]]; do
|
||||
local status_response
|
||||
status_response=$(binarylane_api GET "/servers/$BINARYLANE_SERVER_ID")
|
||||
local status
|
||||
status=$(echo "$status_response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['server']['status'])")
|
||||
|
||||
if [[ "$status" == "active" ]]; then
|
||||
BINARYLANE_SERVER_IP=$(echo "$status_response" | python3 -c "import json,sys; networks = json.loads(sys.stdin.read())['server']['networks']['v4']; print([n['ip_address'] for n in networks if n['type'] == 'public'][0])")
|
||||
export BINARYLANE_SERVER_IP
|
||||
log_info "Server active: IP=$BINARYLANE_SERVER_IP"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_warn "Server status: $status ($attempt/$max_attempts)"
|
||||
sleep "${INSTANCE_STATUS_POLL_DELAY}"
|
||||
attempt=$((attempt + 1))
|
||||
done
|
||||
|
||||
log_error "Server did not become active in time"
|
||||
return 1
|
||||
# Wait for server to become active with IP
|
||||
_binarylane_wait_for_active
|
||||
}
|
||||
|
||||
verify_server_connectivity() {
|
||||
|
|
|
|||
|
|
@ -166,6 +166,36 @@ get_server_name() {
|
|||
|
||||
# Create server
|
||||
# Sets CHERRY_SERVER_ID and CHERRY_SERVER_IP as exports
|
||||
# Poll the Cherry Servers API until the server has an IP address
|
||||
# Sets CHERRY_SERVER_IP on success
|
||||
_cherry_wait_for_ip() {
|
||||
local server_id="$1"
|
||||
log_info "Waiting for IP address assignment..."
|
||||
local ip_address=""
|
||||
local attempts=0
|
||||
local max_attempts=60
|
||||
|
||||
while [[ -z "$ip_address" ]] && [[ $attempts -lt $max_attempts ]]; do
|
||||
sleep "${POLL_INTERVAL}"
|
||||
|
||||
local server_info
|
||||
server_info=$(cherry_api GET "/servers/${server_id}")
|
||||
|
||||
ip_address=$(printf '%s' "$server_info" | _cherry_extract_primary_ip)
|
||||
|
||||
attempts=$((attempts + 1))
|
||||
done
|
||||
|
||||
if [[ -z "$ip_address" ]]; then
|
||||
log_error "Failed to get server IP address"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Server IP: $ip_address"
|
||||
CHERRY_SERVER_IP="$ip_address"
|
||||
export CHERRY_SERVER_IP
|
||||
}
|
||||
|
||||
create_server() {
|
||||
local hostname="$1"
|
||||
local plan="${CHERRY_DEFAULT_PLAN}"
|
||||
|
|
@ -215,30 +245,7 @@ print(json.dumps(data))
|
|||
export CHERRY_SERVER_ID
|
||||
|
||||
# Wait for IP assignment
|
||||
log_info "Waiting for IP address assignment..."
|
||||
local ip_address=""
|
||||
local attempts=0
|
||||
local max_attempts=60
|
||||
|
||||
while [[ -z "$ip_address" ]] && [[ $attempts -lt $max_attempts ]]; do
|
||||
sleep "${POLL_INTERVAL}"
|
||||
|
||||
local server_info
|
||||
server_info=$(cherry_api GET "/servers/${server_id}")
|
||||
|
||||
ip_address=$(printf '%s' "$server_info" | _cherry_extract_primary_ip)
|
||||
|
||||
attempts=$((attempts + 1))
|
||||
done
|
||||
|
||||
if [[ -z "$ip_address" ]]; then
|
||||
log_error "Failed to get server IP address"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Server IP: $ip_address"
|
||||
CHERRY_SERVER_IP="$ip_address"
|
||||
export CHERRY_SERVER_IP
|
||||
_cherry_wait_for_ip "$server_id"
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
|
|
|
|||
|
|
@ -310,28 +310,11 @@ _pick_datacenter() {
|
|||
fi
|
||||
}
|
||||
|
||||
# Create a Netcup VPS with cloud-init
|
||||
create_server() {
|
||||
local name="$1"
|
||||
|
||||
# Interactive selections
|
||||
local datacenter
|
||||
datacenter=$(_pick_datacenter)
|
||||
|
||||
local product
|
||||
product=$(_pick_vps_product)
|
||||
|
||||
local image="ubuntu-24.04"
|
||||
|
||||
log_warn "Creating Netcup VPS '$name' (product: $product, datacenter: $datacenter)..."
|
||||
|
||||
# Get cloud-init userdata
|
||||
local userdata
|
||||
userdata=$(get_cloud_init_userdata)
|
||||
|
||||
# Build request param
|
||||
local param
|
||||
param=$(echo "$userdata" | python3 -c "
|
||||
# Build JSON request body for Netcup VPS creation
|
||||
# Reads cloud-init userdata from stdin
|
||||
# Usage: get_cloud_init_userdata | _netcup_build_create_body NAME PRODUCT DATACENTER IMAGE
|
||||
_netcup_build_create_body() {
|
||||
python3 -c "
|
||||
import json, sys
|
||||
userdata = sys.stdin.read()
|
||||
name, product, datacenter, image = sys.argv[1:5]
|
||||
|
|
@ -344,37 +327,12 @@ param = {
|
|||
'userdata': userdata
|
||||
}
|
||||
print(json.dumps(param))
|
||||
" "$name" "$product" "$datacenter" "$image")
|
||||
" "$@"
|
||||
}
|
||||
|
||||
local response
|
||||
response=$(netcup_api "createVServer" "$param")
|
||||
|
||||
# Check for errors
|
||||
local status
|
||||
status=$(echo "$response" | python3 -c "import json, sys; print(json.loads(sys.stdin.read()).get('status', 'error'))")
|
||||
|
||||
if [[ "$status" != "success" ]]; then
|
||||
log_error "Failed to create Netcup VPS"
|
||||
local error_msg
|
||||
error_msg=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('longmessage','Unknown error'))" 2>/dev/null || echo "$response")
|
||||
log_error "API Error: $error_msg"
|
||||
log_error ""
|
||||
log_error "Common issues:"
|
||||
log_error " - Insufficient account balance"
|
||||
log_error " - Product not available in selected datacenter"
|
||||
log_error " - Account limits reached"
|
||||
log_error ""
|
||||
log_error "Check your account: https://ccp.netcup.net/"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract server ID
|
||||
NETCUP_SERVER_ID=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['responsedata']['vserverid'])")
|
||||
export NETCUP_SERVER_ID
|
||||
|
||||
log_info "VPS created: ID=$NETCUP_SERVER_ID"
|
||||
|
||||
# Wait for IP assignment (Netcup assigns IPs asynchronously)
|
||||
# Poll the Netcup API until the VPS has an IPv4 address
|
||||
# Sets NETCUP_SERVER_IP on success
|
||||
_netcup_wait_for_ip() {
|
||||
log_info "Waiting for IP assignment..."
|
||||
local ip=""
|
||||
local attempts=0
|
||||
|
|
@ -401,10 +359,59 @@ except:
|
|||
|
||||
NETCUP_SERVER_IP="$ip"
|
||||
export NETCUP_SERVER_IP
|
||||
|
||||
log_info "Server IP: $NETCUP_SERVER_IP"
|
||||
}
|
||||
|
||||
# Create a Netcup VPS with cloud-init
|
||||
create_server() {
|
||||
local name="$1"
|
||||
|
||||
# Interactive selections
|
||||
local datacenter
|
||||
datacenter=$(_pick_datacenter)
|
||||
|
||||
local product
|
||||
product=$(_pick_vps_product)
|
||||
|
||||
local image="ubuntu-24.04"
|
||||
|
||||
log_warn "Creating Netcup VPS '$name' (product: $product, datacenter: $datacenter)..."
|
||||
|
||||
# Get cloud-init userdata and build request body
|
||||
local param
|
||||
param=$(get_cloud_init_userdata | _netcup_build_create_body "$name" "$product" "$datacenter" "$image")
|
||||
|
||||
local response
|
||||
response=$(netcup_api "createVServer" "$param")
|
||||
|
||||
# Check for errors
|
||||
local status
|
||||
status=$(echo "$response" | python3 -c "import json, sys; print(json.loads(sys.stdin.read()).get('status', 'error'))")
|
||||
|
||||
if [[ "$status" != "success" ]]; then
|
||||
log_error "Failed to create Netcup VPS"
|
||||
local error_msg
|
||||
error_msg=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('longmessage','Unknown error'))" 2>/dev/null || echo "$response")
|
||||
log_error "API Error: $error_msg"
|
||||
log_error ""
|
||||
log_error "Common issues:"
|
||||
log_error " - Insufficient account balance"
|
||||
log_error " - Product not available in selected datacenter"
|
||||
log_error " - Account limits reached"
|
||||
log_error ""
|
||||
log_error "Check your account: https://ccp.netcup.net/"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract server ID
|
||||
NETCUP_SERVER_ID=$(echo "$response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read())['responsedata']['vserverid'])")
|
||||
export NETCUP_SERVER_ID
|
||||
log_info "VPS created: ID=$NETCUP_SERVER_ID"
|
||||
|
||||
# Wait for IP assignment
|
||||
_netcup_wait_for_ip
|
||||
}
|
||||
|
||||
# Wait for SSH connectivity
|
||||
verify_server_connectivity() {
|
||||
local ip="$1"
|
||||
|
|
|
|||
|
|
@ -348,6 +348,62 @@ if networks:
|
|||
"
|
||||
}
|
||||
|
||||
# Build JSON request body for RamNode server creation
|
||||
# Usage: _ramnode_build_server_body NAME FLAVOR IMAGE_ID KEY_NAME USERDATA [NETWORK_ID]
|
||||
_ramnode_build_server_body() {
|
||||
python3 -c "
|
||||
import json, sys
|
||||
name, flavor, image_id, key_name, userdata, network_id = sys.argv[1:7]
|
||||
body = {
|
||||
'server': {
|
||||
'name': name,
|
||||
'flavorRef': flavor,
|
||||
'imageRef': image_id,
|
||||
'key_name': key_name,
|
||||
'user_data': userdata
|
||||
}
|
||||
}
|
||||
if network_id:
|
||||
body['server']['networks'] = [{'uuid': network_id}]
|
||||
print(json.dumps(body))
|
||||
" "$@"
|
||||
}
|
||||
|
||||
# Poll the RamNode API until the server has an IPv4 address
|
||||
# Sets RAMNODE_SERVER_IP on success
|
||||
_ramnode_wait_for_ip() {
|
||||
log_info "Waiting for IP address..."
|
||||
local max_attempts=30
|
||||
local attempt=0
|
||||
while [[ $attempt -lt $max_attempts ]]; do
|
||||
sleep 2
|
||||
local server_info
|
||||
server_info=$(ramnode_compute_api GET "/servers/$RAMNODE_SERVER_ID")
|
||||
|
||||
RAMNODE_SERVER_IP=$(echo "$server_info" | python3 -c "
|
||||
import json, sys
|
||||
data = json.loads(sys.stdin.read())
|
||||
addresses = data.get('server', {}).get('addresses', {})
|
||||
for net_name, addrs in addresses.items():
|
||||
for addr in addrs:
|
||||
if addr.get('version') == 4:
|
||||
print(addr['addr'])
|
||||
sys.exit(0)
|
||||
" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "$RAMNODE_SERVER_IP" ]]; then
|
||||
export RAMNODE_SERVER_IP
|
||||
log_info "IP address assigned: $RAMNODE_SERVER_IP"
|
||||
return 0
|
||||
fi
|
||||
|
||||
attempt=$((attempt + 1))
|
||||
done
|
||||
|
||||
log_error "Timeout waiting for IP address"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Create a RamNode server
|
||||
create_server() {
|
||||
local name="$1"
|
||||
|
|
@ -379,24 +435,8 @@ create_server() {
|
|||
|
||||
log_warn "Creating RamNode instance '$name' (flavor: $flavor)..."
|
||||
|
||||
# Build request body
|
||||
local body
|
||||
body=$(python3 -c "
|
||||
import json, sys
|
||||
name, flavor, image_id, key_name, userdata, network_id = sys.argv[1:7]
|
||||
body = {
|
||||
'server': {
|
||||
'name': name,
|
||||
'flavorRef': flavor,
|
||||
'imageRef': image_id,
|
||||
'key_name': key_name,
|
||||
'user_data': userdata
|
||||
}
|
||||
}
|
||||
if network_id:
|
||||
body['server']['networks'] = [{'uuid': network_id}]
|
||||
print(json.dumps(body))
|
||||
" "$name" "$flavor" "$image_id" "$key_name" "$userdata" "${network_id:-}")
|
||||
body=$(_ramnode_build_server_body "$name" "$flavor" "$image_id" "$key_name" "$userdata" "${network_id:-}")
|
||||
|
||||
local response
|
||||
response=$(ramnode_compute_api POST "/servers" "$body")
|
||||
|
|
@ -417,40 +457,10 @@ print(json.dumps(body))
|
|||
# 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"
|
||||
|
||||
# Wait for server to get IP address
|
||||
log_info "Waiting for IP address..."
|
||||
local max_attempts=30
|
||||
local attempt=0
|
||||
while [[ $attempt -lt $max_attempts ]]; do
|
||||
sleep 2
|
||||
local server_info
|
||||
server_info=$(ramnode_compute_api GET "/servers/$RAMNODE_SERVER_ID")
|
||||
|
||||
RAMNODE_SERVER_IP=$(echo "$server_info" | python3 -c "
|
||||
import json, sys
|
||||
data = json.loads(sys.stdin.read())
|
||||
addresses = data.get('server', {}).get('addresses', {})
|
||||
for net_name, addrs in addresses.items():
|
||||
for addr in addrs:
|
||||
if addr.get('version') == 4:
|
||||
print(addr['addr'])
|
||||
sys.exit(0)
|
||||
" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -n "$RAMNODE_SERVER_IP" ]]; then
|
||||
export RAMNODE_SERVER_IP
|
||||
log_info "IP address assigned: $RAMNODE_SERVER_IP"
|
||||
return 0
|
||||
fi
|
||||
|
||||
attempt=$((attempt + 1))
|
||||
done
|
||||
|
||||
log_error "Timeout waiting for IP address"
|
||||
return 1
|
||||
# Wait for IP assignment
|
||||
_ramnode_wait_for_ip
|
||||
}
|
||||
|
||||
# Wait for SSH connectivity
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue