From 4b43e8ea77517c3ca3c1d3c9f78301541efcdcf1 Mon Sep 17 00:00:00 2001 From: RafaelGSS Date: Tue, 21 May 2024 17:42:12 -0300 Subject: [PATCH] feat: initial commit --- .github/workflows/action.yml | 23 +++++ .github/workflows/instance_start_stop.bash | 103 +++++++++++++++++++++ README.md | 28 ++++++ action.yml | 18 ++++ 4 files changed, 172 insertions(+) create mode 100644 .github/workflows/action.yml create mode 100755 .github/workflows/instance_start_stop.bash create mode 100644 action.yml diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml new file mode 100644 index 0000000..9bd787b --- /dev/null +++ b/.github/workflows/action.yml @@ -0,0 +1,23 @@ +name: 'Runner Starter' +description: 'Turn on/off a self-hosted runner' +inputs: + instance_id: + description: 'ID of the EC2 instance to start OR the name of the scaling group to scale down' + required: true + action: + description: 'Define if I want to start or stop the runner' + required: true + aws_default_region: + description: 'AWS region to use' + required: true + +runs: + using: "composite" + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Start/Stop Runner Instance + shell: bash + run: | + ${GITHUB_ACTION_PATH}/instance_start_stop.bash --instance-id=${{ inputs.instance_id }} --action=${{ inputs.action }} diff --git a/.github/workflows/instance_start_stop.bash b/.github/workflows/instance_start_stop.bash new file mode 100755 index 0000000..3e1e23a --- /dev/null +++ b/.github/workflows/instance_start_stop.bash @@ -0,0 +1,103 @@ +#!/bin/bash + +# Function to print messages in a fancy way +function printMessage { + local message="$1" + local type="$2" + local length=${#message} + local line=$(printf "%-${length}s" | tr ' ' '-') + echo "" + case "$type" in + "info") + echo -e "\033[1;34m$line\033[0m" + echo -e "\033[1;34m$message\033[0m" + echo -e "\033[1;34m$line\033[0m" + ;; + "success") + echo -e "\033[1;32m$line\033[0m" + echo -e "\033[1;32m$message\033[0m" + echo -e "\033[1;32m$line\033[0m" + ;; + "error") + echo -e "\033[1;31m$line\033[0m" + echo -e "\033[1;31m$message\033[0m" + echo -e "\033[1;31m$line\033[0m" + ;; + *) + echo -e "\033[1;34m$line\033[0m" + echo -e "\033[1;34m$message\033[0m" + echo -e "\033[1;34m$line\033[0m" + ;; + esac + echo "" +} + +# Parse command line arguments +while [ $# -gt 0 ]; do + case "$1" in + --instance-id=*) + INSTANCE_ID="${1#*=}" + ;; + --action=*) + ACTION="${1#*=}" + ;; + *) + printMessage "Invalid argument: $1" "error" + exit 1 + ;; + esac + shift +done + +# Check if instance ID is null +if [ -z "$INSTANCE_ID" ]; then + printMessage "--instance-id=XX-XXX is required" "error" + exit 1 +fi + +# Check if action is null +if [ -z "$ACTION" ]; then + printMessage "--action=start|stop is required" "error" + exit 1 +fi + +# Check if action is valid +if [ "$ACTION" != "start" ] && [ "$ACTION" != "stop" ]; then + printMessage "Invalid action: $ACTION" "error" + exit 1 +fi + +# Check if instance is stopped or started +INSTANCE_STATE=$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" --query "Reservations[].Instances[].State.Name" --output text) + +if [ "$ACTION" = "start" ]; then + if [ "$INSTANCE_STATE" = "stopped" ]; then + INSTANCE_NAME=$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" --query "Reservations[].Instances[].Tags[?Key=='Name'].Value" --output text) + printMessage "Starting instance $INSTANCE_NAME ($INSTANCE_ID)..." "info" + aws ec2 start-instances --instance-ids "$INSTANCE_ID" >/dev/null + printMessage "Waiting for instance $INSTANCE_ID to start..." "info" + aws ec2 wait instance-running --instance-ids "$INSTANCE_ID" + printMessage "Instance $INSTANCE_ID is now running" "success" + elif [ "$INSTANCE_STATE" = "running" ]; then + INSTANCE_NAME=$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" --query "Reservations[].Instances[].Tags[?Key=='Name'].Value" --output text) + printMessage "Instance $INSTANCE_NAME ($INSTANCE_ID) is already running" "success" + else + printMessage "Instance $INSTANCE_ID is in an unknown state: $INSTANCE_STATE" "error" + exit 1 + fi +elif [ "$ACTION" = "stop" ]; then + if [ "$INSTANCE_STATE" = "running" ]; then + INSTANCE_NAME=$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" --query "Reservations[].Instances[].Tags[?Key=='Name'].Value" --output text) + printMessage "Stopping instance $INSTANCE_NAME ($INSTANCE_ID)..." "info" + aws ec2 stop-instances --instance-ids "$INSTANCE_ID" >/dev/null + printMessage "Waiting for instance $INSTANCE_ID to stop..." "info" + aws ec2 wait instance-stopped --instance-ids "$INSTANCE_ID" + printMessage "Instance $INSTANCE_ID is now stopped" "success" + elif [ "$INSTANCE_STATE" = "stopped" ]; then + INSTANCE_NAME=$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" --query "Reservations[].Instances[].Tags[?Key=='Name'].Value" --output text) + printMessage "Instance $INSTANCE_NAME ($INSTANCE_ID) is already stopped" "success" + else + printMessage "Instance $INSTANCE_ID is in an unknown state: $INSTANCE_STATE" "error" + exit 1 + fi +fi diff --git a/README.md b/README.md index ff1dda4..8f37f14 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,31 @@ The runner is kept active every 2 days (to not be removed as a Github Runner) an ## Installation TODO + +## Usage + +```yaml + steps: + # Configure AWS Credentials + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: us-west-2 + role-to-assume: $ARN_IAM_ROLE + + - name: Start Runner + uses: nodesource/aws-eco-runner@v1 + with: + instance_id: $INSTANCE_ID + action: 'start' + aws_default_region: 'us-west-2' + + ... + + - name: Stop Runner + uses: nodesource/aws-eco-runner@v1 + with: + instance_id: $INSTANCE_ID + action: 'stop' + aws_default_region: 'us-west-2' +``` diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..a151d70 --- /dev/null +++ b/action.yml @@ -0,0 +1,18 @@ +name: 'aws-eco-runner' +description: 'AWS Eco Runner is a solution designed to optimize the cost of using GitHub Actions runners on AWS. By automating the activation and deactivation of the instance, it ensures that you only incur costs when necessary' +author: 'NodeSource' + +runs: + using: 'node20' + main: '.github/workflows/action.yml' + +inputs: + instance_id: + description: 'ID of the EC2 instance to start OR the name of the scaling group to scale down' + required: true + action: + description: 'Define if I want to start or stop the runner' + required: true + aws_default_region: + description: 'AWS region to use' + required: true