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

Set image default tag on pull #2671

Merged
merged 2 commits into from
Oct 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 11 additions & 4 deletions docker/api/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,13 +343,14 @@ def prune_images(self, filters=None):
return self._result(self._post(url, params=params), True)

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.

Args:
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
consume the generator, otherwise pull might get cancelled.
auth_config (dict): Override the credentials that are found in the
Expand All @@ -358,6 +359,8 @@ def pull(self, repository, tag=None, stream=False, auth_config=None,
decode (bool): Decode the JSON data from the server into dicts.
Only applies with ``stream=True``
platform (str): Platform in the format ``os[/arch[/variant]]``
all_tags (bool): Pull all image tags, the ``tag`` parameter is
ignored.

Returns:
(generator or str): The output
Expand All @@ -382,8 +385,12 @@ def pull(self, repository, tag=None, stream=False, auth_config=None,
}

"""
if not tag:
repository, tag = utils.parse_repository_tag(repository)
repository, image_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)

params = {
Expand Down
22 changes: 12 additions & 10 deletions docker/models/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,12 +395,13 @@ def load(self, data):

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
``docker pull`` command.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wording of the docstring should be updated to make it more clear. E.g.:

If no tag is specified, all tags from that repository will be pulled.

should be

If tag is None, all tags from that repository will be pulled.

If no tag is specified, all tags from that repository will be
pulled.
If ``tag`` is ``None`` or empty, it is set to ``latest``.
If ``all_tags`` is set, the ``tag`` parameter is ignored and all image
tags will be pulled.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though it's intuitive, it's probably good to add explicit documentation that if tag is None or empty, then it will be set to "latest". Also in the api.image module.


If you want to get the raw pull output, use the
:py:meth:`~docker.api.image.ImageApiMixin.pull` method in the
Expand All @@ -413,10 +414,11 @@ def pull(self, repository, tag=None, **kwargs):
config for this request. ``auth_config`` should contain the
``username`` and ``password`` keys to be valid.
platform (str): Platform in the format ``os[/arch[/variant]]``
all_tags (bool): Pull all image tags

Returns:
(: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.

Raises:
Expand All @@ -426,13 +428,13 @@ def pull(self, repository, tag=None, **kwargs):
Example:

>>> # 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
>>> images = client.images.pull('busybox')
>>> images = client.images.pull('busybox', all_tags=True)
"""
if not tag:
repository, tag = parse_repository_tag(repository)
repository, image_tag = parse_repository_tag(repository)
tag = tag or image_tag or 'latest'

if 'stream' in kwargs:
warnings.warn(
Expand All @@ -442,14 +444,14 @@ def pull(self, repository, tag=None, **kwargs):
del kwargs['stream']

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:
# We don't do anything with the logs, but we need
# to keep the connection alive and wait for the image
# to be pulled.
pass
if tag:
if not all_tags:
return self.get('{0}{2}{1}'.format(
repository, tag, '@' if tag.startswith('sha256:') else ':'
))
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/api_image_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_pull(self):
self.client.remove_image('hello-world')
except docker.errors.APIError:
pass
res = self.client.pull('hello-world', tag='latest')
res = self.client.pull('hello-world')
self.tmp_imgs.append('hello-world')
assert type(res) == six.text_type
assert len(self.client.images('hello-world')) >= 1
Expand All @@ -55,7 +55,7 @@ def test_pull_streaming(self):
except docker.errors.APIError:
pass
stream = self.client.pull(
'hello-world', tag='latest', stream=True, decode=True)
'hello-world', stream=True, decode=True)
self.tmp_imgs.append('hello-world')
for chunk in stream:
assert isinstance(chunk, dict)
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/models_images_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def test_pull_with_sha(self):

def test_pull_multiple(self):
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 any([
'hello-world:latest' in img.attrs['RepoTags'] for img in images
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/api_image_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def test_pull(self):
args = fake_request.call_args
assert args[0][1] == url_prefix + 'images/create'
assert args[1]['params'] == {
'tag': None, 'fromImage': 'joffrey/test001'
'tag': 'latest', 'fromImage': 'joffrey/test001'
}
assert not args[1]['stream']

Expand All @@ -77,7 +77,7 @@ def test_pull_stream(self):
args = fake_request.call_args
assert args[0][1] == url_prefix + 'images/create'
assert args[1]['params'] == {
'tag': None, 'fromImage': 'joffrey/test001'
'tag': 'latest', 'fromImage': 'joffrey/test001'
}
assert args[1]['stream']

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/models_containers_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ def test_run_pull(self):

assert container.id == FAKE_CONTAINER_ID
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):
Expand Down
24 changes: 20 additions & 4 deletions tests/unit/models_images_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,35 @@ def test_load(self):

def test_pull(self):
client = make_fake_client()
image = client.images.pull('test_image:latest')
image = client.images.pull('test_image:test')
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')
assert isinstance(image, Image)
assert image.id == FAKE_IMAGE_ID

def test_pull_multiple(self):
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(
'test_image', tag=None, stream=True
'test_image', tag='latest', all_tags=True, stream=True
)
client.api.images.assert_called_with(
all=False, name='test_image', filters=None
Expand Down