Merge pull request #175 from mukundans91/validateImageName

Added regex pattern based validation for image name argument
pull/179/head
Yuvi Panda 2017-12-23 15:11:02 -08:00 zatwierdzone przez GitHub
commit 5293080add
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
3 zmienionych plików z 188 dodań i 2 usunięć

Wyświetl plik

@ -30,7 +30,7 @@ from .buildpacks import (
PythonBuildPack, DockerBuildPack, LegacyBinderDockerBuildPack,
CondaBuildPack, JuliaBuildPack, Python2BuildPack, BaseImage
)
from .utils import execute_cmd, ByteSpecification, maybe_cleanup
from .utils import execute_cmd, ByteSpecification, maybe_cleanup, is_valid_docker_image_name
from . import __version__
@ -180,6 +180,25 @@ class Repo2Docker(Application):
extra=dict(phase='failed'))
sys.exit(1)
def validate_image_name(self, image_name):
"""
Validate image_name read by argparse contains only lowercase characters
Args:
image_name (string): argument read by the argument parser
Returns:
unmodified image_name
Raises:
ArgumentTypeError: if image_name contains characters that are not lowercase
"""
if not is_valid_docker_image_name(image_name):
msg = "%r is not a valid docker image name. Image name can contain only lowercase characters." % image_name
raise argparse.ArgumentTypeError(msg)
return image_name
def get_argparser(self):
argparser = argparse.ArgumentParser()
argparser.add_argument(
@ -204,7 +223,8 @@ class Repo2Docker(Application):
argparser.add_argument(
'--image-name',
help=('Name of image to be built. If unspecified will be '
'autogenerated')
'autogenerated'),
type=self.validate_image_name
)
argparser.add_argument(

Wyświetl plik

@ -2,6 +2,7 @@ from contextlib import contextmanager
from functools import partial
import shutil
import subprocess
import re
from traitlets import Integer
@ -56,6 +57,81 @@ def maybe_cleanup(path, cleanup=False):
shutil.rmtree(path, ignore_errors=True)
def is_valid_docker_image_name(image_name):
"""
Function that constructs a regex representing the docker image name and tests it against the given image_name
Reference Regex definition in https://github.com/docker/distribution/blob/master/reference/regexp.go
Args:
image_name: string representing a docker image name
Returns:
True if image_name is valid else False
Example:
'test.Com/name:latest' is a valid tag
'Test/name:latest' is not a valid tag
Note:
This function has a stricter pattern than https://github.com/docker/distribution/blob/master/reference/regexp.go
This pattern will not allow cases like
'TEST.com/name:latest' though docker considers it a valid tag
"""
reference_regex = re.compile(r"""^ # Anchored at start and end of string
( # Start capturing name
(?: # start grouping the optional registry domain name part
(?:[a-z0-9]|[a-z0-9][a-z0-9-]*[a-z0-9]) # lowercase only '<domain-name-component>'
(?: # start optional group
(?:\.(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+ # multiple repetitions of pattern '.<domain-name-component>'
)? # end optional grouping part of the '.' separated domain name
(?::[0-9]+)?/ # '<domain-name>' followed by an optional '<port>' component followed by '/' literal
)? # end grouping the optional registry domain part
# start <name-pattern>
[a-z0-9]+ # must have a <name-component>
(?:
(?:(?:[\._]|__|[-]*)[a-z0-9]+)+ # repeat the pattern '<separator><name-component>'
)? # optionally have multiple repetitions of the above line
# end <name-pattern>
(?: # start optional name components
(?: # start multiple repetitions
/ # separate multiple name components by /
# start <name-pattern>
[a-z0-9]+ # must have a <name-component>
(?:
(?:(?:[\._]|__|[-]*)[a-z0-9]+)+ # repeat the pattern '<separator><name-component>'
)? # optionally have multiple repetitions of the above line
# end <name-pattern>
)+ # multiple repetitions of the pattern '/<name-component><separator><name-component>'
)? # optionally have the above group
) # end capturing name
(?::([\w][\w.-]{0,127}))? # optional capture <tag-pattern>=':<tag>'
(?:@[A-Za-z][A-Za-z0-9]*(?:[-_+.][A-Za-z][A-Za-z0-9]*)*[:][[:xdigit:]]{32,})? # optionally capture <digest-pattern>='@<digest>'
$""",
re.VERBOSE)
return reference_regex.match(image_name) is not None
class ByteSpecification(Integer):
"""
Allow easily specifying bytes in units of 1024 with suffixes

Wyświetl plik

@ -0,0 +1,90 @@
"""
Tests that runs validity checks on arguments passed in from shell
"""
import os
import subprocess
def does_validate_image_name(builddir, image_name):
try:
output = subprocess.check_output(
[
'repo2docker',
'--no-run',
'--no-build',
'--image-name',
str(image_name),
builddir
],
stderr=subprocess.STDOUT,
).decode()
return True
except subprocess.CalledProcessError as e:
output = e.output.decode()
if "error: argument --image-name: %r is not a valid docker image name. " \
"Image name can contain only lowercase characters." % image_name in output:
return False
else:
raise
def test_image_name_fail():
"""
Test to check if repo2docker throws image_name validation error on --image-name argument containing
uppercase characters and _ characters in incorrect positions.
"""
builddir = os.path.dirname(__file__)
assert not does_validate_image_name(builddir, 'Test/Invalid_name:1.0.0')
def test_image_name_underscore_fail():
"""
Test to check if repo2docker throws image_name validation error on --image-name argument starts with _.
"""
builddir = os.path.dirname(__file__)
assert not does_validate_image_name(builddir, '_test/invalid_name:1.0.0')
def test_image_name_double_dot_fail():
"""
Test to check if repo2docker throws image_name validation error on --image-name argument contains consecutive dots.
"""
builddir = os.path.dirname(__file__)
assert not does_validate_image_name(builddir, 'test..com/invalid_name:1.0.0')
def test_image_name_valid_restircted_registry_domain_name_fail():
"""
Test to check if repo2docker throws image_name validation error on -image-name argument being invalid. Based on the
regex definitions first part of registry domain cannot contain uppercase characters
"""
builddir = os.path.dirname(__file__)
assert not does_validate_image_name(builddir, 'Test.com/valid_name:1.0.0')
def test_image_name_valid_registry_domain_name_success():
"""
Test to check if repo2docker runs with a valid --image-name argument.
"""
builddir = os.path.dirname(__file__) + '/dockerfile/simple/'
assert does_validate_image_name(builddir, 'test.COM/valid_name:1.0.0')
def test_image_name_valid_name_success():
"""
Test to check if repo2docker runs with a valid --image-name argument.
"""
builddir = os.path.dirname(__file__) + '/dockerfile/simple/'
assert does_validate_image_name(builddir, 'test.com/valid_name:1.0.0')