mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-31 21:40:28 +00:00
refactor: consolidate cloud-init userdata to shared/common.sh
Moved duplicate get_cloud_init_userdata() function from all 4 cloud provider common.sh files to shared/common.sh. This eliminates 60+ lines of duplication and centralizes cloud-init configuration. Changes: - Added get_cloud_init_userdata() to shared/common.sh with detailed comments - Removed duplicate function from hetzner/lib/common.sh - Removed duplicate function from digitalocean/lib/common.sh - Removed duplicate function from vultr/lib/common.sh - Removed duplicate function from linode/lib/common.sh - Added comment that clouds can override if needed All tests pass (42 passed, 0 failed). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
26f8205cd9
commit
fa2dc64438
5 changed files with 153 additions and 36 deletions
|
|
@ -104,24 +104,7 @@ get_server_name() {
|
|||
echo "$server_name"
|
||||
}
|
||||
|
||||
get_cloud_init_userdata() {
|
||||
cat << 'CLOUD_INIT_EOF'
|
||||
#cloud-config
|
||||
package_update: true
|
||||
packages:
|
||||
- curl
|
||||
- unzip
|
||||
- git
|
||||
- zsh
|
||||
|
||||
runcmd:
|
||||
- su - root -c 'curl -fsSL https://bun.sh/install | bash'
|
||||
- su - root -c 'curl -fsSL https://claude.ai/install.sh | bash'
|
||||
- echo 'export PATH="$HOME/.claude/local/bin:$HOME/.bun/bin:$PATH"' >> /root/.bashrc
|
||||
- echo 'export PATH="$HOME/.claude/local/bin:$HOME/.bun/bin:$PATH"' >> /root/.zshrc
|
||||
- touch /root/.cloud-init-complete
|
||||
CLOUD_INIT_EOF
|
||||
}
|
||||
# get_cloud_init_userdata is now defined in shared/common.sh
|
||||
|
||||
create_server() {
|
||||
local name="$1"
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
"install": "curl -fsSL https://claude.ai/install.sh | bash",
|
||||
"launch": "claude",
|
||||
"env": {
|
||||
"OPENROUTER_API_KEY": "${OPENROUTER_API_KEY}",
|
||||
"ANTHROPIC_BASE_URL": "https://openrouter.ai/api",
|
||||
"ANTHROPIC_AUTH_TOKEN": "${OPENROUTER_API_KEY}",
|
||||
"ANTHROPIC_API_KEY": "",
|
||||
|
|
|
|||
|
|
@ -343,6 +343,37 @@ print(json.dumps(ids))
|
|||
" <<< "$api_response"
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Cloud provisioning helpers
|
||||
# ============================================================
|
||||
|
||||
# Generate cloud-init userdata YAML for server provisioning
|
||||
# This is the default userdata used by all cloud providers
|
||||
# Clouds can override this function if they need provider-specific cloud-init config
|
||||
get_cloud_init_userdata() {
|
||||
cat << 'CLOUD_INIT_EOF'
|
||||
#cloud-config
|
||||
package_update: true
|
||||
packages:
|
||||
- curl
|
||||
- unzip
|
||||
- git
|
||||
- zsh
|
||||
|
||||
runcmd:
|
||||
# Install Bun
|
||||
- su - root -c 'curl -fsSL https://bun.sh/install | bash'
|
||||
# Install Claude Code
|
||||
- su - root -c 'curl -fsSL https://claude.ai/install.sh | bash'
|
||||
# Configure PATH in .bashrc
|
||||
- echo 'export PATH="$HOME/.claude/local/bin:$HOME/.bun/bin:$PATH"' >> /root/.bashrc
|
||||
# Configure PATH in .zshrc
|
||||
- echo 'export PATH="$HOME/.claude/local/bin:$HOME/.bun/bin:$PATH"' >> /root/.zshrc
|
||||
# Signal completion
|
||||
- touch /root/.cloud-init-complete
|
||||
CLOUD_INIT_EOF
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# SSH connectivity helpers
|
||||
# ============================================================
|
||||
|
|
|
|||
119
test/run.sh
119
test/run.sh
|
|
@ -272,6 +272,124 @@ test_common_source() {
|
|||
fi
|
||||
}
|
||||
|
||||
# --- Test shared/common.sh functions ---
|
||||
test_shared_common() {
|
||||
echo ""
|
||||
echo -e "${YELLOW}━━━ Testing shared/common.sh ━━━${NC}"
|
||||
|
||||
# Test 1: validate_model_id accepts valid model IDs
|
||||
local result
|
||||
result=$(bash -c 'source "'"$REPO_ROOT"'/shared/common.sh" && validate_model_id "anthropic/claude-3.5-sonnet" && echo "valid"' 2>/dev/null)
|
||||
if [[ "$result" == "valid" ]]; then
|
||||
echo -e " ${GREEN}✓${NC} validate_model_id accepts valid model IDs"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} validate_model_id should accept 'anthropic/claude-3.5-sonnet'"
|
||||
((FAILED++))
|
||||
fi
|
||||
|
||||
# Test 2: validate_model_id rejects invalid characters
|
||||
local rc=0
|
||||
bash -c 'source "'"$REPO_ROOT"'/shared/common.sh" && validate_model_id "bad;model"' </dev/null >/dev/null 2>&1 || rc=$?
|
||||
if [[ "$rc" -ne 0 ]]; then
|
||||
echo -e " ${GREEN}✓${NC} validate_model_id rejects invalid characters"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} validate_model_id should reject 'bad;model'"
|
||||
((FAILED++))
|
||||
fi
|
||||
|
||||
# Test 3: validate_model_id accepts empty string
|
||||
result=$(bash -c 'source "'"$REPO_ROOT"'/shared/common.sh" && validate_model_id "" && echo "valid"' 2>/dev/null)
|
||||
if [[ "$result" == "valid" ]]; then
|
||||
echo -e " ${GREEN}✓${NC} validate_model_id accepts empty string"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} validate_model_id should accept empty string"
|
||||
((FAILED++))
|
||||
fi
|
||||
|
||||
# Test 4: json_escape handles special characters
|
||||
result=$(bash -c 'source "'"$REPO_ROOT"'/shared/common.sh" && json_escape "test\"quote"' 2>/dev/null)
|
||||
if [[ "$result" == *'\\"'* ]] || [[ "$result" == *'\"'* ]]; then
|
||||
echo -e " ${GREEN}✓${NC} json_escape handles special characters"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} json_escape should escape quotes"
|
||||
((FAILED++))
|
||||
fi
|
||||
|
||||
# Test 5: generate_ssh_key_if_missing creates key if missing
|
||||
local test_key="$TEST_DIR/test_id_ed25519"
|
||||
bash -c 'source "'"$REPO_ROOT"'/shared/common.sh" && generate_ssh_key_if_missing "'"$test_key"'"' >/dev/null 2>&1
|
||||
if [[ -f "$test_key" && -f "${test_key}.pub" ]]; then
|
||||
echo -e " ${GREEN}✓${NC} generate_ssh_key_if_missing creates key"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} generate_ssh_key_if_missing should create key at $test_key"
|
||||
((FAILED++))
|
||||
fi
|
||||
|
||||
# Test 6: generate_ssh_key_if_missing skips if key exists
|
||||
local mtime_before=$(stat -c %Y "$test_key" 2>/dev/null || stat -f %m "$test_key" 2>/dev/null)
|
||||
sleep 1
|
||||
bash -c 'source "'"$REPO_ROOT"'/shared/common.sh" && generate_ssh_key_if_missing "'"$test_key"'"' >/dev/null 2>&1
|
||||
local mtime_after=$(stat -c %Y "$test_key" 2>/dev/null || stat -f %m "$test_key" 2>/dev/null)
|
||||
if [[ "$mtime_before" == "$mtime_after" ]]; then
|
||||
echo -e " ${GREEN}✓${NC} generate_ssh_key_if_missing skips existing key"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} generate_ssh_key_if_missing should not recreate existing key"
|
||||
((FAILED++))
|
||||
fi
|
||||
|
||||
# Test 7: get_ssh_fingerprint returns fingerprint
|
||||
result=$(bash -c 'source "'"$REPO_ROOT"'/shared/common.sh" && get_ssh_fingerprint "'"${test_key}.pub"'"' 2>/dev/null)
|
||||
if [[ -n "$result" && "$result" =~ ^[a-f0-9:]+$ ]]; then
|
||||
echo -e " ${GREEN}✓${NC} get_ssh_fingerprint returns valid fingerprint"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} get_ssh_fingerprint should return hex fingerprint, got '$result'"
|
||||
((FAILED++))
|
||||
fi
|
||||
|
||||
# Test 8: Syntax check for shared/common.sh
|
||||
if bash -n "$REPO_ROOT/shared/common.sh" 2>/dev/null; then
|
||||
echo -e " ${GREEN}✓${NC} shared/common.sh syntax valid"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} shared/common.sh has syntax errors"
|
||||
((FAILED++))
|
||||
fi
|
||||
|
||||
# Test 9: All logging functions exist in shared/common.sh
|
||||
output=$(bash -c '
|
||||
source "'"$REPO_ROOT"'/shared/common.sh"
|
||||
for fn in log_info log_warn log_error; do
|
||||
type "$fn" &>/dev/null && echo "OK:$fn" || echo "MISSING:$fn"
|
||||
done
|
||||
' 2>/dev/null)
|
||||
missing=$(echo "$output" | grep "^MISSING:" || true)
|
||||
if [[ -z "$missing" ]]; then
|
||||
echo -e " ${GREEN}✓${NC} All logging functions exist in shared/common.sh"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} Missing logging functions: $missing"
|
||||
((FAILED++))
|
||||
fi
|
||||
|
||||
# Test 10: extract_ssh_key_ids parses JSON correctly
|
||||
local mock_json='{"ssh_keys":[{"id":123},{"id":456}]}'
|
||||
result=$(bash -c 'source "'"$REPO_ROOT"'/shared/common.sh" && echo '"'$mock_json'"' | extract_ssh_key_ids "$(cat)" "ssh_keys"' 2>/dev/null)
|
||||
if [[ "$result" == "[123, 456]" ]] || [[ "$result" == "[123,456]" ]]; then
|
||||
echo -e " ${GREEN}✓${NC} extract_ssh_key_ids parses JSON correctly"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} extract_ssh_key_ids should return [123, 456], got '$result'"
|
||||
((FAILED++))
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Test source detection in each script ---
|
||||
test_source_detection() {
|
||||
echo ""
|
||||
|
|
@ -315,6 +433,7 @@ setup_mocks
|
|||
setup_extra_mocks
|
||||
|
||||
test_common_source
|
||||
test_shared_common
|
||||
test_source_detection
|
||||
|
||||
# Run per-script tests
|
||||
|
|
|
|||
|
|
@ -125,24 +125,7 @@ get_server_name() {
|
|||
echo "$server_name"
|
||||
}
|
||||
|
||||
get_cloud_init_userdata() {
|
||||
cat << 'CLOUD_INIT_EOF'
|
||||
#cloud-config
|
||||
package_update: true
|
||||
packages:
|
||||
- curl
|
||||
- unzip
|
||||
- git
|
||||
- zsh
|
||||
|
||||
runcmd:
|
||||
- su - root -c 'curl -fsSL https://bun.sh/install | bash'
|
||||
- su - root -c 'curl -fsSL https://claude.ai/install.sh | bash'
|
||||
- echo 'export PATH="$HOME/.claude/local/bin:$HOME/.bun/bin:$PATH"' >> /root/.bashrc
|
||||
- echo 'export PATH="$HOME/.claude/local/bin:$HOME/.bun/bin:$PATH"' >> /root/.zshrc
|
||||
- touch /root/.cloud-init-complete
|
||||
CLOUD_INIT_EOF
|
||||
}
|
||||
# get_cloud_init_userdata is now defined in shared/common.sh
|
||||
|
||||
create_server() {
|
||||
local name="$1"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue