Kaydet (Commit) 581ccc9f authored tarafından Joffrey F's avatar Joffrey F

Add chunk_size parameter to data downloading methods (export, get_archive, save)

Signed-off-by: 's avatarJoffrey F <joffrey@docker.com>
üst 9e75609a
...@@ -350,10 +350,10 @@ class APIClient( ...@@ -350,10 +350,10 @@ class APIClient(
break break
yield data yield data
def _stream_raw_result(self, response): def _stream_raw_result(self, response, chunk_size=1, decode=True):
''' Stream result for TTY-enabled container ''' ''' Stream result for TTY-enabled container and raw binary data'''
self._raise_for_status(response) self._raise_for_status(response)
for out in response.iter_content(chunk_size=1, decode_unicode=True): for out in response.iter_content(chunk_size, decode):
yield out yield out
def _read_from_socket(self, response, stream, tty=False): def _read_from_socket(self, response, stream, tty=False):
......
...@@ -3,6 +3,7 @@ from datetime import datetime ...@@ -3,6 +3,7 @@ from datetime import datetime
from .. import errors from .. import errors
from .. import utils from .. import utils
from ..constants import DEFAULT_DATA_CHUNK_SIZE
from ..types import ( from ..types import (
ContainerConfig, EndpointConfig, HostConfig, NetworkingConfig ContainerConfig, EndpointConfig, HostConfig, NetworkingConfig
) )
...@@ -643,12 +644,15 @@ class ContainerApiMixin(object): ...@@ -643,12 +644,15 @@ class ContainerApiMixin(object):
) )
@utils.check_resource('container') @utils.check_resource('container')
def export(self, container): def export(self, container, chunk_size=DEFAULT_DATA_CHUNK_SIZE):
""" """
Export the contents of a filesystem as a tar archive. Export the contents of a filesystem as a tar archive.
Args: Args:
container (str): The container to export container (str): The container to export
chunk_size (int): The number of bytes returned by each iteration
of the generator. If ``None``, data will be streamed as it is
received. Default: 2 MB
Returns: Returns:
(generator): The archived filesystem data stream (generator): The archived filesystem data stream
...@@ -660,10 +664,10 @@ class ContainerApiMixin(object): ...@@ -660,10 +664,10 @@ class ContainerApiMixin(object):
res = self._get( res = self._get(
self._url("/containers/{0}/export", container), stream=True self._url("/containers/{0}/export", container), stream=True
) )
return self._stream_raw_result(res) return self._stream_raw_result(res, chunk_size, False)
@utils.check_resource('container') @utils.check_resource('container')
def get_archive(self, container, path): def get_archive(self, container, path, chunk_size=DEFAULT_DATA_CHUNK_SIZE):
""" """
Retrieve a file or folder from a container in the form of a tar Retrieve a file or folder from a container in the form of a tar
archive. archive.
...@@ -671,6 +675,9 @@ class ContainerApiMixin(object): ...@@ -671,6 +675,9 @@ class ContainerApiMixin(object):
Args: Args:
container (str): The container where the file is located container (str): The container where the file is located
path (str): Path to the file or folder to retrieve path (str): Path to the file or folder to retrieve
chunk_size (int): The number of bytes returned by each iteration
of the generator. If ``None``, data will be streamed as it is
received. Default: 2 MB
Returns: Returns:
(tuple): First element is a raw tar data stream. Second element is (tuple): First element is a raw tar data stream. Second element is
...@@ -688,7 +695,7 @@ class ContainerApiMixin(object): ...@@ -688,7 +695,7 @@ class ContainerApiMixin(object):
self._raise_for_status(res) self._raise_for_status(res)
encoded_stat = res.headers.get('x-docker-container-path-stat') encoded_stat = res.headers.get('x-docker-container-path-stat')
return ( return (
self._stream_raw_result(res), self._stream_raw_result(res, chunk_size, False),
utils.decode_json_header(encoded_stat) if encoded_stat else None utils.decode_json_header(encoded_stat) if encoded_stat else None
) )
......
...@@ -4,6 +4,7 @@ import os ...@@ -4,6 +4,7 @@ import os
import six import six
from .. import auth, errors, utils from .. import auth, errors, utils
from ..constants import DEFAULT_DATA_CHUNK_SIZE
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -11,12 +12,15 @@ log = logging.getLogger(__name__) ...@@ -11,12 +12,15 @@ log = logging.getLogger(__name__)
class ImageApiMixin(object): class ImageApiMixin(object):
@utils.check_resource('image') @utils.check_resource('image')
def get_image(self, image): def get_image(self, image, chunk_size=DEFAULT_DATA_CHUNK_SIZE):
""" """
Get a tarball of an image. Similar to the ``docker save`` command. Get a tarball of an image. Similar to the ``docker save`` command.
Args: Args:
image (str): Image name to get image (str): Image name to get
chunk_size (int): The number of bytes returned by each iteration
of the generator. If ``None``, data will be streamed as it is
received. Default: 2 MB
Returns: Returns:
(generator): A stream of raw archive data. (generator): A stream of raw archive data.
...@@ -34,7 +38,7 @@ class ImageApiMixin(object): ...@@ -34,7 +38,7 @@ class ImageApiMixin(object):
>>> f.close() >>> f.close()
""" """
res = self._get(self._url("/images/{0}/get", image), stream=True) res = self._get(self._url("/images/{0}/get", image), stream=True)
return self._stream_raw_result(res) return self._stream_raw_result(res, chunk_size, False)
@utils.check_resource('image') @utils.check_resource('image')
def history(self, image): def history(self, image):
......
...@@ -17,3 +17,4 @@ IS_WINDOWS_PLATFORM = (sys.platform == 'win32') ...@@ -17,3 +17,4 @@ IS_WINDOWS_PLATFORM = (sys.platform == 'win32')
DEFAULT_USER_AGENT = "docker-sdk-python/{0}".format(version) DEFAULT_USER_AGENT = "docker-sdk-python/{0}".format(version)
DEFAULT_NUM_POOLS = 25 DEFAULT_NUM_POOLS = 25
DEFAULT_DATA_CHUNK_SIZE = 1024 * 2048
...@@ -3,6 +3,7 @@ import ntpath ...@@ -3,6 +3,7 @@ import ntpath
from collections import namedtuple from collections import namedtuple
from ..api import APIClient from ..api import APIClient
from ..constants import DEFAULT_DATA_CHUNK_SIZE
from ..errors import (ContainerError, ImageNotFound, from ..errors import (ContainerError, ImageNotFound,
create_unexpected_kwargs_error) create_unexpected_kwargs_error)
from ..types import HostConfig from ..types import HostConfig
...@@ -181,10 +182,15 @@ class Container(Model): ...@@ -181,10 +182,15 @@ class Container(Model):
exec_output exec_output
) )
def export(self): def export(self, chunk_size=DEFAULT_DATA_CHUNK_SIZE):
""" """
Export the contents of the container's filesystem as a tar archive. Export the contents of the container's filesystem as a tar archive.
Args:
chunk_size (int): The number of bytes returned by each iteration
of the generator. If ``None``, data will be streamed as it is
received. Default: 2 MB
Returns: Returns:
(str): The filesystem tar archive (str): The filesystem tar archive
...@@ -192,15 +198,18 @@ class Container(Model): ...@@ -192,15 +198,18 @@ class Container(Model):
:py:class:`docker.errors.APIError` :py:class:`docker.errors.APIError`
If the server returns an error. If the server returns an error.
""" """
return self.client.api.export(self.id) return self.client.api.export(self.id, chunk_size)
def get_archive(self, path): def get_archive(self, path, chunk_size=DEFAULT_DATA_CHUNK_SIZE):
""" """
Retrieve a file or folder from the container in the form of a tar Retrieve a file or folder from the container in the form of a tar
archive. archive.
Args: Args:
path (str): Path to the file or folder to retrieve path (str): Path to the file or folder to retrieve
chunk_size (int): The number of bytes returned by each iteration
of the generator. If ``None``, data will be streamed as it is
received. Default: 2 MB
Returns: Returns:
(tuple): First element is a raw tar data stream. Second element is (tuple): First element is a raw tar data stream. Second element is
...@@ -210,7 +219,7 @@ class Container(Model): ...@@ -210,7 +219,7 @@ class Container(Model):
:py:class:`docker.errors.APIError` :py:class:`docker.errors.APIError`
If the server returns an error. If the server returns an error.
""" """
return self.client.api.get_archive(self.id, path) return self.client.api.get_archive(self.id, path, chunk_size)
def kill(self, signal=None): def kill(self, signal=None):
""" """
......
...@@ -4,6 +4,7 @@ import re ...@@ -4,6 +4,7 @@ import re
import six import six
from ..api import APIClient from ..api import APIClient
from ..constants import DEFAULT_DATA_CHUNK_SIZE
from ..errors import BuildError, ImageLoadError from ..errors import BuildError, ImageLoadError
from ..utils import parse_repository_tag from ..utils import parse_repository_tag
from ..utils.json_stream import json_stream from ..utils.json_stream import json_stream
...@@ -58,10 +59,15 @@ class Image(Model): ...@@ -58,10 +59,15 @@ class Image(Model):
""" """
return self.client.api.history(self.id) return self.client.api.history(self.id)
def save(self): def save(self, chunk_size=DEFAULT_DATA_CHUNK_SIZE):
""" """
Get a tarball of an image. Similar to the ``docker save`` command. Get a tarball of an image. Similar to the ``docker save`` command.
Args:
chunk_size (int): The number of bytes returned by each iteration
of the generator. If ``None``, data will be streamed as it is
received. Default: 2 MB
Returns: Returns:
(generator): A stream of raw archive data. (generator): A stream of raw archive data.
...@@ -77,7 +83,7 @@ class Image(Model): ...@@ -77,7 +83,7 @@ class Image(Model):
>>> f.write(chunk) >>> f.write(chunk)
>>> f.close() >>> f.close()
""" """
return self.client.api.get_image(self.id) return self.client.api.get_image(self.id, chunk_size)
def tag(self, repository, tag=None, **kwargs): def tag(self, repository, tag=None, **kwargs):
""" """
......
import docker import docker
from docker.constants import DEFAULT_DATA_CHUNK_SIZE
from docker.models.containers import Container, _create_container_args from docker.models.containers import Container, _create_container_args
from docker.models.images import Image from docker.models.images import Image
import unittest import unittest
...@@ -422,13 +423,17 @@ class ContainerTest(unittest.TestCase): ...@@ -422,13 +423,17 @@ class ContainerTest(unittest.TestCase):
client = make_fake_client() client = make_fake_client()
container = client.containers.get(FAKE_CONTAINER_ID) container = client.containers.get(FAKE_CONTAINER_ID)
container.export() container.export()
client.api.export.assert_called_with(FAKE_CONTAINER_ID) client.api.export.assert_called_with(
FAKE_CONTAINER_ID, DEFAULT_DATA_CHUNK_SIZE
)
def test_get_archive(self): def test_get_archive(self):
client = make_fake_client() client = make_fake_client()
container = client.containers.get(FAKE_CONTAINER_ID) container = client.containers.get(FAKE_CONTAINER_ID)
container.get_archive('foo') container.get_archive('foo')
client.api.get_archive.assert_called_with(FAKE_CONTAINER_ID, 'foo') client.api.get_archive.assert_called_with(
FAKE_CONTAINER_ID, 'foo', DEFAULT_DATA_CHUNK_SIZE
)
def test_image(self): def test_image(self):
client = make_fake_client() client = make_fake_client()
......
from docker.constants import DEFAULT_DATA_CHUNK_SIZE
from docker.models.images import Image from docker.models.images import Image
import unittest import unittest
...@@ -116,7 +117,9 @@ class ImageTest(unittest.TestCase): ...@@ -116,7 +117,9 @@ class ImageTest(unittest.TestCase):
client = make_fake_client() client = make_fake_client()
image = client.images.get(FAKE_IMAGE_ID) image = client.images.get(FAKE_IMAGE_ID)
image.save() image.save()
client.api.get_image.assert_called_with(FAKE_IMAGE_ID) client.api.get_image.assert_called_with(
FAKE_IMAGE_ID, DEFAULT_DATA_CHUNK_SIZE
)
def test_tag(self): def test_tag(self):
client = make_fake_client() client = make_fake_client()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment