diff --git a/docker/api/container.py b/docker/api/container.py index c59a6d01a5..fce73af640 100644 --- a/docker/api/container.py +++ b/docker/api/container.py @@ -473,16 +473,12 @@ def create_host_config(self, *args, **kwargs): signals and reaps processes init_path (str): Path to the docker-init binary ipc_mode (str): Set the IPC mode for the container. - isolation (str): Isolation technology to use. Default: `None`. - links (dict or list of tuples): Either a dictionary mapping name - to alias or as a list of ``(name, alias)`` tuples. - log_config (dict): Logging configuration, as a dictionary with - keys: - - - ``type`` The logging driver name. - - ``config`` A dictionary of configuration for the logging - driver. - + isolation (str): Isolation technology to use. Default: ``None``. + links (dict): Mapping of links using the + ``{'container': 'alias'}`` format. The alias is optional. + Containers declared in this dict will be linked to the new + container using the provided alias. Default: ``None``. + log_config (LogConfig): Logging configuration lxc_conf (dict): LXC config. mem_limit (float or str): Memory limit. Accepts float values (which represent the memory limit of the created container in @@ -543,7 +539,7 @@ def create_host_config(self, *args, **kwargs): } ulimits (:py:class:`list`): Ulimits to set inside the container, - as a list of dicts. + as a list of :py:class:`docker.types.Ulimit` instances. userns_mode (str): Sets the user namespace mode for the container when user namespace remapping option is enabled. Supported values are: ``host`` @@ -611,9 +607,10 @@ def create_endpoint_config(self, *args, **kwargs): aliases (:py:class:`list`): A list of aliases for this endpoint. Names in that list can be used within the network to reach the container. Defaults to ``None``. - links (:py:class:`list`): A list of links for this endpoint. - Containers declared in this list will be linked to this - container. Defaults to ``None``. + links (dict): Mapping of links for this endpoint using the + ``{'container': 'alias'}`` format. The alias is optional. + Containers declared in this dict will be linked to this + container using the provided alias. Defaults to ``None``. ipv4_address (str): The IP address of this container on the network, using the IPv4 protocol. Defaults to ``None``. ipv6_address (str): The IP address of this container on the @@ -628,7 +625,7 @@ def create_endpoint_config(self, *args, **kwargs): >>> endpoint_config = client.create_endpoint_config( aliases=['web', 'app'], - links=['app_db'], + links={'app_db': 'db', 'another': None}, ipv4_address='132.65.0.123' ) @@ -697,6 +694,18 @@ def get_archive(self, container, path, chunk_size=DEFAULT_DATA_CHUNK_SIZE): Raises: :py:class:`docker.errors.APIError` If the server returns an error. + + Example: + + >>> c = docker.APIClient() + >>> f = open('./sh_bin.tar', 'wb') + >>> bits, stat = c.get_archive(container, '/bin/sh') + >>> print(stat) + {'name': 'sh', 'size': 1075464, 'mode': 493, + 'mtime': '2018-10-01T15:37:48-07:00', 'linkTarget': ''} + >>> for chunk in bits: + ... f.write(chunk) + >>> f.close() """ params = { 'path': path @@ -1074,7 +1083,8 @@ def stats(self, container, decode=None, stream=True): Args: container (str): The container to stream statistics from decode (bool): If set to true, stream will be decoded into dicts - on the fly. False by default. + on the fly. Only applicable if ``stream`` is True. + False by default. stream (bool): If set to false, only the current stats will be returned instead of a stream. True by default. @@ -1088,6 +1098,10 @@ def stats(self, container, decode=None, stream=True): return self._stream_helper(self._get(url, stream=True), decode=decode) else: + if decode: + raise errors.InvalidArgument( + "decode is only available in conjuction with stream=True" + ) return self._result(self._get(url, params={'stream': False}), json=True) diff --git a/docker/api/daemon.py b/docker/api/daemon.py index 76a94cf034..431e7d41cd 100644 --- a/docker/api/daemon.py +++ b/docker/api/daemon.py @@ -42,8 +42,8 @@ def events(self, since=None, until=None, filters=None, decode=None): Example: - >>> for event in client.events() - ... print event + >>> for event in client.events(decode=True) + ... print(event) {u'from': u'image/with:tag', u'id': u'container-id', u'status': u'start', @@ -54,7 +54,7 @@ def events(self, since=None, until=None, filters=None, decode=None): >>> events = client.events() >>> for event in events: - ... print event + ... print(event) >>> # and cancel from another thread >>> events.close() """ diff --git a/docker/api/image.py b/docker/api/image.py index 5f05d8877e..a9f801e93b 100644 --- a/docker/api/image.py +++ b/docker/api/image.py @@ -32,7 +32,7 @@ def get_image(self, image, chunk_size=DEFAULT_DATA_CHUNK_SIZE): Example: >>> image = cli.get_image("busybox:latest") - >>> f = open('/tmp/busybox-latest.tar', 'w') + >>> f = open('/tmp/busybox-latest.tar', 'wb') >>> for chunk in image: >>> f.write(chunk) >>> f.close() @@ -352,8 +352,8 @@ def pull(self, repository, tag=None, stream=False, auth_config=None, Example: - >>> for line in cli.pull('busybox', stream=True): - ... print(json.dumps(json.loads(line), indent=4)) + >>> for line in cli.pull('busybox', stream=True, decode=True): + ... print(json.dumps(line, indent=4)) { "status": "Pulling image (latest) from busybox", "progressDetail": {}, @@ -428,12 +428,12 @@ def push(self, repository, tag=None, stream=False, auth_config=None, If the server returns an error. Example: - >>> for line in cli.push('yourname/app', stream=True): - ... print line - {"status":"Pushing repository yourname/app (1 tags)"} - {"status":"Pushing","progressDetail":{},"id":"511136ea3c5a"} - {"status":"Image already pushed, skipping","progressDetail":{}, - "id":"511136ea3c5a"} + >>> for line in cli.push('yourname/app', stream=True, decode=True): + ... print(line) + {'status': 'Pushing repository yourname/app (1 tags)'} + {'status': 'Pushing','progressDetail': {}, 'id': '511136ea3c5a'} + {'status': 'Image already pushed, skipping', 'progressDetail':{}, + 'id': '511136ea3c5a'} ... """ diff --git a/docker/api/service.py b/docker/api/service.py index 8b956b63e1..08e2591730 100644 --- a/docker/api/service.py +++ b/docker/api/service.py @@ -197,7 +197,8 @@ def inspect_service(self, service, insert_defaults=None): into the service inspect output. Returns: - ``True`` if successful. + (dict): A dictionary of the server-side representation of the + service, including all relevant properties. Raises: :py:class:`docker.errors.APIError` diff --git a/docker/models/containers.py b/docker/models/containers.py index f60ba6e225..9d6f2cc6af 100644 --- a/docker/models/containers.py +++ b/docker/models/containers.py @@ -15,7 +15,12 @@ class Container(Model): - + """ Local representation of a container object. Detailed configuration may + be accessed through the :py:attr:`attrs` attribute. Note that local + attributes are cached; users may call :py:meth:`reload` to + query the Docker daemon for the current properties, causing + :py:attr:`attrs` to be refreshed. + """ @property def name(self): """ @@ -228,6 +233,17 @@ def get_archive(self, path, chunk_size=DEFAULT_DATA_CHUNK_SIZE): Raises: :py:class:`docker.errors.APIError` If the server returns an error. + + Example: + + >>> f = open('./sh_bin.tar', 'wb') + >>> bits, stat = container.get_archive('/bin/sh') + >>> print(stat) + {'name': 'sh', 'size': 1075464, 'mode': 493, + 'mtime': '2018-10-01T15:37:48-07:00', 'linkTarget': ''} + >>> for chunk in bits: + ... f.write(chunk) + >>> f.close() """ return self.client.api.get_archive(self.id, path, chunk_size) @@ -380,7 +396,8 @@ def stats(self, **kwargs): Args: decode (bool): If set to true, stream will be decoded into dicts - on the fly. False by default. + on the fly. Only applicable if ``stream`` is True. + False by default. stream (bool): If set to false, only the current stats will be returned instead of a stream. True by default. @@ -574,15 +591,11 @@ def run(self, image, command=None, stdout=True, stderr=False, ``{"label1": "value1", "label2": "value2"}``) or a list of names of labels to set with empty values (e.g. ``["label1", "label2"]``) - links (dict or list of tuples): Either a dictionary mapping name - to alias or as a list of ``(name, alias)`` tuples. - log_config (dict): Logging configuration, as a dictionary with - keys: - - - ``type`` The logging driver name. - - ``config`` A dictionary of configuration for the logging - driver. - + links (dict): Mapping of links using the + ``{'container': 'alias'}`` format. The alias is optional. + Containers declared in this dict will be linked to the new + container using the provided alias. Default: ``None``. + log_config (LogConfig): Logging configuration. mac_address (str): MAC address to assign to the container. mem_limit (int or str): Memory limit. Accepts float values (which represent the memory limit of the created container in @@ -691,8 +704,8 @@ def run(self, image, command=None, stdout=True, stderr=False, } tty (bool): Allocate a pseudo-TTY. - ulimits (:py:class:`list`): Ulimits to set inside the container, as - a list of dicts. + ulimits (:py:class:`list`): Ulimits to set inside the container, + as a list of :py:class:`docker.types.Ulimit` instances. user (str or int): Username or UID to run commands as inside the container. userns_mode (str): Sets the user namespace mode for the container diff --git a/docker/models/images.py b/docker/models/images.py index 28b1fd3ffd..4578c0bd89 100644 --- a/docker/models/images.py +++ b/docker/models/images.py @@ -84,7 +84,7 @@ def save(self, chunk_size=DEFAULT_DATA_CHUNK_SIZE, named=False): Example: >>> image = cli.get_image("busybox:latest") - >>> f = open('/tmp/busybox-latest.tar', 'w') + >>> f = open('/tmp/busybox-latest.tar', 'wb') >>> for chunk in image: >>> f.write(chunk) >>> f.close() diff --git a/docker/types/containers.py b/docker/types/containers.py index 9dfea8ceb8..d040c0fb5e 100644 --- a/docker/types/containers.py +++ b/docker/types/containers.py @@ -23,6 +23,36 @@ class LogConfigTypesEnum(object): class LogConfig(DictType): + """ + Configure logging for a container, when provided as an argument to + :py:meth:`~docker.api.container.ContainerApiMixin.create_host_config`. + You may refer to the + `official logging driver documentation `_ + for more information. + + Args: + type (str): Indicate which log driver to use. A set of valid drivers + is provided as part of the :py:attr:`LogConfig.types` + enum. Other values may be accepted depending on the engine version + and available logging plugins. + config (dict): A driver-dependent configuration dictionary. Please + refer to the driver's documentation for a list of valid config + keys. + + Example: + + >>> from docker.types import LogConfig + >>> lc = LogConfig(type=LogConfig.types.JSON, config={ + ... 'max-size': '1g', + ... 'labels': 'production_status,geo' + ... }) + >>> hc = client.create_host_config(log_config=lc) + >>> container = client.create_container('busybox', 'true', + ... host_config=hc) + >>> client.inspect_container(container)['HostConfig']['LogConfig'] + {'Type': 'json-file', 'Config': {'labels': 'production_status,geo', 'max-size': '1g'}} + + """ # flake8: noqa types = LogConfigTypesEnum def __init__(self, **kwargs): @@ -50,14 +80,40 @@ def config(self): return self['Config'] def set_config_value(self, key, value): + """ Set a the value for ``key`` to ``value`` inside the ``config`` + dict. + """ self.config[key] = value def unset_config(self, key): + """ Remove the ``key`` property from the ``config`` dict. """ if key in self.config: del self.config[key] class Ulimit(DictType): + """ + Create a ulimit declaration to be used with + :py:meth:`~docker.api.container.ContainerApiMixin.create_host_config`. + + Args: + + name (str): Which ulimit will this apply to. A list of valid names can + be found `here `_. + soft (int): The soft limit for this ulimit. Optional. + hard (int): The hard limit for this ulimit. Optional. + + Example: + + >>> nproc_limit = docker.types.Ulimit(name='nproc', soft=1024) + >>> hc = client.create_host_config(ulimits=[nproc_limit]) + >>> container = client.create_container( + 'busybox', 'true', host_config=hc + ) + >>> client.inspect_container(container)['HostConfig']['Ulimits'] + [{'Name': 'nproc', 'Hard': 0, 'Soft': 1024}] + + """ def __init__(self, **kwargs): name = kwargs.get('name', kwargs.get('Name')) soft = kwargs.get('soft', kwargs.get('Soft')) diff --git a/docker/types/daemon.py b/docker/types/daemon.py index 700f9a90c4..af3e5bcb5e 100644 --- a/docker/types/daemon.py +++ b/docker/types/daemon.py @@ -15,7 +15,7 @@ class CancellableStream(object): Example: >>> events = client.events() >>> for event in events: - ... print event + ... print(event) >>> # and cancel from another thread >>> events.close() """ diff --git a/docker/utils/utils.py b/docker/utils/utils.py index 4e04cafdb4..61e307adc7 100644 --- a/docker/utils/utils.py +++ b/docker/utils/utils.py @@ -386,7 +386,10 @@ def convert_filters(filters): v = 'true' if v else 'false' if not isinstance(v, list): v = [v, ] - result[k] = v + result[k] = [ + str(item) if not isinstance(item, six.string_types) else item + for item in v + ] return json.dumps(result) @@ -441,7 +444,7 @@ def normalize_links(links): if isinstance(links, dict): links = six.iteritems(links) - return ['{0}:{1}'.format(k, v) for k, v in sorted(links)] + return ['{0}:{1}'.format(k, v) if v else k for k, v in sorted(links)] def parse_env_file(env_file): diff --git a/docs/api.rst b/docs/api.rst index 6931245716..1682128951 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -140,6 +140,7 @@ Configuration types .. autoclass:: Healthcheck .. autoclass:: IPAMConfig .. autoclass:: IPAMPool +.. autoclass:: LogConfig .. autoclass:: Mount .. autoclass:: Placement .. autoclass:: Privileges @@ -151,4 +152,5 @@ Configuration types .. autoclass:: SwarmExternalCA .. autoclass:: SwarmSpec(*args, **kwargs) .. autoclass:: TaskTemplate +.. autoclass:: Ulimit .. autoclass:: UpdateConfig diff --git a/tests/unit/utils_test.py b/tests/unit/utils_test.py index c862a1cec9..a4e9c9c53e 100644 --- a/tests/unit/utils_test.py +++ b/tests/unit/utils_test.py @@ -457,8 +457,8 @@ def test_convert_filters(self): tests = [ ({'dangling': True}, '{"dangling": ["true"]}'), ({'dangling': "true"}, '{"dangling": ["true"]}'), - ({'exited': 0}, '{"exited": [0]}'), - ({'exited': [0, 1]}, '{"exited": [0, 1]}'), + ({'exited': 0}, '{"exited": ["0"]}'), + ({'exited': [0, 1]}, '{"exited": ["0", "1"]}'), ] for filters, expected in tests: