-
-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add monitoring endpoint for oldest task in a given status
- Loading branch information
Showing
6 changed files
with
190 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
from routes import API_PATH | ||
from routes.base import BaseBlueprint | ||
from routes.status.status import StatusMonitorRoute # , StatusFooRoute | ||
|
||
|
||
class Blueprint(BaseBlueprint): | ||
def __init__(self): | ||
super().__init__("status", __name__, url_prefix=f"{API_PATH}/status") | ||
|
||
self.register_route(StatusMonitorRoute()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import datetime | ||
import logging | ||
|
||
import sqlalchemy as sa | ||
import sqlalchemy.orm as so | ||
from flask import request | ||
|
||
import db.models as dbm | ||
from db import dbsession | ||
from routes.base import BaseRoute | ||
from routes.errors import BadRequest | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class StatusMonitorRoute(BaseRoute): | ||
rule = "/<string:monitor_name>" | ||
name = "status" | ||
methods = ["GET"] | ||
|
||
def oldest_task_older_than(self, session: so.Session): | ||
request_args = request.args.to_dict() | ||
|
||
if not request_args.get("threshold_secs"): | ||
raise BadRequest("threshold_secs query parameter is mandatory") | ||
|
||
if not request_args.get("status"): | ||
raise BadRequest("status query parameter is mandatory") | ||
|
||
threshold_secs = int(request_args.get("threshold_secs")) | ||
status = request_args.get("status") | ||
|
||
now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) | ||
min_date: datetime.datetime = min( | ||
[now] | ||
+ session.execute( | ||
( | ||
sa.select( | ||
dbm.Task.timestamp[status], | ||
).filter(dbm.Task.status == status) | ||
) | ||
) | ||
.scalars() | ||
.all(), | ||
) | ||
|
||
return ( | ||
f"oldest_task_older_than for {status} and {threshold_secs}s: " | ||
f"{'KO' if (now-min_date).total_seconds() > threshold_secs else 'OK'}" | ||
) | ||
|
||
@dbsession | ||
def get(self, monitor_name: str, session: so.Session): | ||
"""Get Zimfarm status for a given monitor""" | ||
|
||
handlers = { | ||
"oldest_task_older_than": self.oldest_task_older_than, | ||
} | ||
|
||
if monitor_name not in handlers: | ||
raise BadRequest( | ||
f"Monitor '{monitor_name}' is not supported. Supported " | ||
f"monitors: {','.join(handlers.keys())}" | ||
) | ||
|
||
return handlers[monitor_name](session) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
dispatcher/backend/src/tests/integration/routes/status/test_status.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import pytest | ||
|
||
|
||
class TestStatusGet: | ||
url = "/status/" | ||
|
||
@pytest.mark.parametrize( | ||
"query,expected_error_part", | ||
[ | ||
pytest.param( | ||
"unknown_monitor?status=scraper_started&threshold_secs=5", | ||
"Monitor 'unknown_monitor' is not supported", | ||
id="unknown_monitor", | ||
), | ||
pytest.param( | ||
"oldest_task_older_than?status=scraper_started", | ||
"threshold_secs query parameter is mandatory", | ||
id="threshold_secs_missing", | ||
), | ||
pytest.param( | ||
"oldest_task_older_than?threshold_secs=5", | ||
"status query parameter is mandatory", | ||
id="status_missing", | ||
), | ||
], | ||
) | ||
def test_status_bad_queries(self, client, query, expected_error_part): | ||
headers = {"Content-Type": "application/json"} | ||
response = client.get( | ||
self.url + query, | ||
headers=headers, | ||
) | ||
assert response.status_code == 400 | ||
response = response.json | ||
assert "error" in response | ||
assert expected_error_part in response["error"] | ||
|
||
@pytest.mark.parametrize( | ||
"query,expected_reponse", | ||
[ | ||
pytest.param( | ||
"oldest_task_older_than?status=scraper_started&threshold_secs=500", | ||
"oldest_task_older_than for scraper_started and 500s: OK", | ||
id="oldest_task_older_than_ok", | ||
), | ||
pytest.param( | ||
"oldest_task_older_than?status=scraper_started&threshold_secs=5", | ||
"oldest_task_older_than for scraper_started and 5s: KO", | ||
id="oldest_task_older_than_ko", | ||
), | ||
], | ||
) | ||
def test_status_normal_queries(self, client, tasks, query, expected_reponse): | ||
headers = {"Content-Type": "application/json"} | ||
response = client.get( | ||
self.url + "oldest_task_older_than?status=scraper_started&threshold_secs=5", | ||
headers=headers, | ||
) | ||
assert response.status_code == 200 | ||
assert response.text == "oldest_task_older_than for scraper_started and 5s: KO" |