Merge pull request #2671 from aiordache/default_tag

Set image default tag on pull
This commit is contained in:
Anca Iordache 2020-10-16 11:35:16 +02:00 committed by GitHub
commit daa9f179c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 49 additions and 24 deletions

View File

@ -343,13 +343,14 @@ class ImageApiMixin(object):
return self._result(self._post(url, params=params), True) return self._result(self._post(url, params=params), True)
def pull(self, repository, tag=None, stream=False, auth_config=None, def pull(self, repository, tag=None, stream=False, auth_config=None,
decode=False, platform=None): decode=False, platform=None, all_tags=False):
""" """
Pulls an image. Similar to the ``docker pull`` command. Pulls an image. Similar to the ``docker pull`` command.
Args: Args:
repository (str): The repository to pull repository (str): The repository to pull
tag (str): The tag to pull tag (str): The tag to pull. If ``tag`` is ``None`` or empty, it
is set to ``latest``.
stream (bool): Stream the output as a generator. Make sure to stream (bool): Stream the output as a generator. Make sure to
consume the generator, otherwise pull might get cancelled. consume the generator, otherwise pull might get cancelled.
auth_config (dict): Override the credentials that are found in the auth_config (dict): Override the credentials that are found in the
@ -358,6 +359,8 @@ class ImageApiMixin(object):
decode (bool): Decode the JSON data from the server into dicts. decode (bool): Decode the JSON data from the server into dicts.
Only applies with ``stream=True`` Only applies with ``stream=True``
platform (str): Platform in the format ``os[/arch[/variant]]`` platform (str): Platform in the format ``os[/arch[/variant]]``
all_tags (bool): Pull all image tags, the ``tag`` parameter is
ignored.
Returns: Returns:
(generator or str): The output (generator or str): The output
@ -382,8 +385,12 @@ class ImageApiMixin(object):
} }
""" """
if not tag: repository, image_tag = utils.parse_repository_tag(repository)
repository, tag = utils.parse_repository_tag(repository) tag = tag or image_tag or 'latest'
if all_tags:
tag = None
registry, repo_name = auth.resolve_repository_name(repository) registry, repo_name = auth.resolve_repository_name(repository)
params = { params = {

View File

@ -395,12 +395,13 @@ class ImageCollection(Collection):
return [self.get(i) for i in images] return [self.get(i) for i in images]
def pull(self, repository, tag=None, **kwargs): def pull(self, repository, tag=None, all_tags=False, **kwargs):
""" """
Pull an image of the given name and return it. Similar to the Pull an image of the given name and return it. Similar to the
``docker pull`` command. ``docker pull`` command.
If no tag is specified, all tags from that repository will be If ``tag`` is ``None`` or empty, it is set to ``latest``.
pulled. If ``all_tags`` is set, the ``tag`` parameter is ignored and all image
tags will be pulled.
If you want to get the raw pull output, use the If you want to get the raw pull output, use the
:py:meth:`~docker.api.image.ImageApiMixin.pull` method in the :py:meth:`~docker.api.image.ImageApiMixin.pull` method in the
@ -413,10 +414,11 @@ class ImageCollection(Collection):
config for this request. ``auth_config`` should contain the config for this request. ``auth_config`` should contain the
``username`` and ``password`` keys to be valid. ``username`` and ``password`` keys to be valid.
platform (str): Platform in the format ``os[/arch[/variant]]`` platform (str): Platform in the format ``os[/arch[/variant]]``
all_tags (bool): Pull all image tags
Returns: Returns:
(:py:class:`Image` or list): The image that has been pulled. (:py:class:`Image` or list): The image that has been pulled.
If no ``tag`` was specified, the method will return a list If ``all_tags`` is True, the method will return a list
of :py:class:`Image` objects belonging to this repository. of :py:class:`Image` objects belonging to this repository.
Raises: Raises:
@ -426,13 +428,13 @@ class ImageCollection(Collection):
Example: Example:
>>> # Pull the image tagged `latest` in the busybox repo >>> # Pull the image tagged `latest` in the busybox repo
>>> image = client.images.pull('busybox:latest') >>> image = client.images.pull('busybox')
>>> # Pull all tags in the busybox repo >>> # Pull all tags in the busybox repo
>>> images = client.images.pull('busybox') >>> images = client.images.pull('busybox', all_tags=True)
""" """
if not tag: repository, image_tag = parse_repository_tag(repository)
repository, tag = parse_repository_tag(repository) tag = tag or image_tag or 'latest'
if 'stream' in kwargs: if 'stream' in kwargs:
warnings.warn( warnings.warn(
@ -442,14 +444,14 @@ class ImageCollection(Collection):
del kwargs['stream'] del kwargs['stream']
pull_log = self.client.api.pull( pull_log = self.client.api.pull(
repository, tag=tag, stream=True, **kwargs repository, tag=tag, stream=True, all_tags=all_tags, **kwargs
) )
for _ in pull_log: for _ in pull_log:
# We don't do anything with the logs, but we need # We don't do anything with the logs, but we need
# to keep the connection alive and wait for the image # to keep the connection alive and wait for the image
# to be pulled. # to be pulled.
pass pass
if tag: if not all_tags:
return self.get('{0}{2}{1}'.format( return self.get('{0}{2}{1}'.format(
repository, tag, '@' if tag.startswith('sha256:') else ':' repository, tag, '@' if tag.startswith('sha256:') else ':'
)) ))

View File

@ -42,7 +42,7 @@ class PullImageTest(BaseAPIIntegrationTest):
self.client.remove_image('hello-world') self.client.remove_image('hello-world')
except docker.errors.APIError: except docker.errors.APIError:
pass pass
res = self.client.pull('hello-world', tag='latest') res = self.client.pull('hello-world')
self.tmp_imgs.append('hello-world') self.tmp_imgs.append('hello-world')
assert type(res) == six.text_type assert type(res) == six.text_type
assert len(self.client.images('hello-world')) >= 1 assert len(self.client.images('hello-world')) >= 1
@ -55,7 +55,7 @@ class PullImageTest(BaseAPIIntegrationTest):
except docker.errors.APIError: except docker.errors.APIError:
pass pass
stream = self.client.pull( stream = self.client.pull(
'hello-world', tag='latest', stream=True, decode=True) 'hello-world', stream=True, decode=True)
self.tmp_imgs.append('hello-world') self.tmp_imgs.append('hello-world')
for chunk in stream: for chunk in stream:
assert isinstance(chunk, dict) assert isinstance(chunk, dict)

View File

@ -86,7 +86,7 @@ class ImageCollectionTest(BaseIntegrationTest):
def test_pull_multiple(self): def test_pull_multiple(self):
client = docker.from_env(version=TEST_API_VERSION) client = docker.from_env(version=TEST_API_VERSION)
images = client.images.pull('hello-world') images = client.images.pull('hello-world', all_tags=True)
assert len(images) >= 1 assert len(images) >= 1
assert any([ assert any([
'hello-world:latest' in img.attrs['RepoTags'] for img in images 'hello-world:latest' in img.attrs['RepoTags'] for img in images

View File

@ -67,7 +67,7 @@ class ImageTest(BaseAPIClientTest):
args = fake_request.call_args args = fake_request.call_args
assert args[0][1] == url_prefix + 'images/create' assert args[0][1] == url_prefix + 'images/create'
assert args[1]['params'] == { assert args[1]['params'] == {
'tag': None, 'fromImage': 'joffrey/test001' 'tag': 'latest', 'fromImage': 'joffrey/test001'
} }
assert not args[1]['stream'] assert not args[1]['stream']
@ -77,7 +77,7 @@ class ImageTest(BaseAPIClientTest):
args = fake_request.call_args args = fake_request.call_args
assert args[0][1] == url_prefix + 'images/create' assert args[0][1] == url_prefix + 'images/create'
assert args[1]['params'] == { assert args[1]['params'] == {
'tag': None, 'fromImage': 'joffrey/test001' 'tag': 'latest', 'fromImage': 'joffrey/test001'
} }
assert args[1]['stream'] assert args[1]['stream']

View File

@ -233,7 +233,7 @@ class ContainerCollectionTest(unittest.TestCase):
assert container.id == FAKE_CONTAINER_ID assert container.id == FAKE_CONTAINER_ID
client.api.pull.assert_called_with( client.api.pull.assert_called_with(
'alpine', platform=None, tag=None, stream=True 'alpine', platform=None, tag='latest', all_tags=False, stream=True
) )
def test_run_with_error(self): def test_run_with_error(self):

View File

@ -44,9 +44,25 @@ class ImageCollectionTest(unittest.TestCase):
def test_pull(self): def test_pull(self):
client = make_fake_client() client = make_fake_client()
image = client.images.pull('test_image:latest') image = client.images.pull('test_image:test')
client.api.pull.assert_called_with( client.api.pull.assert_called_with(
'test_image', tag='latest', stream=True 'test_image', tag='test', all_tags=False, stream=True
)
client.api.inspect_image.assert_called_with('test_image:test')
assert isinstance(image, Image)
assert image.id == FAKE_IMAGE_ID
def test_pull_tag_precedence(self):
client = make_fake_client()
image = client.images.pull('test_image:latest', tag='test')
client.api.pull.assert_called_with(
'test_image', tag='test', all_tags=False, stream=True
)
client.api.inspect_image.assert_called_with('test_image:test')
image = client.images.pull('test_image')
client.api.pull.assert_called_with(
'test_image', tag='latest', all_tags=False, stream=True
) )
client.api.inspect_image.assert_called_with('test_image:latest') client.api.inspect_image.assert_called_with('test_image:latest')
assert isinstance(image, Image) assert isinstance(image, Image)
@ -54,9 +70,9 @@ class ImageCollectionTest(unittest.TestCase):
def test_pull_multiple(self): def test_pull_multiple(self):
client = make_fake_client() client = make_fake_client()
images = client.images.pull('test_image') images = client.images.pull('test_image', all_tags=True)
client.api.pull.assert_called_with( client.api.pull.assert_called_with(
'test_image', tag=None, stream=True 'test_image', tag='latest', all_tags=True, stream=True
) )
client.api.images.assert_called_with( client.api.images.assert_called_with(
all=False, name='test_image', filters=None all=False, name='test_image', filters=None