Skip to content

Commit

Permalink
Add methods for /distribution/<name>/json endpoint
Browse files Browse the repository at this point in the history
Signed-off-by: Joffrey F <[email protected]>
  • Loading branch information
shin- committed Mar 29, 2018
1 parent f39c0dc commit bedabbf
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 1 deletion.
21 changes: 21 additions & 0 deletions docker/api/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,27 @@ def inspect_image(self, image):
self._get(self._url("/images/{0}/json", image)), True
)

@utils.minimum_version('1.30')
@utils.check_resource('image')
def inspect_distribution(self, image):
"""
Get image digest and platform information by contacting the registry.
Args:
image (str): The image name to inspect
Returns:
(dict): A dict containing distribution data
Raises:
:py:class:`docker.errors.APIError`
If the server returns an error.
"""

return self._result(
self._get(self._url("/distribution/{0}/json", image)), True
)

def load_image(self, data, quiet=None):
"""
Load an image that was previously saved using
Expand Down
107 changes: 106 additions & 1 deletion docker/models/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from ..api import APIClient
from ..constants import DEFAULT_DATA_CHUNK_SIZE
from ..errors import BuildError, ImageLoadError
from ..errors import BuildError, ImageLoadError, InvalidArgument
from ..utils import parse_repository_tag
from ..utils.json_stream import json_stream
from .resource import Collection, Model
Expand Down Expand Up @@ -105,6 +105,81 @@ def tag(self, repository, tag=None, **kwargs):
return self.client.api.tag(self.id, repository, tag=tag, **kwargs)


class RegistryData(Model):
"""
Image metadata stored on the registry, including available platforms.
"""
def __init__(self, image_name, *args, **kwargs):
super(RegistryData, self).__init__(*args, **kwargs)
self.image_name = image_name

@property
def id(self):
"""
The ID of the object.
"""
return self.attrs['Descriptor']['digest']

@property
def short_id(self):
"""
The ID of the image truncated to 10 characters, plus the ``sha256:``
prefix.
"""
return self.id[:17]

def pull(self, platform=None):
"""
Pull the image digest.
Args:
platform (str): The platform to pull the image for.
Default: ``None``
Returns:
(:py:class:`Image`): A reference to the pulled image.
"""
repository, _ = parse_repository_tag(self.image_name)
return self.collection.pull(repository, tag=self.id, platform=platform)

def has_platform(self, platform):
"""
Check whether the given platform identifier is available for this
digest.
Args:
platform (str or dict): A string using the ``os[/arch[/variant]]``
format, or a platform dictionary.
Returns:
(bool): ``True`` if the platform is recognized as available,
``False`` otherwise.
Raises:
:py:class:`docker.errors.InvalidArgument`
If the platform argument is not a valid descriptor.
"""
if platform and not isinstance(platform, dict):
parts = platform.split('/')
if len(parts) > 3 or len(parts) < 1:
raise InvalidArgument(
'"{0}" is not a valid platform descriptor'.format(platform)
)
platform = {'os': parts[0]}
if len(parts) > 2:
platform['variant'] = parts[2]
if len(parts) > 1:
platform['architecture'] = parts[1]
return normalize_platform(
platform, self.client.version()
) in self.attrs['Platforms']

def reload(self):
self.attrs = self.client.api.inspect_distribution(self.image_name)

reload.__doc__ = Model.reload.__doc__


class ImageCollection(Collection):
model = Image

Expand Down Expand Up @@ -219,6 +294,26 @@ def get(self, name):
"""
return self.prepare_model(self.client.api.inspect_image(name))

def get_registry_data(self, name):
"""
Gets the registry data for an image.
Args:
name (str): The name of the image.
Returns:
(:py:class:`RegistryData`): The data object.
Raises:
:py:class:`docker.errors.APIError`
If the server returns an error.
"""
return RegistryData(
image_name=name,
attrs=self.client.api.inspect_distribution(name),
client=self.client,
collection=self,
)

def list(self, name=None, all=False, filters=None):
"""
List images on the server.
Expand Down Expand Up @@ -336,3 +431,13 @@ def search(self, *args, **kwargs):
def prune(self, filters=None):
return self.client.api.prune_images(filters=filters)
prune.__doc__ = APIClient.prune_images.__doc__


def normalize_platform(platform, engine_info):
if platform is None:
platform = {}
if 'os' not in platform:
platform['os'] = engine_info['Os']
if 'architecture' not in platform:
platform['architecture'] = engine_info['Arch']
return platform
19 changes: 19 additions & 0 deletions docs/images.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Methods available on ``client.images``:
.. automethod:: build
.. automethod:: get
.. automethod:: get_registry_data
.. automethod:: list(**kwargs)
.. automethod:: load
.. automethod:: prune
Expand Down Expand Up @@ -41,3 +42,21 @@ Image objects
.. automethod:: reload
.. automethod:: save
.. automethod:: tag

RegistryData objects
--------------------

.. autoclass:: RegistryData()

.. py:attribute:: attrs
The raw representation of this object from the server.

.. autoattribute:: id
.. autoattribute:: short_id



.. automethod:: has_platform
.. automethod:: pull
.. automethod:: reload
9 changes: 9 additions & 0 deletions tests/integration/api_image_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,12 @@ def test_get_image_load_image(self):
success = True
break
assert success is True


@requires_api_version('1.30')
class InspectDistributionTest(BaseAPIIntegrationTest):
def test_inspect_distribution(self):
data = self.client.inspect_distribution('busybox:latest')
assert data is not None
assert 'Platforms' in data
assert {'os': 'linux', 'architecture': 'amd64'} in data['Platforms']

0 comments on commit bedabbf

Please sign in to comment.