Files
workflows/orchestrator/orchestrating.sh
2025-09-24 11:49:07 +08:00

267 lines
7.7 KiB
Bash

#!/bin/bash
# Record start time
START_TIME=$(date +%s)
# Function to check list of required environment variables
check_env_vars() {
local missing_vars=()
for var in "$@"; do
if [ -z "${!var}" ]; then
missing_vars+=("$var")
fi
done
if [ ${#missing_vars[@]} -ne 0 ]; then
echo "Error: The following environment variables are not set: ${missing_vars[*]}"
exit 1
fi
}
# Function to check if doctl is available
check_doctl() {
if ! command -v doctl &> /dev/null; then
echo "Error: doctl is not installed or not in PATH"
echo "Please install doctl: https://docs.digitalocean.com/reference/doctl/how-to/install/"
exit 1
fi
}
# Function to setup SSH private key
setup_ssh_key() {
if [ -z "$SSH_PRIVATE_KEY_DECODED" ]; then
echo "Error: SSH_PRIVATE_KEY_DECODED environment variable is not set"
exit 1
fi
# Create SSH directory if it doesn't exist
mkdir -p ~/.ssh
chmod 700 ~/.ssh
# Decode base64 encoded SSH private key
echo "$SSH_PRIVATE_KEY_DECODED" | base64 -d > ~/.ssh/id_rsa
# Ensure proper line endings for OpenSSH key format
sed -i 's/\r$//' ~/.ssh/id_rsa
# Add final newline if missing
if [ -s ~/.ssh/id_rsa ] && [ "$(tail -c1 ~/.ssh/id_rsa | wc -l)" -eq 0 ]; then
echo >> ~/.ssh/id_rsa
fi
chmod 600 ~/.ssh/id_rsa
# Validate SSH key formats
if ! ssh-keygen -y -f ~/.ssh/id_rsa > /dev/null 2>&1; then
echo "Error: Invalid SSH private key format"
echo "Key content preview (first 100 chars):"
head -c 100 ~/.ssh/id_rsa
echo
echo "Key file size: $(wc -c < ~/.ssh/id_rsa) bytes"
echo "Key file lines: $(wc -l < ~/.ssh/id_rsa) lines"
exit 1
fi
echo "SSH private key has been set up and validated successfully"
}
# Function to destroy droplet
destroy_droplet() {
local droplet_id="$1"
if [ -z "$droplet_id" ]; then
echo "Error: No droplet ID provided for destruction"
return 1
fi
echo "Destroying droplet with ID: $droplet_id"
# Authenticate doctl
doctl auth init --access-token "$DO_TOKEN"
# Destroy droplet
if doctl compute droplet delete "$droplet_id" --force; then
echo "Droplet $droplet_id destroyed successfully"
return 0
else
echo "Error: Failed to destroy droplet $droplet_id"
return 1
fi
}
# Function to create droplet
create_droplet() {
local name="${1:-runner-${RUNNER_ID}}"
local size="${2:-s-1vcpu-1gb}"
local image="${3:-200895606}"
local region="${4:-sgp1}"
echo "Creating droplet: $name"
echo "Size: $size, Image: $image, Region: $region"
# Authenticate doctl
doctl auth init --access-token "$DO_TOKEN"
# Create droplet
local droplet_id=$(doctl compute droplet create "$name" \
--size "$size" \
--image "$image" \
--region "$region" \
--ssh-keys $(doctl compute ssh-key list --format ID --no-header | tr '\n' ',' | sed 's/,$//' || echo "") \
--format ID \
--no-header \
--wait)
if [ -n "$droplet_id" ]; then
echo "Droplet created successfully with ID: $droplet_id"
echo "Droplet name: $name"
echo "droplet_id=$droplet_id" >> $GITHUB_OUTPUT
# Get droplet IP
local droplet_ip=$(doctl compute droplet get "$droplet_id" --format PublicIPv4 --no-header)
echo "Droplet IP: $droplet_ip"
echo "droplet_ip=$droplet_ip" >> $GITHUB_OUTPUT
# Test SSH connection
if test_ssh_connection "$droplet_ip"; then
echo "Droplet is ready and accessible via SSH"
# Setup VM with required services
if setup_vm "$droplet_ip"; then
echo "VM setup completed successfully"
else
echo "Error: VM setup failed"
echo "Destroying failed droplet..."
destroy_droplet "$droplet_id"
return 1
fi
else
echo "Warning: Droplet created but SSH connection failed"
echo "Destroying failed droplet..."
destroy_droplet "$droplet_id"
return 1
fi
return 0
else
echo "Error: Failed to create droplet"
return 1
fi
}
# Function to test SSH connection
test_ssh_connection() {
local droplet_ip="$1"
local max_attempts=10
local attempt=1
local wait_time=10
echo "Testing SSH connection to $droplet_ip..."
while [ $attempt -le $max_attempts ]; do
echo "Attempt $attempt/$max_attempts: Testing SSH connection..."
# Test SSH connection with verbose error handling
ssh_output=$(ssh -i ~/.ssh/id_rsa \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o ConnectTimeout=10 \
-o BatchMode=yes \
-o PasswordAuthentication=no \
root@"$droplet_ip" "echo 'SSH connection successful'" 2>&1)
ssh_exit_code=$?
if [ $ssh_exit_code -eq 0 ]; then
echo "SSH connection to $droplet_ip established successfully"
echo "Response: $ssh_output"
return 0
else
echo "SSH connection failed (exit code: $ssh_exit_code)"
echo "Error output: $ssh_output"
if [ $attempt -lt $max_attempts ]; then
echo "Waiting $wait_time seconds before retry..."
sleep $wait_time
fi
attempt=$((attempt + 1))
fi
done
echo "Error: Failed to establish SSH connection to $droplet_ip after $max_attempts attempts"
return 1
}
# Function to setup VM after successful SSH connection
setup_vm() {
local droplet_ip="$1"
if [ -z "$droplet_ip" ]; then
echo "Error: No droplet IP provided for VM setup"
return 1
fi
echo "Setting up VM at $droplet_ip..."
# Execute setup commands via SSH
echo "Step 1: Running prepare.sh config.yml..."
if ! ssh -i ~/.ssh/id_rsa \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o ConnectTimeout=30 \
root@"$droplet_ip" \
"./prepare.sh config.yml RUNNER_ID $RUNNER_ID"; then
echo "Error: Failed to run prepare.sh config.yml"
return 1
fi
echo "Step 2: Running prepare.sh docker-compose.yml..."
if ! ssh -i ~/.ssh/id_rsa \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o ConnectTimeout=30 \
root@"$droplet_ip" \
"./prepare.sh docker-compose.yml RUNNER_ID $RUNNER_ID"; then
echo "Error: Failed to run prepare.sh docker-compose.yml"
return 1
fi
echo "Step 3: Starting docker compose services..."
if ! ssh -i ~/.ssh/id_rsa \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o ConnectTimeout=30 \
root@"$droplet_ip" \
"docker compose up -d"; then
echo "Error: Failed to start docker compose services"
return 1
fi
echo "Step 4: Checking docker compose status..."
ssh -i ~/.ssh/id_rsa \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o ConnectTimeout=30 \
root@"$droplet_ip" \
"docker compose ps"
echo "VM setup completed successfully"
return 0
}
# Check required environment variables
check_env_vars "SPEC" "IMAGE" "RUNNER_ID" "DO_TOKEN" "SSH_PRIVATE_KEY_DECODED"
# Check if doctl is available
check_doctl
# Setup SSH private key
setup_ssh_key
create_droplet "$RUNNER_ID" "$SPEC" "$IMAGE"
# Calculate and display execution time
END_TIME=$(date +%s)
EXECUTION_TIME=$((END_TIME - START_TIME))
echo "Script execution time: ${EXECUTION_TIME} seconds"
echo "ready=true" >> $GITHUB_OUTPUT