Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for 3+-part image names (like on GitLab) #986

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion binderhub/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import string
import time
import escapism
import re

import docker
from tornado.concurrent import chain_future, Future
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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!
Expand Down
17 changes: 17 additions & 0 deletions binderhub/tests/test_builder.py
Original file line number Diff line number Diff line change
@@ -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