Skip to content

Commit a15a1d2

Browse files
authored
Merge pull request #1856 from docker/1855-platform-option
Add support for experimental platform flag in build and pull
2 parents f85f31a + f95b958 commit a15a1d2

File tree

7 files changed

+54
-7
lines changed

7 files changed

+54
-7
lines changed

docker/api/build.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def build(self, path=None, tag=None, quiet=False, fileobj=None,
1919
forcerm=False, dockerfile=None, container_limits=None,
2020
decode=False, buildargs=None, gzip=False, shmsize=None,
2121
labels=None, cache_from=None, target=None, network_mode=None,
22-
squash=None, extra_hosts=None):
22+
squash=None, extra_hosts=None, platform=None):
2323
"""
2424
Similar to the ``docker build`` command. Either ``path`` or ``fileobj``
2525
needs to be set. ``path`` can be a local path (to a directory
@@ -103,6 +103,7 @@ def build(self, path=None, tag=None, quiet=False, fileobj=None,
103103
single layer.
104104
extra_hosts (dict): Extra hosts to add to /etc/hosts in building
105105
containers, as a mapping of hostname to IP address.
106+
platform (str): Platform in the format ``os[/arch[/variant]]``
106107
107108
Returns:
108109
A generator for the build output.
@@ -243,6 +244,13 @@ def build(self, path=None, tag=None, quiet=False, fileobj=None,
243244
extra_hosts = utils.format_extra_hosts(extra_hosts)
244245
params.update({'extrahosts': extra_hosts})
245246

247+
if platform is not None:
248+
if utils.version_lt(self._version, '1.32'):
249+
raise errors.InvalidVersion(
250+
'platform was only introduced in API version 1.32'
251+
)
252+
params['platform'] = platform
253+
246254
if context is not None:
247255
headers = {'Content-Type': 'application/tar'}
248256
if encoding:

docker/api/image.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,8 @@ def prune_images(self, filters=None):
323323
return self._result(self._post(url, params=params), True)
324324

325325
def pull(self, repository, tag=None, stream=False,
326-
insecure_registry=False, auth_config=None, decode=False):
326+
insecure_registry=False, auth_config=None, decode=False,
327+
platform=None):
327328
"""
328329
Pulls an image. Similar to the ``docker pull`` command.
329330
@@ -336,6 +337,7 @@ def pull(self, repository, tag=None, stream=False,
336337
:py:meth:`~docker.api.daemon.DaemonApiMixin.login` has set for
337338
this request. ``auth_config`` should contain the ``username``
338339
and ``password`` keys to be valid.
340+
platform (str): Platform in the format ``os[/arch[/variant]]``
339341
340342
Returns:
341343
(generator or str): The output
@@ -376,7 +378,7 @@ def pull(self, repository, tag=None, stream=False,
376378
}
377379
headers = {}
378380

379-
if utils.compare_version('1.5', self._version) >= 0:
381+
if utils.version_gte(self._version, '1.5'):
380382
if auth_config is None:
381383
header = auth.get_config_header(self, registry)
382384
if header:
@@ -385,6 +387,13 @@ def pull(self, repository, tag=None, stream=False,
385387
log.debug('Sending supplied auth config')
386388
headers['X-Registry-Auth'] = auth.encode_header(auth_config)
387389

390+
if platform is not None:
391+
if utils.version_lt(self._version, '1.32'):
392+
raise errors.InvalidVersion(
393+
'platform was only introduced in API version 1.32'
394+
)
395+
params['platform'] = platform
396+
388397
response = self._post(
389398
self._url('/images/create'), params=params, headers=headers,
390399
stream=stream, timeout=None

docker/models/containers.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,8 @@ def run(self, image, command=None, stdout=True, stderr=False,
579579
inside the container.
580580
pids_limit (int): Tune a container's pids limit. Set ``-1`` for
581581
unlimited.
582+
platform (str): Platform in the format ``os[/arch[/variant]]``.
583+
Only used if the method needs to pull the requested image.
582584
ports (dict): Ports to bind inside the container.
583585
584586
The keys of the dictionary are the ports to bind inside the
@@ -700,7 +702,9 @@ def run(self, image, command=None, stdout=True, stderr=False,
700702
if isinstance(image, Image):
701703
image = image.id
702704
stream = kwargs.pop('stream', False)
703-
detach = kwargs.pop("detach", False)
705+
detach = kwargs.pop('detach', False)
706+
platform = kwargs.pop('platform', None)
707+
704708
if detach and remove:
705709
if version_gte(self.client.api._version, '1.25'):
706710
kwargs["auto_remove"] = True
@@ -718,7 +722,7 @@ def run(self, image, command=None, stdout=True, stderr=False,
718722
container = self.create(image=image, command=command,
719723
detach=detach, **kwargs)
720724
except ImageNotFound:
721-
self.client.images.pull(image)
725+
self.client.images.pull(image, platform=platform)
722726
container = self.create(image=image, command=command,
723727
detach=detach, **kwargs)
724728

docker/models/images.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ def build(self, **kwargs):
157157
single layer.
158158
extra_hosts (dict): Extra hosts to add to /etc/hosts in building
159159
containers, as a mapping of hostname to IP address.
160+
platform (str): Platform in the format ``os[/arch[/variant]]``.
160161
161162
Returns:
162163
(:py:class:`Image`): The built image.
@@ -265,6 +266,7 @@ def pull(self, name, tag=None, **kwargs):
265266
:py:meth:`~docker.client.DockerClient.login` has set for
266267
this request. ``auth_config`` should contain the ``username``
267268
and ``password`` keys to be valid.
269+
platform (str): Platform in the format ``os[/arch[/variant]]``
268270
269271
Returns:
270272
(:py:class:`Image`): The image that has been pulled.

tests/integration/api_build_test.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,18 @@ def test_build_with_dockerfile_empty_lines(self):
377377
def test_build_gzip_custom_encoding(self):
378378
with self.assertRaises(errors.DockerException):
379379
self.client.build(path='.', gzip=True, encoding='text/html')
380+
381+
@requires_api_version('1.32')
382+
@requires_experimental(until=None)
383+
def test_build_invalid_platform(self):
384+
script = io.BytesIO('FROM busybox\n'.encode('ascii'))
385+
386+
with pytest.raises(errors.APIError) as excinfo:
387+
stream = self.client.build(
388+
fileobj=script, stream=True, platform='foobar'
389+
)
390+
for _ in stream:
391+
pass
392+
393+
assert excinfo.value.status_code == 400
394+
assert 'invalid platform' in excinfo.exconly()

tests/integration/api_image_test.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import docker
1616

17-
from ..helpers import requires_api_version
17+
from ..helpers import requires_api_version, requires_experimental
1818
from .base import BaseAPIIntegrationTest, BUSYBOX
1919

2020

@@ -67,6 +67,15 @@ def test_pull_streaming(self):
6767
img_info = self.client.inspect_image('hello-world')
6868
self.assertIn('Id', img_info)
6969

70+
@requires_api_version('1.32')
71+
@requires_experimental(until=None)
72+
def test_pull_invalid_platform(self):
73+
with pytest.raises(docker.errors.APIError) as excinfo:
74+
self.client.pull('hello-world', platform='foobar')
75+
76+
assert excinfo.value.status_code == 500
77+
assert 'invalid platform' in excinfo.exconly()
78+
7079

7180
class CommitTest(BaseAPIIntegrationTest):
7281
def test_commit(self):

tests/unit/models_containers_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ def test_run_pull(self):
225225
container = client.containers.run('alpine', 'sleep 300', detach=True)
226226

227227
assert container.id == FAKE_CONTAINER_ID
228-
client.api.pull.assert_called_with('alpine', tag=None)
228+
client.api.pull.assert_called_with('alpine', platform=None, tag=None)
229229

230230
def test_run_with_error(self):
231231
client = make_fake_client()

0 commit comments

Comments
 (0)