Skip to content

Commit

Permalink
feat!: unregister runner using its unique id
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
soonum committed Dec 3, 2024
1 parent b599158 commit e8d3b74
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 44 deletions.
9 changes: 6 additions & 3 deletions .github/workflows/test_spawn_terminate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/test_start_stop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
32 changes: 18 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand All @@ -62,6 +63,9 @@ jobs:
profile: cpu-test

do-the-job:
needs: start-runner
runs-on: ${{ needs.start-runner.outputs.label }}
steps:
# ... #

stop-runner:
Expand All @@ -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
Expand Down
14 changes: 8 additions & 6 deletions action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,22 @@ 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

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
23 changes: 13 additions & 10 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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`
)
Expand Down
2 changes: 1 addition & 1 deletion src/gh.js
Original file line number Diff line number Diff line change
Expand Up @@ -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...')
Expand Down
11 changes: 7 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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')

Expand Down
6 changes: 3 additions & 3 deletions src/slab.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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',
Expand Down

0 comments on commit e8d3b74

Please sign in to comment.