We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Create single build updates in pending status for rawhide
Now that we have the automation in place to automatically move an update from pending to testing we should use that for single build updates too.

For that we need to change the automatic_update consumer (https://github.com/fedora-infra/bodhi/blob/develop/bodhi/server/consumers/automatic_updates.py) to create the update in the pending status. 

And make sure that the signed consumer will move the updates from pending to testing when the build is signed.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bodhi/server/consumers/automatic_updates.py`
Content:
```
1 # Copyright © 2019 Red Hat, Inc. and others.
2 #
3 # This file is part of Bodhi.
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License along with
16 # this program; if not, write to the Free Software Foundation, Inc., 51
17 # Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 """
19 The Bodhi handler that creates updates automatically from tagged builds.
20 
21 This module is responsible for the process of creating updates when builds are
22 tagged with certain tags.
23 """
24 
25 import logging
26 
27 import fedora_messaging
28 
29 from bodhi.server import buildsys
30 from bodhi.server.config import config
31 from bodhi.server.models import Build, ContentType, Package, Release, TestGatingStatus
32 from bodhi.server.models import Update, UpdateStatus, UpdateType, User
33 from bodhi.server.util import transactional_session_maker
34 
35 log = logging.getLogger('bodhi')
36 
37 
38 class AutomaticUpdateHandler:
39     """
40     The Bodhi Automatic Update Handler.
41 
42     A consumer that listens for messages about tagged builds and creates
43     updates from them.
44     """
45 
46     def __init__(self, db_factory: transactional_session_maker = None):
47         """
48         Initialize the Automatic Update Handler.
49 
50         Args:
51             db_factory: If given, used as the db_factory for this handler. If
52             None (the default), a new TransactionalSessionMaker is created and
53             used.
54         """
55         if not db_factory:
56             self.db_factory = transactional_session_maker()
57         else:
58             self.db_factory = db_factory
59 
60     def __call__(self, message: fedora_messaging.api.Message) -> None:
61         """Create updates from appropriately tagged builds.
62 
63         Args:
64             message: The message we are processing.
65         """
66         body = message.body
67 
68         missing = []
69         for mandatory in ('tag', 'build_id', 'name', 'version', 'release'):
70             if mandatory not in body:
71                 missing.append(mandatory)
72         if missing:
73             log.debug(f"Received incomplete tag message. Missing: {', '.join(missing)}")
74             return
75 
76         btag = body['tag']
77         bnvr = '{name}-{version}-{release}'.format(**body)
78 
79         koji = buildsys.get_session()
80 
81         kbuildinfo = koji.getBuild(bnvr)
82         if not kbuildinfo:
83             log.debug(f"Can't find Koji build for {bnvr}.")
84             return
85 
86         if 'nvr' not in kbuildinfo:
87             log.debug(f"Koji build info for {bnvr} doesn't contain 'nvr'.")
88             return
89 
90         if 'owner_name' not in kbuildinfo:
91             log.debug(f"Koji build info for {bnvr} doesn't contain 'owner_name'.")
92             return
93 
94         if kbuildinfo['owner_name'] in config.get('automatic_updates_blacklist'):
95             log.debug(f"{bnvr} owned by {kbuildinfo['owner_name']} who is listed in "
96                       "automatic_updates_blacklist, skipping.")
97             return
98 
99         # some APIs want the Koji build info, some others want the same
100         # wrapped in a larger (request?) structure
101         rbuildinfo = {
102             'info': kbuildinfo,
103             'nvr': kbuildinfo['nvr'].rsplit('-', 2),
104         }
105 
106         with self.db_factory() as dbsession:
107             rel = dbsession.query(Release).filter_by(create_automatic_updates=True,
108                                                      pending_testing_tag=btag).first()
109             if not rel:
110                 log.debug(f"Ignoring build being tagged into {btag!r}, no release configured for "
111                           "automatic updates for it found.")
112                 return
113 
114             bcls = ContentType.infer_content_class(Build, kbuildinfo)
115             build = bcls.get(bnvr)
116             if build and build.update:
117                 if build.update.status == UpdateStatus.pending:
118                     log.info(
119                         f"Build, active update for {bnvr} exists already "
120                         "in Pending, moving it along.")
121                     build.update.status = UpdateStatus.testing
122                     build.update.request = None
123                     dbsession.add(build)
124                     if config.get('test_gating.required'):
125                         log.debug(
126                             'Test gating is required, marking the update as waiting on test '
127                             'gating and updating it from Greenwave to get the real status.')
128                         build.update.test_gating_status = TestGatingStatus.waiting
129                         build.update.update_test_gating_status()
130                     dbsession.commit()
131                 else:
132                     log.info(f"Build, active update for {bnvr} exists already, skipping.")
133                 return
134 
135             if not build:
136                 log.debug(f"Build for {bnvr} doesn't exist yet, creating.")
137 
138                 # Package.get_or_create() infers content type already
139                 log.debug("Getting/creating related package object.")
140                 pkg = Package.get_or_create(rbuildinfo)
141 
142                 log.debug("Creating build object, adding it to the DB.")
143                 build = bcls(nvr=bnvr, package=pkg)
144                 dbsession.add(build)
145 
146             owner_name = kbuildinfo['owner_name']
147             user = User.get(owner_name)
148             if not user:
149                 log.debug(f"Creating bodhi user for '{owner_name}'.")
150                 # Leave email, groups blank, these will be filled
151                 # in or updated when they log into Bodhi next time, see
152                 # bodhi.server.security:remember_me().
153                 user = User(name=owner_name)
154                 dbsession.add(user)
155 
156             log.debug(f"Creating new update for {bnvr}.")
157             update = Update(
158                 release=rel,
159                 builds=[build],
160                 notes=f"Automatic update for {bnvr}.",
161                 type=UpdateType.unspecified,
162                 stable_karma=3,
163                 unstable_karma=-3,
164                 autokarma=False,
165                 user=user,
166                 status=UpdateStatus.testing,
167             )
168 
169             # Comment on the update that it was automatically created.
170             update.comment(
171                 dbsession,
172                 str("This update was automatically created"),
173                 author="bodhi",
174             )
175 
176             if config.get('test_gating.required'):
177                 log.debug(
178                     'Test gating required is enforced, marking the update as '
179                     'waiting on test gating and updating it from Greenwave to '
180                     'get the real status.')
181                 update.test_gating_status = TestGatingStatus.waiting
182                 update.update_test_gating_status()
183 
184             log.debug("Adding new update to the database.")
185             dbsession.add(update)
186 
187             log.debug("Committing changes to the database.")
188             dbsession.commit()
189
```

Path: `bodhi/server/consumers/signed.py`
Content:
```
1 # Copyright © 2016-2019 Red Hat, Inc.
2 #
3 # This file is part of Bodhi.
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License along with
16 # this program; if not, write to the Free Software Foundation, Inc., 51
17 # Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 """
19 The "signed handler".
20 
21 This module is responsible for marking builds as "signed" when they get moved
22 from the pending-signing to pending-updates-testing tag by RoboSignatory.
23 """
24 
25 import logging
26 
27 import fedora_messaging
28 from sqlalchemy import func
29 
30 from bodhi.server.models import Build, UpdateStatus
31 from bodhi.server.util import transactional_session_maker
32 
33 log = logging.getLogger('bodhi')
34 
35 
36 class SignedHandler(object):
37     """
38     The Bodhi Signed Handler.
39 
40     A fedora-messaging listener waiting for messages from koji about builds being tagged.
41     """
42 
43     def __init__(self):
44         """Initialize the SignedHandler."""
45         self.db_factory = transactional_session_maker()
46 
47     def __call__(self, message: fedora_messaging.api.Message):
48         """
49         Handle messages arriving with the configured topic.
50 
51         This marks a build as signed if it is assigned to the pending testing release tag.
52 
53         Example message format::
54             {
55                 'body': {
56                     'build_id': 442562,
57                     'name': 'colord',
58                     'tag_id': 214,
59                     'instance': 's390',
60                     'tag': 'f26-updates-testing-pending',
61                     'user': 'sharkcz',
62                     'version': '1.3.4',
63                     'owner': 'sharkcz',
64                     'release': '1.fc26'
65                 },
66             }
67 
68         The message can contain additional keys.
69 
70         Duplicate messages: this method is idempotent.
71 
72         Args:
73             message: The incoming message in the format described above.
74         """
75         message = message.body
76         build_nvr = '%(name)s-%(version)s-%(release)s' % message
77         tag = message['tag']
78 
79         log.info("%s tagged into %s" % (build_nvr, tag))
80 
81         with self.db_factory():
82             build = Build.get(build_nvr)
83             if not build:
84                 log.info("Build was not submitted, skipping")
85                 return
86 
87             if not build.release:
88                 log.info('Build is not assigned to release, skipping')
89                 return
90 
91             if build.update.from_tag:
92                 koji_testing_tag = build.release.get_testing_side_tag(build.update.from_tag)
93                 if tag != koji_testing_tag:
94                     log.info("Tag is not testing side tag, skipping")
95                     return
96             else:
97                 if build.release.pending_testing_tag != tag:
98                     log.info("Tag is not pending_testing tag, skipping")
99                     return
100 
101             if build.signed:
102                 log.info("Build was already marked as signed (maybe a duplicate message)")
103                 return
104 
105             # This build was moved into the pending_testing tag for the applicable release, which
106             # is done by RoboSignatory to indicate that the build has been correctly signed and
107             # written out. Mark it as such.
108             log.info("Build has been signed, marking")
109             build.signed = True
110             log.info("Build %s has been marked as signed" % build_nvr)
111 
112             # If every build in update is signed change status to testing
113             if build.update.from_tag and build.update.signed():
114                 log.info("Every build in update is signed, set status to testing")
115 
116                 build.update.status = UpdateStatus.testing
117                 build.update.date_testing = func.current_timestamp()
118                 build.update.request = None
119                 log.info(f"Update {build.update.display_name} status has been set to testing")
120
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/bodhi/server/consumers/automatic_updates.py b/bodhi/server/consumers/automatic_updates.py
--- a/bodhi/server/consumers/automatic_updates.py
+++ b/bodhi/server/consumers/automatic_updates.py
@@ -28,7 +28,7 @@
 
 from bodhi.server import buildsys
 from bodhi.server.config import config
-from bodhi.server.models import Build, ContentType, Package, Release, TestGatingStatus
+from bodhi.server.models import Build, ContentType, Package, Release
 from bodhi.server.models import Update, UpdateStatus, UpdateType, User
 from bodhi.server.util import transactional_session_maker
 
@@ -105,7 +105,7 @@
 
         with self.db_factory() as dbsession:
             rel = dbsession.query(Release).filter_by(create_automatic_updates=True,
-                                                     pending_testing_tag=btag).first()
+                                                     candidate_tag=btag).first()
             if not rel:
                 log.debug(f"Ignoring build being tagged into {btag!r}, no release configured for "
                           "automatic updates for it found.")
@@ -114,22 +114,7 @@
             bcls = ContentType.infer_content_class(Build, kbuildinfo)
             build = bcls.get(bnvr)
             if build and build.update:
-                if build.update.status == UpdateStatus.pending:
-                    log.info(
-                        f"Build, active update for {bnvr} exists already "
-                        "in Pending, moving it along.")
-                    build.update.status = UpdateStatus.testing
-                    build.update.request = None
-                    dbsession.add(build)
-                    if config.get('test_gating.required'):
-                        log.debug(
-                            'Test gating is required, marking the update as waiting on test '
-                            'gating and updating it from Greenwave to get the real status.')
-                        build.update.test_gating_status = TestGatingStatus.waiting
-                        build.update.update_test_gating_status()
-                    dbsession.commit()
-                else:
-                    log.info(f"Build, active update for {bnvr} exists already, skipping.")
+                log.info(f"Build, active update for {bnvr} exists already, skipping.")
                 return
 
             if not build:
@@ -163,7 +148,7 @@
                 unstable_karma=-3,
                 autokarma=False,
                 user=user,
-                status=UpdateStatus.testing,
+                status=UpdateStatus.pending,
             )
 
             # Comment on the update that it was automatically created.
@@ -173,14 +158,6 @@
                 author="bodhi",
             )
 
-            if config.get('test_gating.required'):
-                log.debug(
-                    'Test gating required is enforced, marking the update as '
-                    'waiting on test gating and updating it from Greenwave to '
-                    'get the real status.')
-                update.test_gating_status = TestGatingStatus.waiting
-                update.update_test_gating_status()
-
             log.debug("Adding new update to the database.")
             dbsession.add(update)
 
diff --git a/bodhi/server/consumers/signed.py b/bodhi/server/consumers/signed.py
--- a/bodhi/server/consumers/signed.py
+++ b/bodhi/server/consumers/signed.py
@@ -27,7 +27,8 @@
 import fedora_messaging
 from sqlalchemy import func
 
-from bodhi.server.models import Build, UpdateStatus
+from bodhi.server.config import config
+from bodhi.server.models import Build, UpdateStatus, TestGatingStatus
 from bodhi.server.util import transactional_session_maker
 
 log = logging.getLogger('bodhi')
@@ -110,10 +111,17 @@
             log.info("Build %s has been marked as signed" % build_nvr)
 
             # If every build in update is signed change status to testing
-            if build.update.from_tag and build.update.signed():
+            if not build.update.release.composed_by_bodhi and build.update.signed():
                 log.info("Every build in update is signed, set status to testing")
 
                 build.update.status = UpdateStatus.testing
                 build.update.date_testing = func.current_timestamp()
                 build.update.request = None
+
+                if config.get("test_gating.required"):
+                    log.debug('Test gating is required, marking the update as waiting on test '
+                              'gating and updating it from Greenwave to get the real status.')
+                    build.update.test_gating_status = TestGatingStatus.waiting
+                    build.update.update_test_gating_status()
+
                 log.info(f"Update {build.update.display_name} status has been set to testing")

{"golden_diff": "diff --git a/bodhi/server/consumers/automatic_updates.py b/bodhi/server/consumers/automatic_updates.py\n--- a/bodhi/server/consumers/automatic_updates.py\n+++ b/bodhi/server/consumers/automatic_updates.py\n@@ -28,7 +28,7 @@\n \n from bodhi.server import buildsys\n from bodhi.server.config import config\n-from bodhi.server.models import Build, ContentType, Package, Release, TestGatingStatus\n+from bodhi.server.models import Build, ContentType, Package, Release\n from bodhi.server.models import Update, UpdateStatus, UpdateType, User\n from bodhi.server.util import transactional_session_maker\n \n@@ -105,7 +105,7 @@\n \n         with self.db_factory() as dbsession:\n             rel = dbsession.query(Release).filter_by(create_automatic_updates=True,\n-                                                     pending_testing_tag=btag).first()\n+                                                     candidate_tag=btag).first()\n             if not rel:\n                 log.debug(f\"Ignoring build being tagged into {btag!r}, no release configured for \"\n                           \"automatic updates for it found.\")\n@@ -114,22 +114,7 @@\n             bcls = ContentType.infer_content_class(Build, kbuildinfo)\n             build = bcls.get(bnvr)\n             if build and build.update:\n-                if build.update.status == UpdateStatus.pending:\n-                    log.info(\n-                        f\"Build, active update for {bnvr} exists already \"\n-                        \"in Pending, moving it along.\")\n-                    build.update.status = UpdateStatus.testing\n-                    build.update.request = None\n-                    dbsession.add(build)\n-                    if config.get('test_gating.required'):\n-                        log.debug(\n-                            'Test gating is required, marking the update as waiting on test '\n-                            'gating and updating it from Greenwave to get the real status.')\n-                        build.update.test_gating_status = TestGatingStatus.waiting\n-                        build.update.update_test_gating_status()\n-                    dbsession.commit()\n-                else:\n-                    log.info(f\"Build, active update for {bnvr} exists already, skipping.\")\n+                log.info(f\"Build, active update for {bnvr} exists already, skipping.\")\n                 return\n \n             if not build:\n@@ -163,7 +148,7 @@\n                 unstable_karma=-3,\n                 autokarma=False,\n                 user=user,\n-                status=UpdateStatus.testing,\n+                status=UpdateStatus.pending,\n             )\n \n             # Comment on the update that it was automatically created.\n@@ -173,14 +158,6 @@\n                 author=\"bodhi\",\n             )\n \n-            if config.get('test_gating.required'):\n-                log.debug(\n-                    'Test gating required is enforced, marking the update as '\n-                    'waiting on test gating and updating it from Greenwave to '\n-                    'get the real status.')\n-                update.test_gating_status = TestGatingStatus.waiting\n-                update.update_test_gating_status()\n-\n             log.debug(\"Adding new update to the database.\")\n             dbsession.add(update)\n \ndiff --git a/bodhi/server/consumers/signed.py b/bodhi/server/consumers/signed.py\n--- a/bodhi/server/consumers/signed.py\n+++ b/bodhi/server/consumers/signed.py\n@@ -27,7 +27,8 @@\n import fedora_messaging\n from sqlalchemy import func\n \n-from bodhi.server.models import Build, UpdateStatus\n+from bodhi.server.config import config\n+from bodhi.server.models import Build, UpdateStatus, TestGatingStatus\n from bodhi.server.util import transactional_session_maker\n \n log = logging.getLogger('bodhi')\n@@ -110,10 +111,17 @@\n             log.info(\"Build %s has been marked as signed\" % build_nvr)\n \n             # If every build in update is signed change status to testing\n-            if build.update.from_tag and build.update.signed():\n+            if not build.update.release.composed_by_bodhi and build.update.signed():\n                 log.info(\"Every build in update is signed, set status to testing\")\n \n                 build.update.status = UpdateStatus.testing\n                 build.update.date_testing = func.current_timestamp()\n                 build.update.request = None\n+\n+                if config.get(\"test_gating.required\"):\n+                    log.debug('Test gating is required, marking the update as waiting on test '\n+                              'gating and updating it from Greenwave to get the real status.')\n+                    build.update.test_gating_status = TestGatingStatus.waiting\n+                    build.update.update_test_gating_status()\n+\n                 log.info(f\"Update {build.update.display_name} status has been set to testing\")\n", "issue": "Create single build updates in pending status for rawhide\nNow that we have the automation in place to automatically move an update from pending to testing we should use that for single build updates too.\r\n\r\nFor that we need to change the automatic_update consumer (https://github.com/fedora-infra/bodhi/blob/develop/bodhi/server/consumers/automatic_updates.py) to create the update in the pending status. \r\n\r\nAnd make sure that the signed consumer will move the updates from pending to testing when the build is signed.\n", "before_files": [{"content": "# Copyright \u00a9 2019 Red Hat, Inc. and others.\n#\n# This file is part of Bodhi.\n#\n# This program is free software; you can redistribute it and/or\n# modify it under the terms of the GNU General Public License\n# as published by the Free Software Foundation; either version 2\n# of the License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n\"\"\"\nThe Bodhi handler that creates updates automatically from tagged builds.\n\nThis module is responsible for the process of creating updates when builds are\ntagged with certain tags.\n\"\"\"\n\nimport logging\n\nimport fedora_messaging\n\nfrom bodhi.server import buildsys\nfrom bodhi.server.config import config\nfrom bodhi.server.models import Build, ContentType, Package, Release, TestGatingStatus\nfrom bodhi.server.models import Update, UpdateStatus, UpdateType, User\nfrom bodhi.server.util import transactional_session_maker\n\nlog = logging.getLogger('bodhi')\n\n\nclass AutomaticUpdateHandler:\n    \"\"\"\n    The Bodhi Automatic Update Handler.\n\n    A consumer that listens for messages about tagged builds and creates\n    updates from them.\n    \"\"\"\n\n    def __init__(self, db_factory: transactional_session_maker = None):\n        \"\"\"\n        Initialize the Automatic Update Handler.\n\n        Args:\n            db_factory: If given, used as the db_factory for this handler. If\n            None (the default), a new TransactionalSessionMaker is created and\n            used.\n        \"\"\"\n        if not db_factory:\n            self.db_factory = transactional_session_maker()\n        else:\n            self.db_factory = db_factory\n\n    def __call__(self, message: fedora_messaging.api.Message) -> None:\n        \"\"\"Create updates from appropriately tagged builds.\n\n        Args:\n            message: The message we are processing.\n        \"\"\"\n        body = message.body\n\n        missing = []\n        for mandatory in ('tag', 'build_id', 'name', 'version', 'release'):\n            if mandatory not in body:\n                missing.append(mandatory)\n        if missing:\n            log.debug(f\"Received incomplete tag message. Missing: {', '.join(missing)}\")\n            return\n\n        btag = body['tag']\n        bnvr = '{name}-{version}-{release}'.format(**body)\n\n        koji = buildsys.get_session()\n\n        kbuildinfo = koji.getBuild(bnvr)\n        if not kbuildinfo:\n            log.debug(f\"Can't find Koji build for {bnvr}.\")\n            return\n\n        if 'nvr' not in kbuildinfo:\n            log.debug(f\"Koji build info for {bnvr} doesn't contain 'nvr'.\")\n            return\n\n        if 'owner_name' not in kbuildinfo:\n            log.debug(f\"Koji build info for {bnvr} doesn't contain 'owner_name'.\")\n            return\n\n        if kbuildinfo['owner_name'] in config.get('automatic_updates_blacklist'):\n            log.debug(f\"{bnvr} owned by {kbuildinfo['owner_name']} who is listed in \"\n                      \"automatic_updates_blacklist, skipping.\")\n            return\n\n        # some APIs want the Koji build info, some others want the same\n        # wrapped in a larger (request?) structure\n        rbuildinfo = {\n            'info': kbuildinfo,\n            'nvr': kbuildinfo['nvr'].rsplit('-', 2),\n        }\n\n        with self.db_factory() as dbsession:\n            rel = dbsession.query(Release).filter_by(create_automatic_updates=True,\n                                                     pending_testing_tag=btag).first()\n            if not rel:\n                log.debug(f\"Ignoring build being tagged into {btag!r}, no release configured for \"\n                          \"automatic updates for it found.\")\n                return\n\n            bcls = ContentType.infer_content_class(Build, kbuildinfo)\n            build = bcls.get(bnvr)\n            if build and build.update:\n                if build.update.status == UpdateStatus.pending:\n                    log.info(\n                        f\"Build, active update for {bnvr} exists already \"\n                        \"in Pending, moving it along.\")\n                    build.update.status = UpdateStatus.testing\n                    build.update.request = None\n                    dbsession.add(build)\n                    if config.get('test_gating.required'):\n                        log.debug(\n                            'Test gating is required, marking the update as waiting on test '\n                            'gating and updating it from Greenwave to get the real status.')\n                        build.update.test_gating_status = TestGatingStatus.waiting\n                        build.update.update_test_gating_status()\n                    dbsession.commit()\n                else:\n                    log.info(f\"Build, active update for {bnvr} exists already, skipping.\")\n                return\n\n            if not build:\n                log.debug(f\"Build for {bnvr} doesn't exist yet, creating.\")\n\n                # Package.get_or_create() infers content type already\n                log.debug(\"Getting/creating related package object.\")\n                pkg = Package.get_or_create(rbuildinfo)\n\n                log.debug(\"Creating build object, adding it to the DB.\")\n                build = bcls(nvr=bnvr, package=pkg)\n                dbsession.add(build)\n\n            owner_name = kbuildinfo['owner_name']\n            user = User.get(owner_name)\n            if not user:\n                log.debug(f\"Creating bodhi user for '{owner_name}'.\")\n                # Leave email, groups blank, these will be filled\n                # in or updated when they log into Bodhi next time, see\n                # bodhi.server.security:remember_me().\n                user = User(name=owner_name)\n                dbsession.add(user)\n\n            log.debug(f\"Creating new update for {bnvr}.\")\n            update = Update(\n                release=rel,\n                builds=[build],\n                notes=f\"Automatic update for {bnvr}.\",\n                type=UpdateType.unspecified,\n                stable_karma=3,\n                unstable_karma=-3,\n                autokarma=False,\n                user=user,\n                status=UpdateStatus.testing,\n            )\n\n            # Comment on the update that it was automatically created.\n            update.comment(\n                dbsession,\n                str(\"This update was automatically created\"),\n                author=\"bodhi\",\n            )\n\n            if config.get('test_gating.required'):\n                log.debug(\n                    'Test gating required is enforced, marking the update as '\n                    'waiting on test gating and updating it from Greenwave to '\n                    'get the real status.')\n                update.test_gating_status = TestGatingStatus.waiting\n                update.update_test_gating_status()\n\n            log.debug(\"Adding new update to the database.\")\n            dbsession.add(update)\n\n            log.debug(\"Committing changes to the database.\")\n            dbsession.commit()\n", "path": "bodhi/server/consumers/automatic_updates.py"}, {"content": "# Copyright \u00a9 2016-2019 Red Hat, Inc.\n#\n# This file is part of Bodhi.\n#\n# This program is free software; you can redistribute it and/or\n# modify it under the terms of the GNU General Public License\n# as published by the Free Software Foundation; either version 2\n# of the License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n\"\"\"\nThe \"signed handler\".\n\nThis module is responsible for marking builds as \"signed\" when they get moved\nfrom the pending-signing to pending-updates-testing tag by RoboSignatory.\n\"\"\"\n\nimport logging\n\nimport fedora_messaging\nfrom sqlalchemy import func\n\nfrom bodhi.server.models import Build, UpdateStatus\nfrom bodhi.server.util import transactional_session_maker\n\nlog = logging.getLogger('bodhi')\n\n\nclass SignedHandler(object):\n    \"\"\"\n    The Bodhi Signed Handler.\n\n    A fedora-messaging listener waiting for messages from koji about builds being tagged.\n    \"\"\"\n\n    def __init__(self):\n        \"\"\"Initialize the SignedHandler.\"\"\"\n        self.db_factory = transactional_session_maker()\n\n    def __call__(self, message: fedora_messaging.api.Message):\n        \"\"\"\n        Handle messages arriving with the configured topic.\n\n        This marks a build as signed if it is assigned to the pending testing release tag.\n\n        Example message format::\n            {\n                'body': {\n                    'build_id': 442562,\n                    'name': 'colord',\n                    'tag_id': 214,\n                    'instance': 's390',\n                    'tag': 'f26-updates-testing-pending',\n                    'user': 'sharkcz',\n                    'version': '1.3.4',\n                    'owner': 'sharkcz',\n                    'release': '1.fc26'\n                },\n            }\n\n        The message can contain additional keys.\n\n        Duplicate messages: this method is idempotent.\n\n        Args:\n            message: The incoming message in the format described above.\n        \"\"\"\n        message = message.body\n        build_nvr = '%(name)s-%(version)s-%(release)s' % message\n        tag = message['tag']\n\n        log.info(\"%s tagged into %s\" % (build_nvr, tag))\n\n        with self.db_factory():\n            build = Build.get(build_nvr)\n            if not build:\n                log.info(\"Build was not submitted, skipping\")\n                return\n\n            if not build.release:\n                log.info('Build is not assigned to release, skipping')\n                return\n\n            if build.update.from_tag:\n                koji_testing_tag = build.release.get_testing_side_tag(build.update.from_tag)\n                if tag != koji_testing_tag:\n                    log.info(\"Tag is not testing side tag, skipping\")\n                    return\n            else:\n                if build.release.pending_testing_tag != tag:\n                    log.info(\"Tag is not pending_testing tag, skipping\")\n                    return\n\n            if build.signed:\n                log.info(\"Build was already marked as signed (maybe a duplicate message)\")\n                return\n\n            # This build was moved into the pending_testing tag for the applicable release, which\n            # is done by RoboSignatory to indicate that the build has been correctly signed and\n            # written out. Mark it as such.\n            log.info(\"Build has been signed, marking\")\n            build.signed = True\n            log.info(\"Build %s has been marked as signed\" % build_nvr)\n\n            # If every build in update is signed change status to testing\n            if build.update.from_tag and build.update.signed():\n                log.info(\"Every build in update is signed, set status to testing\")\n\n                build.update.status = UpdateStatus.testing\n                build.update.date_testing = func.current_timestamp()\n                build.update.request = None\n                log.info(f\"Update {build.update.display_name} status has been set to testing\")\n", "path": "bodhi/server/consumers/signed.py"}], "after_files": [{"content": "# Copyright \u00a9 2019 Red Hat, Inc. and others.\n#\n# This file is part of Bodhi.\n#\n# This program is free software; you can redistribute it and/or\n# modify it under the terms of the GNU General Public License\n# as published by the Free Software Foundation; either version 2\n# of the License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n\"\"\"\nThe Bodhi handler that creates updates automatically from tagged builds.\n\nThis module is responsible for the process of creating updates when builds are\ntagged with certain tags.\n\"\"\"\n\nimport logging\n\nimport fedora_messaging\n\nfrom bodhi.server import buildsys\nfrom bodhi.server.config import config\nfrom bodhi.server.models import Build, ContentType, Package, Release\nfrom bodhi.server.models import Update, UpdateStatus, UpdateType, User\nfrom bodhi.server.util import transactional_session_maker\n\nlog = logging.getLogger('bodhi')\n\n\nclass AutomaticUpdateHandler:\n    \"\"\"\n    The Bodhi Automatic Update Handler.\n\n    A consumer that listens for messages about tagged builds and creates\n    updates from them.\n    \"\"\"\n\n    def __init__(self, db_factory: transactional_session_maker = None):\n        \"\"\"\n        Initialize the Automatic Update Handler.\n\n        Args:\n            db_factory: If given, used as the db_factory for this handler. If\n            None (the default), a new TransactionalSessionMaker is created and\n            used.\n        \"\"\"\n        if not db_factory:\n            self.db_factory = transactional_session_maker()\n        else:\n            self.db_factory = db_factory\n\n    def __call__(self, message: fedora_messaging.api.Message) -> None:\n        \"\"\"Create updates from appropriately tagged builds.\n\n        Args:\n            message: The message we are processing.\n        \"\"\"\n        body = message.body\n\n        missing = []\n        for mandatory in ('tag', 'build_id', 'name', 'version', 'release'):\n            if mandatory not in body:\n                missing.append(mandatory)\n        if missing:\n            log.debug(f\"Received incomplete tag message. Missing: {', '.join(missing)}\")\n            return\n\n        btag = body['tag']\n        bnvr = '{name}-{version}-{release}'.format(**body)\n\n        koji = buildsys.get_session()\n\n        kbuildinfo = koji.getBuild(bnvr)\n        if not kbuildinfo:\n            log.debug(f\"Can't find Koji build for {bnvr}.\")\n            return\n\n        if 'nvr' not in kbuildinfo:\n            log.debug(f\"Koji build info for {bnvr} doesn't contain 'nvr'.\")\n            return\n\n        if 'owner_name' not in kbuildinfo:\n            log.debug(f\"Koji build info for {bnvr} doesn't contain 'owner_name'.\")\n            return\n\n        if kbuildinfo['owner_name'] in config.get('automatic_updates_blacklist'):\n            log.debug(f\"{bnvr} owned by {kbuildinfo['owner_name']} who is listed in \"\n                      \"automatic_updates_blacklist, skipping.\")\n            return\n\n        # some APIs want the Koji build info, some others want the same\n        # wrapped in a larger (request?) structure\n        rbuildinfo = {\n            'info': kbuildinfo,\n            'nvr': kbuildinfo['nvr'].rsplit('-', 2),\n        }\n\n        with self.db_factory() as dbsession:\n            rel = dbsession.query(Release).filter_by(create_automatic_updates=True,\n                                                     candidate_tag=btag).first()\n            if not rel:\n                log.debug(f\"Ignoring build being tagged into {btag!r}, no release configured for \"\n                          \"automatic updates for it found.\")\n                return\n\n            bcls = ContentType.infer_content_class(Build, kbuildinfo)\n            build = bcls.get(bnvr)\n            if build and build.update:\n                log.info(f\"Build, active update for {bnvr} exists already, skipping.\")\n                return\n\n            if not build:\n                log.debug(f\"Build for {bnvr} doesn't exist yet, creating.\")\n\n                # Package.get_or_create() infers content type already\n                log.debug(\"Getting/creating related package object.\")\n                pkg = Package.get_or_create(rbuildinfo)\n\n                log.debug(\"Creating build object, adding it to the DB.\")\n                build = bcls(nvr=bnvr, package=pkg)\n                dbsession.add(build)\n\n            owner_name = kbuildinfo['owner_name']\n            user = User.get(owner_name)\n            if not user:\n                log.debug(f\"Creating bodhi user for '{owner_name}'.\")\n                # Leave email, groups blank, these will be filled\n                # in or updated when they log into Bodhi next time, see\n                # bodhi.server.security:remember_me().\n                user = User(name=owner_name)\n                dbsession.add(user)\n\n            log.debug(f\"Creating new update for {bnvr}.\")\n            update = Update(\n                release=rel,\n                builds=[build],\n                notes=f\"Automatic update for {bnvr}.\",\n                type=UpdateType.unspecified,\n                stable_karma=3,\n                unstable_karma=-3,\n                autokarma=False,\n                user=user,\n                status=UpdateStatus.pending,\n            )\n\n            # Comment on the update that it was automatically created.\n            update.comment(\n                dbsession,\n                str(\"This update was automatically created\"),\n                author=\"bodhi\",\n            )\n\n            log.debug(\"Adding new update to the database.\")\n            dbsession.add(update)\n\n            log.debug(\"Committing changes to the database.\")\n            dbsession.commit()\n", "path": "bodhi/server/consumers/automatic_updates.py"}, {"content": "# Copyright \u00a9 2016-2019 Red Hat, Inc.\n#\n# This file is part of Bodhi.\n#\n# This program is free software; you can redistribute it and/or\n# modify it under the terms of the GNU General Public License\n# as published by the Free Software Foundation; either version 2\n# of the License, or (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n\"\"\"\nThe \"signed handler\".\n\nThis module is responsible for marking builds as \"signed\" when they get moved\nfrom the pending-signing to pending-updates-testing tag by RoboSignatory.\n\"\"\"\n\nimport logging\n\nimport fedora_messaging\nfrom sqlalchemy import func\n\nfrom bodhi.server.config import config\nfrom bodhi.server.models import Build, UpdateStatus, TestGatingStatus\nfrom bodhi.server.util import transactional_session_maker\n\nlog = logging.getLogger('bodhi')\n\n\nclass SignedHandler(object):\n    \"\"\"\n    The Bodhi Signed Handler.\n\n    A fedora-messaging listener waiting for messages from koji about builds being tagged.\n    \"\"\"\n\n    def __init__(self):\n        \"\"\"Initialize the SignedHandler.\"\"\"\n        self.db_factory = transactional_session_maker()\n\n    def __call__(self, message: fedora_messaging.api.Message):\n        \"\"\"\n        Handle messages arriving with the configured topic.\n\n        This marks a build as signed if it is assigned to the pending testing release tag.\n\n        Example message format::\n            {\n                'body': {\n                    'build_id': 442562,\n                    'name': 'colord',\n                    'tag_id': 214,\n                    'instance': 's390',\n                    'tag': 'f26-updates-testing-pending',\n                    'user': 'sharkcz',\n                    'version': '1.3.4',\n                    'owner': 'sharkcz',\n                    'release': '1.fc26'\n                },\n            }\n\n        The message can contain additional keys.\n\n        Duplicate messages: this method is idempotent.\n\n        Args:\n            message: The incoming message in the format described above.\n        \"\"\"\n        message = message.body\n        build_nvr = '%(name)s-%(version)s-%(release)s' % message\n        tag = message['tag']\n\n        log.info(\"%s tagged into %s\" % (build_nvr, tag))\n\n        with self.db_factory():\n            build = Build.get(build_nvr)\n            if not build:\n                log.info(\"Build was not submitted, skipping\")\n                return\n\n            if not build.release:\n                log.info('Build is not assigned to release, skipping')\n                return\n\n            if build.update.from_tag:\n                koji_testing_tag = build.release.get_testing_side_tag(build.update.from_tag)\n                if tag != koji_testing_tag:\n                    log.info(\"Tag is not testing side tag, skipping\")\n                    return\n            else:\n                if build.release.pending_testing_tag != tag:\n                    log.info(\"Tag is not pending_testing tag, skipping\")\n                    return\n\n            if build.signed:\n                log.info(\"Build was already marked as signed (maybe a duplicate message)\")\n                return\n\n            # This build was moved into the pending_testing tag for the applicable release, which\n            # is done by RoboSignatory to indicate that the build has been correctly signed and\n            # written out. Mark it as such.\n            log.info(\"Build has been signed, marking\")\n            build.signed = True\n            log.info(\"Build %s has been marked as signed\" % build_nvr)\n\n            # If every build in update is signed change status to testing\n            if not build.update.release.composed_by_bodhi and build.update.signed():\n                log.info(\"Every build in update is signed, set status to testing\")\n\n                build.update.status = UpdateStatus.testing\n                build.update.date_testing = func.current_timestamp()\n                build.update.request = None\n\n                if config.get(\"test_gating.required\"):\n                    log.debug('Test gating is required, marking the update as waiting on test '\n                              'gating and updating it from Greenwave to get the real status.')\n                    build.update.test_gating_status = TestGatingStatus.waiting\n                    build.update.update_test_gating_status()\n\n                log.info(f\"Update {build.update.display_name} status has been set to testing\")\n", "path": "bodhi/server/consumers/signed.py"}]}