summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoffrey F <joffrey@docker.com>2018-01-08 18:11:29 -0800
committerJoffrey F <joffrey@docker.com>2018-01-23 16:59:09 -0800
commitf95b958429b38dab50929e013db3c636a12e1536 (patch)
treeb8de1cfd18d1c3c932592388143665f446e871f6
parentbf5e7702df3c835a5db4fc6b86500b4f4b659c14 (diff)
downloaddocker-py-1855-platform-option.tar.gz
Add support for experimental platform flag in build and pull1855-platform-option
Signed-off-by: Joffrey F <joffrey@docker.com>
-rw-r--r--docker/api/build.py10
-rw-r--r--docker/api/image.py13
-rw-r--r--docker/models/containers.py8
-rw-r--r--docker/models/images.py2
-rw-r--r--tests/integration/api_build_test.py15
-rw-r--r--tests/integration/api_image_test.py11
-rw-r--r--tests/unit/models_containers_test.py2
7 files changed, 54 insertions, 7 deletions
diff --git a/docker/api/build.py b/docker/api/build.py
index 34456ab..32238ef 100644
--- a/docker/api/build.py
+++ b/docker/api/build.py
@@ -19,7 +19,7 @@ class BuildApiMixin(object):
forcerm=False, dockerfile=None, container_limits=None,
decode=False, buildargs=None, gzip=False, shmsize=None,
labels=None, cache_from=None, target=None, network_mode=None,
- squash=None, extra_hosts=None):
+ squash=None, extra_hosts=None, platform=None):
"""
Similar to the ``docker build`` command. Either ``path`` or ``fileobj``
needs to be set. ``path`` can be a local path (to a directory
@@ -103,6 +103,7 @@ class BuildApiMixin(object):
single layer.
extra_hosts (dict): Extra hosts to add to /etc/hosts in building
containers, as a mapping of hostname to IP address.
+ platform (str): Platform in the format ``os[/arch[/variant]]``
Returns:
A generator for the build output.
@@ -243,6 +244,13 @@ class BuildApiMixin(object):
extra_hosts = utils.format_extra_hosts(extra_hosts)
params.update({'extrahosts': extra_hosts})
+ if platform is not None:
+ if utils.version_lt(self._version, '1.32'):
+ raise errors.InvalidVersion(
+ 'platform was only introduced in API version 1.32'
+ )
+ params['platform'] = platform
+
if context is not None:
headers = {'Content-Type': 'application/tar'}
if encoding:
diff --git a/docker/api/image.py b/docker/api/image.py
index 7755312..065fae3 100644
--- a/docker/api/image.py
+++ b/docker/api/image.py
@@ -323,7 +323,8 @@ class ImageApiMixin(object):
return self._result(self._post(url, params=params), True)
def pull(self, repository, tag=None, stream=False,
- insecure_registry=False, auth_config=None, decode=False):
+ insecure_registry=False, auth_config=None, decode=False,
+ platform=None):
"""
Pulls an image. Similar to the ``docker pull`` command.
@@ -336,6 +337,7 @@ class ImageApiMixin(object):
:py:meth:`~docker.api.daemon.DaemonApiMixin.login` has set for
this request. ``auth_config`` should contain the ``username``
and ``password`` keys to be valid.
+ platform (str): Platform in the format ``os[/arch[/variant]]``
Returns:
(generator or str): The output
@@ -376,7 +378,7 @@ class ImageApiMixin(object):
}
headers = {}
- if utils.compare_version('1.5', self._version) >= 0:
+ if utils.version_gte(self._version, '1.5'):
if auth_config is None:
header = auth.get_config_header(self, registry)
if header:
@@ -385,6 +387,13 @@ class ImageApiMixin(object):
log.debug('Sending supplied auth config')
headers['X-Registry-Auth'] = auth.encode_header(auth_config)
+ if platform is not None:
+ if utils.version_lt(self._version, '1.32'):
+ raise errors.InvalidVersion(
+ 'platform was only introduced in API version 1.32'
+ )
+ params['platform'] = platform
+
response = self._post(
self._url('/images/create'), params=params, headers=headers,
stream=stream, timeout=None
diff --git a/docker/models/containers.py b/docker/models/containers.py
index 6ba308e..5e2aa88 100644
--- a/docker/models/containers.py
+++ b/docker/models/containers.py
@@ -579,6 +579,8 @@ class ContainerCollection(Collection):
inside the container.
pids_limit (int): Tune a container's pids limit. Set ``-1`` for
unlimited.
+ platform (str): Platform in the format ``os[/arch[/variant]]``.
+ Only used if the method needs to pull the requested image.
ports (dict): Ports to bind inside the container.
The keys of the dictionary are the ports to bind inside the
@@ -700,7 +702,9 @@ class ContainerCollection(Collection):
if isinstance(image, Image):
image = image.id
stream = kwargs.pop('stream', False)
- detach = kwargs.pop("detach", False)
+ detach = kwargs.pop('detach', False)
+ platform = kwargs.pop('platform', None)
+
if detach and remove:
if version_gte(self.client.api._version, '1.25'):
kwargs["auto_remove"] = True
@@ -718,7 +722,7 @@ class ContainerCollection(Collection):
container = self.create(image=image, command=command,
detach=detach, **kwargs)
except ImageNotFound:
- self.client.images.pull(image)
+ self.client.images.pull(image, platform=platform)
container = self.create(image=image, command=command,
detach=detach, **kwargs)
diff --git a/docker/models/images.py b/docker/models/images.py
index 82ca541..891c565 100644
--- a/docker/models/images.py
+++ b/docker/models/images.py
@@ -157,6 +157,7 @@ class ImageCollection(Collection):
single layer.
extra_hosts (dict): Extra hosts to add to /etc/hosts in building
containers, as a mapping of hostname to IP address.
+ platform (str): Platform in the format ``os[/arch[/variant]]``.
Returns:
(:py:class:`Image`): The built image.
@@ -265,6 +266,7 @@ class ImageCollection(Collection):
:py:meth:`~docker.client.DockerClient.login` has set for
this request. ``auth_config`` should contain the ``username``
and ``password`` keys to be valid.
+ platform (str): Platform in the format ``os[/arch[/variant]]``
Returns:
(:py:class:`Image`): The image that has been pulled.
diff --git a/tests/integration/api_build_test.py b/tests/integration/api_build_test.py
index 7cc3234..245214e 100644
--- a/tests/integration/api_build_test.py
+++ b/tests/integration/api_build_test.py
@@ -377,3 +377,18 @@ class BuildTest(BaseAPIIntegrationTest):
def test_build_gzip_custom_encoding(self):
with self.assertRaises(errors.DockerException):
self.client.build(path='.', gzip=True, encoding='text/html')
+
+ @requires_api_version('1.32')
+ @requires_experimental(until=None)
+ def test_build_invalid_platform(self):
+ script = io.BytesIO('FROM busybox\n'.encode('ascii'))
+
+ with pytest.raises(errors.APIError) as excinfo:
+ stream = self.client.build(
+ fileobj=script, stream=True, platform='foobar'
+ )
+ for _ in stream:
+ pass
+
+ assert excinfo.value.status_code == 400
+ assert 'invalid platform' in excinfo.exconly()
diff --git a/tests/integration/api_image_test.py b/tests/integration/api_image_test.py
index 14fb77a..178c34e 100644
--- a/tests/integration/api_image_test.py
+++ b/tests/integration/api_image_test.py
@@ -14,7 +14,7 @@ from six.moves import socketserver
import docker
-from ..helpers import requires_api_version
+from ..helpers import requires_api_version, requires_experimental
from .base import BaseAPIIntegrationTest, BUSYBOX
@@ -67,6 +67,15 @@ class PullImageTest(BaseAPIIntegrationTest):
img_info = self.client.inspect_image('hello-world')
self.assertIn('Id', img_info)
+ @requires_api_version('1.32')
+ @requires_experimental(until=None)
+ def test_pull_invalid_platform(self):
+ with pytest.raises(docker.errors.APIError) as excinfo:
+ self.client.pull('hello-world', platform='foobar')
+
+ assert excinfo.value.status_code == 500
+ assert 'invalid platform' in excinfo.exconly()
+
class CommitTest(BaseAPIIntegrationTest):
def test_commit(self):
diff --git a/tests/unit/models_containers_test.py b/tests/unit/models_containers_test.py
index a479e83..95295a9 100644
--- a/tests/unit/models_containers_test.py
+++ b/tests/unit/models_containers_test.py
@@ -225,7 +225,7 @@ class ContainerCollectionTest(unittest.TestCase):
container = client.containers.run('alpine', 'sleep 300', detach=True)
assert container.id == FAKE_CONTAINER_ID
- client.api.pull.assert_called_with('alpine', tag=None)
+ client.api.pull.assert_called_with('alpine', platform=None, tag=None)
def test_run_with_error(self):
client = make_fake_client()