Skip to content

Commit

Permalink
Self-hosted runner cleanup: use Ubuntu instead of azure/cli action
Browse files Browse the repository at this point in the history
The `azure/cli` GitHub Action runs in a Docker container with Alpine or Azure Linux, meaning that many tools like `node` and `gh` (which we rely on) aren't available.

Since the Azure CLI is available in the GitHub-hosted `ubuntu-latest` image anyway, let's leverage that instead. In the end, it has all the other tools we need as well.

Ref: https://github.com/actions/runner-images/blob/ce2a3e8e67858dc8c3dd77aca6b29e2956d934ed/images/ubuntu/Ubuntu2404-Readme.md?plain=1#L105
Signed-off-by: Dennis Ameling <[email protected]>
  • Loading branch information
dennisameling committed Sep 22, 2024
1 parent 295d792 commit 26e02ca
Showing 1 changed file with 64 additions and 44 deletions.
108 changes: 64 additions & 44 deletions .github/workflows/cleanup-self-hosted-runners.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,58 +13,78 @@ on:
# az ad sp create-for-rbac --name "{YOUR_DESCRIPTIVE_NAME_HERE}" --role contributor \
# --scopes /subscriptions/{SUBSCRIPTION_ID_HERE}/resourceGroups/{RESOURCE_GROUP_HERE} \
# --sdk-auth
# AZURE_RESOURCE_GROUP - Resource group to create the runner(s) in
# AZURE_RESOURCE_GROUP - Resource group to find the runner(s) in. It's recommended to set up a resource
# group specifically for self-hosted Actions Runners.
jobs:
delete-runner:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v2
- name: Process Azure credentials
uses: actions/github-script@v7
env:
AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }}
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
script: |
if (!process.env.AZURE_CREDENTIALS) {
core.setFailed('The AZURE_CREDENTIALS secret is required.')
process.exit(1)
}
const azureCredentials = JSON.parse(process.env.AZURE_CREDENTIALS)
const {clientId, clientSecret, tenantId, subscriptionId} = azureCredentials
core.setSecret(clientId)
core.exportVariable('AZURE_CLIENT_ID', clientId)
core.setSecret(clientSecret)
core.exportVariable('AZURE_CLIENT_SECRET', clientSecret)
core.setSecret(tenantId)
core.exportVariable('AZURE_TENANT_ID', tenantId)
core.setSecret(subscriptionId)
core.exportVariable('AZURE_SUBSCRIPTION_ID', subscriptionId)
- name: Discover VMs to delete
uses: azure/CLI@v2
env:
GH_APP_ID: ${{ secrets.GH_APP_ID }}
GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
with:
# Stick to 2.63.0 until jq is added to 2.64.0+ https://github.com/Azure/azure-cli/issues/29830
azcliversion: 2.63.0
inlineScript: |
active_vms=$(az vm list -g ${{ secrets.AZURE_RESOURCE_GROUP }} | jq -c '.[] | {name,timeCreated}')
current_time=$(date +%s)
one_hour_ago=$(($current_time - 3600))
GH_APP_ID: ${{ secrets.GH_APP_ID }}
GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
run: |
az login --service-principal -u ${{ env.AZURE_CLIENT_ID }} -p ${{ env.AZURE_CLIENT_SECRET }} --tenant ${{ env.AZURE_TENANT_ID }}
az account set --subscription ${{ env.AZURE_SUBSCRIPTION_ID }}
active_vms=$(az vm list -g ${{ secrets.AZURE_RESOURCE_GROUP }} | jq -c '.[] | {name,timeCreated}')
current_time=$(date +%s)
one_hour_ago=$(($current_time - 3600))
if [ -z "$active_vms" ]; then
echo "No active VMs found, nothing to do."
exit 0
else
echo "Found these active VMs:"
echo $active_vms
fi
if [ -z "$active_vms" ]; then
echo "No active VMs found, nothing to do."
exit 0
else
echo "Found these active VMs:"
echo $active_vms
fi
for active_vm in ${active_vms[@]}; do
vm_name=$(echo $active_vm | jq '.name')
# Use jq to extract and format the date-time string
vm_creation_time_string="$(echo $active_vm |
jq -r '.timeCreated | sub("\\.[0-9]+[+-][0-9]+:[0-9]+$"; "") | sub("T"; " ")')"
vm_creation_time=$(TZ=UTC date -d "$vm_creation_time_string" +%s)
for active_vm in ${active_vms[@]}; do
vm_name=$(echo $active_vm | jq '.name')
# Use jq to extract and format the date-time string
vm_creation_time_string="$(echo $active_vm |
jq -r '.timeCreated | sub("\\.[0-9]+[+-][0-9]+:[0-9]+$"; "") | sub("T"; " ")')"
vm_creation_time=$(TZ=UTC date -d "$vm_creation_time_string" +%s)
if [ "$one_hour_ago" -lt "$vm_creation_time" ]; then
echo "::notice::The VM ${vm_name} was created less then 1 hour ago and shouldn't be deleted yet. Skipping."
elif test true = "$(if test ! -f .cli-authenticated; then
./gh-cli-auth-as-app.sh &&
>.cli-authenticated # only authenticate once
fi &&
gh api repos/$GITHUB_REPOSITORY/actions/runners \
--jq '.runners[] | select(.name == "'$vm_name'") | .busy')"; then
echo "::notice::The VM ${vm_name} is still busy."
else
echo "::warning::The VM ${vm_name} was created more than 3 hours ago and wasn't deleted. Let's do that now."
az vm delete -n "$vm_name" -g ${{ secrets.AZURE_RESOURCE_GROUP }} --yes
az network nsg delete -n "$vm_name"-nsg -g ${{ secrets.AZURE_RESOURCE_GROUP }}
az network vnet delete -n "$vm_name"-vnet -g ${{ secrets.AZURE_RESOURCE_GROUP }}
az network public-ip delete -n "$vm_name"-ip -g ${{ secrets.AZURE_RESOURCE_GROUP }}
fi
done
if [ "$one_hour_ago" -lt "$vm_creation_time" ]; then
echo "::notice::The VM ${vm_name} was created less then 1 hour ago and shouldn't be deleted yet. Skipping."
elif test true = "$(if test ! -f .cli-authenticated; then
./gh-cli-auth-as-app.sh &&
>.cli-authenticated # only authenticate once
fi &&
gh api repos/$GITHUB_REPOSITORY/actions/runners \
--jq '.runners[] | select(.name == "'$vm_name'") | .busy')"; then
echo "::notice::The VM ${vm_name} is still busy."
else
echo "::warning::The VM ${vm_name} was created more than 3 hours ago and wasn't deleted. Let's do that now."
az vm delete -n "$vm_name" -g ${{ secrets.AZURE_RESOURCE_GROUP }} --yes
az network nsg delete -n "$vm_name"-nsg -g ${{ secrets.AZURE_RESOURCE_GROUP }}
az network vnet delete -n "$vm_name"-vnet -g ${{ secrets.AZURE_RESOURCE_GROUP }}
az network public-ip delete -n "$vm_name"-ip -g ${{ secrets.AZURE_RESOURCE_GROUP }}
fi
done

0 comments on commit 26e02ca

Please sign in to comment.