Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packit_service/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4882,6 +4882,24 @@ def get_by_log_detective_analysis_id(cls, analysis_id: str) -> "LogDetectiveRunM
with sa_session_transaction() as session:
return session.query(LogDetectiveRunModel).filter_by(analysis_id=analysis_id).first()

@classmethod
def get_by_id(cls, id_: int) -> Optional["LogDetectiveRunModel"]:
with sa_session_transaction() as session:
return session.query(LogDetectiveRunModel).filter_by(id=id_).first()

@classmethod
def get_range(
cls,
first: int,
last: int,
) -> Iterable["LogDetectiveRunModel"]:
with sa_session_transaction() as session:
query = session.query(LogDetectiveRunModel).order_by(
desc(LogDetectiveRunModel.id),
)

return query.slice(first, last)


class LogDetectiveRunGroupModel(ProjectAndEventsConnector, GroupModel, Base):
__tablename__ = "log_detective_run_groups"
Expand Down
2 changes: 2 additions & 0 deletions packit_service/service/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from packit_service.service.api.installations import ns as installations_ns
from packit_service.service.api.koji_builds import koji_builds_ns
from packit_service.service.api.koji_tag_requests import koji_tag_requests_ns
from packit_service.service.api.logdetective import ns as logdetective_ns
from packit_service.service.api.osh_scans import ns as osh_scans_ns
from packit_service.service.api.projects import ns as projects_ns
from packit_service.service.api.propose_downstream import ns as propose_downstream_ns
Expand Down Expand Up @@ -48,3 +49,4 @@
api.add_namespace(system_ns)
api.add_namespace(bodhi_updates_ns)
api.add_namespace(osh_scans_ns)
api.add_namespace(logdetective_ns)
7 changes: 6 additions & 1 deletion packit_service/service/api/copr_builds.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
optional_timestamp,
)
from packit_service.service.api.parsers import indices, pagination_arguments
from packit_service.service.api.utils import get_project_info_from_build, response_maker
from packit_service.service.api.utils import (
get_log_detective_runs,
get_project_info_from_build,
response_maker,
)

logger = getLogger("packit_service")

Expand Down Expand Up @@ -97,6 +101,7 @@ def get(self, id):
"srpm_build_id": build.get_srpm_build().id,
"run_ids": sorted(run.id for run in build.group_of_targets.runs),
"built_packages": build.built_packages,
"log_detective_runs": get_log_detective_runs(build=build),
Comment thread
jpodivin marked this conversation as resolved.
}

build_dict.update(get_project_info_from_build(build))
Expand Down
7 changes: 6 additions & 1 deletion packit_service/service/api/koji_builds.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
optional_timestamp,
)
from packit_service.service.api.parsers import indices, pagination_arguments
from packit_service.service.api.utils import get_project_info_from_build, response_maker
from packit_service.service.api.utils import (
get_log_detective_runs,
get_project_info_from_build,
response_maker,
)

logger = getLogger("packit_service")

Expand Down Expand Up @@ -99,6 +103,7 @@ def get(self, id):
"run_ids": sorted(run.id for run in build.group_of_targets.runs),
"build_submission_stdout": build.build_submission_stdout,
"error_message": build.data.get("error") if build.data else None,
"log_detective_runs": get_log_detective_runs(build=build),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

}

build_dict.update(get_project_info_from_build(build))
Expand Down
119 changes: 119 additions & 0 deletions packit_service/service/api/logdetective.py
Comment thread
jpodivin marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

import logging
from http import HTTPStatus

from flask_restx import Namespace, Resource

from packit_service.models import (
LogDetectiveRunGroupModel,
LogDetectiveRunModel,
optional_timestamp,
)
from packit_service.service.api.parsers import indices, pagination_arguments
from packit_service.service.api.utils import get_project_info_from_build, response_maker

logger = logging.getLogger("packit_service")
Comment thread
jpodivin marked this conversation as resolved.

ns = Namespace("log-detective", description="Log Detective")


@ns.route("/<int:id>")
@ns.param("id", "Packit id of the Log Detective run")
class LogDetectiveResult(Resource):
@ns.response(HTTPStatus.OK.value, "OK, Log Detective run details follow")
@ns.response(HTTPStatus.NOT_FOUND.value, "No info about Log Detective run stored in DB")
def get(self, id):
"""A specific Log Detective run details."""
log_detective_run_model = LogDetectiveRunModel.get_by_id(int(id))

if not log_detective_run_model:
return response_maker(
{"error": "No info about Log Detective run stored in DB"},
status=HTTPStatus.NOT_FOUND,
)
run_ids = []
if log_detective_run_model.group_of_targets.runs:
run_ids = sorted(run.id for run in log_detective_run_model.group_of_targets.runs)

log_detective_result_dict = {
"packit_id": log_detective_run_model.id,
"analysis_id": log_detective_run_model.analysis_id,
"status": log_detective_run_model.status.value,
Comment thread
jpodivin marked this conversation as resolved.
"chroot": log_detective_run_model.target,
"commit_sha": log_detective_run_model.commit_sha,
"log_detective_response": log_detective_run_model.log_detective_response,
"target_build": log_detective_run_model.target_build,
"run_ids": run_ids,
"submitted_time": optional_timestamp(log_detective_run_model.submitted_time),
}

log_detective_result_dict.update(get_project_info_from_build(log_detective_run_model))
return response_maker(log_detective_result_dict)


@ns.route("/groups/<int:id>")
@ns.param("id", "Packit id of the Log Detective run group")
class LogDetectiveGroup(Resource):
@ns.response(HTTPStatus.OK, "OK, Log Detective run group details follow")
@ns.response(
HTTPStatus.NOT_FOUND.value,
"No info about Log Detective run group stored in DB",
)
def get(self, id):
"""A specific Log Detective run group details."""
group_model = LogDetectiveRunGroupModel.get_by_id(int(id))
Comment thread
jpodivin marked this conversation as resolved.

if not group_model:
return response_maker(
{"error": "No info about group stored in DB"},
status=HTTPStatus.NOT_FOUND,
)
run_ids = []
if group_model.runs:
run_ids = sorted(run.id for run in group_model.runs)
group_dict = {
"packit_id": group_model.id,
"submitted_time": optional_timestamp(group_model.submitted_time),
"run_ids": run_ids,
"log_detective_target_ids": sorted(ld_run.id for ld_run in group_model.grouped_targets),
}

group_dict.update(get_project_info_from_build(group_model))
return response_maker(group_dict)


@ns.route("")
class LogDetectiveResultList(Resource):
@ns.expect(pagination_arguments)
@ns.response(HTTPStatus.PARTIAL_CONTENT, "Log Detective result list follows")
def get(self):
"""List all Log Detective results."""

first, last = indices()
result = []

for log_detective_run_model in LogDetectiveRunModel.get_range(first, last):
run_ids = []
if log_detective_run_model.group_of_targets.runs:
run_ids = sorted(run.id for run in log_detective_run_model.group_of_targets.runs)
log_detective_result_dict = {
"packit_id": log_detective_run_model.id,
"analysis_id": log_detective_run_model.analysis_id,
"status": log_detective_run_model.status.value,
"chroot": log_detective_run_model.target,
"commit_sha": log_detective_run_model.commit_sha,
"log_detective_response": log_detective_run_model.log_detective_response,
"target_build": log_detective_run_model.target_build,
"run_ids": run_ids,
"submitted_time": optional_timestamp(log_detective_run_model.submitted_time),
}
result.append(log_detective_result_dict)

resp = response_maker(
result,
status=HTTPStatus.PARTIAL_CONTENT,
)
resp.headers["Content-Range"] = f"log-detective-results {first + 1}-{last}/*"
return resp
14 changes: 14 additions & 0 deletions packit_service/service/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
GitProjectModel,
KojiBuildGroupModel,
KojiBuildTargetModel,
LogDetectiveRunGroupModel,
LogDetectiveRunModel,
SRPMBuildModel,
SyncReleaseModel,
SyncReleaseTargetModel,
Expand Down Expand Up @@ -43,6 +45,8 @@ def get_project_info_from_build(
SyncReleaseModel,
BodhiUpdateTargetModel,
VMImageBuildTargetModel,
LogDetectiveRunModel,
LogDetectiveRunGroupModel,
],
) -> dict[str, Any]:
if not (project := build.get_project()):
Expand Down Expand Up @@ -132,3 +136,13 @@ def get_project_info(project: Union[AnityaProjectModel, GitProjectModel]):
result_dict["non_git_upstream"] = isinstance(project, AnityaProjectModel)

return result_dict


def get_log_detective_runs(
build: Union[CoprBuildTargetModel, KojiBuildTargetModel],
) -> list[str]:
"""Get a list of Log Detective run UUIDs."""

if build.log_detective_runs:
return [run.id for run in build.log_detective_runs]
return []
8 changes: 7 additions & 1 deletion packit_service/service/urls.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

from typing import Union

from packit_service.config import ServiceConfig

DASHBOARD_URL = ServiceConfig().get_service_config().dashboard_url


def _get_url_for_dashboard_results(job_type: str, id_: int) -> str:
def _get_url_for_dashboard_results(job_type: str, id_: Union[int, str]) -> str:
"""
Generates an URL to the dashboard result page that can be used for setting
URL in a commit status.
Expand Down Expand Up @@ -54,3 +56,7 @@ def get_openscanhub_info_url(id_: int) -> str:

def get_bodhi_update_info_url(id_: int) -> str:
return _get_url_for_dashboard_results("bodhi", id_)


def get_logdetective_info_url(id_: int) -> str:
return _get_url_for_dashboard_results("log-detective", id_)
Comment thread
jpodivin marked this conversation as resolved.
4 changes: 3 additions & 1 deletion packit_service/worker/handlers/logdetective.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
LogDetectiveResult,
LogDetectiveRunModel,
)
from packit_service.service.urls import get_logdetective_info_url
from packit_service.utils import elapsed_seconds
from packit_service.worker.checker.abstract import Checker
from packit_service.worker.handlers.abstract import (
Expand Down Expand Up @@ -131,7 +132,8 @@ def _run(self) -> TaskResults:
else:
self.branch_name = build.get_branch_name()

url = build.web_url or ""
url = get_logdetective_info_url(log_detective_run_model.id)

# LDRunModel.target is "target-arch" for Koji (i.e. rawhide-x86_64),
# for Copr it would be chroot which also includes arch information
self._ci_helper = FedoraCIHelper(
Expand Down
10 changes: 7 additions & 3 deletions tests/unit/events/test_logdetective.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ def test_logdetective_run_success(
copr_build_target_id=999,
koji_build_target_id=999,
target="fedora-44-x86_64" if build_system == "copr" else "fc44-x86_64",
id=123,
)
else:
run_model = flexmock(
Expand All @@ -193,6 +194,7 @@ def test_logdetective_run_success(
copr_build_target_id=999,
koji_build_target_id=999,
target="fedora-44-x86_64" if build_system == "copr" else "fc44-x86_64",
id=123,
)
flexmock(LogDetectiveRunModel).should_receive("get_by_log_detective_analysis_id").with_args(
analysis_id="123456"
Expand Down Expand Up @@ -222,7 +224,7 @@ def test_logdetective_run_success(
flexmock(FedoraCIHelper).should_receive("report").with_args(
state=expected_status,
description=f"Log Detective analysis status: {status_str}",
url="https://build.url",
url="/jobs/log-detective/123",
check_name="Packit - Log Detective analysis",
).once()

Expand Down Expand Up @@ -265,7 +267,7 @@ def test_logdetective_run_already_processed(handler_and_models):
handler = handler_and_models

# Simulate DB already having the same status
run_model = flexmock(status=LogDetectiveResult.complete)
run_model = flexmock(status=LogDetectiveResult.complete, id=123)
flexmock(LogDetectiveRunModel).should_receive("get_by_log_detective_analysis_id").and_return(
run_model
)
Expand All @@ -287,6 +289,7 @@ def test_logdetective_run_build_not_found(handler_and_models):
status=LogDetectiveResult.running,
submitted_time=datetime.now(timezone.utc),
copr_build_target_id=10,
id=123,
)
flexmock(LogDetectiveRunModel).should_receive("get_by_log_detective_analysis_id").and_return(
run_model
Expand Down Expand Up @@ -315,6 +318,7 @@ def test_logdetective_run_empty_url_fallback(handler_and_models):
submitted_time=datetime.now(timezone.utc),
copr_build_target_id=10,
target="fedora-rawhide-x86_64",
id=123,
)
run_model.should_receive("set_status")
flexmock(LogDetectiveRunModel).should_receive("get_by_log_detective_analysis_id").and_return(
Expand All @@ -329,7 +333,7 @@ def test_logdetective_run_empty_url_fallback(handler_and_models):
flexmock(FedoraCIHelper).should_receive("report").with_args(
state=BaseCommitStatus.success,
description="Log Detective analysis status: complete",
url="",
url="/jobs/log-detective/123",
check_name="Packit - Log Detective analysis",
).once()

Expand Down
Loading
Loading