Skip to content

Commit ccb8f59

Browse files
committed
Refactor mr.bash to support adding and deleting multiple runners by --count option
1 parent 557d6fa commit ccb8f59

2 files changed

Lines changed: 100 additions & 56 deletions

File tree

README.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ Environment variables:
3232
Sub-commands:
3333
add Add one self-hosted runner on this host
3434
e.g. ./mr.bash add --org ORG --repo REPO --labels cloud:ali,region:cn-shanghai
35+
e.g. ./mr.bash add --org ORG --count 3
3536
del Delete one self-hosted runner on this host
3637
e.g. ./mr.bash del --user runner-1
38+
e.g. ./mr.bash del --org ORG --count 3
3739
list List all runners on this host
3840
e.g. ./mr.bash list
3941
download Download GitHub Actions Runner release tar to /tmp/
@@ -51,6 +53,7 @@ Options:
5153
--group Runner group for the runner
5254
--token Runner registration token, takes precedence over MR_GITHUB_PAT
5355
--dotenv The lines to set in runner's '.env' files
56+
--count The number to add or del, optional, defaults to 1 for add and all for del
5457
-h --help Show this help.
5558
```
5659

@@ -114,13 +117,10 @@ To setup multi-runners, you can simplify run following command multi times:
114117
./mr.bash add --org <ORG-NAME-1> --repo <REPO-NAME-1>
115118

116119
# 2 runners for repository `<ORG-NAME-1>/<REPO-NAME-2>`
117-
./mr.bash add --org <ORG-NAME-1> --repo <REPO-NAME-2>
118-
./mr.bash add --org <ORG-NAME-1> --repo <REPO-NAME-2>
120+
./mr.bash add --org <ORG-NAME-1> --repo <REPO-NAME-2> --count 2
119121

120122
# 3 runners for organization `<ORG-NAME-2>`
121-
./mr.bash add --org <ORG-NAME-2>
122-
./mr.bash add --org <ORG-NAME-2>
123-
./mr.bash add --org <ORG-NAME-2>
123+
./mr.bash add --org <ORG-NAME-2> --count 3
124124
```
125125

126126
This application will create one Linux local user for one runner via `useradd` command. The *Base Directory* of these users is read from `HOME` setting in your `/etc/default/useradd` file by default (typically `/home`). You can also set it in environment variable `MR_USER_BASE` to override system-wide default.
@@ -146,10 +146,15 @@ runner-5 537M running https://github.com/<ORG-NAME-2>
146146

147147
### Delete an existing runner
148148

149-
You can delete an existing runner by its local Linux username.
150-
151149
```bash
150+
# delete an existing runner by its local Linux username.
152151
./mr.bash del --user <runner-?>
152+
153+
# delete all runners for specific repository
154+
./mr.bash del --org <ORG-NAME-1> --repo <REPO-NAME-2>
155+
156+
# delete multi runners by `--count` options.
157+
./mr.bash del --org <ORG-NAME-2> --count 2
153158
```
154159

155160
### Specify runner in workflow file

mr.bash

Lines changed: 88 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -256,74 +256,106 @@ function mr::downloadRunner {
256256
# $6: extra labels, optional
257257
# $7: group, defaults to `default`
258258
# $8: lines to set in runner's '.env' files, optional
259+
# $9: count of runners, optional, defaults to 1
259260
# $?: 0 if successful and non-zero otherwise
260261
function mr::addRunner {
261-
local user="$1" enterprise="$2" org="$3" repo="$4" token="$5" extraLabels="$6" group="${7:-default}" dotenv="$8" tarpath=''
262+
local username="$1" enterprise="$2" org="$3" repo="$4" token="$5" extraLabels="$6" group="${7:-default}" dotenv="$8" tarpath=''
263+
local -i count="${9:-1}"
262264
str::allVarsNotEmpty enterprise org || return $?
263265

266+
[[ -n "$username" ]] && ((count > 1)) && {
267+
log::_ ERROR "Count must be 1 when username is given!"
268+
return 1
269+
}
270+
264271
[[ -z "$token" ]] && { token="$(mr::pat2token "$enterprise" "$org" "$repo")" || return $?; }
265272
tarpath="$(mr::downloadRunner)" || return $?
266-
user="$(mr::addUser "$user")" || return $?
267-
268-
local name="$user@$HOSTNAME"
269-
270-
local labels="controller:${MR_URL#https://},username:$user,hostname:$HOSTNAME"
271-
[[ -r /etc/os-release ]] && labels="$labels,os:$(source /etc/os-release && echo "$ID-$VERSION_ID")"
272-
[[ -n "$extraLabels" ]] && labels="$labels,$extraLabels"
273-
274-
local url=''
275-
if [[ -n "$enterprise" ]]; then
276-
url="$MR_GITHUB_BASEURL/enterprises/$enterprise"
277-
labels="$labels,$enterprise"
278-
elif [[ -z "$repo" ]]; then
279-
url="$MR_GITHUB_BASEURL/$org"
280-
labels="$labels,$org"
281-
else
282-
url="$MR_GITHUB_BASEURL/$org/$repo"
283-
labels="$labels,$org/$repo"
284-
fi
285273

286-
log::_ INFO "Adding runner into local user '$user' for $url"
287-
run::logFailed sudo su --login "$user" -- -eo pipefail <<-__
288-
mkdir -p runner/mr.d && cd runner/mr.d
289-
echo -n '$enterprise' > enterprise && echo -n '$org' > org && echo -n '$repo' > repo && echo -n '$url' > url
290-
echo -n '$name' > name && echo -n '$labels' > labels && echo -n '$tarpath' > tarpath
291-
cd .. && tar -xzf "$tarpath"
292-
echo "$dotenv" >> .env
293-
./config.sh --unattended --replace --url '$url' --token '$token' --name '$name' --labels '$labels' --runnergroup '$group'
294-
sudo ./svc.sh install '$user'
295-
if [[ "$(getenforce 2>/dev/null)" == "Enforcing" ]]; then
296-
chcon -t bin_t ./runsvc.sh # https://github.com/vbem/multi-runners/issues/9
274+
local commonLabels="controller:${MR_URL#https://},hostname:$HOSTNAME"
275+
commonLabels+=",createdtime:$(date --iso-8601=sec)"
276+
[[ -r /etc/os-release ]] && commonLabels+=",os:$(source /etc/os-release && echo "$ID-$VERSION_ID")"
277+
[[ -n "$extraLabels" ]] && commonLabels+=",$extraLabels"
278+
279+
for i in $(seq 1 "$count"); do
280+
user="$(mr::addUser "$username")" || return $?
281+
local name="$user@$HOSTNAME"
282+
local labels="$commonLabels,username:$user"
283+
local url=''
284+
if [[ -n "$enterprise" ]]; then
285+
url="$MR_GITHUB_BASEURL/enterprises/$enterprise"
286+
labels+=",$enterprise"
287+
elif [[ -z "$repo" ]]; then
288+
url="$MR_GITHUB_BASEURL/$org"
289+
labels+=",$org"
290+
else
291+
url="$MR_GITHUB_BASEURL/$org/$repo"
292+
labels+=",$org/$repo"
297293
fi
298-
sudo ./svc.sh start
294+
295+
log::_ INFO "Installing runner $i in local user '$user' for $url"
296+
run::logFailed sudo su --login "$user" -- -eo pipefail <<-__
297+
mkdir -p runner/mr.d && cd runner/mr.d
298+
echo -n '$enterprise' > enterprise && echo -n '$org' > org && echo -n '$repo' > repo && echo -n '$url' > url
299+
echo -n '$name' > name && echo -n '$labels' > labels && echo -n '$tarpath' > tarpath
300+
cd .. && tar -xzf "$tarpath"
301+
echo "$dotenv" >> .env
302+
./config.sh --unattended --replace --url '$url' --token '$token' --name '$name' --labels '$labels' --runnergroup '$group'
303+
sudo ./svc.sh install '$user'
304+
if [[ "$(getenforce 2>/dev/null)" == "Enforcing" ]]; then
305+
chcon -t bin_t ./runsvc.sh # https://github.com/vbem/multi-runners/issues/9
306+
fi
307+
sudo ./svc.sh start
299308
__
309+
log::failed $? "Failed installing runner $i in local user '$user' for $url!" || return $?
310+
done
300311
}
301312

302313
# Delete GitHub Actions Runner by local username
303-
# $1: username
314+
# $1: username, option
304315
# $2: enterprise, optional
305316
# $3: organization, optional
306317
# $4: repository, optional
307318
# $5: runner registration token, optional
319+
# $6: count of runners, optional, defaults to 0 (all)
308320
# $?: 0 if successful and non-zero otherwise
309321
function mr::delRunner {
310322
local user="$1" enterprise="$2" org="$3" repo="$4" token="$5"
311-
str::anyVarNotEmpty user || return $?
323+
local -i count="${6:-0}"
312324

313-
if [[ -z "$token" ]]; then
314-
[[ -z "$enterprise" ]] && enterprise="$(run::logFailed sudo -Hiu "$user" -- cat runner/mr.d/enterprise)"
315-
[[ -z "$org" ]] && org="$(run::logFailed sudo -Hiu "$user" -- cat runner/mr.d/org)"
316-
[[ -z "$repo" ]] && repo="$(run::logFailed sudo -Hiu "$user" -- cat runner/mr.d/repo)"
317-
token="$(mr::pat2token "$enterprise" "$org" "$repo")"
325+
local -a removals=()
326+
if [[ -n "$user" ]]; then
327+
removals+=("$user")
328+
else
329+
existing="$(run::logFailed getent group 'runners' | cut -d: -f4 | tr ',' '\n' | sort -g)" || return $?
330+
while read -r each; do
331+
[[ -z "$each" ]] && continue
332+
((count != 0)) && ((${#removals[@]} >= count)) && break
333+
each_enterprise="$(run::logFailed sudo -Hiu "$each" -- cat runner/mr.d/enterprise)"
334+
each_org="$(run::logFailed sudo -Hiu "$each" -- cat runner/mr.d/org)"
335+
each_repo="$(run::logFailed sudo -Hiu "$each" -- cat runner/mr.d/repo)"
336+
each_url="$(run::logFailed sudo -Hiu "$each" -- cat runner/mr.d/url)"
337+
if [[ "$enterprise" == "$each_enterprise" && "$org" == "$each_org" && "$repo" == "$each_repo" ]]; then
338+
log::_ DEBUG "Found local user '$each' to delete for $each_url"
339+
removals+=("$each")
340+
fi
341+
done <<<"$existing"
318342
fi
319343

320-
log::_ INFO "Deleting runner and local user '$user' with enterprise='$enterprise' org='$org' repo='$repo'"
321-
run::logFailed sudo su --login "$user" -- <<-__
322-
cd runner
323-
sudo ./svc.sh stop && sudo ./svc.sh uninstall
324-
./config.sh remove --token '$token'
344+
for user in "${removals[@]}"; do
345+
log::_ INFO "Deleting runner in local user '$user'"
346+
if [[ -z "$token" ]]; then
347+
enterprise="$(run::logFailed sudo -Hiu "$user" -- cat runner/mr.d/enterprise)"
348+
org="$(run::logFailed sudo -Hiu "$user" -- cat runner/mr.d/org)"
349+
repo="$(run::logFailed sudo -Hiu "$user" -- cat runner/mr.d/repo)"
350+
token="$(mr::pat2token "$enterprise" "$org" "$repo")"
351+
fi
352+
run::logFailed sudo su --login "$user" -- <<-__
353+
cd runner
354+
sudo ./svc.sh stop && sudo ./svc.sh uninstall
355+
./config.sh remove --token '$token'
325356
__
326-
run::log sudo userdel -rf "$user" || return $?
357+
run::log sudo userdel -rf "$user"
358+
done
327359
}
328360

329361
# List all existing runners
@@ -364,8 +396,10 @@ Environment variables:
364396
Sub-commands:
365397
add Add one self-hosted runner on this host
366398
e.g. ${BASH_SOURCE[0]} add --org ORG --repo REPO --labels cloud:ali,region:cn-shanghai
399+
e.g. ${BASH_SOURCE[0]} add --org ORG --count 3
367400
del Delete one self-hosted runner on this host
368401
e.g. ${BASH_SOURCE[0]} del --user runner-1
402+
e.g. ${BASH_SOURCE[0]} del --org ORG --count 3
369403
list List all runners on this host
370404
e.g. ${BASH_SOURCE[0]} list
371405
download Download GitHub Actions Runner release tar to /tmp/
@@ -383,6 +417,7 @@ Options:
383417
--group Runner group for the runner
384418
--token Runner registration token, takes precedence over MR_GITHUB_PAT
385419
--dotenv The lines to set in runner's '.env' files
420+
--count The number to add or del, optional, defaults to 1 for add and all for del
386421
-h --help Show this help.
387422
"
388423
declare -rg HELP
@@ -391,10 +426,10 @@ declare -rg HELP
391426
# $?: 0 if successful and non-zero otherwise
392427
function mr::main {
393428
local getopt_output='' subCmd=''
394-
local org='' repo='' user='' labels='' token='' group='' dotenv=''
429+
local org='' repo='' user='' labels='' token='' group='' dotenv='' count=''
395430

396431
# parse options into variables
397-
getopt_output="$(getopt -o h -l help,enterprise:,org:,repo:,user:,labels:,token:,group:,dotenv: -n "$FILE_THIS" -- "$@")"
432+
getopt_output="$(getopt -o h -l help,enterprise:,org:,repo:,user:,labels:,token:,group:,dotenv:,count: -n "$FILE_THIS" -- "$@")"
398433
log::failed $? "getopt failed!" || return $?
399434
eval set -- "$getopt_output"
400435

@@ -433,6 +468,10 @@ function mr::main {
433468
dotenv+="$2"$'\n'
434469
shift 2
435470
;;
471+
--count)
472+
count="$2"
473+
shift 2
474+
;;
436475
--)
437476
shift
438477
break
@@ -448,8 +487,8 @@ function mr::main {
448487
subCmd="$1"
449488
shift
450489
case "$subCmd" in
451-
add) mr::addRunner "$user" "$enterprise" "$org" "$repo" "$token" "$labels" "$group" "$dotenv" ;;
452-
del) mr::delRunner "$user" "$enterprise" "$org" "$repo" "$token" ;;
490+
add) mr::addRunner "$user" "$enterprise" "$org" "$repo" "$token" "$labels" "$group" "$dotenv" "$count" ;;
491+
del) mr::delRunner "$user" "$enterprise" "$org" "$repo" "$token" "$count" ;;
453492
list) mr::listRunners ;;
454493
status) mr::statusRunner "$user" ;;
455494
download) mr::downloadRunner ;;

0 commit comments

Comments
 (0)