Skip to content

Commit

Permalink
Merge pull request #52 from kiwix/health-check
Browse files Browse the repository at this point in the history
Health check
  • Loading branch information
rgaudin authored Oct 15, 2024
2 parents 42e69e8 + 63a3451 commit 98e566f
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 19 deletions.
3 changes: 2 additions & 1 deletion backend/src/mirrors_qa_backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from fastapi import FastAPI

from mirrors_qa_backend.db import initialize_mirrors, upgrade_db_schema
from mirrors_qa_backend.routes import auth, tests, worker
from mirrors_qa_backend.routes import auth, health, tests, worker


@asynccontextmanager
Expand All @@ -19,6 +19,7 @@ def create_app(*, debug: bool = True):
app.include_router(router=tests.router)
app.include_router(router=auth.router)
app.include_router(router=worker.router)
app.include_router(router=health.router)

return app

Expand Down
7 changes: 4 additions & 3 deletions backend/src/mirrors_qa_backend/routes/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
import datetime
from typing import Annotated

from fastapi import APIRouter, Header
from fastapi import APIRouter, Depends, Header
from sqlalchemy.orm import Session

from mirrors_qa_backend import logger
from mirrors_qa_backend.cryptography import verify_signed_message
from mirrors_qa_backend.db import gen_dbsession
from mirrors_qa_backend.db.exceptions import RecordDoesNotExistError
from mirrors_qa_backend.db.worker import get_worker
from mirrors_qa_backend.exceptions import PEMPublicKeyLoadError
from mirrors_qa_backend.routes.dependencies import DbSession
from mirrors_qa_backend.routes.http_errors import (
BadRequestError,
ForbiddenError,
Expand All @@ -25,7 +26,7 @@

@router.post("/authenticate")
def authenticate_worker(
session: DbSession,
session: Annotated[Session, Depends(gen_dbsession)],
x_sshauth_message: Annotated[
str,
Header(description="message (format): worker_id:timestamp (UTC ISO)"),
Expand Down
12 changes: 6 additions & 6 deletions backend/src/mirrors_qa_backend/routes/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,12 @@
from mirrors_qa_backend.routes.http_errors import NotFoundError, UnauthorizedError
from mirrors_qa_backend.settings.api import APISettings

DbSession = Annotated[Session, Depends(gen_dbsession)]

security = HTTPBearer(description="Access Token")
AuthorizationCredentials = Annotated[HTTPAuthorizationCredentials, Depends(security)]


def get_current_worker(
session: DbSession,
authorization: AuthorizationCredentials,
session: Annotated[Session, Depends(gen_dbsession)],
authorization: Annotated[HTTPAuthorizationCredentials, Depends(security)],
) -> models.Worker:
token = authorization.credentials
try:
Expand All @@ -51,7 +48,10 @@ def get_current_worker(
CurrentWorker = Annotated[models.Worker, Depends(get_current_worker)]


def get_test(session: DbSession, test_id: Annotated[UUID4, Path()]) -> models.Test:
def get_test(
session: Annotated[Session, Depends(gen_dbsession)],
test_id: Annotated[UUID4, Path()],
) -> models.Test:
"""Fetches the test specified in the request."""
try:
test = db_get_test(session, test_id)
Expand Down
40 changes: 40 additions & 0 deletions backend/src/mirrors_qa_backend/routes/health.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import datetime
from typing import Annotated

from fastapi import APIRouter, Depends
from fastapi import status as status_codes
from sqlalchemy import select
from sqlalchemy.orm import Session

from mirrors_qa_backend.db import count_from_stmt, gen_dbsession
from mirrors_qa_backend.db.models import Test
from mirrors_qa_backend.enums import StatusEnum
from mirrors_qa_backend.schemas import HealthStatus
from mirrors_qa_backend.settings.api import APISettings

router = APIRouter(prefix="/health-check", tags=["health-check"])


@router.get(
"",
status_code=status_codes.HTTP_200_OK,
responses={
status_codes.HTTP_200_OK: {
"description": "Status of monitored parts of mirrors-qa"
}
},
)
def heatlh_status(session: Annotated[Session, Depends(gen_dbsession)]) -> HealthStatus:
test_received_after = datetime.datetime.now() - datetime.timedelta(
seconds=APISettings.UNHEALTHY_NO_TESTS_DURATION_SECONDS
)

nb_recent_tests_received = count_from_stmt(
session,
select(Test).where(
Test.status == StatusEnum.SUCCEEDED,
(Test.started_on >= test_received_after),
),
)

return HealthStatus(receiving_tests=nb_recent_tests_received > 0)
7 changes: 4 additions & 3 deletions backend/src/mirrors_qa_backend/routes/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

from fastapi import APIRouter, Depends, Query
from fastapi import status as status_codes
from sqlalchemy.orm import Session

from mirrors_qa_backend import schemas
from mirrors_qa_backend.db import gen_dbsession
from mirrors_qa_backend.db.tests import list_tests as db_list_tests
from mirrors_qa_backend.db.tests import update_test as update_test_model
from mirrors_qa_backend.db.worker import update_worker_last_seen
from mirrors_qa_backend.enums import SortDirectionEnum, StatusEnum, TestSortColumnEnum
from mirrors_qa_backend.routes.dependencies import (
CurrentWorker,
DbSession,
RetrievedTest,
verify_worker_owns_test,
)
Expand All @@ -29,7 +30,7 @@
},
)
def list_tests(
session: DbSession,
session: Annotated[Session, Depends(gen_dbsession)],
worker_id: Annotated[str | None, Query()] = None,
country_code: Annotated[str | None, Query(min_length=2, max_length=2)] = None,
status: Annotated[list[StatusEnum] | None, Query()] = None,
Expand Down Expand Up @@ -81,7 +82,7 @@ def get_test(test: RetrievedTest) -> Test:
dependencies=[Depends(verify_worker_owns_test)],
)
def update_test(
session: DbSession,
session: Annotated[Session, Depends(gen_dbsession)],
current_worker: CurrentWorker,
test: RetrievedTest,
update: schemas.UpdateTestModel,
Expand Down
14 changes: 10 additions & 4 deletions backend/src/mirrors_qa_backend/routes/worker.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
from typing import Annotated

import pycountry
from fastapi import APIRouter
from fastapi import APIRouter, Depends
from fastapi import status as status_codes
from sqlalchemy.orm import Session

from mirrors_qa_backend.db import gen_dbsession
from mirrors_qa_backend.db.country import update_countries as update_db_countries
from mirrors_qa_backend.db.exceptions import RecordDoesNotExistError
from mirrors_qa_backend.db.worker import get_worker as get_db_worker
from mirrors_qa_backend.db.worker import update_worker as update_db_worker
from mirrors_qa_backend.routes.dependencies import CurrentWorker, DbSession
from mirrors_qa_backend.routes.dependencies import CurrentWorker
from mirrors_qa_backend.routes.http_errors import (
BadRequestError,
NotFoundError,
Expand All @@ -27,7 +31,9 @@
}
},
)
def list_countries(session: DbSession, worker_id: str) -> WorkerCountries:
def list_countries(
session: Annotated[Session, Depends(gen_dbsession)], worker_id: str
) -> WorkerCountries:
try:
worker = get_db_worker(session, worker_id)
except RecordDoesNotExistError as exc:
Expand All @@ -48,7 +54,7 @@ def list_countries(session: DbSession, worker_id: str) -> WorkerCountries:
},
)
def update_countries(
session: DbSession,
session: Annotated[Session, Depends(gen_dbsession)],
worker_id: str,
current_worker: CurrentWorker,
data: UpdateWorkerCountries,
Expand Down
4 changes: 4 additions & 0 deletions backend/src/mirrors_qa_backend/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,7 @@ class JWTClaims(BaseModel):
exp: datetime.datetime
iat: datetime.datetime
subject: str


class HealthStatus(BaseModel):
receiving_tests: bool
12 changes: 10 additions & 2 deletions backend/src/mirrors_qa_backend/settings/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,16 @@ class APISettings(Settings):

JWT_SECRET: str = getenv("JWT_SECRET", mandatory=True)
# number of seconds before a message expire
MESSAGE_VALIDITY_SECONDS = parse_timespan(
MESSAGE_VALIDITY_SECONDS: float = parse_timespan(
getenv("MESSAGE_VALIDITY_DURATION", default="1m")
)
# number of hours before access tokens expire
TOKEN_EXPIRY_SECONDS = parse_timespan(getenv("TOKEN_EXPIRY_DURATION", default="6h"))
TOKEN_EXPIRY_SECONDS: float = parse_timespan(
getenv("TOKEN_EXPIRY_DURATION", default="6h")
)

# number of seconds after which to consider that not having received
# successful Test is an issue
UNHEALTHY_NO_TESTS_DURATION_SECONDS: float = parse_timespan(
getenv("UNHEALTHY_NO_TESTS_DURATION_SECONDS", default="6h")
)

0 comments on commit 98e566f

Please sign in to comment.