Skip to content

Commit 388f291

Browse files
committed
Update save / export methods to return data generators
Signed-off-by: Joffrey F <[email protected]>
1 parent deb8222 commit 388f291

File tree

5 files changed

+53
-20
lines changed

5 files changed

+53
-20
lines changed

docker/api/container.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,7 @@ def export(self, container):
698698
container (str): The container to export
699699
700700
Returns:
701-
(str): The filesystem tar archive
701+
(generator): The archived filesystem data stream
702702
703703
Raises:
704704
:py:class:`docker.errors.APIError`
@@ -707,8 +707,7 @@ def export(self, container):
707707
res = self._get(
708708
self._url("/containers/{0}/export", container), stream=True
709709
)
710-
self._raise_for_status(res)
711-
return res.raw
710+
return self._stream_raw_result(res)
712711

713712
@utils.check_resource('container')
714713
@utils.minimum_version('1.20')
@@ -737,7 +736,7 @@ def get_archive(self, container, path):
737736
self._raise_for_status(res)
738737
encoded_stat = res.headers.get('x-docker-container-path-stat')
739738
return (
740-
res.raw,
739+
self._stream_raw_result(res),
741740
utils.decode_json_header(encoded_stat) if encoded_stat else None
742741
)
743742

docker/api/image.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,22 @@ def get_image(self, image):
2121
image (str): Image name to get
2222
2323
Returns:
24-
(urllib3.response.HTTPResponse object): The response from the
25-
daemon.
24+
(generator): A stream of raw archive data.
2625
2726
Raises:
2827
:py:class:`docker.errors.APIError`
2928
If the server returns an error.
3029
3130
Example:
3231
33-
>>> image = cli.get_image("fedora:latest")
34-
>>> f = open('/tmp/fedora-latest.tar', 'w')
35-
>>> f.write(image.data)
32+
>>> image = cli.get_image("busybox:latest")
33+
>>> f = open('/tmp/busybox-latest.tar', 'w')
34+
>>> for chunk in image:
35+
>>> f.write(chunk)
3636
>>> f.close()
3737
"""
3838
res = self._get(self._url("/images/{0}/get", image), stream=True)
39-
self._raise_for_status(res)
40-
return res.raw
39+
return self._stream_raw_result(res)
4140

4241
@utils.check_resource('image')
4342
def history(self, image):

docker/models/images.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,18 @@ def save(self):
6161
Get a tarball of an image. Similar to the ``docker save`` command.
6262
6363
Returns:
64-
(urllib3.response.HTTPResponse object): The response from the
65-
daemon.
64+
(generator): A stream of raw archive data.
6665
6766
Raises:
6867
:py:class:`docker.errors.APIError`
6968
If the server returns an error.
7069
7170
Example:
7271
73-
>>> image = cli.images.get("fedora:latest")
74-
>>> resp = image.save()
75-
>>> f = open('/tmp/fedora-latest.tar', 'w')
76-
>>> for chunk in resp.stream():
77-
>>> f.write(chunk)
72+
>>> image = cli.get_image("busybox:latest")
73+
>>> f = open('/tmp/busybox-latest.tar', 'w')
74+
>>> for chunk in image:
75+
>>> f.write(chunk)
7876
>>> f.close()
7977
"""
8078
return self.client.api.get_image(self.id)

tests/integration/api_image_test.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ def test_prune_images(self):
329329
img_id = self.client.inspect_image('hello-world')['Id']
330330
result = self.client.prune_images()
331331
assert img_id not in [
332-
img.get('Deleted') for img in result['ImagesDeleted']
332+
img.get('Deleted') for img in result.get('ImagesDeleted') or []
333333
]
334334
result = self.client.prune_images({'dangling': False})
335335
assert result['SpaceReclaimed'] > 0
@@ -339,3 +339,25 @@ def test_prune_images(self):
339339
assert img_id in [
340340
img.get('Deleted') for img in result['ImagesDeleted']
341341
]
342+
343+
344+
class SaveLoadImagesTest(BaseAPIIntegrationTest):
345+
@requires_api_version('1.23')
346+
def test_get_image_load_image(self):
347+
with tempfile.TemporaryFile() as f:
348+
stream = self.client.get_image(BUSYBOX)
349+
for chunk in stream:
350+
f.write(chunk)
351+
352+
f.seek(0)
353+
result = self.client.load_image(f.read())
354+
355+
success = False
356+
result_line = 'Loaded image: {}\n'.format(BUSYBOX)
357+
for data in result:
358+
print(data)
359+
if 'stream' in data:
360+
if data['stream'] == result_line:
361+
success = True
362+
break
363+
assert success is True

tests/integration/models_images_test.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import io
2+
import tempfile
23

34
import docker
45
import pytest
56

6-
from .base import BaseIntegrationTest, TEST_API_VERSION
7+
from .base import BaseIntegrationTest, BUSYBOX, TEST_API_VERSION
78

89

910
class ImageCollectionTest(BaseIntegrationTest):
@@ -76,6 +77,20 @@ def test_load_error(self):
7677
with pytest.raises(docker.errors.ImageLoadError):
7778
client.images.load('abc')
7879

80+
def test_save_and_load(self):
81+
client = docker.from_env(version=TEST_API_VERSION)
82+
image = client.images.get(BUSYBOX)
83+
with tempfile.TemporaryFile() as f:
84+
stream = image.save()
85+
for chunk in stream:
86+
f.write(chunk)
87+
88+
f.seek(0)
89+
result = client.images.load(f.read())
90+
91+
assert len(result) == 1
92+
assert result[0].id == image.id
93+
7994

8095
class ImageTest(BaseIntegrationTest):
8196

0 commit comments

Comments
 (0)