From 7d30c5069150914aec8692516e3e03f8c3353500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Br=C3=B6mmelsiek?= Date: Wed, 23 Oct 2019 18:51:15 +0200 Subject: [PATCH 1/2] improve docker image name derivation to allow GitLab-like names --- binderhub/builder.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/binderhub/builder.py b/binderhub/builder.py index ece621fbf..22692e3ce 100644 --- a/binderhub/builder.py +++ b/binderhub/builder.py @@ -8,6 +8,7 @@ import string import time import escapism +import re import docker from tornado.concurrent import chain_future, Future @@ -187,6 +188,30 @@ async def fail(self, message): 'message': message + '\n', }) + @staticmethod + def _get_image_basename_and_tag(full_name): + """Get a supposed image name and tag without the registry part + + :param full_name: full image specification, e.g. "gitlab.com/user/project:tag" + :return: tuple of image name and tag, e.g. ("user/project", "tag") + """ + # the tag is either after the last (and only) colon, or not given at all, in which case "latest" is implied + tag_splits = full_name.rsplit(':', 1) + if len(tag_splits) == 2: + image_name = tag_splits[0] + tag = tag_splits[1] + else: + image_name = full_name + tag = 'latest' + + if re.fullmatch('[a-z0-9]{4,30}/[a-z0-9\._-]{2,255}', image_name): + # if it looks like a Docker Hub image name, we're done + return image_name, tag + else: + # if the image isn't implied to origin at Docker Hub, the first part has to be a registry + image_basename = '/'.join(image_name.split('/')[1:]) + return image_basename, tag + @authenticated async def get(self, provider_prefix, _unescaped_spec): """Get a built image for a given spec and repo provider. @@ -271,7 +296,7 @@ async def get(self, provider_prefix, _unescaped_spec): ).replace('_', '-').lower() if self.settings['use_registry']: - image_manifest = await self.registry.get_image_manifest(*'/'.join(image_name.split('/')[-2:]).split(':', 1)) + image_manifest = await self.registry.get_image_manifest(*self._get_image_basename_and_tag(image_name)) image_found = bool(image_manifest) else: # Check if the image exists locally! From 12e0327df8c87bf4fa8d620c1afe7acd3d406b0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Br=C3=B6mmelsiek?= Date: Wed, 23 Oct 2019 18:51:34 +0200 Subject: [PATCH 2/2] added image basename resolution test --- binderhub/tests/test_builder.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 binderhub/tests/test_builder.py diff --git a/binderhub/tests/test_builder.py b/binderhub/tests/test_builder.py new file mode 100644 index 000000000..905a2abe4 --- /dev/null +++ b/binderhub/tests/test_builder.py @@ -0,0 +1,17 @@ +import pytest +from binderhub import builder + + +@pytest.mark.parametrize("fullname,basename,tag", [ + ("jupyterhub/k8s-binderhub:0.2.0-a2079a5", "jupyterhub/k8s-binderhub", "0.2.0-a2079a5"), + ("jupyterhub/jupyterhub", "jupyterhub/jupyterhub", "latest"), + ("gcr.io/project/image:tag", "project/image", "tag"), + ("weirdregistry.com/image:tag", "image", "tag"), + ("gitlab-registry.example.com/group/project:some-tag", "group/project", "some-tag"), + ("gitlab-registry.example.com/group/project/image:latest", "group/project/image", "latest"), + ("gitlab-registry.example.com/group/project/my/image:rc1", "group/project/my/image", "rc1") +]) +def test_image_basename_resolution(fullname, basename, tag): + resulting_basename, resulting_tag = builder.BuildHandler._get_image_basename_and_tag(fullname) + assert resulting_basename == basename + assert resulting_tag == tag