summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoffrey F <f.joffrey@gmail.com>2017-01-19 18:12:07 -0800
committerGitHub <noreply@github.com>2017-01-19 18:12:07 -0800
commit269a15e473f47d27cd7b7727d9e4595a6d8f33d3 (patch)
tree42f9e5f12de00051e8865bde1cf1521501a1c64e
parent206c18409d2efd17d24e431ef669c796b96ffd29 (diff)
parentab5a189938929a84e52c9ec8f46cbbbf87f485bf (diff)
downloaddocker-py-269a15e473f47d27cd7b7727d9e4595a6d8f33d3.tar.gz
Merge pull request #1402 from docker/2.0.2-release2.0.2
2.0.2 release
-rw-r--r--docker/api/client.py49
-rw-r--r--docker/api/container.py48
-rw-r--r--docker/api/network.py36
-rw-r--r--docker/api/service.py46
-rw-r--r--docker/api/swarm.py4
-rw-r--r--docker/models/containers.py32
-rw-r--r--docker/models/images.py8
-rw-r--r--docker/models/networks.py19
-rw-r--r--docker/models/resource.py3
-rw-r--r--docker/models/services.py20
-rw-r--r--docker/types/__init__.py2
-rw-r--r--docker/types/networks.py2
-rw-r--r--docker/types/services.py49
-rw-r--r--docker/version.py2
-rw-r--r--docs/api.rst3
-rw-r--r--docs/change-log.md20
-rw-r--r--docs/index.rst3
-rw-r--r--setup.py10
-rw-r--r--tests/integration/api_service_test.py28
-rw-r--r--tests/unit/dockertypes_test.py33
-rw-r--r--tests/unit/models_images_test.py5
-rw-r--r--tests/unit/models_resources_test.py14
22 files changed, 291 insertions, 145 deletions
diff --git a/docker/api/client.py b/docker/api/client.py
index a9fe7d0..22c32b4 100644
--- a/docker/api/client.py
+++ b/docker/api/client.py
@@ -18,16 +18,20 @@ from .service import ServiceApiMixin
from .swarm import SwarmApiMixin
from .volume import VolumeApiMixin
from .. import auth
-from ..constants import (DEFAULT_TIMEOUT_SECONDS, DEFAULT_USER_AGENT,
- IS_WINDOWS_PLATFORM, DEFAULT_DOCKER_API_VERSION,
- STREAM_HEADER_SIZE_BYTES, DEFAULT_NUM_POOLS,
- MINIMUM_DOCKER_API_VERSION)
-from ..errors import (DockerException, TLSParameterError,
- create_api_error_from_http_exception)
+from ..constants import (
+ DEFAULT_TIMEOUT_SECONDS, DEFAULT_USER_AGENT, IS_WINDOWS_PLATFORM,
+ DEFAULT_DOCKER_API_VERSION, STREAM_HEADER_SIZE_BYTES, DEFAULT_NUM_POOLS,
+ MINIMUM_DOCKER_API_VERSION
+)
+from ..errors import (
+ DockerException, TLSParameterError,
+ create_api_error_from_http_exception
+)
from ..tls import TLSConfig
from ..transport import SSLAdapter, UnixAdapter
from ..utils import utils, check_resource, update_headers
from ..utils.socket import frames_iter
+from ..utils.json_stream import json_stream
try:
from ..transport import NpipeAdapter
except ImportError:
@@ -274,27 +278,20 @@ class APIClient(
def _stream_helper(self, response, decode=False):
"""Generator for data coming from a chunked-encoded HTTP response."""
+
if response.raw._fp.chunked:
- reader = response.raw
- while not reader.closed:
- # this read call will block until we get a chunk
- data = reader.read(1)
- if not data:
- break
- if reader._fp.chunk_left:
- data += reader.read(reader._fp.chunk_left)
- if decode:
- if six.PY3:
- data = data.decode('utf-8')
- # remove the trailing newline
- data = data.strip()
- # split the data at any newlines
- data_list = data.split("\r\n")
- # load and yield each line seperately
- for data in data_list:
- data = json.loads(data)
- yield data
- else:
+ if decode:
+ for chunk in json_stream(self._stream_helper(response, False)):
+ yield chunk
+ else:
+ reader = response.raw
+ while not reader.closed:
+ # this read call will block until we get a chunk
+ data = reader.read(1)
+ if not data:
+ break
+ if reader._fp.chunk_left:
+ data += reader.read(reader._fp.chunk_left)
yield data
else:
# Response isn't chunked, meaning we probably
diff --git a/docker/api/container.py b/docker/api/container.py
index afe696c..efcae9b 100644
--- a/docker/api/container.py
+++ b/docker/api/container.py
@@ -388,13 +388,13 @@ class ContainerApiMixin(object):
environment (dict or list): A dictionary or a list of strings in
the following format ``["PASSWORD=xxx"]`` or
``{"PASSWORD": "xxx"}``.
- dns (list): DNS name servers. Deprecated since API version 1.10.
- Use ``host_config`` instead.
- dns_opt (list): Additional options to be added to the container's
- ``resolv.conf`` file
+ dns (:py:class:`list`): DNS name servers. Deprecated since API
+ version 1.10. Use ``host_config`` instead.
+ dns_opt (:py:class:`list`): Additional options to be added to the
+ container's ``resolv.conf`` file
volumes (str or list):
- volumes_from (list): List of container names or Ids to get
- volumes from.
+ volumes_from (:py:class:`list`): List of container names or Ids to
+ get volumes from.
network_disabled (bool): Disable networking
name (str): A name for the container
entrypoint (str or list): An entrypoint
@@ -478,19 +478,19 @@ class ContainerApiMixin(object):
device_write_bps: Limit write rate (bytes per second) from a
device.
device_write_iops: Limit write rate (IO per second) from a device.
- devices (list): Expose host devices to the container, as a list
- of strings in the form
+ devices (:py:class:`list`): Expose host devices to the container,
+ as a list of strings in the form
``<path_on_host>:<path_in_container>:<cgroup_permissions>``.
For example, ``/dev/sda:/dev/xvda:rwm`` allows the container
to have read-write access to the host's ``/dev/sda`` via a
node named ``/dev/xvda`` inside the container.
- dns (list): Set custom DNS servers.
- dns_search (list): DNS search domains.
+ dns (:py:class:`list`): Set custom DNS servers.
+ dns_search (:py:class:`list`): DNS search domains.
extra_hosts (dict): Addtional hostnames to resolve inside the
container, as a mapping of hostname to IP address.
- group_add (list): List of additional group names and/or IDs that
- the container process will run as.
+ group_add (:py:class:`list`): List of additional group names and/or
+ IDs that the container process will run as.
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
@@ -539,8 +539,8 @@ class ContainerApiMixin(object):
- ``Name`` One of ``on-failure``, or ``always``.
- ``MaximumRetryCount`` Number of times to restart the
container on failure.
- security_opt (list): A list of string values to customize labels
- for MLS systems, such as SELinux.
+ security_opt (:py:class:`list`): A list of string values to
+ customize labels for MLS systems, such as SELinux.
shm_size (str or int): Size of /dev/shm (e.g. ``1G``).
sysctls (dict): Kernel parameters to set in the container.
tmpfs (dict): Temporary filesystems to mount, as a dictionary
@@ -555,13 +555,13 @@ class ContainerApiMixin(object):
'/mnt/vol1': 'size=3G,uid=1000'
}
- ulimits (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 dicts.
userns_mode (str): Sets the user namespace mode for the container
when user namespace remapping option is enabled. Supported
values are: ``host``
- volumes_from (list): List of container names or IDs to get
- volumes from.
+ volumes_from (:py:class:`list`): List of container names or IDs to
+ get volumes from.
Returns:
@@ -618,17 +618,17 @@ class ContainerApiMixin(object):
:py:meth:`create_networking_config`.
Args:
- aliases (list): A list of aliases for this endpoint. Names in
- that list can be used within the network to reach the
+ 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 (list): A list of links for this endpoint. Containers
- declared in this list will be linked to this container.
- 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
network, using the IPv6 protocol. Defaults to ``None``.
- link_local_ips (list): A list of link-local (IPv4/IPv6)
+ link_local_ips (:py:class:`list`): A list of link-local (IPv4/IPv6)
addresses.
Returns:
diff --git a/docker/api/network.py b/docker/api/network.py
index c58ea6e..9f6d98f 100644
--- a/docker/api/network.py
+++ b/docker/api/network.py
@@ -1,19 +1,23 @@
-import json
-
from ..errors import InvalidVersion
from ..utils import check_resource, minimum_version
from ..utils import version_lt
+from .. import utils
class NetworkApiMixin(object):
@minimum_version('1.21')
- def networks(self, names=None, ids=None):
+ def networks(self, names=None, ids=None, filters=None):
"""
List networks. Similar to the ``docker networks ls`` command.
Args:
- names (list): List of names to filter by
- ids (list): List of ids to filter by
+ names (:py:class:`list`): List of names to filter by
+ ids (:py:class:`list`): List of ids to filter by
+ filters (dict): Filters to be processed on the network list.
+ Available filters:
+ - ``driver=[<driver-name>]`` Matches a network's driver.
+ - ``label=[<key>]`` or ``label=[<key>=<value>]``.
+ - ``type=["custom"|"builtin"]`` Filters networks by type.
Returns:
(dict): List of network objects.
@@ -23,14 +27,13 @@ class NetworkApiMixin(object):
If the server returns an error.
"""
- filters = {}
+ if filters is None:
+ filters = {}
if names:
filters['name'] = names
if ids:
filters['id'] = ids
-
- params = {'filters': json.dumps(filters)}
-
+ params = {'filters': utils.convert_filters(filters)}
url = self._url("/networks")
res = self._get(url, params=params)
return self._result(res, json=True)
@@ -166,17 +169,18 @@ class NetworkApiMixin(object):
Args:
container (str): container-id/name to be connected to the network
net_id (str): network id
- aliases (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 (list): A list of links for this endpoint. Containers
- declared in this list will be linkedto this container.
- Defaults to ``None``.
+ 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``.
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
network, using the IPv6 protocol. Defaults to ``None``.
- link_local_ips (list): A list of link-local (IPv4/IPv6) addresses.
+ link_local_ips (:py:class:`list`): A list of link-local
+ (IPv4/IPv6) addresses.
"""
data = {
"Container": container,
diff --git a/docker/api/service.py b/docker/api/service.py
index 7708b75..d2621e6 100644
--- a/docker/api/service.py
+++ b/docker/api/service.py
@@ -1,5 +1,6 @@
import warnings
from .. import auth, errors, utils
+from ..types import ServiceMode
class ServiceApiMixin(object):
@@ -13,18 +14,18 @@ class ServiceApiMixin(object):
Create a service.
Args:
- task_template (dict): Specification of the task to start as part
- of the new service.
+ task_template (TaskTemplate): Specification of the task to start as
+ part of the new service.
name (string): User-defined name for the service. Optional.
labels (dict): A map of labels to associate with the service.
Optional.
- mode (string): Scheduling mode for the service (``replicated`` or
- ``global``). Defaults to ``replicated``.
- update_config (dict): Specification for the update strategy of the
- service. Default: ``None``
- networks (list): List of network names or IDs to attach the
- service to. Default: ``None``.
- endpoint_config (dict): Properties that can be configured to
+ mode (ServiceMode): Scheduling mode for the service (replicated
+ or global). Defaults to replicated.
+ update_config (UpdateConfig): Specification for the update strategy
+ of the service. Default: ``None``
+ networks (:py:class:`list`): List of network names or IDs to attach
+ the service to. Default: ``None``.
+ endpoint_spec (EndpointSpec): Properties that can be configured to
access and load balance a service. Default: ``None``.
Returns:
@@ -49,6 +50,9 @@ class ServiceApiMixin(object):
raise errors.DockerException(
'Missing mandatory Image key in ContainerSpec'
)
+ if mode and not isinstance(mode, dict):
+ mode = ServiceMode(mode)
+
registry, repo_name = auth.resolve_repository_name(image)
auth_header = auth.get_config_header(self, registry)
if auth_header:
@@ -159,7 +163,7 @@ class ServiceApiMixin(object):
``label`` and ``desired-state``.
Returns:
- (list): List of task dictionaries.
+ (:py:class:`list`): List of task dictionaries.
Raises:
:py:class:`docker.errors.APIError`
@@ -186,20 +190,18 @@ class ServiceApiMixin(object):
ID).
version (int): The version number of the service object being
updated. This is required to avoid conflicting writes.
- task_template (dict): Specification of the updated task to start
- as part of the service. See the [TaskTemplate
- class](#TaskTemplate) for details.
+ task_template (TaskTemplate): Specification of the updated task to
+ start as part of the service.
name (string): New name for the service. Optional.
labels (dict): A map of labels to associate with the service.
Optional.
- mode (string): Scheduling mode for the service (``replicated`` or
- ``global``). Defaults to ``replicated``.
- update_config (dict): Specification for the update strategy of the
- service. See the [UpdateConfig class](#UpdateConfig) for
- details. Default: ``None``.
- networks (list): List of network names or IDs to attach the
- service to. Default: ``None``.
- endpoint_config (dict): Properties that can be configured to
+ mode (ServiceMode): Scheduling mode for the service (replicated
+ or global). Defaults to replicated.
+ update_config (UpdateConfig): Specification for the update strategy
+ of the service. Default: ``None``.
+ networks (:py:class:`list`): List of network names or IDs to attach
+ the service to. Default: ``None``.
+ endpoint_spec (EndpointSpec): Properties that can be configured to
access and load balance a service. Default: ``None``.
Returns:
@@ -224,6 +226,8 @@ class ServiceApiMixin(object):
if labels is not None:
data['Labels'] = labels
if mode is not None:
+ if not isinstance(mode, dict):
+ mode = ServiceMode(mode)
data['Mode'] = mode
if task_template is not None:
image = task_template.get('ContainerSpec', {}).get('Image', None)
diff --git a/docker/api/swarm.py b/docker/api/swarm.py
index 3ada538..8877056 100644
--- a/docker/api/swarm.py
+++ b/docker/api/swarm.py
@@ -143,8 +143,8 @@ class SwarmApiMixin(object):
Make this Engine join a swarm that has already been created.
Args:
- remote_addrs (list): Addresses of one or more manager nodes already
- participating in the Swarm to join.
+ remote_addrs (:py:class:`list`): Addresses of one or more manager
+ nodes already participating in the Swarm to join.
join_token (string): Secret token for joining this Swarm.
listen_addr (string): Listen address used for inter-manager
communication if the node gets promoted to manager, as well as
diff --git a/docker/models/containers.py b/docker/models/containers.py
index ad1cb61..b1cdd8f 100644
--- a/docker/models/containers.py
+++ b/docker/models/containers.py
@@ -468,17 +468,17 @@ class ContainerCollection(Collection):
device_write_bps: Limit write rate (bytes per second) from a
device.
device_write_iops: Limit write rate (IO per second) from a device.
- devices (list): Expose host devices to the container, as a list
- of strings in the form
+ devices (:py:class:`list`): Expose host devices to the container,
+ as a list of strings in the form
``<path_on_host>:<path_in_container>:<cgroup_permissions>``.
For example, ``/dev/sda:/dev/xvda:rwm`` allows the container
to have read-write access to the host's ``/dev/sda`` via a
node named ``/dev/xvda`` inside the container.
- dns (list): Set custom DNS servers.
- dns_opt (list): Additional options to be added to the container's
- ``resolv.conf`` file.
- dns_search (list): DNS search domains.
+ dns (:py:class:`list`): Set custom DNS servers.
+ dns_opt (:py:class:`list`): Additional options to be added to the
+ container's ``resolv.conf`` file.
+ dns_search (:py:class:`list`): DNS search domains.
domainname (str or list): Set custom DNS search domains.
entrypoint (str or list): The entrypoint for the container.
environment (dict or list): Environment variables to set inside
@@ -486,8 +486,8 @@ class ContainerCollection(Collection):
format ``["SOMEVARIABLE=xxx"]``.
extra_hosts (dict): Addtional hostnames to resolve inside the
container, as a mapping of hostname to IP address.
- group_add (list): List of additional group names and/or IDs that
- the container process will run as.
+ group_add (:py:class:`list`): List of additional group names and/or
+ IDs that the container process will run as.
hostname (str): Optional hostname for the container.
ipc_mode (str): Set the IPC mode for the container.
isolation (str): Isolation technology to use. Default: `None`.
@@ -517,8 +517,8 @@ class ContainerCollection(Collection):
behavior. Accepts number between 0 and 100.
memswap_limit (str or int): Maximum amount of memory + swap a
container is allowed to consume.
- networks (list): A list of network names to connect this
- container to.
+ networks (:py:class:`list`): A list of network names to connect
+ this container to.
name (str): The name for this container.
network_disabled (bool): Disable networking.
network_mode (str): One of:
@@ -574,8 +574,8 @@ class ContainerCollection(Collection):
For example:
``{"Name": "on-failure", "MaximumRetryCount": 5}``
- security_opt (list): A list of string values to customize labels
- for MLS systems, such as SELinux.
+ security_opt (:py:class:`list`): A list of string values to
+ customize labels for MLS systems, such as SELinux.
shm_size (str or int): Size of /dev/shm (e.g. ``1G``).
stdin_open (bool): Keep ``STDIN`` open even if not attached.
stdout (bool): Return logs from ``STDOUT`` when ``detach=False``.
@@ -598,8 +598,8 @@ class ContainerCollection(Collection):
}
tty (bool): Allocate a pseudo-TTY.
- ulimits (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 dicts.
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
@@ -621,8 +621,8 @@ class ContainerCollection(Collection):
{'/home/user1/': {'bind': '/mnt/vol2', 'mode': 'rw'},
'/var/www': {'bind': '/mnt/vol1', 'mode': 'ro'}}
- volumes_from (list): List of container names or IDs to get
- volumes from.
+ volumes_from (:py:class:`list`): List of container names or IDs to
+ get volumes from.
working_dir (str): Path to the working directory.
Returns:
diff --git a/docker/models/images.py b/docker/models/images.py
index 32068e6..6f8f4fe 100644
--- a/docker/models/images.py
+++ b/docker/models/images.py
@@ -30,10 +30,10 @@ class Image(Model):
"""
The image's tags.
"""
- return [
- tag for tag in self.attrs.get('RepoTags', [])
- if tag != '<none>:<none>'
- ]
+ tags = self.attrs.get('RepoTags')
+ if tags is None:
+ tags = []
+ return [tag for tag in tags if tag != '<none>:<none>']
def history(self):
"""
diff --git a/docker/models/networks.py b/docker/models/networks.py
index d5e2097..a80c9f5 100644
--- a/docker/models/networks.py
+++ b/docker/models/networks.py
@@ -32,17 +32,18 @@ class Network(Model):
container (str): Container to connect to this network, as either
an ID, name, or :py:class:`~docker.models.containers.Container`
object.
- aliases (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 (list): A list of links for this endpoint. Containers
- declared in this list will be linkedto this container.
- Defaults to ``None``.
+ 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 linkedto this
+ container. 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
network, using the IPv6 protocol. Defaults to ``None``.
- link_local_ips (list): A list of link-local (IPv4/IPv6) addresses.
+ link_local_ips (:py:class:`list`): A list of link-local (IPv4/IPv6)
+ addresses.
Raises:
:py:class:`docker.errors.APIError`
@@ -167,8 +168,8 @@ class NetworkCollection(Collection):
List networks. Similar to the ``docker networks ls`` command.
Args:
- names (list): List of names to filter by.
- ids (list): List of ids to filter by.
+ names (:py:class:`list`): List of names to filter by.
+ ids (:py:class:`list`): List of ids to filter by.
Returns:
(list of :py:class:`Network`) The networks on the server.
diff --git a/docker/models/resource.py b/docker/models/resource.py
index 95712ae..ed3900a 100644
--- a/docker/models/resource.py
+++ b/docker/models/resource.py
@@ -23,6 +23,9 @@ class Model(object):
def __eq__(self, other):
return isinstance(other, self.__class__) and self.id == other.id
+ def __hash__(self):
+ return hash("%s:%s" % (self.__class__.__name__, self.id))
+
@property
def id(self):
"""
diff --git a/docker/models/services.py b/docker/models/services.py
index d70c9e7..ef6c3e3 100644
--- a/docker/models/services.py
+++ b/docker/models/services.py
@@ -42,7 +42,7 @@ class Service(Model):
``label``, and ``desired-state``.
Returns:
- (list): List of task dictionaries.
+ (:py:class:`list`): List of task dictionaries.
Raises:
:py:class:`docker.errors.APIError`
@@ -92,29 +92,27 @@ class ServiceCollection(Collection):
args (list of str): Arguments to the command.
constraints (list of str): Placement constraints.
container_labels (dict): Labels to apply to the container.
- endpoint_spec (dict): Properties that can be configured to
+ endpoint_spec (EndpointSpec): Properties that can be configured to
access and load balance a service. Default: ``None``.
env (list of str): Environment variables, in the form
``KEY=val``.
labels (dict): Labels to apply to the service.
log_driver (str): Log driver to use for containers.
log_driver_options (dict): Log driver options.
- mode (string): Scheduling mode for the service (``replicated`` or
+ mode (str): Scheduling mode for the service (``replicated`` or
``global``). Defaults to ``replicated``.
mounts (list of str): Mounts for the containers, in the form
``source:target:options``, where options is either
``ro`` or ``rw``.
name (str): Name to give to the service.
- networks (list): List of network names or IDs to attach the
- service to. Default: ``None``.
- resources (dict): Resource limits and reservations. For the
- format, see the Remote API documentation.
- restart_policy (dict): Restart policy for containers. For the
- format, see the Remote API documentation.
+ networks (list of str): List of network names or IDs to attach
+ the service to. Default: ``None``.
+ resources (Resources): Resource limits and reservations.
+ restart_policy (RestartPolicy): Restart policy for containers.
stop_grace_period (int): Amount of time to wait for
containers to terminate before forcefully killing them.
- update_config (dict): Specification for the update strategy of the
- service. Default: ``None``
+ update_config (UpdateConfig): Specification for the update strategy
+ of the service. Default: ``None``
user (str): User to run commands as.
workdir (str): Working directory for commands to run.
diff --git a/docker/types/__init__.py b/docker/types/__init__.py
index 7230723..8e2fc17 100644
--- a/docker/types/__init__.py
+++ b/docker/types/__init__.py
@@ -4,6 +4,6 @@ from .healthcheck import Healthcheck
from .networks import EndpointConfig, IPAMConfig, IPAMPool, NetworkingConfig
from .services import (
ContainerSpec, DriverConfig, EndpointSpec, Mount, Resources, RestartPolicy,
- TaskTemplate, UpdateConfig
+ ServiceMode, TaskTemplate, UpdateConfig
)
from .swarm import SwarmSpec, SwarmExternalCA
diff --git a/docker/types/networks.py b/docker/types/networks.py
index 628ea65..1c7b2c9 100644
--- a/docker/types/networks.py
+++ b/docker/types/networks.py
@@ -48,7 +48,7 @@ class IPAMConfig(dict):
Args:
driver (str): The IPAM driver to use. Defaults to ``default``.
- pool_configs (list): A list of pool configurations
+ pool_configs (:py:class:`list`): A list of pool configurations
(:py:class:`~docker.types.IPAMPool`). Defaults to empty list.
options (dict): Driver options as a key-value dictionary.
Defaults to `None`.
diff --git a/docker/types/services.py b/docker/types/services.py
index 93503dc..ec0fcb1 100644
--- a/docker/types/services.py
+++ b/docker/types/services.py
@@ -20,7 +20,7 @@ class TaskTemplate(dict):
individual container created as part of the service.
restart_policy (RestartPolicy): Specification for the restart policy
which applies to containers created as part of this service.
- placement (list): A list of constraints.
+ placement (:py:class:`list`): A list of constraints.
"""
def __init__(self, container_spec, resources=None, restart_policy=None,
placement=None, log_driver=None):
@@ -62,16 +62,16 @@ class ContainerSpec(dict):
image (string): The image name to use for the container.
command (string or list): The command to be run in the image.
- args (list): Arguments to the command.
+ args (:py:class:`list`): Arguments to the command.
env (dict): Environment variables.
dir (string): The working directory for commands to run in.
user (string): The user inside the container.
labels (dict): A map of labels to associate with the service.
- mounts (list): A list of specifications for mounts to be added to
- containers created as part of the service. See the
- :py:class:`~docker.types.Mount` class for details.
+ mounts (:py:class:`list`): A list of specifications for mounts to be
+ added to containers created as part of the service. See the
+ :py:class:`~docker.types.Mount` class for details.
stop_grace_period (int): Amount of time to wait for the container to
- terminate before forcefully killing it.
+ terminate before forcefully killing it.
"""
def __init__(self, image, command=None, args=None, env=None, workdir=None,
user=None, labels=None, mounts=None, stop_grace_period=None):
@@ -106,7 +106,7 @@ class ContainerSpec(dict):
class Mount(dict):
"""
Describes a mounted folder's configuration inside a container. A list of
- ``Mount``s would be used as part of a
+ :py:class:`Mount`s would be used as part of a
:py:class:`~docker.types.ContainerSpec`.
Args:
@@ -348,3 +348,38 @@ def convert_service_ports(ports):
result.append(port_spec)
return result
+
+
+class ServiceMode(dict):
+ """
+ Indicate whether a service should be deployed as a replicated or global
+ service, and associated parameters
+
+ Args:
+ mode (string): Can be either ``replicated`` or ``global``
+ replicas (int): Number of replicas. For replicated services only.
+ """
+ def __init__(self, mode, replicas=None):
+ if mode not in ('replicated', 'global'):
+ raise errors.InvalidArgument(
+ 'mode must be either "replicated" or "global"'
+ )
+ if mode != 'replicated' and replicas is not None:
+ raise errors.InvalidArgument(
+ 'replicas can only be used for replicated mode'
+ )
+ self[mode] = {}
+ if replicas:
+ self[mode]['Replicas'] = replicas
+
+ @property
+ def mode(self):
+ if 'global' in self:
+ return 'global'
+ return 'replicated'
+
+ @property
+ def replicas(self):
+ if self.mode != 'replicated':
+ return None
+ return self['replicated'].get('Replicas')
diff --git a/docker/version.py b/docker/version.py
index 6979eec..b0c244a 100644
--- a/docker/version.py
+++ b/docker/version.py
@@ -1,2 +1,2 @@
-version = "2.0.1"
+version = "2.0.2"
version_info = tuple([int(d) for d in version.split("-")[0].split(".")])
diff --git a/docs/api.rst b/docs/api.rst
index 5e59aa7..b5c1e92 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -19,8 +19,6 @@ Containers
:members:
:undoc-members:
-.. py:module:: docker.api.image
-
Images
------
@@ -112,5 +110,6 @@ Configuration types
.. autoclass:: Mount
.. autoclass:: Resources
.. autoclass:: RestartPolicy
+.. autoclass:: ServiceMode
.. autoclass:: TaskTemplate
.. autoclass:: UpdateConfig
diff --git a/docs/change-log.md b/docs/change-log.md
index 91eafcc..1dda441 100644
--- a/docs/change-log.md
+++ b/docs/change-log.md
@@ -1,6 +1,26 @@
Change log
==========
+2.0.2
+-----
+
+[List of PRs / issues for this release](https://github.com/docker/docker-py/milestone/28?closed=1)
+
+### Bugfixes
+
+* Installation of the package now fails if the `docker-py` package is
+ installed in order to prevent obscure naming conflicts when both
+ packages co-exist.
+* Added missing `filters` parameter to `APIClient.networks`.
+* Resource objects generated by the `DockerClient` are now hashable.
+* Fixed a bug where retrieving untagged images using `DockerClient`
+ would raise a `TypeError` exception.
+* `mode` parameter in `create_service` is now properly converted to
+ a valid data type for the Engine API. Use `ServiceMode` for advanced
+ configurations.
+* Fixed a bug where the decoded `APIClient.events` stream would sometimes raise
+ an exception when a container is stopped or restarted.
+
2.0.1
-----
diff --git a/docs/index.rst b/docs/index.rst
index 7eadf4c..9f484cd 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -73,13 +73,12 @@ You can manage images:
>>> client.images.list()
[<Image 'ubuntu'>, <Image 'nginx'>, ...]
-That's just a taster of what you can do with the Docker SDK for Python. For more, :doc:`take a look at the reference <client>`.
+That's just a taste of what you can do with the Docker SDK for Python. For more, :doc:`take a look at the reference <client>`.
.. toctree::
:hidden:
:maxdepth: 2
- Home <index>
client
containers
images
diff --git a/setup.py b/setup.py
index b82a74f..9fc4ad6 100644
--- a/setup.py
+++ b/setup.py
@@ -1,10 +1,20 @@
#!/usr/bin/env python
+from __future__ import print_function
+
import codecs
import os
import sys
+import pip
+
from setuptools import setup, find_packages
+if 'docker-py' in [x.project_name for x in pip.get_installed_distributions()]:
+ print(
+ 'ERROR: "docker-py" needs to be uninstalled before installing this'
+ ' package:\npip uninstall docker-py', file=sys.stderr
+ )
+ sys.exit(1)
ROOT_DIR = os.path.dirname(__file__)
SOURCE_DIR = os.path.join(ROOT_DIR)
diff --git a/tests/integration/api_service_test.py b/tests/integration/api_service_test.py
index fc79400..77d7d28 100644
--- a/tests/integration/api_service_test.py
+++ b/tests/integration/api_service_test.py
@@ -251,3 +251,31 @@ class ServiceTest(BaseAPIIntegrationTest):
con_spec = svc_info['Spec']['TaskTemplate']['ContainerSpec']
assert 'Env' in con_spec
assert con_spec['Env'] == ['DOCKER_PY_TEST=1']
+
+ def test_create_service_global_mode(self):
+ container_spec = docker.types.ContainerSpec(
+ 'busybox', ['echo', 'hello']
+ )
+ task_tmpl = docker.types.TaskTemplate(container_spec)
+ name = self.get_service_name()
+ svc_id = self.client.create_service(
+ task_tmpl, name=name, mode='global'
+ )
+ svc_info = self.client.inspect_service(svc_id)
+ assert 'Mode' in svc_info['Spec']
+ assert 'Global' in svc_info['Spec']['Mode']
+
+ def test_create_service_replicated_mode(self):
+ container_spec = docker.types.ContainerSpec(
+ 'busybox', ['echo', 'hello']
+ )
+ task_tmpl = docker.types.TaskTemplate(container_spec)
+ name = self.get_service_name()
+ svc_id = self.client.create_service(
+ task_tmpl, name=name,
+ mode=docker.types.ServiceMode('replicated', 5)
+ )
+ svc_info = self.client.inspect_service(svc_id)
+ assert 'Mode' in svc_info['Spec']
+ assert 'Replicated' in svc_info['Spec']['Mode']
+ assert svc_info['Spec']['Mode']['Replicated'] == {'Replicas': 5}
diff --git a/tests/unit/dockertypes_test.py b/tests/unit/dockertypes_test.py
index d11e4f0..5c470ff 100644
--- a/tests/unit/dockertypes_test.py
+++ b/tests/unit/dockertypes_test.py
@@ -7,7 +7,8 @@ import pytest
from docker.constants import DEFAULT_DOCKER_API_VERSION
from docker.errors import InvalidArgument, InvalidVersion
from docker.types import (
- EndpointConfig, HostConfig, IPAMConfig, IPAMPool, LogConfig, Mount, Ulimit,
+ EndpointConfig, HostConfig, IPAMConfig, IPAMPool, LogConfig, Mount,
+ ServiceMode, Ulimit,
)
try:
@@ -260,7 +261,35 @@ class IPAMConfigTest(unittest.TestCase):
})
-class TestMounts(unittest.TestCase):
+class ServiceModeTest(unittest.TestCase):
+ def test_replicated_simple(self):
+ mode = ServiceMode('replicated')
+ assert mode == {'replicated': {}}
+ assert mode.mode == 'replicated'
+ assert mode.replicas is None
+
+ def test_global_simple(self):
+ mode = ServiceMode('global')
+ assert mode == {'global': {}}
+ assert mode.mode == 'global'
+ assert mode.replicas is None
+
+ def test_global_replicas_error(self):
+ with pytest.raises(InvalidArgument):
+ ServiceMode('global', 21)
+
+ def test_replicated_replicas(self):
+ mode = ServiceMode('replicated', 21)
+ assert mode == {'replicated': {'Replicas': 21}}
+ assert mode.mode == 'replicated'
+ assert mode.replicas == 21
+
+ def test_invalid_mode(self):
+ with pytest.raises(InvalidArgument):
+ ServiceMode('foobar')
+
+
+class MountTest(unittest.TestCase):
def test_parse_mount_string_ro(self):
mount = Mount.parse_mount_string("/foo/bar:/baz:ro")
assert mount['Source'] == "/foo/bar"
diff --git a/tests/unit/models_images_test.py b/tests/unit/models_images_test.py
index 392c58d..efb2116 100644
--- a/tests/unit/models_images_test.py
+++ b/tests/unit/models_images_test.py
@@ -83,6 +83,11 @@ class ImageTest(unittest.TestCase):
})
assert image.tags == []
+ image = Image(attrs={
+ 'RepoTags': None
+ })
+ assert image.tags == []
+
def test_history(self):
client = make_fake_client()
image = client.images.get(FAKE_IMAGE_ID)
diff --git a/tests/unit/models_resources_test.py b/tests/unit/models_resources_test.py
index 25c6a3e..5af24ee 100644
--- a/tests/unit/models_resources_test.py
+++ b/tests/unit/models_resources_test.py
@@ -12,3 +12,17 @@ class ModelTest(unittest.TestCase):
container.reload()
assert client.api.inspect_container.call_count == 2
assert container.attrs['Name'] == "foobar"
+
+ def test_hash(self):
+ client = make_fake_client()
+ container1 = client.containers.get(FAKE_CONTAINER_ID)
+ my_set = set([container1])
+ assert len(my_set) == 1
+
+ container2 = client.containers.get(FAKE_CONTAINER_ID)
+ my_set.add(container2)
+ assert len(my_set) == 1
+
+ image1 = client.images.get(FAKE_CONTAINER_ID)
+ my_set.add(image1)
+ assert len(my_set) == 2