diff --git a/README.md b/README.md index 212b55a..529eaed 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,8 @@ jobs: | Name | Required | Description | Default | |---------------------|----------|-------------|---------| -| `create_wait` | | Wait up to 'create_wait' retries (10 sec each) to create the Hetzner Cloud Server resource. | `360` (1 hour) | +| `create_wait` | | Wait up to `create_wait` retries (10 sec each) to create the Server resource via the Hetzner Cloud API. Retry if: Resource is not available ([Limited availability of Cloud plans](https://status.hetzner.com/incident/aa5ce33b-faa5-4fd0-9782-fde43cd270cf)). | `360` (1 hour) | +| `delete_wait` | | Wait up to `delete_wait` retries (10 sec each) to delete the Server resource via the Hetzner Cloud API. Retry if: Temporary outage of the API ([Fault report on Cloud API and Cloud Console](https://status.hetzner.com/incident/440e6b5f-249c-45fd-8074-e5d79cc4e2a6)). | `360` (1 hour) | | `enable_ipv4` | | Attach an IPv4 on the public NIC (true/false). If false, no IPv4 address will be attached. Warning: The GitHub API requires IPv4. Disabling it will result in connection failures. | `true` | | `enable_ipv6` | | Attach an IPv6 on the public NIC (true/false). If false, no IPv6 address will be attached. | `true` | | `github_token` | ✓ (always) | Fine-grained GitHub Personal Access Token (PAT) with 'Read and write' access to 'Administration' assigned. | | diff --git a/action.sh b/action.sh index f23fcd4..7832297 100644 --- a/action.sh +++ b/action.sh @@ -51,6 +51,9 @@ for MY_FILE in "${MY_FILES[@]}"; do fi done +# Retry wait time in secounds +WAIT_SEC=10 + # # INPUT # @@ -59,11 +62,32 @@ done # https://docs.github.com/en/actions/sharing-automations/creating-actions/metadata-syntax-for-github-actions#inputs # When you specify an input, GitHub creates an environment variable for the input with the name INPUT_. -# Set the Hetzner Cloud API token. -# Retrieves the value from the INPUT_HCLOUD_TOKEN environment variable. -MY_HETZNER_TOKEN=${INPUT_HCLOUD_TOKEN} -if [[ -z "$MY_HETZNER_TOKEN" ]]; then - exit_with_failure "Hetzner Cloud API token is not set." +# Set maximum retries * WAIT_SEC (10 sec) for Hetzner Server creation via the Hetzer Cloud API (default: 360 [1 hour]) +# If INPUT_CREATE_WAIT is set, use its value; otherwise, use "360". +MY_CREATE_WAIT=${INPUT_CREATE_WAIT:-360} +if [[ ! "$MY_CREATE_WAIT" =~ ^[0-9]+$ ]]; then + exit_with_failure "The maximum retries for Hetzner Server creation via the Hetzer Cloud API must be an integer!" +fi + +# Set maximum retries * WAIT_SEC (10 sec) for Hetzner Server deletion via the Hetzer Cloud API (default: 360 [1 hour]) +# If INPUT_DELETE_WAIT is set, use its value; otherwise, use "360". +MY_DELETE_WAIT=${INPUT_DELETE_WAIT:-360} +if [[ ! "$MY_DELETE_WAIT" =~ ^[0-9]+$ ]]; then + exit_with_failure "The maximum retries for Hetzner Server deletion via the Hetzer Cloud API must be an integer!" +fi + +# Enable IPv4 (default: false) +# If INPUT_ENABLE_IPV4 is set, use its value; otherwise, use "false". +MY_ENABLE_IPV4=${INPUT_ENABLE_IPV4:-"true"} +if [[ "$MY_ENABLE_IPV4" != "true" && "$MY_ENABLE_IPV4" != "false" ]]; then + exit_with_failure "Enable IPv4 must be 'true' or 'false'." +fi + +# Enable IPv6 (default: true) +# If INPUT_ENABLE_IPV6 is set, use its value; otherwise, use "true". +MY_ENABLE_IPV6=${INPUT_ENABLE_IPV6:-"true"} +if [[ "$MY_ENABLE_IPV6" != "true" && "$MY_ENABLE_IPV6" != "false" ]]; then + exit_with_failure "Enable IPv6 must be 'true' or 'false'." fi # Set the GitHub Personal Access Token (PAT). @@ -86,27 +110,11 @@ MY_GITHUB_REPOSITORY_OWNER_ID=${GITHUB_REPOSITORY_OWNER_ID:-"0"} # Set The ID of the repository (used for Hetzner Cloud Server label). MY_GITHUB_REPOSITORY_ID=${GITHUB_REPOSITORY_ID:-"0"} -# Specify here which mode you want to use (default: create): -# - create : Create a new runner -# - delete : Delete the previously created runner -# If INPUT_MODE is set, use its value; otherwise, use "create". -MY_MODE=${INPUT_MODE:-"create"} -if [[ "$MY_MODE" != "create" && "$MY_MODE" != "delete" ]]; then - exit_with_failure "Mode must be 'create' or 'delete'." -fi - -# Enable IPv4 (default: false) -# If INPUT_ENABLE_IPV4 is set, use its value; otherwise, use "false". -MY_ENABLE_IPV4=${INPUT_ENABLE_IPV4:-"true"} -if [[ "$MY_ENABLE_IPV4" != "true" && "$MY_ENABLE_IPV4" != "false" ]]; then - exit_with_failure "Enable IPv4 must be 'true' or 'false'." -fi - -# Enable IPv6 (default: true) -# If INPUT_ENABLE_IPV6 is set, use its value; otherwise, use "true". -MY_ENABLE_IPV6=${INPUT_ENABLE_IPV6:-"true"} -if [[ "$MY_ENABLE_IPV6" != "true" && "$MY_ENABLE_IPV6" != "false" ]]; then - exit_with_failure "Enable IPv6 must be 'true' or 'false'." +# Set the Hetzner Cloud API token. +# Retrieves the value from the INPUT_HCLOUD_TOKEN environment variable. +MY_HETZNER_TOKEN=${INPUT_HCLOUD_TOKEN} +if [[ -z "$MY_HETZNER_TOKEN" ]]; then + exit_with_failure "Hetzner Cloud API token is not set." fi # Set the image to use for the instance (default: ubuntu-24.04) @@ -121,6 +129,15 @@ fi # If INPUT_LOCATION is set, use its value; otherwise, use "nbg1". MY_LOCATION=${INPUT_LOCATION:-"nbg1"} +# Specify here which mode you want to use (default: create): +# - create : Create a new runner +# - delete : Delete the previously created runner +# If INPUT_MODE is set, use its value; otherwise, use "create". +MY_MODE=${INPUT_MODE:-"create"} +if [[ "$MY_MODE" != "create" && "$MY_MODE" != "delete" ]]; then + exit_with_failure "Mode must be 'create' or 'delete'." +fi + # Set the name of the instance (default: gh-runner-$RANDOM) # If INPUT_NAME is set, use its value; otherwise, generate a random name using "gh-runner-$RANDOM". MY_NAME=${INPUT_NAME:-"gh-runner-$RANDOM"} @@ -160,26 +177,6 @@ if [[ "$MY_PRIMARY_IPV6" != "null" && ! "$MY_PRIMARY_IPV6" =~ ^[0-9]+$ ]]; then exit_with_failure "The primary IPv6 ID must be 'null' or an integer!" fi -# Set the server type/instance type (default: cx22) -# If INPUT_SERVER_TYPE is set, use its value; otherwise, use "cx22". -MY_SERVER_TYPE=${INPUT_SERVER_TYPE:-"cx22"} - -# Set maximal wait time (retries * 10 sec) for Hetzner Cloud Server (default: 30 [5 min]) -# If INPUT_SERVER_WAIT is set, use its value; otherwise, use "30". -MY_SERVER_WAIT=${INPUT_SERVER_WAIT:-"30"} -# Check if MY_RUNNER_WAIT is an integer -if [[ ! "$MY_SERVER_WAIT" =~ ^[0-9]+$ ]]; then - exit_with_failure "The maximum wait time (reties) for a running Hetzner Cloud Server must be an integer!" -fi - -# Set the SSH key to use for the instance (default: null) -# If INPUT_SSH_KEY is set, use its value; otherwise, use "null". -MY_SSH_KEY=${INPUT_SSH_KEY:-"null"} -# Check if MY_SSH_KEY is an integer -if [[ "$MY_SSH_KEY" != "null" && ! "$MY_SSH_KEY" =~ ^[0-9]+$ ]]; then - exit_with_failure "The SSH key ID must be 'null' or an integer!" -fi - # Set default GitHub Actions Runner installation directory (default: /actions-runner) # If INPUT_RUNNER_DIR is set, its value is used. Otherwise, the default value "/actions-runner" is used. MY_RUNNER_DIR=${INPUT_RUNNER_DIR:-"/actions-runner"} @@ -197,7 +194,7 @@ if [[ "$MY_RUNNER_VERSION" != "latest" && "$MY_RUNNER_VERSION" != "skip" && ! "$ exit_with_failure "'$MY_RUNNER_VERSION' is not a valid GitHub Actions Runner version! Enter 'latest', 'skip' or the version without 'v'." fi -# Set maximal wait time (retries * 10 sec) for GitHub Actions Runner registration (default: 60 [10 min]) +# Set maximal retries * WAIT_SEC (10 sec) for GitHub Actions Runner registration (default: 60 [10 min]) # If INPUT_RUNNER_WAIT is set, use its value; otherwise, use "60". MY_RUNNER_WAIT=${INPUT_RUNNER_WAIT:-"60"} # Check if MY_RUNNER_WAIT is an integer @@ -205,16 +202,29 @@ if [[ ! "$MY_RUNNER_WAIT" =~ ^[0-9]+$ ]]; then exit_with_failure "The maximum wait time (retries) for GitHub Action Runner registration must be an integer!" fi -# Set maximal wait time (retries * 10 sec) for Hetzner Server creation (default: 360 [1 hour]) -# If INPUT_CREATE_WAIT is set, use its value; otherwise, use "360". -MY_CREATE_WAIT=${INPUT_CREATE_WAIT:-360} -if [[ ! "$MY_CREATE_WAIT" =~ ^[0-9]+$ ]]; then - exit_with_failure "The maximum wait time (retries) for Hetzner Server creation must be an integer!" -fi - # Set Hetzner Cloud Server ID +# Check only if mode is delete. MY_HETZNER_SERVER_ID=${INPUT_SERVER_ID} +# Set the server type/instance type (default: cx22) +# If INPUT_SERVER_TYPE is set, use its value; otherwise, use "cx22". +MY_SERVER_TYPE=${INPUT_SERVER_TYPE:-"cx22"} + +# Set maximal retries * WAIT_SEC (10 sec) for Hetzner Cloud Server (default: 30 [5 min]) +# If INPUT_SERVER_WAIT is set, use its value; otherwise, use "30". +MY_SERVER_WAIT=${INPUT_SERVER_WAIT:-"30"} +# Check if MY_RUNNER_WAIT is an integer +if [[ ! "$MY_SERVER_WAIT" =~ ^[0-9]+$ ]]; then + exit_with_failure "The maximum wait time (reties) for a running Hetzner Cloud Server must be an integer!" +fi + +# Set the SSH key to use for the instance (default: null) +# If INPUT_SSH_KEY is set, use its value; otherwise, use "null". +MY_SSH_KEY=${INPUT_SSH_KEY:-"null"} +# Check if MY_SSH_KEY is an integer +if [[ "$MY_SSH_KEY" != "null" && ! "$MY_SSH_KEY" =~ ^[0-9]+$ ]]; then + exit_with_failure "The SSH key ID must be 'null' or an integer!" +fi # # DELETE @@ -228,9 +238,13 @@ if [[ "$MY_MODE" == "delete" ]]; then # Send a DELETE request to the Hetzner Cloud API to delete the server. # https://docs.hetzner.cloud/#servers-delete-a-server + # curl retry: https://everything.curl.dev/usingcurl/downloads/retry.html echo "Delete server..." curl \ -X DELETE \ + --retry "$MY_DELETE_WAIT" \ + --retry-delay "$WAIT_SEC" \ + --retry-all-errors \ --fail-with-body \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${MY_HETZNER_TOKEN}" \ @@ -372,7 +386,6 @@ fi # https://docs.hetzner.cloud/#servers-create-a-server MAX_RETRIES=$MY_CREATE_WAIT RETRY_COUNT=0 -WAIT_SEC=10 while [[ $RETRY_COUNT -lt $MAX_RETRIES ]]; do echo "Create Server..." if curl \ diff --git a/action.yml b/action.yml index effcc64..5575849 100644 --- a/action.yml +++ b/action.yml @@ -7,18 +7,16 @@ branding: color: 'red' inputs: - mode: - description: >- - Choose either 'create' to create a new GitHub Actions Runner or 'delete' to delete a previously created one. - required: true - github_token: + create_wait: description: >- - Fine-grained GitHub Personal Access Token (PAT) with 'Read and write' access to 'Administration' assigned. - required: true - hcloud_token: + Wait up to 'create_wait' retries (10 sec each) to create the Server resource via the Hetzner Cloud API (default: 360 = 1 hour). + required: false + default: '360' + delete_wait: description: >- - Hetzner Cloud API token with 'Read & Write' permissions assigned. - required: true + Wait up to 'delete_wait' retries (10 sec each) to delete the Server resource via the Hetzner Cloud API (default: 360 = 1 hour). + required: false + default: '360' enable_ipv4: description: >- Attach an IPv4 on the public NIC (true/false) . If false, no IPv4 address will be attached. @@ -30,6 +28,14 @@ inputs: Attach an IPv6 on the public NIC (true/false). If false, no IPv6 address will be attached. required: false default: 'true' + github_token: + description: >- + Fine-grained GitHub Personal Access Token (PAT) with 'Read and write' access to 'Administration' assigned. + required: true + hcloud_token: + description: >- + Hetzner Cloud API token with 'Read & Write' permissions assigned. + required: true image: description: >- Name or ID (integer) of the Image the Server is created from. @@ -40,6 +46,10 @@ inputs: Name of Location to create Server in. required: false default: 'nbg1' + mode: + description: >- + Choose either 'create' to create a new GitHub Actions Runner or 'delete' to delete a previously created one. + required: true name: description: >- The name for the server and label for the GitHub Actions Runner (must be unique within the project and conform to hostname rules: '[a-zA-Z0-9_-]'). @@ -49,6 +59,11 @@ inputs: Network ID (integer) which should be attached to the Server private network interface at the creation time. required: false default: 'null' + pre_runner_script: + description: >- + Specifies bash commands to run before the GitHub Actions Runner starts. + It's useful for installing dependencies with apt-get, dnf, zypper etc. + required: false primary_ipv4: description: >- ID (integer) of the IPv4 Primary IP to use. @@ -66,6 +81,11 @@ inputs: GitHub Actions Runner installation directory (created automatically; no trailing slash). required: false default: '/actions-runner' + runner_wait: + description: >- + Wait up to 'runner_wait' retries (10 sec each) for runner registration (default: 10 minutes). + required: false + default: '60' runner_version: description: >- GitHub Actions Runner version (omit 'v'; e.g., '2.321.0'). @@ -73,16 +93,6 @@ inputs: 'skip' will skip the installation. A working installation is expected in the 'runner_dir'. required: false default: 'latest' - create_wait: - description: >- - Wait up to 'create_wait' retries (10 sec each) to create the Hetzner Cloud Server resource (default: 360 = 1 hour). - required: false - default: '360' - runner_wait: - description: >- - Wait up to 'runner_wait' retries (10 sec each) for runner registration (default: 10 minutes). - required: false - default: '60' server_id: description: >- ID (integer) of Hetzner Cloud Server to delete. @@ -102,11 +112,6 @@ inputs: SSH key ID (integer) or name which should be injected into the Server at creation time. required: false default: 'null' - pre_runner_script: - description: >- - Specifies bash commands to run before the GitHub Actions Runner starts. - It's useful for installing dependencies with apt-get, dnf, zypper etc. - required: false outputs: label: @@ -128,6 +133,8 @@ runs: working-directory: ${{ github.action_path }} run: bash action.sh env: + INPUT_CREATE_WAIT: ${{ inputs.create_wait }} + INPUT_DELETE_WAIT: ${{ inputs.delete_wait }} INPUT_ENABLE_IPV4: ${{ inputs.enable_ipv4 }} INPUT_ENABLE_IPV6: ${{ inputs.enable_ipv6 }} INPUT_GITHUB_TOKEN: ${{ inputs.github_token }} @@ -147,4 +154,3 @@ runs: INPUT_SERVER_TYPE: ${{ inputs.server_type }} INPUT_SERVER_WAIT: ${{ inputs.server_wait }} INPUT_SSH_KEY: ${{ inputs.ssh_key }} - INPUT_CREATE_WAIT: ${{ inputs.create_wait }}