mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-19 16:39:50 +00:00
refactor: Extract helpers from civo and kamatera create_server functions (#124)
Civo: Extract build_create_instance_body() for JSON body construction and wait_for_civo_instance() for the status polling loop, reducing create_server() from 113 to 53 lines. Kamatera: Extract validate_kamatera_params() for input validation and build_kamatera_server_body() for JSON body construction, reducing create_server() from 107 to 62 lines. 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
05fa33ead3
commit
8a780e933b
2 changed files with 117 additions and 90 deletions
|
|
@ -183,6 +183,67 @@ if data:
|
|||
echo "$ssh_key_id"
|
||||
}
|
||||
|
||||
# Build the JSON request body for instance creation
|
||||
# Usage: build_create_instance_body NAME SIZE REGION NETWORK_ID TEMPLATE_ID SSH_KEY_ID INIT_SCRIPT
|
||||
build_create_instance_body() {
|
||||
local name="$1" size="$2" region="$3"
|
||||
local network_id="$4" template_id="$5" ssh_key_id="$6"
|
||||
local init_script="$7"
|
||||
|
||||
local json_script
|
||||
json_script=$(json_escape "$init_script")
|
||||
|
||||
python3 -c "
|
||||
import json, sys
|
||||
script = json.loads(sys.stdin.read())
|
||||
body = {
|
||||
'hostname': '$name',
|
||||
'size': '$size',
|
||||
'region': '$region',
|
||||
'network_id': '$network_id',
|
||||
'template_id': '$template_id',
|
||||
'ssh_key_id': '$ssh_key_id',
|
||||
'initial_user': 'root',
|
||||
'script': script,
|
||||
'public_ip': 'create'
|
||||
}
|
||||
print(json.dumps(body))
|
||||
" <<< "$json_script"
|
||||
}
|
||||
|
||||
# Wait for a Civo instance to become ACTIVE and retrieve its public IP
|
||||
# Sets: CIVO_SERVER_IP
|
||||
# Usage: wait_for_civo_instance SERVER_ID [MAX_ATTEMPTS]
|
||||
wait_for_civo_instance() {
|
||||
local server_id="$1"
|
||||
local max_attempts=${2:-60}
|
||||
|
||||
log_warn "Waiting for instance to become active..."
|
||||
local attempt=1
|
||||
while [[ "$attempt" -le "$max_attempts" ]]; do
|
||||
local status_response
|
||||
status_response=$(civo_api GET "/instances/$server_id")
|
||||
local status
|
||||
status=$(echo "$status_response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('status',''))")
|
||||
|
||||
if [[ "$status" == "ACTIVE" ]]; then
|
||||
CIVO_SERVER_IP=$(echo "$status_response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('public_ip',''))")
|
||||
export CIVO_SERVER_IP
|
||||
if [[ -n "$CIVO_SERVER_IP" ]]; then
|
||||
log_info "Instance active: IP=$CIVO_SERVER_IP"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
log_warn "Instance status: $status ($attempt/$max_attempts)"
|
||||
sleep "${INSTANCE_STATUS_POLL_DELAY}"
|
||||
attempt=$((attempt + 1))
|
||||
done
|
||||
|
||||
log_error "Instance did not become active in time"
|
||||
return 1
|
||||
}
|
||||
|
||||
create_server() {
|
||||
local name="$1"
|
||||
local size="${CIVO_SIZE:-g3.medium}"
|
||||
|
|
@ -194,16 +255,10 @@ create_server() {
|
|||
|
||||
log_warn "Creating Civo instance '$name' (size: $size, region: $region)..."
|
||||
|
||||
# Get network ID
|
||||
local network_id
|
||||
# Gather required resource IDs
|
||||
local network_id template_id ssh_key_id
|
||||
network_id=$(get_default_network_id "$region") || return 1
|
||||
|
||||
# Get Ubuntu template ID
|
||||
local template_id
|
||||
template_id=$(get_ubuntu_template_id "$region") || return 1
|
||||
|
||||
# Get SSH key ID
|
||||
local ssh_key_id
|
||||
ssh_key_id=$(get_ssh_key_id) || return 1
|
||||
|
||||
# Build init script for cloud-init equivalent
|
||||
|
|
@ -225,27 +280,9 @@ touch /root/.cloud-init-complete
|
|||
INIT_EOF
|
||||
)
|
||||
|
||||
# Pass init script safely via stdin to avoid triple-quote injection
|
||||
local json_script
|
||||
json_script=$(json_escape "$init_script")
|
||||
|
||||
# Build request body and create instance
|
||||
local body
|
||||
body=$(python3 -c "
|
||||
import json, sys
|
||||
script = json.loads(sys.stdin.read())
|
||||
body = {
|
||||
'hostname': '$name',
|
||||
'size': '$size',
|
||||
'region': '$region',
|
||||
'network_id': '$network_id',
|
||||
'template_id': '$template_id',
|
||||
'ssh_key_id': '$ssh_key_id',
|
||||
'initial_user': 'root',
|
||||
'script': script,
|
||||
'public_ip': 'create'
|
||||
}
|
||||
print(json.dumps(body))
|
||||
" <<< "$json_script")
|
||||
body=$(build_create_instance_body "$name" "$size" "$region" "$network_id" "$template_id" "$ssh_key_id" "$init_script")
|
||||
|
||||
local response
|
||||
response=$(civo_api POST "/instances" "$body")
|
||||
|
|
@ -269,32 +306,7 @@ print(json.dumps(body))
|
|||
return 1
|
||||
fi
|
||||
|
||||
# Wait for instance to become active and get an IP
|
||||
log_warn "Waiting for instance to become active..."
|
||||
local max_attempts=60
|
||||
local attempt=1
|
||||
while [[ "$attempt" -le "$max_attempts" ]]; do
|
||||
local status_response
|
||||
status_response=$(civo_api GET "/instances/$CIVO_SERVER_ID")
|
||||
local status
|
||||
status=$(echo "$status_response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('status',''))")
|
||||
|
||||
if [[ "$status" == "ACTIVE" ]]; then
|
||||
CIVO_SERVER_IP=$(echo "$status_response" | python3 -c "import json,sys; print(json.loads(sys.stdin.read()).get('public_ip',''))")
|
||||
export CIVO_SERVER_IP
|
||||
if [[ -n "$CIVO_SERVER_IP" ]]; then
|
||||
log_info "Instance active: IP=$CIVO_SERVER_IP"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
log_warn "Instance status: $status ($attempt/$max_attempts)"
|
||||
sleep "${INSTANCE_STATUS_POLL_DELAY}"
|
||||
attempt=$((attempt + 1))
|
||||
done
|
||||
|
||||
log_error "Instance did not become active in time"
|
||||
return 1
|
||||
wait_for_civo_instance "$CIVO_SERVER_ID"
|
||||
}
|
||||
|
||||
verify_server_connectivity() {
|
||||
|
|
|
|||
|
|
@ -272,6 +272,54 @@ if power == 'on':
|
|||
return 1
|
||||
}
|
||||
|
||||
# Validate Kamatera server creation parameters
|
||||
# Usage: validate_kamatera_params DATACENTER CPU RAM DISK IMAGE BILLING
|
||||
validate_kamatera_params() {
|
||||
local datacenter="$1" cpu="$2" ram="$3" disk="$4" image="$5" billing="$6"
|
||||
validate_region_name "$datacenter" || { log_error "Invalid KAMATERA_DATACENTER"; return 1; }
|
||||
validate_resource_name "$cpu" || { log_error "Invalid KAMATERA_CPU"; return 1; }
|
||||
if [[ ! "$ram" =~ ^[0-9]+$ ]]; then log_error "Invalid KAMATERA_RAM: must be numeric"; return 1; fi
|
||||
if [[ ! "$disk" =~ ^[a-zA-Z0-9_=,-]+$ ]]; then log_error "Invalid KAMATERA_DISK"; return 1; fi
|
||||
if [[ ! "$image" =~ ^[a-zA-Z0-9_.:-]+$ ]]; then log_error "Invalid KAMATERA_IMAGE"; return 1; fi
|
||||
validate_resource_name "$billing" || { log_error "Invalid KAMATERA_BILLING"; return 1; }
|
||||
}
|
||||
|
||||
# Build the JSON request body for Kamatera server creation
|
||||
# Usage: build_kamatera_server_body NAME PASSWORD DATACENTER IMAGE CPU RAM DISK BILLING SSH_KEY SCRIPT_CONTENT
|
||||
build_kamatera_server_body() {
|
||||
local name="$1" password="$2" datacenter="$3" image="$4"
|
||||
local cpu="$5" ram="$6" disk="$7" billing="$8"
|
||||
local ssh_key="$9" script_content="${10}"
|
||||
|
||||
local json_ssh_key json_script
|
||||
json_ssh_key=$(json_escape "$ssh_key")
|
||||
json_script=$(json_escape "$script_content")
|
||||
|
||||
python3 -c "
|
||||
import json, sys
|
||||
data = json.loads(sys.stdin.read())
|
||||
body = {
|
||||
'name': '$name',
|
||||
'password': '$password',
|
||||
'passwordValidate': '$password',
|
||||
'ssh-key': data['ssh_key'],
|
||||
'datacenter': '$datacenter',
|
||||
'image': '$image',
|
||||
'cpu': '$cpu',
|
||||
'ram': $ram,
|
||||
'disk': '$disk',
|
||||
'dailybackup': 'no',
|
||||
'managed': 'no',
|
||||
'network': 'name=wan,ip=auto',
|
||||
'quantity': 1,
|
||||
'billingcycle': '$billing',
|
||||
'poweronaftercreate': 'yes',
|
||||
'script-file': data['script']
|
||||
}
|
||||
print(json.dumps(body))
|
||||
" <<< "{\"ssh_key\": $json_ssh_key, \"script\": $json_script}"
|
||||
}
|
||||
|
||||
create_server() {
|
||||
local name="$1"
|
||||
local datacenter="${KAMATERA_DATACENTER:-EU}"
|
||||
|
|
@ -281,13 +329,7 @@ create_server() {
|
|||
local image="${KAMATERA_IMAGE:-ubuntu_server_24.04_64-bit}"
|
||||
local billing="${KAMATERA_BILLING:-hourly}"
|
||||
|
||||
# Validate env var inputs to prevent injection into Python code
|
||||
validate_region_name "$datacenter" || { log_error "Invalid KAMATERA_DATACENTER"; return 1; }
|
||||
validate_resource_name "$cpu" || { log_error "Invalid KAMATERA_CPU"; return 1; }
|
||||
if [[ ! "$ram" =~ ^[0-9]+$ ]]; then log_error "Invalid KAMATERA_RAM: must be numeric"; return 1; fi
|
||||
if [[ ! "$disk" =~ ^[a-zA-Z0-9_=,-]+$ ]]; then log_error "Invalid KAMATERA_DISK"; return 1; fi
|
||||
if [[ ! "$image" =~ ^[a-zA-Z0-9_.:-]+$ ]]; then log_error "Invalid KAMATERA_IMAGE"; return 1; fi
|
||||
validate_resource_name "$billing" || { log_error "Invalid KAMATERA_BILLING"; return 1; }
|
||||
validate_kamatera_params "$datacenter" "$cpu" "$ram" "$disk" "$image" "$billing" || return 1
|
||||
|
||||
log_warn "Creating Kamatera server '$name' (datacenter: $datacenter, cpu: $cpu, ram: ${ram}MB)..."
|
||||
|
||||
|
|
@ -321,36 +363,9 @@ touch /root/.cloud-init-complete
|
|||
INIT_EOF
|
||||
)
|
||||
|
||||
# Pass SSH key and script content safely via stdin as JSON
|
||||
local json_ssh_key
|
||||
json_ssh_key=$(json_escape "$ssh_key")
|
||||
local json_script
|
||||
json_script=$(json_escape "$script_content")
|
||||
|
||||
# Build request body and submit server creation
|
||||
local body
|
||||
body=$(python3 -c "
|
||||
import json, sys
|
||||
data = json.loads(sys.stdin.read())
|
||||
body = {
|
||||
'name': '$name',
|
||||
'password': '$password',
|
||||
'passwordValidate': '$password',
|
||||
'ssh-key': data['ssh_key'],
|
||||
'datacenter': '$datacenter',
|
||||
'image': '$image',
|
||||
'cpu': '$cpu',
|
||||
'ram': $ram,
|
||||
'disk': '$disk',
|
||||
'dailybackup': 'no',
|
||||
'managed': 'no',
|
||||
'network': 'name=wan,ip=auto',
|
||||
'quantity': 1,
|
||||
'billingcycle': '$billing',
|
||||
'poweronaftercreate': 'yes',
|
||||
'script-file': data['script']
|
||||
}
|
||||
print(json.dumps(body))
|
||||
" <<< "{\"ssh_key\": $json_ssh_key, \"script\": $json_script}")
|
||||
body=$(build_kamatera_server_body "$name" "$password" "$datacenter" "$image" "$cpu" "$ram" "$disk" "$billing" "$ssh_key" "$script_content")
|
||||
|
||||
local response
|
||||
response=$(kamatera_api POST "/service/server" "$body")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue