Repository: fedora-infra/bodhi

Issue:
Bodhi does not select the correct pull URL for containers and flatpaks
While testing bodhi-3.11.0b7 today in stg, I found that the flatpak and container composes both failed due to using the wrong URLs for the containers:

```
Nov 13 22:57:12 bodhi-backend01.stg.phx2.fedoraproject.org fedmsg-hub-3[3981]: [2018-11-13 22:57:12][bodhi.server   ERROR]f29-flatpak-updates-testing /usr/bin/bodhi-skopeo-lite copy docker://candidate-registry.s
tg.fedoraproject.org/eog@sha256:master-2920181113043343.1 docker://registry.stg.fedoraproject.org/eog@sha256:master-2920181113043343.1 returned a non-0 exit code: 1                                              
Nov 13 22:57:12 bodhi-backend01.stg.phx2.fedoraproject.org fedmsg-hub-3[3981]: [2018-11-13 22:57:12][bodhi.server   ERROR]f28-container-updates-testing /usr/bin/bodhi-skopeo-lite copy docker://candidate-registry
.stg.fedoraproject.org/f28/origin-base@sha256:3.9-1 docker://registry.stg.fedoraproject.org/f28/origin-base@sha256:3.9-1 returned a non-0 exit code: 1
Nov 13 22:57:12 bodhi-backend01.stg.phx2.fedoraproject.org fedmsg-hub-3[3981]: [2018-11-13 22:57:12][bodhi.server   ERROR]f29-flatpak-updates-testing b''                                                         
Nov 13 22:57:12 bodhi-backend01.stg.phx2.fedoraproject.org fedmsg-hub-3[3981]: b'Traceback (most recent call last):\n  File "/usr/bin/bodhi-skopeo-lite", line 11, in <module>\n    load_entry_point(\'bodhi-server
==3.11.0b7\', \'console_scripts\', \'bodhi-skopeo-lite\')()\n  File "/usr/lib/python3.7/site-packages/click/core.py", line 721, in __call__\n    return self.main(*args, **kwargs)\n  File "/usr/lib/python3.7/site
-packages/click/core.py", line 696, in main\n    rv = self.invoke(ctx)\n  File "/usr/lib/python3.7/site-packages/click/core.py", line 1065, in invoke\n    return _process_result(sub_ctx.command.invoke(sub_ctx))\
n  File "/usr/lib/python3.7/site-packages/click/core.py", line 894, in invoke\n    return ctx.invoke(self.callback, **ctx.params)\n  File "/usr/lib/python3.7/site-packages/click/core.py", line 534, in invoke\n 
  return callback(*args, **kwargs)\n  File "/usr/lib/python3.7/site-packages/bodhi/server/scripts/skopeo_lite.py", line 769, in copy\n    Copier(src.get_endpoint(), tmp).copy()\n  File "/usr/lib/python3.7/site-p
ackages/bodhi/server/scripts/skopeo_lite.py", line 726, in copy\n    info = self.src.get_manifest()\n  File "/usr/lib/python3.7/site-packages/bodhi/server/scripts/skopeo_lite.py", line 633, in get_manifest\n   
return get_manifest(self.session, self.repo, self.tag)\n  File "/usr/lib/python3.7/site-packages/bodhi/server/scripts/skopeo_lite.py", line 345, in get_manifest\n    response.raise_for_status()\n  File "/usr/lib
/python3.7/site-packages/requests/models.py", line 940, in raise_for_status\n    raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://candidate-registry.stg.fedoraproject.org/v2/eog@sha256/manifests/master-2920181113043343.1\n'                                                                                                                           
Nov 13 22:57:12 bodhi-backend01.stg.phx2.fedoraproject.org fedmsg-hub-3[3981]: [2018-11-13 22:57:12][bodhi.server   ERROR]f28-container-updates-testing b''                                                       
Nov 13 22:57:12 bodhi-backend01.stg.phx2.fedoraproject.org fedmsg-hub-3[3981]: b'Traceback (most recent call last):\n  File "/usr/bin/bodhi-skopeo-lite", line 11, in <module>\n    load_entry_point(\'bodhi-server==3.11.0b7\', \'console_scripts\', \'bodhi-skopeo-lite\')()\n  File "/usr/lib/python3.7/site-packages/click/core.py", line 721, in __call__\n    return self.main(*args, **kwargs)\n  File "/usr/lib/python3.7/site
-packages/click/core.py", line 696, in main\n    rv = self.invoke(ctx)\n  File "/usr/lib/python3.7/site-packages/click/core.py", line 1065, in invoke\n    return _process_result(sub_ctx.command.invoke(sub_ctx))\
n  File "/usr/lib/python3.7/site-packages/click/core.py", line 894, in invoke\n    return ctx.invoke(self.callback, **ctx.params)\n  File "/usr/lib/python3.7/site-packages/click/core.py", line 534, in invoke\n 
  return callback(*args, **kwargs)\n  File "/usr/lib/python3.7/site-packages/bodhi/server/scripts/skopeo_lite.py", line 769, in copy\n    Copier(src.get_endpoint(), tmp).copy()\n  File "/usr/lib/python3.7/site-p
ackages/bodhi/server/scripts/skopeo_lite.py", line 726, in copy\n    info = self.src.get_manifest()\n  File "/usr/lib/python3.7/site-packages/bodhi/server/scripts/skopeo_lite.py", line 633, in get_manifest\n   
return get_manifest(self.session, self.repo, self.tag)\n  File "/usr/lib/python3.7/site-packages/bodhi/server/scripts/skopeo_lite.py", line 345, in get_manifest\n    response.raise_for_status()\n  File "/usr/lib
/python3.7/site-packages/requests/models.py", line 940, in raise_for_status\n    raise HTTPError(http_error_msg, response=self)\nrequests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://candid
ate-registry.stg.fedoraproject.org/v2/f28/origin-base@sha256/manifests/3.9-1\n'
```

Note that it is including ```@sha256``` in the repository name.


Discussion:
CC @owtaylor @cverna 
@owtaylor I feel like I remember looking in Koji and agreeing that the patch was correct, including its documentation that the format was like ```candidate-registry.fedoraproject.org/f29/cockpit:176-5.fc28```, but I guess I didn't notice that pesky ```@sha256```. When I look at that [example container](https://koji.fedoraproject.org/koji/buildinfo?buildID=1141835), I see this:

```['candidate-registry.fedoraproject.org/f29/cockpit@sha256:2bd64a888535a6b0428a3cfcaa4aa7cec5dd3e3bbb4ed84e315e985e2d5c6bb5', 'candidate-registry.fedoraproject.org/f29/cockpit:176-5.fc28']```

It seems that the [Bodhi code](https://github.com/fedora-infra/bodhi/blob/719a1458700a4d18085d474228679812b067065c/bodhi/server/util.py#L1429-L1451) just uses the first element of that array:

```
    koji = buildsys.get_session()
    koji_build = koji.getBuild(build.nvr)

    pull_specs = koji_build['extra']['image']['index']['pull']
    # Specs are of the form 'candidate-registry.fedoraproject.org/f29/cockpit:176-5.fc28'
    # All the pull specs should have the same repository, so which one we use is arbitrary
    base, tag = pull_specs[0].split(':', 1)
    server, repository = base.split('/', 1)
```

The problem is that the first one has the ```@sha256``` in it, at least sometimes. Is there any documentation on the format being stored there, or what order the elements are in? It seems we want the second one in this case.

Without knowing whether the order is consistent, I suppose we could try to detect the @ character and also strip off anything to the right of it, or use it to know to try the next element.
Another solution I can think of is that we do know the NVR, so we could loop on the list until we find one that has the expected VR from the NVR in it. Maybe that's not really better than searching for the @ character and killing it, but just another idea.

Resolving patch:
diff --git a/bodhi/server/buildsys.py b/bodhi/server/buildsys.py
--- a/bodhi/server/buildsys.py
+++ b/bodhi/server/buildsys.py
@@ -277,9 +277,9 @@ def getBuild(self, build='TurboGears-1.0.2.2-2.fc17', other=False, testing=False
                     'container_koji_task_id': 19708268,
                     'image': {
                         'index': {
-                            'pull': ['{registry}/{repository}:{hash}'
+                            'pull': ['{registry}/{repository}@sha256:{hash}'
                                      .format(**format_data),
-                                     '{registry}/{repository}:{version}:{release}'
+                                     '{registry}/{repository}:{version}-{release}'
                                      .format(**format_data)],
                         }
                     },
diff --git a/bodhi/server/util.py b/bodhi/server/util.py
--- a/bodhi/server/util.py
+++ b/bodhi/server/util.py
@@ -25,6 +25,7 @@
 import json
 import os
 import pkg_resources
+import re
 import socket
 import subprocess
 import tempfile
@@ -1428,11 +1429,11 @@ def __exit__(self, *args, **kwargs):
 
 def _get_build_repository(build):
     """
-    Return the registry repository name for the given Build.
+    Return the registry repository name for the given Build from the container's pull string.
 
-    For example, if the Build's container pull string is:
-
-    'candidate-registry.fedoraproject.org/f29/cockpit:176-5.fc28', this will return 'f29/cockpit'.
+    Examples -
+    'candidate-registry.fedoraproject.org/f29/cockpit:176-5.fc28' => 'f29/cockpit'.
+    'candidate-registry.fedoraproject.org/myrepo@sha256:<hash>' => 'myrepo'.
 
     Args:
         build (bodhi.server.models.Build): A Build representing a container or flatpak.
@@ -1443,9 +1444,8 @@ def _get_build_repository(build):
     koji_build = koji.getBuild(build.nvr)
 
     pull_specs = koji_build['extra']['image']['index']['pull']
-    # Specs are of the form 'candidate-registry.fedoraproject.org/f29/cockpit:176-5.fc28'
     # All the pull specs should have the same repository, so which one we use is arbitrary
-    base, tag = pull_specs[0].split(':', 1)
+    base, tag = re.compile(r'[:@]').split(pull_specs[0], 1)
     server, repository = base.split('/', 1)
 
     return repository


Test patch:
diff --git a/bodhi/tests/server/test_util.py b/bodhi/tests/server/test_util.py
--- a/bodhi/tests/server/test_util.py
+++ b/bodhi/tests/server/test_util.py
@@ -1458,6 +1458,30 @@ def test_err_zero_return_code(self, mock_popen, mock_error, mock_debug):
         mock_error.assert_not_called()
         mock_debug.assert_called_with('output\nerror')
 
+    @mock.patch('bodhi.server.buildsys.get_session')
+    def test__get_build_repository(self, session):
+        session.return_value = mock.Mock()
+        session.return_value.getBuild.side_effect = [
+            {
+                'extra': {
+                    'image': {
+                        'index': {
+                            'pull': [pullspec]
+                        }
+                    }
+                }
+            }
+            for pullspec in [
+                'candidate-registry.fedoraproject.org/f29/cockpit:176-5.fc28',
+                'candidate-registry.fedoraproject.org/myrepo@sha256:abcdefg123456'
+            ]
+        ]
+        build = mock.Mock()
+        build.nvr = 'cockpit-167-5'
+        self.assertEqual(util._get_build_repository(build), 'f29/cockpit')
+        self.assertEqual(util._get_build_repository(build), 'myrepo')
+        session.return_value.getBuild.assert_called_with('cockpit-167-5')
+
 
 class TestTransactionalSessionMaker(base.BaseTestCase):
     """This class contains tests on the TransactionalSessionMaker class."""