Skip to content

Commit 99a4199

Browse files
run user-defined functionality in job_started and job_completed hooks (#649)
Adds the ability to inject bash into the job started and job completed hook through the runner group in the runner-manager.yaml config Fixes: #577
1 parent 77cdd70 commit 99a4199

File tree

6 files changed

+44
-0
lines changed

6 files changed

+44
-0
lines changed

runner_manager/bin/startup.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,15 @@ function job_started {
5454
sleep 5
5555
done
5656
fi
57+
# Run bash injected through runner group config
58+
bash /opt/runner/job_started_script.sh
5759
echo "Done"
5860
}
5961

6062
function job_completed {
6163
# This function is called when the job is completed
64+
# Run bash injected through runner group config
65+
bash /opt/runner/job_completed_script.sh
6266
echo "Job completed"
6367

6468
}
@@ -126,6 +130,14 @@ function setup_runner {
126130
sudo -H -u actions bash -c "nohup /home/actions/actions-runner/run.sh --jitconfig \"${JIT_CONFIG}\" 2>/home/actions/actions-runner/logs &"
127131
fi
128132

133+
cat <<EOF >/opt/runner/job_started_script.sh
134+
${RUNNER_JOB_STARTED_SCRIPT}
135+
EOF
136+
137+
cat <<EOF >/opt/runner/job_completed_script.sh
138+
${RUNNER_JOB_COMPLETED_SCRIPT}
139+
EOF
140+
129141
}
130142

131143
function install_docker {

runner_manager/models/backend.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class RunnerEnv(BaseModel):
4242
RUNNER_GROUP: Optional[str] = None
4343
RUNNER_REDHAT_USERNAME: Optional[str] = None
4444
RUNNER_REDHAT_PASSWORD: Optional[str] = None
45+
RUNNER_JOB_STARTED_SCRIPT: Optional[str] = ""
46+
RUNNER_JOB_COMPLETED_SCRIPT: Optional[str] = ""
4547

4648

4749
class InstanceConfig(BaseSettings):
@@ -63,6 +65,8 @@ def runner_env(self, runner: Runner) -> RunnerEnv:
6365
RUNNER_REDHAT_PASSWORD=(
6466
self.redhat_password if self.redhat_password else None
6567
),
68+
RUNNER_JOB_STARTED_SCRIPT=runner.job_started_script,
69+
RUNNER_JOB_COMPLETED_SCRIPT=runner.job_completed_script,
6670
)
6771

6872
def template_startup(self, runner: Runner) -> str:

runner_manager/models/runner.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ class Runner(BaseModel):
7070
organization: str = Field(default=None, index=True, description="Organization name")
7171
created_at: Optional[datetime]
7272
started_at: Optional[datetime]
73+
job_started_script: Optional[str] = ""
74+
job_completed_script: Optional[str] = ""
7375

7476
def __str__(self):
7577
return f"{self.name} (status: {self.status}, busy: {self.busy})"

runner_manager/models/runner_group.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ class BaseRunnerGroup(PydanticBaseModel):
4444
max: Optional[int] = Field(ge=1, default=20)
4545
min: Optional[int] = Field(ge=0, default=0)
4646
labels: List[str]
47+
job_started_script: Optional[str] = ""
48+
job_completed_script: Optional[str] = ""
4749

4850
backend: Annotated[
4951
Union[BaseBackend, DockerBackend, GCPBackend, AWSBackend, VsphereBackend],
@@ -62,6 +64,8 @@ class RunnerGroup(BaseModel, BaseRunnerGroup):
6264
queued: int = Field(default=0, ge=0)
6365
os: str = Field(default="linux")
6466
arch: str = Field(default="x64")
67+
job_started_script: Optional[str] = Field(default="")
68+
job_completed_script: Optional[str] = Field(default="")
6569

6670
def __str__(self) -> str:
6771
return (
@@ -149,6 +153,8 @@ def create_runner(self, github: GitHub) -> Runner | None:
149153
labels=self.runner_labels,
150154
manager=self.manager,
151155
download_url=self.download_url(github),
156+
job_started_script=self.job_started_script,
157+
job_completed_script=self.job_completed_script,
152158
)
153159
runner.save()
154160
runner.generate_jit_config(github)

tests/unit/backend/test_base.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,12 @@ def test_setup_redhat_credentials(runner, monkeypatch):
6565
template = runner_group.backend.instance_config.template_startup(runner)
6666
assert 'REDHAT_USERNAME="username"' in template
6767
assert 'REDHAT_PASSWORD="password"' in template
68+
69+
70+
def test_job_scripts(runner_group, runner):
71+
runner.job_started_script = 'echo "job started"'
72+
runner.job_completed_script = 'echo "job completed"'
73+
# Ensure that the template is rendered correctly
74+
template = runner_group.backend.instance_config.template_startup(runner)
75+
assert 'echo "job started"' in template
76+
assert 'echo "job completed"' in template

tests/unit/models/test_runner_group.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ def test_create_runner_from_group(runner_group: RunnerGroup, github: GitHub):
5959
assert runner.encoded_jit_config is not None
6060
assert runner.created_at is not None
6161
assert runner.created_at.tzinfo == timezone.utc
62+
assert runner.job_started_script == ""
63+
assert runner.job_completed_script == ""
6264

6365

6466
def test_list_runners_from_group(runner_group: RunnerGroup, github: GitHub):
@@ -163,6 +165,15 @@ def test_runner_group_name():
163165
)
164166

165167

168+
def test_job_scripts(runner_group: RunnerGroup, github: GitHub):
169+
runner_group.job_started_script = "Hello"
170+
runner_group.min = 1
171+
runner_group.save()
172+
runner = runner_group.create_runner(github)
173+
assert runner.job_completed_script == ""
174+
assert runner.job_started_script == runner_group.job_started_script
175+
176+
166177
def test_need_new_runner(runner_group: RunnerGroup, github: GitHub):
167178
runner_group.max = 2
168179
runner_group.min = 1

0 commit comments

Comments
 (0)