Repository: fedora-infra/bodhi

Issue:
Celery worker used to tag builds are failing.
We have the following exception when trying to tag a side-tag update

```
[2020-03-23 13:46:12,096: ERROR/ForkPoolWorker-4] Task handle_side_and_related_tags[0049d0e8-6830-41e0-97c1-b4208714b70f] raised unexpected: AttributeError("'dict' object has no attribute 'release'")
Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/celery/app/trace.py", line 382, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/usr/lib/python3.7/site-packages/celery/app/trace.py", line 641, in __protected_call__
    return self.run(*args, **kwargs)
  File "/usr/lib/python3.7/site-packages/bodhi/server/tasks/__init__.py", line 134, in handle_side_and_related_tags_task
    main(updates, from_tag)
  File "/usr/lib/python3.7/site-packages/bodhi/server/tasks/handle_side_and_related_tags.py", line 39, in main
    release = update.release
AttributeError: 'dict' object has no attribute 'release'
```


Discussion:

Resolving patch:
diff --git a/bodhi/server/tasks/__init__.py b/bodhi/server/tasks/__init__.py
--- a/bodhi/server/tasks/__init__.py
+++ b/bodhi/server/tasks/__init__.py
@@ -27,9 +27,6 @@
 from bodhi.server.config import config
 from bodhi.server.util import pyfile_to_module
 
-if typing.TYPE_CHECKING:  # pragma: no cover
-    from bodhi.server.models import Update
-
 
 # Workaround https://github.com/celery/celery/issues/5416
 if celery.version_info < (4, 3) and sys.version_info >= (3, 7):  # pragma: no cover
@@ -126,7 +123,7 @@ def expire_overrides_task(**kwargs):
 
 
 @app.task(name="handle_side_and_related_tags")
-def handle_side_and_related_tags_task(updates: typing.List["Update"], from_tag: str):
+def handle_side_and_related_tags_task(updates: typing.List[dict], from_tag: str):
     """Handle side-tags and related tags for updates in Koji."""
     from .handle_side_and_related_tags import main
     log.info("Received an order for handling update tags")
@@ -135,7 +132,7 @@ def handle_side_and_related_tags_task(updates: typing.List["Update"], from_tag:
 
 
 @app.task(name="tag_update_builds")
-def tag_update_builds_task(update: "Update", builds: typing.List[str]):
+def tag_update_builds_task(update: dict, builds: typing.List[str]):
     """Handle tagging builds for an update in Koji."""
     from .tag_update_builds import main
     log.info("Received an order to tag builds for an update")
diff --git a/bodhi/server/tasks/handle_side_and_related_tags.py b/bodhi/server/tasks/handle_side_and_related_tags.py
--- a/bodhi/server/tasks/handle_side_and_related_tags.py
+++ b/bodhi/server/tasks/handle_side_and_related_tags.py
@@ -20,14 +20,13 @@
 import logging
 import typing
 
-from bodhi.server import buildsys
+from bodhi.server import buildsys, util
 from bodhi.server.models import Update
 
-
 log = logging.getLogger(__name__)
 
 
-def main(updates: typing.List[Update], from_tag: str):
+def main(updates: typing.List[dict], from_tag: str):
     """Handle side-tags and related tags for updates in Koji.
 
     Args:
@@ -35,31 +34,35 @@ def main(updates: typing.List[Update], from_tag: str):
         from_tag: the tag into which the builds were built
     """
     koji = buildsys.get_session()
-    for update in updates:
-        release = update.release
-        if not release.composed_by_bodhi:
-            # Before the Bodhi activation point of a release, keep builds tagged
-            # with the side-tag and its associate tags. Validate that
-            # <koji_tag>-pending-signing and <koji-tag>-testing exists, if not create
-            # them.
-            side_tag_signing_pending = update.release.get_pending_signing_side_tag(
-                from_tag
-            )
-            side_tag_testing_pending = update.release.get_testing_side_tag(from_tag)
-            if not koji.getTag(side_tag_signing_pending):
-                koji.createTag(side_tag_signing_pending, parent=from_tag)
-            if not koji.getTag(side_tag_testing_pending):
-                koji.createTag(side_tag_testing_pending, parent=from_tag)
-                koji.editTag2(side_tag_testing_pending, perm="autosign")
+    db_factory = util.transactional_session_maker()
+    with db_factory() as session:
+        for update_alias in updates:
+            update = session.query(Update).filter_by(alias=update_alias["alias"]).first()
+            if update is not None:
+                release = update.release
+                if not release.composed_by_bodhi:
+                    # Before the Bodhi activation point of a release, keep builds tagged
+                    # with the side-tag and its associate tags. Validate that
+                    # <koji_tag>-pending-signing and <koji-tag>-testing exists, if not create
+                    # them.
+                    side_tag_signing_pending = update.release.get_pending_signing_side_tag(
+                        from_tag
+                    )
+                    side_tag_testing_pending = update.release.get_testing_side_tag(from_tag)
+                    if not koji.getTag(side_tag_signing_pending):
+                        koji.createTag(side_tag_signing_pending, parent=from_tag)
+                    if not koji.getTag(side_tag_testing_pending):
+                        koji.createTag(side_tag_testing_pending, parent=from_tag)
+                        koji.editTag2(side_tag_testing_pending, perm="autosign")
 
-            to_tag = side_tag_signing_pending
-            # Move every new build to <from_tag>-signing-pending tag
-            update.add_tag(to_tag)
-        else:
-            # After the Bodhi activation point of a release, add the pending-signing tag
-            # of the release to funnel the builds back into a normal workflow for a
-            # stable release.
-            update.add_tag(release.pending_signing_tag)
+                    to_tag = side_tag_signing_pending
+                    # Move every new build to <from_tag>-signing-pending tag
+                    update.add_tag(to_tag)
+                else:
+                    # After the Bodhi activation point of a release, add the pending-signing tag
+                    # of the release to funnel the builds back into a normal workflow for a
+                    # stable release.
+                    update.add_tag(release.pending_signing_tag)
 
-            # From here on out, we don't need the side-tag anymore.
-            koji.removeSideTag(from_tag)
+                    # From here on out, we don't need the side-tag anymore.
+                    koji.removeSideTag(from_tag)
diff --git a/bodhi/server/tasks/tag_update_builds.py b/bodhi/server/tasks/tag_update_builds.py
--- a/bodhi/server/tasks/tag_update_builds.py
+++ b/bodhi/server/tasks/tag_update_builds.py
@@ -20,14 +20,14 @@
 import logging
 import typing
 
-from bodhi.server import buildsys
+from bodhi.server import buildsys, util
 from bodhi.server.models import Update
 
 
 log = logging.getLogger(__name__)
 
 
-def main(update: Update, builds: typing.List[str]):
+def main(update: dict, builds: typing.List[str]):
     """Handle tagging builds for an update in Koji.
 
     Args:
@@ -36,16 +36,21 @@ def main(update: Update, builds: typing.List[str]):
     """
     koji = buildsys.get_session()
     koji.multicall = True
-    for build in builds:
-        if update.from_tag:
-            # this is a sidetag based update. use the sidetag pending signing tag
-            side_tag_pending_signing = update.release.get_pending_signing_side_tag(update.from_tag)
-            koji.tagBuild(side_tag_pending_signing, build)
-        elif update.release.pending_signing_tag:
-            # Add the release's pending_signing_tag to all new builds
-            koji.tagBuild(update.release.pending_signing_tag, build)
-        else:
-            # EL6 doesn't have these, and that's okay...
-            # We still warn in case the config gets messed up.
-            log.warning(f'{update.release.name} has no pending_signing_tag')
-    koji.multiCall()
+    db_factory = util.transactional_session_maker()
+    with db_factory() as session:
+        update = session.query(Update).filter_by(alias=update["alias"]).first()
+        if update is not None:
+            for build in builds:
+                if update.from_tag:
+                    # this is a sidetag based update. use the sidetag pending signing tag
+                    side_tag_pending_signing = update.release.get_pending_signing_side_tag(
+                        update.from_tag)
+                    koji.tagBuild(side_tag_pending_signing, build)
+                elif update.release.pending_signing_tag:
+                    # Add the release's pending_signing_tag to all new builds
+                    koji.tagBuild(update.release.pending_signing_tag, build)
+                else:
+                    # EL6 doesn't have these, and that's okay...
+                    # We still warn in case the config gets messed up.
+                    log.warning(f'{update.release.name} has no pending_signing_tag')
+            koji.multiCall()


Test patch:
diff --git a/bodhi/tests/server/tasks/test_handle_side_and_related_tags.py b/bodhi/tests/server/tasks/test_handle_side_and_related_tags.py
--- a/bodhi/tests/server/tasks/test_handle_side_and_related_tags.py
+++ b/bodhi/tests/server/tasks/test_handle_side_and_related_tags.py
@@ -29,6 +29,7 @@ class TestMain(BaseTaskTestCase):
 
     def test_side_tag_composed_by_bodhi(self):
         updates = self.db.query(models.Update).all()
+        updates = [u.__json__() for u in updates]
         handle_srtags_main(updates, "f17-build-side-1234")
         koji = buildsys.get_session()
 
@@ -39,7 +40,7 @@ def test_side_tag_not_composed_by_bodhi(self):
         update = self.db.query(models.Update).first()
         update.release.composed_by_bodhi = False
         self.db.commit()
-        handle_srtags_main([update], "f32-build-side-1234")
+        handle_srtags_main([update.__json__()], "f32-build-side-1234")
         koji = buildsys.get_session()
 
         assert ('f32-build-side-1234-signing-pending', 'bodhi-2.0-1.fc17') in koji.__added__
diff --git a/bodhi/tests/server/tasks/test_tag_update_builds.py b/bodhi/tests/server/tasks/test_tag_update_builds.py
--- a/bodhi/tests/server/tasks/test_tag_update_builds.py
+++ b/bodhi/tests/server/tasks/test_tag_update_builds.py
@@ -31,7 +31,7 @@ class TestMain(BaseTaskTestCase):
     def test_tag_pending_signing_builds(self):
         update = self.db.query(models.Update).first()
         pending_signing_tag = update.release.pending_signing_tag
-        tag_update_builds_main(update, update.builds)
+        tag_update_builds_main(update.__json__(), update.builds)
         koji = buildsys.get_session()
         assert (pending_signing_tag, update.builds[0]) in koji.__added__
 
@@ -41,7 +41,7 @@ def test_tag_pending_signing_side_tag(self):
         side_tag_pending_signing = "f17-build-side-1234-signing-pending"
         self.db.commit()
 
-        tag_update_builds_main(update, update.builds)
+        tag_update_builds_main(update.__json__(), update.builds)
 
         koji = buildsys.get_session()
         assert (side_tag_pending_signing, update.builds[0]) in koji.__added__
@@ -52,6 +52,6 @@ def test_tag_release_no_pending_signing_tag(self, caplog):
         update = self.db.query(models.Update).first()
         update.release.pending_signing_tag = ""
         self.db.commit()
-        tag_update_builds_main(update, update.builds)
+        tag_update_builds_main(update.__json__(), update.builds)
 
         assert f'{update.release.name} has no pending_signing_tag' in caplog.messages