Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat(bodhi): Retrigger bodhi update via dist-git PR command #1729

Merged
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
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,
)
Comment on lines +216 to +221
Copy link
Member

Choose a reason for hiding this comment

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

Will Packit provide any feedback back to the user if one of these checkers fails?

Copy link
Member Author

Choose a reason for hiding this comment

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

You are right, there is no feedback for the users when checks fail. For none of the them.
If you agree I will create a new card to add the feedback, I think there is some work on it. Right now there is no BodhiUpdateJobHelper class and for the other services is this kind of helper class that handles the feedback.

Copy link
Member

Choose a reason for hiding this comment

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

agreed! we should absolutely have something to comment back so users know what's happening in Packit

Copy link
Member Author

Choose a reason for hiding this comment

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

agreed! we should absolutely have something to comment back so users know what's happening in Packit

#1747

Loading