Skip to content

Commit

Permalink
Feat(bodhi): Retrigger bodhi update via dist-git PR command
Browse files Browse the repository at this point in the history
Co-author-by: Maja Massarini <[email protected]>
  • Loading branch information
nikromen authored and majamassarini committed Nov 3, 2022
1 parent 96a59e7 commit b365a66
Show file tree
Hide file tree
Showing 11 changed files with 412 additions and 46 deletions.
52 changes: 44 additions & 8 deletions packit_service/worker/checker/bodhi.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,72 @@

from packit_service.constants import KojiBuildState

from packit_service.worker.checker.abstract import Checker
from packit_service.worker.handlers.mixin import GetKojiBuildEventMixin
from packit_service.worker.checker.abstract import ActorChecker, Checker
from packit_service.worker.handlers.mixin import (
GetKojiBuildData,
GetKojiBuildDataFromKojiBuildEventMixin,
GetKojiBuildDataFromKojiServiceMixin,
GetKojiBuildEventMixin,
)
from packit_service.worker.mixin import ConfigMixin

logger = logging.getLogger(__name__)


class IsKojiBuildCompleteAndBranchConfigured(Checker, GetKojiBuildEventMixin):
class IsKojiBuildCompleteAndBranchConfigured(Checker, GetKojiBuildData):
def pre_check(self) -> bool:
"""Check if builds are finished (=KojiBuildState.complete)
and branches are configured.
By default, we use `fedora-stable` alias.
(Rawhide updates are already created automatically.)
"""
if self.koji_build_event.state != KojiBuildState.complete:
if self.state != KojiBuildState.complete:
logger.debug(
f"Skipping build '{self.koji_build_event.build_id}' "
f"on '{self.koji_build_event.git_ref}'. "
f"Skipping build '{self.build_id}' "
f"on '{self.dist_git_branch}'. "
f"Build not finished yet."
)
return False

if self.koji_build_event.git_ref not in (
if self.dist_git_branch not in (
configured_branches := get_branches(
*(self.job_config.dist_git_branches or {"fedora-stable"}),
default_dg_branch="rawhide", # Koji calls it rawhide, not main
)
):
logger.info(
f"Skipping build on '{self.data.git_ref}'. "
f"Skipping build on '{self.dist_git_branch}'. "
f"Bodhi update configured only for '{configured_branches}'."
)
return False
return True


class IsKojiBuildCompleteAndBranchConfiguredCheckEvent(
IsKojiBuildCompleteAndBranchConfigured,
GetKojiBuildEventMixin,
GetKojiBuildDataFromKojiBuildEventMixin,
):
...


class IsKojiBuildCompleteAndBranchConfiguredCheckService(
IsKojiBuildCompleteAndBranchConfigured, GetKojiBuildDataFromKojiServiceMixin
):
...


class HasAuthorWriteAccess(ActorChecker, ConfigMixin):
def _pre_check(self) -> bool:
logger.debug(
f"Bodhi update will be re-triggered via dist-git PR comment by user {self.actor}."
)

if not self.project.has_write_access(user=self.actor):
logger.info(
f"Re-triggering Bodhi update via dist-git comment in PR#{self.data.pr_id}"
f" and project {self.project.repo} is not allowed for the user: {self.actor}."
)
return False

return True
1 change: 1 addition & 0 deletions packit_service/worker/handlers/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ class TaskName(str, enum.Enum):
# downstream_koji_build_report = "task.run_downstream_koji_build_report_handler"
sync_from_downstream = "task.run_sync_from_downstream_handler"
bodhi_update = "task.bodhi_update"
retrigger_bodhi_update = "task.retrigger_bodhi_update"
github_fas_verification = "task.github_fas_verification"


Expand Down
92 changes: 68 additions & 24 deletions packit_service/worker/handlers/bodhi.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,47 @@

from packit.exceptions import PackitException


from packit.config import JobConfig, JobType, PackageConfig
from packit_service.config import PackageConfigGetter
from packit_service.constants import (
CONTACTS_URL,
RETRY_INTERVAL_IN_MINUTES_WHEN_USER_ACTION_IS_NEEDED,
)
from packit_service.worker.checker.abstract import Checker
from packit_service.worker.checker.bodhi import IsKojiBuildCompleteAndBranchConfigured
from packit_service.worker.checker.bodhi import (
HasAuthorWriteAccess,
IsKojiBuildCompleteAndBranchConfiguredCheckEvent,
IsKojiBuildCompleteAndBranchConfiguredCheckService,
)
from packit_service.worker.events import PullRequestCommentPagureEvent
from packit_service.worker.events.koji import KojiBuildEvent
from packit_service.worker.handlers.abstract import (
TaskName,
configured_as,
reacts_to,
RetriableJobHandler,
run_for_comment,
)
from packit_service.worker.handlers.mixin import (
GetKojiBuildData,
GetKojiBuildDataFromKojiBuildEventMixin,
GetKojiBuildDataFromKojiServiceMixin,
GetKojiBuildEventMixin,
)
from packit_service.worker.mixin import (
PackitAPIWithDownstreamMixin,
)
from packit_service.worker.handlers.mixin import GetKojiBuildEventMixin
from packit_service.worker.mixin import LocalProjectMixin
from packit_service.worker.result import TaskResults

logger = logging.getLogger(__name__)


@configured_as(job_type=JobType.bodhi_update)
@reacts_to(event=KojiBuildEvent)
class CreateBodhiUpdateHandler(
RetriableJobHandler, LocalProjectMixin, GetKojiBuildEventMixin
class BodhiUpdateHandler(
RetriableJobHandler, PackitAPIWithDownstreamMixin, GetKojiBuildData
):
"""
This handler can create a bodhi update for successful Koji builds.
"""

topic = "org.fedoraproject.prod.buildsys.build.state.change"
task_name = TaskName.bodhi_update

def __init__(
self,
Expand All @@ -60,28 +68,19 @@ def __init__(
celery_task=celery_task,
)

@staticmethod
def get_checkers() -> Tuple[Type[Checker], ...]:
"""We react only on finished builds (=KojiBuildState.complete)
and configured branches.
"""
return (IsKojiBuildCompleteAndBranchConfigured,)

def run(self) -> TaskResults:
try:
self.packit_api.create_update(
dist_git_branch=self.koji_build_event.git_ref,
dist_git_branch=self.dist_git_branch,
update_type="enhancement",
koji_builds=[
self.koji_build_event.nvr # it accepts NVRs, not build IDs
],
koji_builds=[self.nvr], # it accepts NVRs, not build IDs
)
except PackitException as ex:
logger.debug(f"Bodhi update failed to be created: {ex}")

if isinstance(ex.__cause__, AuthError):
body = (
f"Bodhi update creation failed for `{self.koji_build_event.nvr}` "
f"Bodhi update creation failed for `{self.nvr}` "
f"because of the missing permissions.\n\n"
f"Please, give {self.service_config.fas_user} user `commit` rights in the "
f"[dist-git settings]({self.data.project_url}/adduser).\n\n"
Expand Down Expand Up @@ -110,7 +109,7 @@ def run(self) -> TaskResults:
known_error = True
else:
body = (
f"Bodhi update creation failed for `{self.koji_build_event.nvr}`:\n"
f"Bodhi update creation failed for `{self.nvr}`:\n"
"```\n"
f"{ex}\n"
"```"
Expand Down Expand Up @@ -167,3 +166,48 @@ def notify_user_about_failure(self, body: str) -> None:
+ f"\n\n---\n\n*Get in [touch with us]({CONTACTS_URL}) if you need some help.*",
comment_to_existing=body,
)


@configured_as(job_type=JobType.bodhi_update)
@reacts_to(event=KojiBuildEvent)
class CreateBodhiUpdateHandler(
BodhiUpdateHandler,
RetriableJobHandler,
GetKojiBuildEventMixin,
GetKojiBuildDataFromKojiBuildEventMixin,
):
"""
This handler can create a bodhi update for successful Koji builds.
"""

task_name = TaskName.bodhi_update

@staticmethod
def get_checkers() -> Tuple[Type[Checker], ...]:
"""We react only on finished builds (=KojiBuildState.complete)
and configured branches.
"""
return (IsKojiBuildCompleteAndBranchConfiguredCheckEvent,)


@configured_as(job_type=JobType.bodhi_update)
@reacts_to(event=PullRequestCommentPagureEvent)
@run_for_comment(command="create-update")
class RetriggerBodhiUpdateHandler(
BodhiUpdateHandler, GetKojiBuildDataFromKojiServiceMixin
):
"""
This handler can re-trigger a bodhi update if any successfull Koji build.
"""

task_name = TaskName.retrigger_bodhi_update

@staticmethod
def get_checkers() -> Tuple[Type[Checker], ...]:
"""We react only on finished builds (=KojiBuildState.complete)
and configured branches.
"""
return (
HasAuthorWriteAccess,
IsKojiBuildCompleteAndBranchConfiguredCheckService,
)
87 changes: 84 additions & 3 deletions packit_service/worker/handlers/mixin.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT

from abc import abstractmethod
import logging
from typing import Optional, Protocol
from abc import abstractmethod
from typing import Any, Optional, Protocol
from koji import ClientSession
from packit.config import PackageConfig, JobConfig
from packit.constants import KOJI_BASEURL
from packit_service.utils import get_packit_commands_from_comment
from packit_service.config import ProjectToSync
from packit_service.constants import COPR_SRPM_CHROOT
from packit_service.constants import COPR_SRPM_CHROOT, KojiBuildState
from packit_service.models import (
AbstractTriggerDbType,
CoprBuildTargetModel,
Expand All @@ -27,6 +29,8 @@
from packit_service.worker.events.koji import KojiBuildEvent
from packit_service.worker.monitoring import Pushgateway

from packit.distgit import DistGit


logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -80,6 +84,83 @@ def koji_build_helper(self) -> KojiBuildJobHelper:
return self._koji_build_helper


class GetKojiBuildData(Protocol):
@property
@abstractmethod
def nvr(self) -> str:
...

@property
@abstractmethod
def build_id(self) -> int:
...

@property
@abstractmethod
def dist_git_branch(self) -> str:
...

@property
@abstractmethod
def state(self) -> KojiBuildState:
...


class GetKojiBuildDataFromKojiBuildEventMixin(GetKojiBuildData, GetKojiBuildEvent):
@property
def nvr(self) -> str:
return self.koji_build_event.nvr

@property
def build_id(self) -> int:
return self.koji_build_event.build_id

@property
def dist_git_branch(self) -> str:
return self.koji_build_event.git_ref

@property
def state(self) -> KojiBuildState:
return self.koji_build_event.state


class GetKojiBuildDataFromKojiServiceMixin(ConfigMixin, GetKojiBuildData):
_nvr: Optional[str] = None
_build: Optional[Any] = None

def _query_service_for_nvr(self):
self._nvr = DistGit.get_latest_build_in_tag(
downstream_package_name=self.project.repo,
dist_git_branch=self.dist_git_branch,
)

def _query_service_for_build(self):
session = ClientSession(baseurl=KOJI_BASEURL)
self._build = session.getBuild(buildInfo=self.nvr)

@property
def nvr(self) -> str:
if not self._nvr:
self._query_service_for_nvr()
return self._nvr

@property
def build_id(self) -> int:
if not self._build:
self._query_service_for_build()
return self._build["build_id"]

@property
def dist_git_branch(self) -> str:
return self.project.get_pr(self.data.pr_id).target_branch

@property
def state(self) -> KojiBuildState:
if not self._build:
self._query_service_for_build()
return KojiBuildState.from_number(self._build["state"])


class GetCoprBuildEvent(Protocol):
data: EventData

Expand Down
4 changes: 2 additions & 2 deletions packit_service/worker/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ def is_project_public_or_enabled_private(self) -> bool:

return True

def check_explicit_matching(self):
def check_explicit_matching(self) -> List[JobConfig]:
"""Force explicit event/jobs matching for triggers
Returns:
Expand All @@ -480,7 +480,7 @@ def check_explicit_matching(self):
if isinstance(self.event, PullRequestCommentPagureEvent):
for job in self.event.package_config.jobs:
if (
job.type == JobType.koji_build
job.type in [JobType.koji_build, JobType.bodhi_update]
and job.trigger == JobConfigTriggerType.commit
and self.event.job_config_trigger_type
== JobConfigTriggerType.pull_request
Expand Down
Loading

0 comments on commit b365a66

Please sign in to comment.