Skip to content

Commit 0f6c6e6

Browse files
Automatically update builds in Bodhi update created from a sidetag (#2549)
Automatically update builds in Bodhi update created from a sidetag Fixes #2497. Merge after packit/specfile#416. Reviewed-by: Laura Barcziová
2 parents aab45bf + 8d568d5 commit 0f6c6e6

File tree

10 files changed

+277
-213
lines changed

10 files changed

+277
-213
lines changed

packit_service/models.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2350,6 +2350,20 @@ def get_all_projects(cls) -> Set["GitProjectModel"]:
23502350
projects = [branch.project for branch in project_event_branches]
23512351
return set(projects)
23522352

2353+
@classmethod
2354+
def get_first_successful_by_sidetag(
2355+
cls, sidetag: str
2356+
) -> Optional["BodhiUpdateTargetModel"]:
2357+
with sa_session_transaction() as session:
2358+
return (
2359+
session.query(BodhiUpdateTargetModel)
2360+
.filter(
2361+
BodhiUpdateTargetModel.status == "success",
2362+
BodhiUpdateTargetModel.sidetag == sidetag,
2363+
)
2364+
.first()
2365+
)
2366+
23532367

23542368
class BodhiUpdateGroupModel(ProjectAndEventsConnector, GroupModel, Base):
23552369
__tablename__ = "bodhi_update_groups"

packit_service/worker/handlers/bodhi.py

Lines changed: 36 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@
88
import logging
99
from datetime import datetime
1010
from os import getenv
11-
from typing import List, Tuple, Type, Optional
11+
from typing import Tuple, Type, Optional
1212

1313
from celery import Task
1414

1515
from packit.config import JobConfig, JobType, PackageConfig, Deployment
1616
from packit.exceptions import PackitException
17-
from packit.utils.koji_helper import KojiHelper
18-
from specfile.utils import NEVR
1917
from packit_service.config import ServiceConfig
2018
from packit_service.constants import (
2119
MSG_RETRIGGER,
@@ -28,8 +26,6 @@
2826
PipelineModel,
2927
BodhiUpdateTargetModel,
3028
KojiBuildTargetModel,
31-
SidetagGroupModel,
32-
SidetagModel,
3329
)
3430
from packit_service.worker.checker.abstract import Checker
3531
from packit_service.worker.checker.bodhi import (
@@ -46,6 +42,7 @@
4642
IssueCommentGitlabEvent,
4743
)
4844
from packit_service.worker.events.koji import KojiBuildEvent
45+
from packit_service.worker.helpers.sidetag import SidetagHelper
4946
from packit_service.worker.handlers.abstract import (
5047
TaskName,
5148
configured_as,
@@ -76,98 +73,11 @@
7673
logger = logging.getLogger(__name__)
7774

7875

79-
class SidetagHelper:
80-
_koji_helper: Optional[KojiHelper] = None
81-
_sidetag_group: Optional[SidetagGroupModel] = None
82-
83-
def __init__(self, job_config: JobConfig) -> None:
84-
self.job_config = job_config
85-
86-
@property
87-
def koji_helper(self) -> KojiHelper:
88-
if not self._koji_helper:
89-
self._koji_helper = KojiHelper()
90-
return self._koji_helper
91-
92-
@property
93-
def sidetag_group(self) -> SidetagGroupModel:
94-
if not self._sidetag_group:
95-
self._sidetag_group = SidetagGroupModel.get_by_name(
96-
self.job_config.sidetag_group
97-
)
98-
return self._sidetag_group
99-
100-
def get_builds_in_sidetag(self, dist_git_branch: str) -> Tuple[str, List[str]]:
101-
"""
102-
Gets the latest builds of required packages from a sidetag identified
103-
by the configured sidetag group and the specified dist-git branch.
104-
105-
Args:
106-
dist_git_branch: dist-git branch.
107-
108-
Returns:
109-
A tuple where the first element is the name of the sidetag
110-
and the second element is a list of NVRs.
111-
112-
Raises:
113-
PackitException if a sidetag was not found or if job dependencies
114-
are not satisfied.
115-
"""
116-
sidetag = self.sidetag_group.get_sidetag_by_target(dist_git_branch)
117-
if not sidetag:
118-
raise PackitException(
119-
f"No sidetag found for {self.sidetag_group.name} and {dist_git_branch}"
120-
)
121-
122-
builds = self.koji_helper.get_builds_in_tag(sidetag.koji_name)
123-
tagged_packages = {b["package_name"] for b in builds}
124-
125-
# check if dependencies are satisfied within the sidetag
126-
dependencies = set(self.job_config.dependencies or [])
127-
dependencies.add(self.job_config.downstream_package_name) # include self
128-
if not dependencies <= tagged_packages:
129-
missing = dependencies - tagged_packages
130-
raise PackitException(f"Missing dependencies for Bodhi update: {missing}")
131-
132-
nvrs = []
133-
for package in dependencies:
134-
latest_stable_nvr = self.koji_helper.get_latest_stable_nvr(
135-
package, dist_git_branch, include_candidate=True
136-
)
137-
latest_build_in_sidetag = max(
138-
(b for b in builds if b["package_name"] == package),
139-
key=lambda b: NEVR.from_string(b["nvr"]),
140-
)
141-
# exclude NVRs that are already in stable or candidate tags - if a build
142-
# has been manually tagged into the sidetag to satisfy dependencies,
143-
# we don't want it in the update
144-
if NEVR.from_string(latest_build_in_sidetag["nvr"]) > NEVR.from_string(
145-
latest_stable_nvr
146-
):
147-
nvrs.append(latest_build_in_sidetag["nvr"])
148-
149-
return sidetag.koji_name, nvrs
150-
151-
def remove_sidetag(self, koji_name: str) -> None:
152-
"""Removes the specified sidetag."""
153-
self.koji_helper.remove_sidetag(koji_name)
154-
if sidetag := SidetagModel.get_by_koji_name(koji_name):
155-
sidetag.delete()
156-
157-
15876
class BodhiUpdateHandler(
15977
RetriableJobHandler, PackitAPIWithDownstreamMixin, GetKojiBuildData
16078
):
16179
topic = "org.fedoraproject.prod.buildsys.build.state.change"
16280

163-
_sidetag_helper: Optional[SidetagHelper] = None
164-
165-
@property
166-
def sidetag_helper(self) -> SidetagHelper:
167-
if not self._sidetag_helper:
168-
self._sidetag_helper = SidetagHelper(self.job_config)
169-
return self._sidetag_helper
170-
17181
def __init__(
17282
self,
17383
package_config: PackageConfig,
@@ -194,8 +104,21 @@ def run(self) -> TaskResults:
194104
errors = {}
195105
for target_model in group.grouped_targets:
196106
try:
107+
existing_alias = None
108+
if target_model.sidetag:
109+
# get update alias from previous run(s) from the same sidetag (if any)
110+
if model := BodhiUpdateTargetModel.get_first_successful_by_sidetag(
111+
target_model.sidetag
112+
):
113+
existing_alias = model.alias
114+
197115
logger.debug(
198-
f"Create update for dist-git branch: {target_model.target} "
116+
(
117+
f"Edit update {existing_alias} "
118+
if existing_alias
119+
else "Create update "
120+
)
121+
+ f"for dist-git branch: {target_model.target} "
199122
f"and nvrs: {target_model.koji_nvrs}"
200123
+ (
201124
f" from sidetag: {target_model.sidetag}."
@@ -208,21 +131,13 @@ def run(self) -> TaskResults:
208131
update_type="enhancement",
209132
koji_builds=target_model.koji_nvrs.split(), # it accepts NVRs, not build IDs
210133
sidetag=target_model.sidetag,
134+
alias=existing_alias,
211135
)
212136
if not result:
213137
# update was already created
214138
target_model.set_status("skipped")
215139
continue
216140

217-
if target_model.sidetag:
218-
# remove the sidetag now; Bodhi would remove it for us
219-
# when the update hits stable, but we would be blocked
220-
# from creating new updates until that happens
221-
logger.debug(f"Removing sidetag {target_model.sidetag}")
222-
# we need Kerberos ticket to remove a sidetag
223-
self.packit_api.init_kerberos_ticket()
224-
self.sidetag_helper.remove_sidetag(target_model.sidetag)
225-
226141
alias, url = result
227142
target_model.set_status("success")
228143
target_model.set_alias(alias)
@@ -290,17 +205,30 @@ def _get_or_create_bodhi_update_group_model(self) -> BodhiUpdateGroupModel:
290205
group = BodhiUpdateGroupModel.create(run_model)
291206

292207
for koji_build_data in self:
208+
sidetag = builds = None
293209
if self.job_config.sidetag_group:
294-
sidetag, builds = self.sidetag_helper.get_builds_in_sidetag(
295-
koji_build_data.dist_git_branch
210+
sidetag = SidetagHelper.get_sidetag(
211+
self.job_config.sidetag_group, koji_build_data.dist_git_branch
212+
)
213+
# check if dependencies are satisfied within the sidetag
214+
dependencies = set(self.job_config.dependencies or [])
215+
dependencies.add(
216+
self.job_config.downstream_package_name # include self
217+
)
218+
if missing_dependencies := sidetag.get_missing_dependencies(
219+
dependencies
220+
):
221+
raise PackitException(
222+
f"Missing dependencies for Bodhi update: {missing_dependencies}"
223+
)
224+
builds = " ".join(
225+
str(b) for b in sidetag.get_builds_suitable_for_update(dependencies)
296226
)
297-
else:
298-
sidetag = builds = None
299227

300228
BodhiUpdateTargetModel.create(
301229
target=koji_build_data.dist_git_branch,
302-
koji_nvrs=koji_build_data.nvr if not builds else " ".join(builds),
303-
sidetag=sidetag,
230+
koji_nvrs=koji_build_data.nvr if not builds else builds,
231+
sidetag=sidetag.koji_name if sidetag else None,
304232
status="queued",
305233
bodhi_update_group=group,
306234
)

packit_service/worker/handlers/distgit.py

Lines changed: 16 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
PackitDownloadFailedException,
2424
ReleaseSkippedPackitException,
2525
)
26-
from packit.utils.koji_helper import KojiHelper
2726
from packit_service import sentry_integration
2827
from packit_service.config import PackageConfigGetter, ServiceConfig
2928
from packit_service.constants import (
@@ -45,8 +44,6 @@
4544
KojiBuildTargetModel,
4645
PipelineModel,
4746
KojiBuildGroupModel,
48-
SidetagGroupModel,
49-
SidetagModel,
5047
)
5148
from packit_service.service.urls import (
5249
get_propose_downstream_info_url,
@@ -90,6 +87,7 @@
9087
RetriableJobHandler,
9188
)
9289
from packit_service.worker.handlers.mixin import GetProjectToSyncMixin
90+
from packit_service.worker.helpers.sidetag import SidetagHelper
9391
from packit_service.worker.helpers.sync_release.propose_downstream import (
9492
ProposeDownstreamJobHelper,
9593
)
@@ -716,8 +714,6 @@ class AbstractDownstreamKojiBuildHandler(
716714
topic = "org.fedoraproject.prod.pagure.git.receive"
717715
task_name = TaskName.downstream_koji_build
718716

719-
_koji_helper: Optional[KojiHelper] = None
720-
721717
def __init__(
722718
self,
723719
package_config: PackageConfig,
@@ -737,12 +733,6 @@ def __init__(
737733
self._packit_api = None
738734
self._koji_group_model_id = koji_group_model_id
739735

740-
@property
741-
def koji_helper(self):
742-
if not self._koji_helper:
743-
self._koji_helper = KojiHelper()
744-
return self._koji_helper
745-
746736
@staticmethod
747737
def get_checkers() -> Tuple[Type[Checker], ...]:
748738
return (
@@ -781,13 +771,6 @@ def get_branches(self) -> List[str]:
781771
def run(self) -> TaskResults:
782772
errors = {}
783773

784-
if self.job_config.sidetag_group:
785-
sidetag_group = SidetagGroupModel.get_or_create(
786-
self.job_config.sidetag_group
787-
)
788-
else:
789-
sidetag_group = None
790-
791774
group = self._get_or_create_koji_group_model()
792775
for koji_build_model in group.grouped_targets:
793776
branch = koji_build_model.target
@@ -804,32 +787,22 @@ def run(self) -> TaskResults:
804787
koji_build_model.set_status("pending")
805788

806789
try:
807-
if sidetag_group:
808-
sidetag = SidetagModel.get_or_create(sidetag_group, branch)
809-
if not sidetag.koji_name or not self.koji_helper.get_tag_info(
810-
sidetag.koji_name
790+
sidetag = None
791+
if self.job_config.sidetag_group:
792+
# we need Kerberos ticket to create a new sidetag
793+
self.packit_api.init_kerberos_ticket()
794+
sidetag = SidetagHelper.get_or_create_sidetag(
795+
self.job_config.sidetag_group, branch
796+
)
797+
# skip submitting build for a branch if dependencies
798+
# are not satisfied within a sidetag
799+
dependencies = set(self.job_config.dependencies or [])
800+
if missing_dependencies := sidetag.get_missing_dependencies(
801+
dependencies
811802
):
812-
# we need Kerberos ticket to create a new sidetag
813-
self.packit_api.init_kerberos_ticket()
814-
tag_info = self.koji_helper.create_sidetag(branch)
815-
if not tag_info:
816-
raise PackitException(
817-
f"Failed to create sidetag for {branch}"
818-
)
819-
sidetag.set_koji_name(tag_info["name"])
820-
else:
821-
sidetag = None
822-
823-
# skip submitting build for a branch if dependencies
824-
# are not satisfied within a sidetag
825-
if sidetag and self.job_config.dependencies:
826-
builds = self.koji_helper.get_builds_in_tag(sidetag.koji_name)
827-
tagged_packages = {b["package_name"] for b in builds}
828-
if not set(self.job_config.dependencies) <= tagged_packages:
829-
missing = set(self.job_config.dependencies) - tagged_packages
830803
logger.debug(
831804
f"Skipping downstream Koji build for branch {branch}, "
832-
f"missing dependencies: {missing}"
805+
f"missing dependencies: {missing_dependencies}"
833806
)
834807
koji_build_model.set_status("skipped")
835808
continue
@@ -1041,44 +1014,16 @@ class TagIntoSidetagHandler(
10411014
):
10421015
task_name = TaskName.tag_into_sidetag
10431016

1044-
_koji_helper: Optional[KojiHelper] = None
1045-
1046-
@property
1047-
def koji_helper(self):
1048-
if not self._koji_helper:
1049-
self._koji_helper = KojiHelper()
1050-
return self._koji_helper
1051-
10521017
@staticmethod
10531018
def get_checkers() -> Tuple[Type[Checker], ...]:
10541019
return (PermissionOnDistgit,)
10551020

10561021
def run_for_branch(self, job_config: JobConfig, branch: str) -> None:
1057-
sidetag_group = SidetagGroupModel.get_or_create(job_config.sidetag_group)
1058-
sidetag = SidetagModel.get_or_create(sidetag_group, branch)
10591022
# we need Kerberos ticket to tag a build into sidetag
10601023
# and to create a new sidetag (if needed)
10611024
self.packit_api.init_kerberos_ticket()
1062-
if not sidetag.koji_name or not self.koji_helper.get_tag_info(
1063-
sidetag.koji_name
1064-
):
1065-
tag_info = self.koji_helper.create_sidetag(branch)
1066-
if not tag_info:
1067-
logger.error(f"Failed to create sidetag for {branch}")
1068-
return
1069-
sidetag.set_koji_name(tag_info["name"])
1070-
if not (
1071-
nvr := self.koji_helper.get_latest_stable_nvr(
1072-
job_config.downstream_package_name, branch
1073-
)
1074-
):
1075-
logger.debug(
1076-
"Failed to get the latest stable build "
1077-
f"of {job_config.downstream_package_name} for {branch}"
1078-
)
1079-
return
1080-
logger.debug(f"Tagging {nvr} into {sidetag.koji_name}")
1081-
self.koji_helper.tag_build(nvr, sidetag.koji_name)
1025+
sidetag = SidetagHelper.get_or_create_sidetag(job_config.sidetag_group, branch)
1026+
sidetag.tag_latest_stable_build(job_config.downstream_package_name)
10821027

10831028
def run(self) -> TaskResults:
10841029
comment = self.data.event_dict.get("comment")

0 commit comments

Comments
 (0)