From 4d3661c05ff73564137bb159632aaf62040b1d68 Mon Sep 17 00:00:00 2001 From: Golam Rashed Date: Thu, 15 May 2025 18:12:47 +1000 Subject: [PATCH 1/5] Add retry logic for server creation with configurable attempts and delay Introduce create_retries and create_retry_delay inputs to control the number of retry attempts and delay between attempts when creating a Hetzner Cloud server. Update action.sh to implement the retry loop and improve error handling for transient failures. Document new options in README.md and action.yml to enhance reliability. --- README.md | 6 ++++++ action.sh | 35 +++++++++++++++++++++++++++++------ action.yml | 12 ++++++++++++ 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2335cd3..dc9607b 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,12 @@ jobs: | `server_type` | | Name of the Server type this Server should be created with. | `cx22` (Intel x86, 2 vCPU, 4GB RAM, 40GB SSD) | | `server_wait` | | Wait up to `server_wait` retries (10 sec each) for the Hetzner Cloud Server to start. | `30` (5 min) | | `ssh_key` | | SSH key ID (integer) which should be injected into the Server at creation time. | `null` | +| `create_retries` | | Number of retry attempts for runner creation if it fails. | `1` | +| `create_retry_delay`| | Delay (in seconds) between retry attempts for runner creation. | `10` | + +### Retry Logic + +If runner creation fails due to a transient error, the action will retry up to `create_retries` times, waiting `create_retry_delay` seconds between attempts. All attempts and errors are logged. ## Outputs diff --git a/action.sh b/action.sh index b8cf28e..9ac8d8d 100644 --- a/action.sh +++ b/action.sh @@ -202,7 +202,17 @@ fi MY_RUNNER_WAIT=${INPUT_RUNNER_WAIT:-"60"} # Check if MY_RUNNER_WAIT is an integer if [[ ! "$MY_RUNNER_WAIT" =~ ^[0-9]+$ ]]; then - exit_with_failure "The maximum wait time (reties) for GitHub Action Runner registration must be an integer!" + exit_with_failure "The maximum wait time (retries) for GitHub Action Runner registration must be an integer!" +fi + +# Set create retry logic inputs +MY_CREATE_RETRIES=${INPUT_CREATE_RETRIES:-1} +MY_CREATE_RETRY_DELAY=${INPUT_CREATE_RETRY_DELAY:-10} +if [[ ! "$MY_CREATE_RETRIES" =~ ^[0-9]+$ ]]; then + exit_with_failure "The create_retries value must be an integer!" +fi +if [[ ! "$MY_CREATE_RETRY_DELAY" =~ ^[0-9]+$ ]]; then + exit_with_failure "The create_retry_delay value must be an integer!" fi # Set Hetzner Cloud Server ID @@ -363,8 +373,10 @@ fi # Send a POST request to the Hetzner Cloud API to create a server. # https://docs.hetzner.cloud/#servers-create-a-server -echo "Create server..." -if ! curl \ +MY_CREATE_ATTEMPT=1 +while [[ $MY_CREATE_ATTEMPT -le $MY_CREATE_RETRIES ]]; do + echo "Create server (attempt $MY_CREATE_ATTEMPT of $MY_CREATE_RETRIES)..." + if curl \ -X POST \ --fail-with-body \ -o "servers.json" \ @@ -372,9 +384,20 @@ if ! curl \ -H "Authorization: Bearer ${MY_HETZNER_TOKEN}" \ -d @create-server.json \ "https://api.hetzner.cloud/v1/servers"; then - cat "servers.json" - exit_with_failure "Failed to create Server in Hetzner Cloud!" -fi + echo "Server created successfully." + break + else + echo "Server creation failed (attempt $MY_CREATE_ATTEMPT)." + if [[ $MY_CREATE_ATTEMPT -lt $MY_CREATE_RETRIES ]]; then + echo "Retrying in $MY_CREATE_RETRY_DELAY seconds..." + sleep "$MY_CREATE_RETRY_DELAY" + else + cat "servers.json" + exit_with_failure "Failed to create Server in Hetzner Cloud after $MY_CREATE_RETRIES attempts!" + fi + fi + MY_CREATE_ATTEMPT=$((MY_CREATE_ATTEMPT + 1)) +done # Get the Hetzner Server ID from the JSON response (assuming valid JSON) MY_HETZNER_SERVER_ID=$(jq -er '.server.id' < "servers.json") diff --git a/action.yml b/action.yml index fe2fdeb..eb35aec 100644 --- a/action.yml +++ b/action.yml @@ -102,6 +102,16 @@ inputs: 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 + create_retries: + description: >- + Number of retry attempts for runner creation if it fails. + required: false + default: '1' + create_retry_delay: + description: >- + Delay (in seconds) between retry attempts for runner creation. + required: false + default: '10' outputs: label: @@ -142,3 +152,5 @@ runs: INPUT_SERVER_TYPE: ${{ inputs.server_type }} INPUT_SERVER_WAIT: ${{ inputs.server_wait }} INPUT_SSH_KEY: ${{ inputs.ssh_key }} + INPUT_CREATE_RETRIES: ${{ inputs.create_retries }} + INPUT_CREATE_RETRY_DELAY: ${{ inputs.create_retry_delay }} From 835faba0b4919b8af9e882bc46b2d3b0fd7a06c0 Mon Sep 17 00:00:00 2001 From: Nils Date: Sat, 17 May 2025 16:25:24 +0200 Subject: [PATCH 2/5] Update action.sh --- action.sh | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/action.sh b/action.sh index 9ac8d8d..4947fdb 100644 --- a/action.sh +++ b/action.sh @@ -197,22 +197,19 @@ 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: 30 [5 min]) -# If MY_RUNNER_WAIT is set, use its value; otherwise, use "30". +# Set maximal wait time (retries * 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 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 create retry logic inputs -MY_CREATE_RETRIES=${INPUT_CREATE_RETRIES:-1} -MY_CREATE_RETRY_DELAY=${INPUT_CREATE_RETRY_DELAY:-10} +# 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_RETRIES" =~ ^[0-9]+$ ]]; then - exit_with_failure "The create_retries value must be an integer!" -fi -if [[ ! "$MY_CREATE_RETRY_DELAY" =~ ^[0-9]+$ ]]; then - exit_with_failure "The create_retry_delay value must be an integer!" + exit_with_failure "The maximum wait time (retries) for Hetzner Server creation must be an integer!" fi # Set Hetzner Cloud Server ID @@ -373,9 +370,11 @@ fi # Send a POST request to the Hetzner Cloud API to create a server. # https://docs.hetzner.cloud/#servers-create-a-server -MY_CREATE_ATTEMPT=1 -while [[ $MY_CREATE_ATTEMPT -le $MY_CREATE_RETRIES ]]; do - echo "Create server (attempt $MY_CREATE_ATTEMPT of $MY_CREATE_RETRIES)..." +MAX_RETRIES=$MY_CREATE_WAIT +RETRY_COUNT=0 +WAIT_SEC=10 +while [[ $RETRY_COUNT -lt $MAX_RETRIES ]]; do + echo "Create Server..." if curl \ -X POST \ --fail-with-body \ @@ -387,16 +386,20 @@ while [[ $MY_CREATE_ATTEMPT -le $MY_CREATE_RETRIES ]]; do echo "Server created successfully." break else - echo "Server creation failed (attempt $MY_CREATE_ATTEMPT)." - if [[ $MY_CREATE_ATTEMPT -lt $MY_CREATE_RETRIES ]]; then - echo "Retrying in $MY_CREATE_RETRY_DELAY seconds..." - sleep "$MY_CREATE_RETRY_DELAY" - else - cat "servers.json" - exit_with_failure "Failed to create Server in Hetzner Cloud after $MY_CREATE_RETRIES attempts!" - fi + # Check if the error is related to resource unavailability + if grep -q -E "resource_unavailable|resource_limit_exceeded" "servers.json"; then + echo "Resource limitation detected." + # If error is not resource-related, don't retry + else + cat "servers.json" + exit_with_failure "Failed to create Server in Hetzner Cloud!" + fi fi - MY_CREATE_ATTEMPT=$((MY_CREATE_ATTEMPT + 1)) + + RETRY_COUNT=$((RETRY_COUNT + 1)) # Increment retry counter + + echo "Failed to create Server. Wait $WAIT_SEC seconds... (Attempt $RETRY_COUNT/$MAX_RETRIES)" + sleep "$WAIT_SEC" done # Get the Hetzner Server ID from the JSON response (assuming valid JSON) @@ -416,7 +419,6 @@ echo "server_id=$MY_HETZNER_SERVER_ID" >> "$GITHUB_OUTPUT" # Wait for server MAX_RETRIES=$MY_SERVER_WAIT -WAIT_SEC=10 RETRY_COUNT=0 echo "Wait for server..." while [[ $RETRY_COUNT -lt $MAX_RETRIES ]]; do From d1606647da053e3f880418c2b89f24e99663482e Mon Sep 17 00:00:00 2001 From: Nils Date: Sat, 17 May 2025 16:29:04 +0200 Subject: [PATCH 3/5] Update action.yml --- action.yml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/action.yml b/action.yml index eb35aec..d531f1d 100644 --- a/action.yml +++ b/action.yml @@ -73,6 +73,11 @@ 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). @@ -102,16 +107,6 @@ inputs: 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 - create_retries: - description: >- - Number of retry attempts for runner creation if it fails. - required: false - default: '1' - create_retry_delay: - description: >- - Delay (in seconds) between retry attempts for runner creation. - required: false - default: '10' outputs: label: From be6f3a89691b067012e3f5b0fe3538089996b9f8 Mon Sep 17 00:00:00 2001 From: Nils Date: Sat, 17 May 2025 16:29:50 +0200 Subject: [PATCH 4/5] Update action.yml --- action.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/action.yml b/action.yml index d531f1d..effcc64 100644 --- a/action.yml +++ b/action.yml @@ -147,5 +147,4 @@ runs: INPUT_SERVER_TYPE: ${{ inputs.server_type }} INPUT_SERVER_WAIT: ${{ inputs.server_wait }} INPUT_SSH_KEY: ${{ inputs.ssh_key }} - INPUT_CREATE_RETRIES: ${{ inputs.create_retries }} - INPUT_CREATE_RETRY_DELAY: ${{ inputs.create_retry_delay }} + INPUT_CREATE_WAIT: ${{ inputs.create_wait }} From 89cd00e607f5d7624673f49279706266bc65d282 Mon Sep 17 00:00:00 2001 From: Nils Date: Sat, 17 May 2025 16:32:01 +0200 Subject: [PATCH 5/5] Update README.md --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index dc9607b..212b55a 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,7 @@ 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) | | `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. | | @@ -160,12 +161,6 @@ jobs: | `server_type` | | Name of the Server type this Server should be created with. | `cx22` (Intel x86, 2 vCPU, 4GB RAM, 40GB SSD) | | `server_wait` | | Wait up to `server_wait` retries (10 sec each) for the Hetzner Cloud Server to start. | `30` (5 min) | | `ssh_key` | | SSH key ID (integer) which should be injected into the Server at creation time. | `null` | -| `create_retries` | | Number of retry attempts for runner creation if it fails. | `1` | -| `create_retry_delay`| | Delay (in seconds) between retry attempts for runner creation. | `10` | - -### Retry Logic - -If runner creation fails due to a transient error, the action will retry up to `create_retries` times, waiting `create_retry_delay` seconds between attempts. All attempts and errors are logged. ## Outputs