summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoffrey F <f.joffrey@gmail.com>2018-01-29 16:19:45 -0800
committerGitHub <noreply@github.com>2018-01-29 16:19:45 -0800
commit5728eebf79b0f43e7033952fbf8f8e4b7f7bef20 (patch)
treee6b93bdfe361d7334a2762b96aa4690ac4a49ed9
parent9c0332eb2e2225bef6fb291c5c1220566a1426a0 (diff)
parent388f291b13fca76f4974a1ee89225ff7f3afb85b (diff)
downloaddocker-py-5728eebf79b0f43e7033952fbf8f8e4b7f7bef20.tar.gz
Merge pull request #1874 from docker/1774-export-methods
Update save / export methods to return data generators
-rw-r--r--docker/api/container.py7
-rw-r--r--docker/api/image.py13
-rw-r--r--docker/models/images.py12
-rw-r--r--tests/integration/api_image_test.py24
-rw-r--r--tests/integration/models_images_test.py17
5 files changed, 53 insertions, 20 deletions
diff --git a/docker/api/container.py b/docker/api/container.py
index b08032c..49230c7 100644
--- a/docker/api/container.py
+++ b/docker/api/container.py
@@ -698,7 +698,7 @@ class ContainerApiMixin(object):
container (str): The container to export
Returns:
- (str): The filesystem tar archive
+ (generator): The archived filesystem data stream
Raises:
:py:class:`docker.errors.APIError`
@@ -707,8 +707,7 @@ class ContainerApiMixin(object):
res = self._get(
self._url("/containers/{0}/export", container), stream=True
)
- self._raise_for_status(res)
- return res.raw
+ return self._stream_raw_result(res)
@utils.check_resource('container')
@utils.minimum_version('1.20')
@@ -737,7 +736,7 @@ class ContainerApiMixin(object):
self._raise_for_status(res)
encoded_stat = res.headers.get('x-docker-container-path-stat')
return (
- res.raw,
+ self._stream_raw_result(res),
utils.decode_json_header(encoded_stat) if encoded_stat else None
)
diff --git a/docker/api/image.py b/docker/api/image.py
index 065fae3..b3dcd3a 100644
--- a/docker/api/image.py
+++ b/docker/api/image.py
@@ -21,8 +21,7 @@ class ImageApiMixin(object):
image (str): Image name to get
Returns:
- (urllib3.response.HTTPResponse object): The response from the
- daemon.
+ (generator): A stream of raw archive data.
Raises:
:py:class:`docker.errors.APIError`
@@ -30,14 +29,14 @@ class ImageApiMixin(object):
Example:
- >>> image = cli.get_image("fedora:latest")
- >>> f = open('/tmp/fedora-latest.tar', 'w')
- >>> f.write(image.data)
+ >>> image = cli.get_image("busybox:latest")
+ >>> f = open('/tmp/busybox-latest.tar', 'w')
+ >>> for chunk in image:
+ >>> f.write(chunk)
>>> f.close()
"""
res = self._get(self._url("/images/{0}/get", image), stream=True)
- self._raise_for_status(res)
- return res.raw
+ return self._stream_raw_result(res)
@utils.check_resource('image')
def history(self, image):
diff --git a/docker/models/images.py b/docker/models/images.py
index c4e727b..282d046 100644
--- a/docker/models/images.py
+++ b/docker/models/images.py
@@ -62,8 +62,7 @@ class Image(Model):
Get a tarball of an image. Similar to the ``docker save`` command.
Returns:
- (urllib3.response.HTTPResponse object): The response from the
- daemon.
+ (generator): A stream of raw archive data.
Raises:
:py:class:`docker.errors.APIError`
@@ -71,11 +70,10 @@ class Image(Model):
Example:
- >>> image = cli.images.get("fedora:latest")
- >>> resp = image.save()
- >>> f = open('/tmp/fedora-latest.tar', 'w')
- >>> for chunk in resp.stream():
- >>> f.write(chunk)
+ >>> image = cli.get_image("busybox:latest")
+ >>> f = open('/tmp/busybox-latest.tar', 'w')
+ >>> for chunk in image:
+ >>> f.write(chunk)
>>> f.close()
"""
return self.client.api.get_image(self.id)
diff --git a/tests/integration/api_image_test.py b/tests/integration/api_image_test.py
index 178c34e..ae93190 100644
--- a/tests/integration/api_image_test.py
+++ b/tests/integration/api_image_test.py
@@ -329,7 +329,7 @@ class PruneImagesTest(BaseAPIIntegrationTest):
img_id = self.client.inspect_image('hello-world')['Id']
result = self.client.prune_images()
assert img_id not in [
- img.get('Deleted') for img in result['ImagesDeleted']
+ img.get('Deleted') for img in result.get('ImagesDeleted') or []
]
result = self.client.prune_images({'dangling': False})
assert result['SpaceReclaimed'] > 0
@@ -339,3 +339,25 @@ class PruneImagesTest(BaseAPIIntegrationTest):
assert img_id in [
img.get('Deleted') for img in result['ImagesDeleted']
]
+
+
+class SaveLoadImagesTest(BaseAPIIntegrationTest):
+ @requires_api_version('1.23')
+ def test_get_image_load_image(self):
+ with tempfile.TemporaryFile() as f:
+ stream = self.client.get_image(BUSYBOX)
+ for chunk in stream:
+ f.write(chunk)
+
+ f.seek(0)
+ result = self.client.load_image(f.read())
+
+ success = False
+ result_line = 'Loaded image: {}\n'.format(BUSYBOX)
+ for data in result:
+ print(data)
+ if 'stream' in data:
+ if data['stream'] == result_line:
+ success = True
+ break
+ assert success is True
diff --git a/tests/integration/models_images_test.py b/tests/integration/models_images_test.py
index 900555d..94164ce 100644
--- a/tests/integration/models_images_test.py
+++ b/tests/integration/models_images_test.py
@@ -1,9 +1,10 @@
import io
+import tempfile
import docker
import pytest
-from .base import BaseIntegrationTest, TEST_API_VERSION
+from .base import BaseIntegrationTest, BUSYBOX, TEST_API_VERSION
class ImageCollectionTest(BaseIntegrationTest):
@@ -79,6 +80,20 @@ class ImageCollectionTest(BaseIntegrationTest):
with pytest.raises(docker.errors.ImageLoadError):
client.images.load('abc')
+ def test_save_and_load(self):
+ client = docker.from_env(version=TEST_API_VERSION)
+ image = client.images.get(BUSYBOX)
+ with tempfile.TemporaryFile() as f:
+ stream = image.save()
+ for chunk in stream:
+ f.write(chunk)
+
+ f.seek(0)
+ result = client.images.load(f.read())
+
+ assert len(result) == 1
+ assert result[0].id == image.id
+
class ImageTest(BaseIntegrationTest):