diff --git a/Dockerfile b/Dockerfile index 67929cc..2fb359c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ FROM alpine:latest -RUN apk --no-cache add curl bash git doctl +RUN apk --no-cache add curl bash git doctl openssh WORKDIR /app diff --git a/actions/orchestrating.sh b/actions/orchestrating.sh index 1f6b93f..edfd00e 100755 --- a/actions/orchestrating.sh +++ b/actions/orchestrating.sh @@ -28,8 +28,8 @@ check_doctl() { # Function to setup SSH private key setup_ssh_key() { - if [ -z "$SSH_PRIVATE_KEY" ]; then - echo "Error: SSH_PRIVATE_KEY environment variable is not set" + if [ -z "$SSH_PRIVATE_KEY_DECODED" ]; then + echo "Error: SSH_PRIVATE_KEY_DECODED environment variable is not set" exit 1 fi @@ -37,22 +37,62 @@ setup_ssh_key() { mkdir -p ~/.ssh chmod 700 ~/.ssh - # Write private key to file (decode if base64) - if echo "$SSH_PRIVATE_KEY" | base64 -d > /dev/null 2>&1; then - echo "$SSH_PRIVATE_KEY" | base64 -d > ~/.ssh/id_rsa - else - echo -e "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa + # 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 - echo "SSH private key has been set up successfully" + # Validate SSH key format + 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:-ubuntu-22-04-x64}" + local image="${3:-ubuntu-24-04-x64}" local region="${4:-sgp1}" echo "Creating droplet: $name" @@ -79,6 +119,16 @@ create_droplet() { local droplet_ip=$(doctl compute droplet get "$droplet_id" --format PublicIPv4 --no-header) echo "Droplet IP: $droplet_ip" + # Test SSH connection + if test_ssh_connection "$droplet_ip"; then + echo "Droplet is ready and accessible via SSH" + 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" @@ -86,8 +136,51 @@ create_droplet() { fi } +# Function to test SSH connection +test_ssh_connection() { + local droplet_ip="$1" + local max_attempts=10 + local attempt=1 + local wait_time=30 + + 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 +} + # Check required environment variables -check_env_vars "SPEC" "RUNNER_ID" "DO_TOKEN" "SSH_PRIVATE_KEY" +check_env_vars "SPEC" "RUNNER_ID" "DO_TOKEN" "SSH_PRIVATE_KEY_DECODED" # Check if doctl is available check_doctl diff --git a/docker-compose.yml b/docker-compose.yml index c5cb414..065b25b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,9 +5,7 @@ services: dockerfile: Dockerfile entrypoint: "" command: ["sleep", "3600"] - environment: - - DO_TOKEN=dop_v1_edf8bb32e734cf0dbbb40ce9d2b7ad494277caced8636a4823aede610b6c31fa - - RUNNER_ID=pandeganteng - - SPEC=default + env_file: + - .env volumes: - ./actions:/actions \ No newline at end of file