Skip to content

Commit

Permalink
Merge pull request #1729 from majamassarini/retrigger-bodhi
Browse files Browse the repository at this point in the history
Feat(bodhi): Retrigger bodhi update via dist-git PR command

I have rebased the Jirka K. work and I have used Mixins to manage Koji data needed to create a new bodhi update.
This comes from #1654
Fixes #1541
Merge after packit/packit#1764

 call is_packager as aked by Laura: #1654 (comment)

Note: since I am using this method if for any reason the actor is None then the body update will succeed, I don't know if this could be a problem.

RELEASE NOTES BEGIN
Packit now allows to re-trigger Bodhi update via dist-git PR comment /packit create-update.
RELEASE NOTES END

Reviewed-by: Tomas Tomecek <[email protected]>
Reviewed-by: Laura Barcziová <None>
Reviewed-by: Maja Massarini <None>
  • Loading branch information
softwarefactory-project-zuul[bot] authored Nov 10, 2022
2 parents f535ccc + 2914848 commit a53ff50
Show file tree
Hide file tree
Showing 12 changed files with 580 additions and 71 deletions.
62 changes: 54 additions & 8 deletions packit_service/worker/checker/bodhi.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,82 @@

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, PackitAPIWithDownstreamMixin

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:
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


class IsAuthorAPackager(ActorChecker, PackitAPIWithDownstreamMixin):
def _pre_check(self) -> bool:

if not self.is_packager(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, user {self.actor} "
"is not a packager."
)
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
150 changes: 101 additions & 49 deletions packit_service/worker/handlers/bodhi.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,48 @@

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,
IsAuthorAPackager,
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,57 +69,23 @@ 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"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"
)

body += (
f"*Try {self.celery_task.retries + 1}/"
f"{self.celery_task.get_retry_limit() + 1}"
)

# Notify user on each task run and set a more generous retry interval
# to let the user fix this issue in the meantime.
if not self.celery_task.is_last_try():
body += (
f": Task will be retried in "
f"{RETRY_INTERVAL_IN_MINUTES_WHEN_USER_ACTION_IS_NEEDED} minutes.*"
)
self.celery_task.retry(
delay=RETRY_INTERVAL_IN_MINUTES_WHEN_USER_ACTION_IS_NEEDED * 60,
ex=ex,
)
else:
body += "*"

body = self._error_message_for_auth_error(ex)
notify = True
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 All @@ -133,6 +108,35 @@ def run(self) -> TaskResults:
# Sentry issue will be created otherwise.
return TaskResults(success=True, details={})

def _error_message_for_auth_error(self, ex: PackitException) -> str:
body = (
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"
)

body += (
f"*Try {self.celery_task.retries + 1}/"
f"{self.celery_task.get_retry_limit() + 1}"
)

# Notify user on each task run and set a more generous retry interval
# to let the user fix this issue in the meantime.
if not self.celery_task.is_last_try():
body += (
f": Task will be retried in "
f"{RETRY_INTERVAL_IN_MINUTES_WHEN_USER_ACTION_IS_NEEDED} minutes.*"
)
self.celery_task.retry(
delay=RETRY_INTERVAL_IN_MINUTES_WHEN_USER_ACTION_IS_NEEDED * 60,
ex=ex,
)
else:
body += "*"

return body

def notify_user_about_failure(self, body: str) -> None:
"""
If user configures `issue_repository`,
Expand Down Expand Up @@ -167,3 +171,51 @@ 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.
"""
logger.debug("Bodhi update will be re-triggered via dist-git PR comment.")
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.
"""
logger.debug("Bodhi update will be re-triggered via dist-git PR comment.")
return (
HasAuthorWriteAccess,
IsAuthorAPackager,
IsKojiBuildCompleteAndBranchConfiguredCheckService,
)
Loading

0 comments on commit a53ff50

Please sign in to comment.