From e8d3b74aa38b36310a02a233bd4d181c43fb0630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Test=C3=A9?= Date: Thu, 28 Nov 2024 11:24:44 +0100 Subject: [PATCH] feat!: unregister runner using its unique id BREAKING CHANGE: runner name cannot be used anymore to remove it from GitHub platform. A runner can be removed directly, through the REST API, using its ID. This change has been made to avoid Slab server to fetch all registered runners before being able to filter by name and finally find ID of the runner to remove. --- .github/workflows/test_spawn_terminate.yml | 9 ++++-- .github/workflows/test_start_stop.yml | 4 ++- README.md | 32 ++++++++++++---------- action.yaml | 14 ++++++---- dist/index.js | 23 +++++++++------- src/config.js | 4 +-- src/gh.js | 2 +- src/index.js | 11 +++++--- src/slab.js | 6 ++-- 9 files changed, 61 insertions(+), 44 deletions(-) diff --git a/.github/workflows/test_spawn_terminate.yml b/.github/workflows/test_spawn_terminate.yml index d8ebcb1..72eb2d2 100644 --- a/.github/workflows/test_spawn_terminate.yml +++ b/.github/workflows/test_spawn_terminate.yml @@ -21,7 +21,9 @@ jobs: fail-fast: false outputs: runner-aws: ${{ steps.gen-output.outputs.runner_aws }} + runner-aws-id: ${{ steps.gen-output.outputs.runner_aws_id }} runner-hyperstack: ${{ steps.gen-output.outputs.runner_hyperstack }} + runner-hyperstack-id: ${{ steps.gen-output.outputs.runner_hyperstack_id }} steps: - name: Checkout id: checkout @@ -42,6 +44,7 @@ jobs: id: gen-output run: | echo "runner_${{ matrix.provider }}=${{ steps.test-start.outputs.label }}" >> "${GITHUB_OUTPUT}" + echo "runner_${{ matrix.provider }}_id=${{ steps.test-start.outputs.id }}" >> "${GITHUB_OUTPUT}" test-runner-alive-aws: name: Test runner is alive (AWS) @@ -64,8 +67,8 @@ jobs: if: ${{ always() && needs.action-start.result != 'skipped' }} strategy: matrix: - runner: [ "${{ needs.action-start.outputs.runner-aws }}", - "${{ needs.action-start.outputs.runner-hyperstack }}" ] + runner-id: [ "${{ needs.action-start.outputs.runner-aws-id }}", + "${{ needs.action-start.outputs.runner-hyperstack-id }}" ] fail-fast: false steps: - name: Checkout @@ -80,7 +83,7 @@ jobs: github-token: ${{ secrets.SLAB_ACTION_TOKEN }} slab-url: ${{ secrets.SLAB_BASE_URL_PRE_PROD }} job-secret: ${{ secrets.JOB_SECRET }} - label: ${{ matrix.runner }} + runner-id: ${{ matrix.runner-id }} test-runner-removed-aws: name: Test runner is removed (AWS) diff --git a/.github/workflows/test_start_stop.yml b/.github/workflows/test_start_stop.yml index c054624..54212c1 100644 --- a/.github/workflows/test_start_stop.yml +++ b/.github/workflows/test_start_stop.yml @@ -25,6 +25,8 @@ jobs: name: GitHub Actions Test (start) runs-on: ubuntu-latest needs: [ test-runner-exist ] + outputs: + runner-id: ${{ steps.test-start.outputs.id }} steps: - name: Checkout id: checkout @@ -66,7 +68,7 @@ jobs: github-token: ${{ secrets.SLAB_ACTION_TOKEN }} slab-url: ${{ secrets.SLAB_BASE_URL_PRE_PROD }} job-secret: ${{ secrets.JOB_SECRET }} - label: ci-persistent-runner + runner-id: ${{ needs.action-start.outputs.runner-id}} test-runner-persist: name: Test runner is still registered diff --git a/README.md b/README.md index b60db35..6708695 100644 --- a/README.md +++ b/README.md @@ -21,21 +21,22 @@ See [below](#example) the YAML code of the depicted workflow. ### Inputs -| Name | Required | Description | -|----------------|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `mode` | Always required. | Specify here which mode you want to use: `start` to start a new runner, `stop` to stop the previously created runner. | -| `github-token` | Always required. | GitHub Personal Access Token with the `repo` scope assigned. | -| `slab-url` | Always required. | URL to Slab CI server. | -| `job-secret` | Always required. | Secret key used by Slab to perform HMAC computation. | -| `backend` | Required with `start` mode. | Backend provider name to look for in slab.toml file in repository that uses the action. | -| `profile` | Required with `start` mode. | Profile to use as described slab.toml file in repository that uses the action. | -| `label` | Required with `stop` mode. | Name of the unique label assigned to the runner.The label is provided by the output of the action in the `start` mode.The label is used to remove the runner from GitHub when the runner is not needed anymore. | +| Name | Required | Description | +|----------------|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `mode` | Always required. | Specify here which mode you want to use: `start` to start a new runner, `stop` to stop the previously created runner. | +| `github-token` | Always required. | GitHub Personal Access Token with the `repo` scope assigned. | +| `slab-url` | Always required. | URL to Slab CI server. | +| `job-secret` | Always required. | Secret key used by Slab to perform HMAC computation. | +| `backend` | Required with `start` mode. | Backend provider name to look for in slab.toml file in repository that uses the action. | +| `profile` | Required with `start` mode. | Profile to use as described slab.toml file in repository that uses the action. | +| `runner-id` | Required with `stop` mode. | Unique ID assigned to the runner.The ID is provided by the output of the action in the `start` mode. The ID is used to remove the runner from GitHub when the runner is not needed anymore. | ### Outputs -| Name | Description | -|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `label` | Name of the unique label assigned to the runner. The label is used in two cases: to use as the input of `runs-on` property for the following jobs and to remove the runner from GitHub when it is not needed anymore. | +| Name | Description | +|---------|-------------------------------------------------------------------------------------------------------------------------------------------------| +| `label` | Name of the unique label assigned to the runner. Use `label` as the input of `runs-on` workflow property to run subsequent jobs. | +| `id` | Unique ID assigned to the runner. Use `id` as input of `runner-id` action property remove the runner from GitHub when it is not needed anymore. | ### Examples @@ -50,7 +51,7 @@ jobs: runs-on: ubuntu-latest outputs: label: ${{ steps.start-ec2-runner.outputs.label }} - ec2-instance-id: ${{ steps.start-ec2-runner.outputs.ec2-instance-id }} + id: ${{ steps.start-ec2-runner.outputs.id }} steps: - name: Start EC2 runner id: start-ec2-runner @@ -62,6 +63,9 @@ jobs: profile: cpu-test do-the-job: + needs: start-runner + runs-on: ${{ needs.start-runner.outputs.label }} + steps: # ... # stop-runner: @@ -77,7 +81,7 @@ jobs: with: mode: stop github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }} - label: ${{ needs.start-runner.outputs.label }} + runner-id: ${{ needs.start-runner.outputs.id }} ``` ## License Summary diff --git a/action.yaml b/action.yaml index 041f8d3..8707557 100644 --- a/action.yaml +++ b/action.yaml @@ -33,10 +33,10 @@ inputs: Profile to use as described slab.toml file in repository that uses the action. This input is required if you use the 'start' mode. required: false - label: + runner-id: description: >- - Name of the unique label assigned to the runner. - The label is used to remove the runner from GitHub when the runner is not needed anymore. + Unique ID assigned to the runner. + The runner ID is used to remove the runner from GitHub when the runner is not needed anymore. This input is required if you use the 'stop' mode. required: false @@ -44,9 +44,11 @@ outputs: label: description: >- Name of the unique label assigned to the runner. - The label is used in two cases: - - to use as the input of 'runs-on' property for the following jobs; - - to remove the runner from GitHub when it is not needed anymore. + Use `label` as the input of `runs-on` workflow property to run subsequent jobs. + id: + description: >- + Unique ID assigned to the runner. + Use `id` as input of `runner-id` action property remove the runner from GitHub when it is not needed anymore. runs: using: node20 main: ./dist/index.js diff --git a/dist/index.js b/dist/index.js index f8e4d18..a673a94 100644 --- a/dist/index.js +++ b/dist/index.js @@ -49570,7 +49570,7 @@ class Config { jobSecret: core.getInput('job-secret'), backend: core.getInput('backend').toLowerCase(), profile: core.getInput('profile').toLowerCase(), - label: core.getInput('label') + runnerId: core.getInput('runner-id') } // the values of github.context.repo.owner and github.context.repo.repo are taken from @@ -49610,7 +49610,7 @@ class Config { ) } } else if (this.input.mode === 'stop') { - if (!this.input.label) { + if (!this.input.runnerId) { throw new Error( `Not all the required inputs are provided for the 'stop' mode` ) @@ -49681,7 +49681,7 @@ async function waitForRunnerRegistered(label) { core.info( `GitHub self-hosted runner ${runner.name} is registered and ready to use` ) - return + return runner.id } else { waitSeconds += retryIntervalSeconds core.info('Checking...') @@ -49814,11 +49814,11 @@ async function waitForInstance(taskId, taskName) { } } -async function terminateInstanceRequest(runnerName) { +async function terminateInstanceRequest(runnerId) { const url = config.input.slabUrl const payload = { - runner_name: runnerName, + runner_id: parseInt(runnerId, 10), action: 'terminate', sha: config.githubContext.sha, git_ref: config.githubContext.ref @@ -49828,7 +49828,7 @@ async function terminateInstanceRequest(runnerName) { const signature = getSignature(body) try { - core.info(`Request instance termination (runner: ${runnerName})`) + core.info(`Request instance termination (runner ID: ${runnerId})`) const response = await fetch(concat_path(url, 'job'), { method: 'POST', @@ -51854,8 +51854,9 @@ const config = __nccwpck_require__(4570) const core = __nccwpck_require__(2186) const { waitForRunnerRegistered } = __nccwpck_require__(6989) -function setOutput(label) { +function setOutput(label, id) { core.setOutput('label', label) + core.setOutput('id', id) } async function start() { @@ -51876,14 +51877,16 @@ async function start() { const instance_id = wait_instance_response.start.instance_id core.info(`${provider} instance started with ID: ${instance_id}`) - setOutput(start_instance_response.runner_name) + const runner_id = await waitForRunnerRegistered( + start_instance_response.runner_name + ) - await waitForRunnerRegistered(start_instance_response.runner_name) + setOutput(start_instance_response.runner_name, runner_id) } async function stop() { const stop_instance_response = await slab.terminateInstanceRequest( - config.input.label + config.input.runnerId ) await slab.waitForInstance(stop_instance_response.task_id, 'stop') diff --git a/src/config.js b/src/config.js index 6518b4a..3158444 100644 --- a/src/config.js +++ b/src/config.js @@ -10,7 +10,7 @@ class Config { jobSecret: core.getInput('job-secret'), backend: core.getInput('backend').toLowerCase(), profile: core.getInput('profile').toLowerCase(), - label: core.getInput('label') + runnerId: core.getInput('runner-id') } // the values of github.context.repo.owner and github.context.repo.repo are taken from @@ -50,7 +50,7 @@ class Config { ) } } else if (this.input.mode === 'stop') { - if (!this.input.label) { + if (!this.input.runnerId) { throw new Error( `Not all the required inputs are provided for the 'stop' mode` ) diff --git a/src/gh.js b/src/gh.js index ab50d9d..b285bdb 100644 --- a/src/gh.js +++ b/src/gh.js @@ -45,7 +45,7 @@ async function waitForRunnerRegistered(label) { core.info( `GitHub self-hosted runner ${runner.name} is registered and ready to use` ) - return + return runner.id } else { waitSeconds += retryIntervalSeconds core.info('Checking...') diff --git a/src/index.js b/src/index.js index cfd8bec..02f279f 100644 --- a/src/index.js +++ b/src/index.js @@ -3,8 +3,9 @@ const config = require('./config') const core = require('@actions/core') const { waitForRunnerRegistered } = require('./gh') -function setOutput(label) { +function setOutput(label, id) { core.setOutput('label', label) + core.setOutput('id', id) } async function start() { @@ -25,14 +26,16 @@ async function start() { const instance_id = wait_instance_response.start.instance_id core.info(`${provider} instance started with ID: ${instance_id}`) - setOutput(start_instance_response.runner_name) + const runner_id = await waitForRunnerRegistered( + start_instance_response.runner_name + ) - await waitForRunnerRegistered(start_instance_response.runner_name) + setOutput(start_instance_response.runner_name, runner_id) } async function stop() { const stop_instance_response = await slab.terminateInstanceRequest( - config.input.label + config.input.runnerId ) await slab.waitForInstance(stop_instance_response.task_id, 'stop') diff --git a/src/slab.js b/src/slab.js index 8c562c4..d127c20 100644 --- a/src/slab.js +++ b/src/slab.js @@ -106,11 +106,11 @@ async function waitForInstance(taskId, taskName) { } } -async function terminateInstanceRequest(runnerName) { +async function terminateInstanceRequest(runnerId) { const url = config.input.slabUrl const payload = { - runner_name: runnerName, + runner_id: parseInt(runnerId, 10), action: 'terminate', sha: config.githubContext.sha, git_ref: config.githubContext.ref @@ -120,7 +120,7 @@ async function terminateInstanceRequest(runnerName) { const signature = getSignature(body) try { - core.info(`Request instance termination (runner: ${runnerName})`) + core.info(`Request instance termination (runner ID: ${runnerId})`) const response = await fetch(concat_path(url, 'job'), { method: 'POST',