Skip to content
This repository was archived by the owner on Mar 13, 2026. It is now read-only.

Commit b3bd49c

Browse files
Merge pull request #23 from midokura/Add-job-event-handler
Add job event handler
2 parents e3f267b + 595ec09 commit b3bd49c

8 files changed

Lines changed: 140 additions & 22 deletions

File tree

.github/workflows/tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ jobs:
2929
flake8
3030
- name: Test with pytest
3131
run: |
32-
pytest --cov=src
32+
pytest --cov=src tests/

.pre-commit-config.yaml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v4.4.0
3+
rev: v4.6.0
44
hooks:
55
- id: trailing-whitespace
66
- id: check-docstring-first
@@ -12,16 +12,20 @@ repos:
1212
- id: debug-statements
1313
- id: end-of-file-fixer
1414
- repo: https://github.com/myint/docformatter
15-
rev: v1.5.1
15+
rev: v1.7.5
1616
hooks:
1717
- id: docformatter
1818
args: [--in-place]
1919
- repo: https://github.com/asottile/pyupgrade
20-
rev: v3.3.1
20+
rev: v3.15.2
2121
hooks:
2222
- id: pyupgrade
2323
args: [--py38-plus]
2424
- repo: https://github.com/PyCQA/flake8
25-
rev: 6.0.0
25+
rev: 7.0.0
2626
hooks:
2727
- id: flake8
28+
- repo: https://github.com/psf/black
29+
rev: 24.4.2
30+
hooks:
31+
- id: black

src/app.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from const import GithubHeaders, LOGGING_CONFIG
1111
from github import GithubJob
12+
from jobs import JobEventsHandler
1213
from utils import dict_to_logfmt
1314

1415
dictConfig(LOGGING_CONFIG)
@@ -23,9 +24,10 @@
2324
if hasattr(logging, loglevel_flask):
2425
loglevel_flask = getattr(logging, loglevel_flask)
2526
log.setLevel(loglevel_flask)
26-
logging.getLogger('apscheduler.executors.default').setLevel(logging.WARNING)
27+
logging.getLogger("apscheduler.executors.default").setLevel(logging.WARNING)
2728

2829
jobs = dict()
30+
job_handler = JobEventsHandler()
2931

3032

3133
# check all calls are valid
@@ -50,7 +52,10 @@ def validate_origin_github():
5052

5153

5254
def process_workflow_job():
53-
job = GithubJob(request.get_json())
55+
event = request.get_json()
56+
job_handler.process_event(event)
57+
58+
job = GithubJob(event)
5459

5560
context_details = {
5661
"action": job.action,
@@ -77,9 +82,7 @@ def process_workflow_job():
7782
app.logger.error(f"Job {job.id} was in progress before being queued")
7883
del jobs[job.id]
7984
else:
80-
time_to_start = (
81-
job.time_start - job_requested.time_start
82-
).seconds
85+
time_to_start = (job.time_start - job_requested.time_start).seconds
8386

8487
context_details = {
8588
**context_details,
@@ -100,9 +103,7 @@ def process_workflow_job():
100103
app.logger.warning(f"Job {job.id} is {job.action} but not stored!")
101104
time_to_finish = 0
102105
else:
103-
time_to_finish = (
104-
job.time_completed - job.time_start
105-
).seconds
106+
time_to_finish = (job.time_completed - job.time_start).seconds
106107
# delete from memory
107108
del jobs[job.id]
108109

@@ -124,7 +125,7 @@ def process_workflow_job():
124125
return True
125126

126127

127-
@scheduler.task('interval', id='monitor_queued', seconds=30)
128+
@scheduler.task("interval", id="monitor_queued", seconds=30)
128129
def monitor_queued_jobs():
129130
"""Return the job that has been queued and not starting for long time."""
130131
app.logger.debug("Starting monitor_queued_jobs")

src/github.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from utils import parse_datetime
22

33

4-
class GithubJob():
4+
class GithubJob:
55
def __init__(self, json_body: str):
66
self.data = json_body
77

src/jobs.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from github import GithubJob
2+
3+
4+
class Job:
5+
def __init__(self, github_job: GithubJob) -> None:
6+
self.github_job = github_job
7+
8+
9+
class JobEventsHandler:
10+
def __init__(self) -> None:
11+
self.queued = dict()
12+
self.in_progress = dict()
13+
14+
def process_event(self, event: dict):
15+
status = event["action"]
16+
17+
if status == "queued":
18+
self._process_queued_event(event)
19+
20+
elif status == "in_progress":
21+
self._process_in_progress_event(event)
22+
23+
elif status == "completed":
24+
self._process_completed_event(event)
25+
26+
else:
27+
pass
28+
29+
def _get_event_job_id(self, event: dict):
30+
return event["workflow_job"]["id"]
31+
32+
def _create_job(self, githubJob: GithubJob) -> Job:
33+
return Job(github_job=githubJob)
34+
35+
def _process_queued_event(self, event: dict):
36+
job = self._create_job(GithubJob(event))
37+
self.queued[self._get_event_job_id(event)] = job
38+
39+
def _process_in_progress_event(self, event: dict):
40+
job_id = self._get_event_job_id(event)
41+
job = self.queued.pop(job_id, None)
42+
43+
if not job:
44+
job = self._create_job(GithubJob(event))
45+
else:
46+
# Update github job event from job
47+
job.github_job = GithubJob(event)
48+
49+
self.in_progress[job_id] = job
50+
51+
# TODO send final time in queue
52+
53+
def _process_completed_event(self, event: dict):
54+
job_id = self._get_event_job_id(event)
55+
self.in_progress.pop(job_id, None)
56+
57+
# TODO send final time in progress

tests/requirements.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
Flask
2-
Flask-APScheduler==1.13.1
3-
pytest
4-
pytest-cov
5-
flake8
1+
-e .
2+
pytest==8.2.0
3+
pytest-cov==5.0.0
4+
flake8==7.0.0
5+
black==24.4.2

tests/test_jobs.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import pytest
2+
3+
from unittest.mock import Mock
4+
5+
from jobs import JobEventsHandler
6+
7+
8+
@pytest.fixture
9+
def new_job_event():
10+
return {"workflow_job": {"id": "workflow_id"}, "action": "queued"}
11+
12+
13+
@pytest.fixture
14+
def in_progress_job_event():
15+
return {"workflow_job": {"id": "workflow_id"}, "action": "in_progress"}
16+
17+
18+
@pytest.fixture
19+
def completed_job_event():
20+
return {"workflow_job": {"id": "workflow_id"}, "action": "completed"}
21+
22+
23+
def test_new_job(new_job_event):
24+
handler = JobEventsHandler()
25+
26+
handler.process_event(new_job_event)
27+
28+
assert handler.queued.get("workflow_id")
29+
30+
31+
def test_in_progress_job(in_progress_job_event):
32+
handler = JobEventsHandler()
33+
job = Mock()
34+
handler.queued["workflow_id"] = job
35+
36+
handler.process_event(in_progress_job_event)
37+
38+
assert not handler.queued.get("workflow_id")
39+
assert handler.in_progress.get("workflow_id") == job
40+
41+
42+
def test_unprocessed_in_progress_job(in_progress_job_event):
43+
handler = JobEventsHandler()
44+
handler.process_event(in_progress_job_event)
45+
46+
assert handler.in_progress.get("workflow_id")
47+
48+
49+
def test_completed_job(completed_job_event):
50+
handler = JobEventsHandler()
51+
handler.in_progress["workflow_id"] = Mock()
52+
53+
handler.process_event(completed_job_event)
54+
55+
assert not handler.queued.get("workflow_id")
56+
assert not handler.in_progress.get("workflow_id")

tests/tests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def test_method_not_allowed(client):
4141

4242

4343
def test_headers_not_correct(client, caplog):
44-
response = client.post(HOOK_ENDPOINT, headers={'User-Agent': 'foo'})
44+
response = client.post(HOOK_ENDPOINT, headers={"User-Agent": "foo"})
4545
assert response.status_code == 401
4646
assert caplog.messages == [
4747
"User-Agent is foo",
@@ -197,5 +197,5 @@ def test_line_break_in_job_name(client, caplog):
197197
assert caplog.messages == [
198198
'action=queued repository=foo/foo branch=new-feature-branch job_id=6 run_id=10 job_name="Build and push '
199199
'images (actions-runner-dind, NPROC=2 , runner-images/devops/actions-runner-dind, l..."'
200-
' workflow=CI requestor=testerbot'
200+
" workflow=CI requestor=testerbot"
201201
]

0 commit comments

Comments
 (0)