-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Restart test executions functionality (#161)
* Add dummy script to be used by jenkins * Add dummy restart endpoint * Remove unused import * Fix OCI image not building by recreating poetry.lock file * Fix tests * Rename restart endpoint to reruns to be more RESTFull * Basic implementation of test execution reruns * Add another test case * Rename some db types due to alembic/sqlalchemy upgrade * Return family with test execution pending reruns * Add test_execution_rerunner.py script to be used by jenkins * Return is_rerun_requested with test execution objects * Fix migration merge conflict * Add a rerun example to seed data script * Delete rerun request when starting a rerun * More reruns in seed script * Add frontend button to rerun test executions * Small refactor
- Loading branch information
Showing
22 changed files
with
1,977 additions
and
1,408 deletions.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
...migrations/versions/2024_04_30_0926-08bc88dcb1e1_create_test_execution_rerun_requests_.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,40 @@ | ||
"""Create test execution rerun requests table | ||
Revision ID: 08bc88dcb1e1 | ||
Revises: 5d36de5a8a48 | ||
Create Date: 2024-04-30 09:26:48.766175+00:00 | ||
""" | ||
|
||
import sqlalchemy as sa | ||
from alembic import op | ||
|
||
# revision identifiers, used by Alembic. | ||
revision = "08bc88dcb1e1" | ||
down_revision = "5d36de5a8a48" | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade() -> None: | ||
op.create_table( | ||
"test_execution_rerun_request", | ||
sa.Column("test_execution_id", sa.Integer(), nullable=False), | ||
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), | ||
sa.Column("created_at", sa.DateTime(), nullable=False), | ||
sa.Column("updated_at", sa.DateTime(), nullable=False), | ||
sa.ForeignKeyConstraint( | ||
["test_execution_id"], | ||
["test_execution.id"], | ||
name=op.f("test_execution_rerun_request_test_execution_id_fkey"), | ||
), | ||
sa.PrimaryKeyConstraint("id", name=op.f("test_execution_rerun_request_pkey")), | ||
sa.UniqueConstraint( | ||
"test_execution_id", | ||
name=op.f("test_execution_rerun_request_test_execution_id_key"), | ||
), | ||
) | ||
|
||
|
||
def downgrade() -> None: | ||
op.drop_table("test_execution_rerun_request") |
25 changes: 25 additions & 0 deletions
25
...grations/versions/2024_04_30_1029-33c0383ea9ca_rename_types_due_to_sqlalchemy_alembic_.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,25 @@ | ||
"""Rename types due to sqlalchemy/alembic upgrade | ||
Revision ID: 33c0383ea9ca | ||
Revises: 08bc88dcb1e1 | ||
Create Date: 2024-04-30 10:29:23.440961+00:00 | ||
""" | ||
|
||
from alembic import op | ||
|
||
# revision identifiers, used by Alembic. | ||
revision = "33c0383ea9ca" | ||
down_revision = "08bc88dcb1e1" | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade() -> None: | ||
op.execute("ALTER TYPE artefact_status_enum RENAME TO artefactstatus") | ||
op.execute("ALTER TYPE test_status_enum RENAME TO testexecutionstatus") | ||
|
||
|
||
def downgrade() -> None: | ||
op.execute("ALTER TYPE artefactstatus RENAME TO artefact_status_enum") | ||
op.execute("ALTER TYPE testexecutionstatus RENAME TO test_status_enum") |
Large diffs are not rendered by default.
Oops, something went wrong.
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,65 @@ | ||
""" | ||
This script will be used by jenkins to rerun test executions as requested | ||
by test observer. Please note that jenkins will download this file then run it. | ||
Therefore, this script can't import from the rest of the codebase and shouldn't be | ||
renamed. Dependencies used by this script must be installed on jenkins. | ||
""" | ||
|
||
import logging | ||
import re | ||
from os import environ | ||
|
||
import requests | ||
from requests.auth import HTTPBasicAuth | ||
|
||
reruns_link = "https://test-observer.canonical.com/v1/test-executions/reruns" | ||
|
||
|
||
class Main: | ||
def __init__(self, jenkins_api_token: str | None = None): | ||
self.jenkins_auth = HTTPBasicAuth( | ||
"admin", jenkins_api_token or environ["JENKINS_API_TOKEN"] | ||
) | ||
|
||
def run(self): | ||
self._load_rerun_requests() | ||
self._submit_rerun_requests() | ||
|
||
def _load_rerun_requests(self) -> None: | ||
response = requests.get(reruns_link) | ||
self.rerun_requests = response.json() | ||
logging.info(f"Received the following rerun requests:\n{self.rerun_requests}") | ||
|
||
def _submit_rerun_requests(self) -> None: | ||
for rerun_request in self.rerun_requests: | ||
self._submit_rerun(rerun_request) | ||
|
||
def _submit_rerun(self, rerun_request: dict) -> None: | ||
base_job_link = self._extract_base_job_link_from_ci_link( | ||
rerun_request["ci_link"] | ||
) | ||
if base_job_link: | ||
family = rerun_request["family"] | ||
if family == "deb": | ||
self._submit_deb_rerun(base_job_link) | ||
elif family == "snap": | ||
self._submit_snap_rerun(base_job_link) | ||
else: | ||
logging.error(f"Invalid family name {family}") | ||
|
||
def _extract_base_job_link_from_ci_link(self, ci_link: str) -> str | None: | ||
matching = re.match(r"(.+/)\d+/", ci_link) | ||
if matching: | ||
return matching.group(1) | ||
return None | ||
|
||
def _submit_snap_rerun(self, base_job_link: str) -> None: | ||
rerun_link = f"{base_job_link}/build" | ||
logging.info(f"POST {rerun_link}") | ||
requests.post(rerun_link, auth=self.jenkins_auth) | ||
|
||
def _submit_deb_rerun(self, base_job_link: str) -> None: | ||
rerun_link = f"{base_job_link}/buildWithParameters" | ||
data = {"TESTPLAN": "full"} | ||
logging.info(f"POST {rerun_link} {data}") | ||
requests.post(rerun_link, auth=self.jenkins_auth, json=data) |
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 |
---|---|---|
|
@@ -18,8 +18,9 @@ | |
# Omar Selo <[email protected]> | ||
# Nadzeya Hutsko <[email protected]> | ||
from datetime import date | ||
from typing import Any | ||
|
||
from pydantic import AliasPath, BaseModel, ConfigDict, Field | ||
from pydantic import AliasPath, BaseModel, ConfigDict, Field, computed_field | ||
|
||
from test_observer.data_access.models_enums import ( | ||
ArtefactStatus, | ||
|
@@ -74,6 +75,11 @@ class TestExecutionDTO(BaseModel): | |
# reasons, we allow multiple reasons to be picked for the approval | ||
review_decision: set[TestExecutionReviewDecision] | ||
review_comment: str | ||
rerun_request: Any = Field(exclude=True) | ||
|
||
@computed_field | ||
def is_rerun_requested(self) -> bool: | ||
return bool(self.rerun_request) | ||
|
||
|
||
class ArtefactBuildDTO(BaseModel): | ||
|
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
40 changes: 40 additions & 0 deletions
40
backend/test_observer/controllers/test_executions/reruns.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,40 @@ | ||
from fastapi import APIRouter, Depends, HTTPException | ||
from sqlalchemy import select | ||
from sqlalchemy.orm import Session, joinedload | ||
|
||
from test_observer.data_access.models import ( | ||
Artefact, | ||
ArtefactBuild, | ||
Stage, | ||
TestExecution, | ||
TestExecutionRerunRequest, | ||
) | ||
from test_observer.data_access.repository import get_or_create | ||
from test_observer.data_access.setup import get_db | ||
|
||
from .models import PendingRerun, RerunRequest | ||
|
||
router = APIRouter() | ||
|
||
|
||
@router.post("/reruns") | ||
def create_a_rerun_request(request: RerunRequest, db: Session = Depends(get_db)): | ||
te = db.get(TestExecution, request.test_execution_id) | ||
if not te: | ||
msg = f"No test execution with id {request.test_execution_id} found" | ||
raise HTTPException(status_code=404, detail=msg) | ||
|
||
get_or_create(db, TestExecutionRerunRequest, {"test_execution_id": te.id}) | ||
|
||
|
||
@router.get("/reruns", response_model=list[PendingRerun]) | ||
def get_rerun_requests(db: Session = Depends(get_db)): | ||
return db.scalars( | ||
select(TestExecutionRerunRequest).options( | ||
joinedload(TestExecutionRerunRequest.test_execution) | ||
.joinedload(TestExecution.artefact_build) | ||
.joinedload(ArtefactBuild.artefact) | ||
.joinedload(Artefact.stage) | ||
.joinedload(Stage.family) | ||
) | ||
) |
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
Oops, something went wrong.