summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Firshman <ben@firshman.co.uk>2016-12-02 14:35:43 +0000
committerGitHub <noreply@github.com>2016-12-02 14:35:43 +0000
commitff6601cf64d8a8296d851b0d8ac44ec34ed46cca (patch)
treeadd38dbbce53d5356bd9dcf59da8bee3ac2edeca
parent01c33c0f684868290249945f9db9e00d7f03ecd9 (diff)
parent993f298e85539751343048467004a679414426ec (diff)
downloaddocker-py-ff6601cf64d8a8296d851b0d8ac44ec34ed46cca.tar.gz
Merge pull request #1319 from docker/config_types
Move config type creation from docker.utils functions to classes in docker.types
-rw-r--r--docker/api/container.py16
-rw-r--r--docker/api/network.py7
-rw-r--r--docker/api/swarm.py3
-rw-r--r--docker/models/containers.py4
-rw-r--r--docker/models/networks.py6
-rw-r--r--docker/types/__init__.py5
-rw-r--r--docker/types/containers.py490
-rw-r--r--docker/types/networks.py104
-rw-r--r--docker/types/services.py143
-rw-r--r--docker/utils/__init__.py9
-rw-r--r--docker/utils/types.py7
-rw-r--r--docker/utils/utils.py611
-rw-r--r--docs/api.rst25
-rw-r--r--tests/integration/api_container_test.py8
-rw-r--r--tests/integration/api_network_test.py23
-rw-r--r--tests/unit/api_network_test.py8
-rw-r--r--tests/unit/dockertypes_test.py255
-rw-r--r--tests/unit/utils_test.py248
18 files changed, 999 insertions, 973 deletions
diff --git a/docker/api/container.py b/docker/api/container.py
index 10d7ff4..afe696c 100644
--- a/docker/api/container.py
+++ b/docker/api/container.py
@@ -4,7 +4,9 @@ from datetime import datetime
from .. import errors
from .. import utils
-from ..utils.utils import create_networking_config, create_endpoint_config
+from ..types import (
+ ContainerConfig, EndpointConfig, HostConfig, NetworkingConfig
+)
class ContainerApiMixin(object):
@@ -430,8 +432,8 @@ class ContainerApiMixin(object):
)
config = self.create_container_config(
- image, command, hostname, user, detach, stdin_open,
- tty, mem_limit, ports, environment, dns, volumes, volumes_from,
+ image, command, hostname, user, detach, stdin_open, tty, mem_limit,
+ ports, dns, environment, volumes, volumes_from,
network_disabled, entrypoint, cpu_shares, working_dir, domainname,
memswap_limit, cpuset, host_config, mac_address, labels,
volume_driver, stop_signal, networking_config, healthcheck,
@@ -439,7 +441,7 @@ class ContainerApiMixin(object):
return self.create_container_from_config(config, name)
def create_container_config(self, *args, **kwargs):
- return utils.create_container_config(self._version, *args, **kwargs)
+ return ContainerConfig(self._version, *args, **kwargs)
def create_container_from_config(self, config, name=None):
u = self._url("/containers/create")
@@ -582,7 +584,7 @@ class ContainerApiMixin(object):
"keyword argument 'version'"
)
kwargs['version'] = self._version
- return utils.create_host_config(*args, **kwargs)
+ return HostConfig(*args, **kwargs)
def create_networking_config(self, *args, **kwargs):
"""
@@ -608,7 +610,7 @@ class ContainerApiMixin(object):
)
"""
- return create_networking_config(*args, **kwargs)
+ return NetworkingConfig(*args, **kwargs)
def create_endpoint_config(self, *args, **kwargs):
"""
@@ -641,7 +643,7 @@ class ContainerApiMixin(object):
)
"""
- return create_endpoint_config(self._version, *args, **kwargs)
+ return EndpointConfig(self._version, *args, **kwargs)
@utils.check_resource
def diff(self, container):
diff --git a/docker/api/network.py b/docker/api/network.py
index 65aeb30..33da7ea 100644
--- a/docker/api/network.py
+++ b/docker/api/network.py
@@ -46,8 +46,7 @@ class NetworkApiMixin(object):
name (str): Name of the network
driver (str): Name of the driver used to create the network
options (dict): Driver options as a key-value dictionary
- ipam (dict): Optional custom IP scheme for the network.
- Created with :py:meth:`~docker.utils.create_ipam_config`.
+ ipam (IPAMConfig): Optional custom IP scheme for the network.
check_duplicate (bool): Request daemon to check for networks with
same name. Default: ``True``.
internal (bool): Restrict external access to the network. Default
@@ -74,11 +73,11 @@ class NetworkApiMixin(object):
.. code-block:: python
- >>> ipam_pool = docker.utils.create_ipam_pool(
+ >>> ipam_pool = docker.types.IPAMPool(
subnet='192.168.52.0/24',
gateway='192.168.52.254'
)
- >>> ipam_config = docker.utils.create_ipam_config(
+ >>> ipam_config = docker.types.IPAMConfig(
pool_configs=[ipam_pool]
)
>>> docker_client.create_network("network1", driver="bridge",
diff --git a/docker/api/swarm.py b/docker/api/swarm.py
index 521076f..6a1b752 100644
--- a/docker/api/swarm.py
+++ b/docker/api/swarm.py
@@ -1,5 +1,6 @@
import logging
from six.moves import http_client
+from .. import types
from .. import utils
log = logging.getLogger(__name__)
@@ -50,7 +51,7 @@ class SwarmApiMixin(object):
force_new_cluster=False, swarm_spec=spec
)
"""
- return utils.SwarmSpec(*args, **kwargs)
+ return types.SwarmSpec(*args, **kwargs)
@utils.minimum_version('1.24')
def init_swarm(self, advertise_addr=None, listen_addr='0.0.0.0:2377',
diff --git a/docker/models/containers.py b/docker/models/containers.py
index 9682248..ad1cb61 100644
--- a/docker/models/containers.py
+++ b/docker/models/containers.py
@@ -2,7 +2,7 @@ import copy
from ..errors import (ContainerError, ImageNotFound,
create_unexpected_kwargs_error)
-from ..utils import create_host_config
+from ..types import HostConfig
from .images import Image
from .resource import Collection, Model
@@ -869,7 +869,7 @@ def _create_container_args(kwargs):
if kwargs:
raise create_unexpected_kwargs_error('run', kwargs)
- create_kwargs['host_config'] = create_host_config(**host_config_kwargs)
+ create_kwargs['host_config'] = HostConfig(**host_config_kwargs)
# Fill in any kwargs which need processing by create_host_config first
port_bindings = create_kwargs['host_config'].get('PortBindings')
diff --git a/docker/models/networks.py b/docker/models/networks.py
index 64af9ad..d5e2097 100644
--- a/docker/models/networks.py
+++ b/docker/models/networks.py
@@ -98,7 +98,7 @@ class NetworkCollection(Collection):
driver (str): Name of the driver used to create the network
options (dict): Driver options as a key-value dictionary
ipam (dict): Optional custom IP scheme for the network.
- Created with :py:meth:`~docker.utils.create_ipam_config`.
+ Created with :py:class:`~docker.types.IPAMConfig`.
check_duplicate (bool): Request daemon to check for networks with
same name. Default: ``True``.
internal (bool): Restrict external access to the network. Default
@@ -125,11 +125,11 @@ class NetworkCollection(Collection):
.. code-block:: python
- >>> ipam_pool = docker.utils.create_ipam_pool(
+ >>> ipam_pool = docker.types.IPAMPool(
subnet='192.168.52.0/24',
gateway='192.168.52.254'
)
- >>> ipam_config = docker.utils.create_ipam_config(
+ >>> ipam_config = docker.types.IPAMConfig(
pool_configs=[ipam_pool]
)
>>> client.networks.create(
diff --git a/docker/types/__init__.py b/docker/types/__init__.py
index a7c3a56..7230723 100644
--- a/docker/types/__init__.py
+++ b/docker/types/__init__.py
@@ -1,8 +1,9 @@
# flake8: noqa
-from .containers import LogConfig, Ulimit
+from .containers import ContainerConfig, HostConfig, LogConfig, Ulimit
+from .healthcheck import Healthcheck
+from .networks import EndpointConfig, IPAMConfig, IPAMPool, NetworkingConfig
from .services import (
ContainerSpec, DriverConfig, EndpointSpec, Mount, Resources, RestartPolicy,
TaskTemplate, UpdateConfig
)
-from .healthcheck import Healthcheck
from .swarm import SwarmSpec, SwarmExternalCA
diff --git a/docker/types/containers.py b/docker/types/containers.py
index 40a44ca..8fdecb3 100644
--- a/docker/types/containers.py
+++ b/docker/types/containers.py
@@ -1,6 +1,14 @@
import six
+import warnings
+from .. import errors
+from ..utils.utils import (
+ convert_port_bindings, convert_tmpfs_mounts, convert_volume_binds,
+ format_environment, normalize_links, parse_bytes, parse_devices,
+ split_command, version_gte, version_lt,
+)
from .base import DictType
+from .healthcheck import Healthcheck
class LogConfigTypesEnum(object):
@@ -90,3 +98,485 @@ class Ulimit(DictType):
@hard.setter
def hard(self, value):
self['Hard'] = value
+
+
+class HostConfig(dict):
+ def __init__(self, version, binds=None, port_bindings=None,
+ lxc_conf=None, publish_all_ports=False, links=None,
+ privileged=False, dns=None, dns_search=None,
+ volumes_from=None, network_mode=None, restart_policy=None,
+ cap_add=None, cap_drop=None, devices=None, extra_hosts=None,
+ read_only=None, pid_mode=None, ipc_mode=None,
+ security_opt=None, ulimits=None, log_config=None,
+ mem_limit=None, memswap_limit=None, mem_reservation=None,
+ kernel_memory=None, mem_swappiness=None, cgroup_parent=None,
+ group_add=None, cpu_quota=None, cpu_period=None,
+ blkio_weight=None, blkio_weight_device=None,
+ device_read_bps=None, device_write_bps=None,
+ device_read_iops=None, device_write_iops=None,
+ oom_kill_disable=False, shm_size=None, sysctls=None,
+ tmpfs=None, oom_score_adj=None, dns_opt=None, cpu_shares=None,
+ cpuset_cpus=None, userns_mode=None, pids_limit=None,
+ isolation=None):
+
+ if mem_limit is not None:
+ self['Memory'] = parse_bytes(mem_limit)
+
+ if memswap_limit is not None:
+ self['MemorySwap'] = parse_bytes(memswap_limit)
+
+ if mem_reservation:
+ if version_lt(version, '1.21'):
+ raise host_config_version_error('mem_reservation', '1.21')
+
+ self['MemoryReservation'] = parse_bytes(mem_reservation)
+
+ if kernel_memory:
+ if version_lt(version, '1.21'):
+ raise host_config_version_error('kernel_memory', '1.21')
+
+ self['KernelMemory'] = parse_bytes(kernel_memory)
+
+ if mem_swappiness is not None:
+ if version_lt(version, '1.20'):
+ raise host_config_version_error('mem_swappiness', '1.20')
+ if not isinstance(mem_swappiness, int):
+ raise host_config_type_error(
+ 'mem_swappiness', mem_swappiness, 'int'
+ )
+
+ self['MemorySwappiness'] = mem_swappiness
+
+ if shm_size is not None:
+ if isinstance(shm_size, six.string_types):
+ shm_size = parse_bytes(shm_size)
+
+ self['ShmSize'] = shm_size
+
+ if pid_mode:
+ if version_lt(version, '1.24') and pid_mode != 'host':
+ raise host_config_value_error('pid_mode', pid_mode)
+ self['PidMode'] = pid_mode
+
+ if ipc_mode:
+ self['IpcMode'] = ipc_mode
+
+ if privileged:
+ self['Privileged'] = privileged
+
+ if oom_kill_disable:
+ if version_lt(version, '1.20'):
+ raise host_config_version_error('oom_kill_disable', '1.19')
+
+ self['OomKillDisable'] = oom_kill_disable
+
+ if oom_score_adj:
+ if version_lt(version, '1.22'):
+ raise host_config_version_error('oom_score_adj', '1.22')
+ if not isinstance(oom_score_adj, int):
+ raise host_config_type_error(
+ 'oom_score_adj', oom_score_adj, 'int'
+ )
+ self['OomScoreAdj'] = oom_score_adj
+
+ if publish_all_ports:
+ self['PublishAllPorts'] = publish_all_ports
+
+ if read_only is not None:
+ self['ReadonlyRootfs'] = read_only
+
+ if dns_search:
+ self['DnsSearch'] = dns_search
+
+ if network_mode:
+ self['NetworkMode'] = network_mode
+ elif network_mode is None and version_gte(version, '1.20'):
+ self['NetworkMode'] = 'default'
+
+ if restart_policy:
+ if not isinstance(restart_policy, dict):
+ raise host_config_type_error(
+ 'restart_policy', restart_policy, 'dict'
+ )
+
+ self['RestartPolicy'] = restart_policy
+
+ if cap_add:
+ self['CapAdd'] = cap_add
+
+ if cap_drop:
+ self['CapDrop'] = cap_drop
+
+ if devices:
+ self['Devices'] = parse_devices(devices)
+
+ if group_add:
+ if version_lt(version, '1.20'):
+ raise host_config_version_error('group_add', '1.20')
+
+ self['GroupAdd'] = [six.text_type(grp) for grp in group_add]
+
+ if dns is not None:
+ self['Dns'] = dns
+
+ if dns_opt is not None:
+ if version_lt(version, '1.21'):
+ raise host_config_version_error('dns_opt', '1.21')
+
+ self['DnsOptions'] = dns_opt
+
+ if security_opt is not None:
+ if not isinstance(security_opt, list):
+ raise host_config_type_error(
+ 'security_opt', security_opt, 'list'
+ )
+
+ self['SecurityOpt'] = security_opt
+
+ if sysctls:
+ if not isinstance(sysctls, dict):
+ raise host_config_type_error('sysctls', sysctls, 'dict')
+ self['Sysctls'] = {}
+ for k, v in six.iteritems(sysctls):
+ self['Sysctls'][k] = six.text_type(v)
+
+ if volumes_from is not None:
+ if isinstance(volumes_from, six.string_types):
+ volumes_from = volumes_from.split(',')
+
+ self['VolumesFrom'] = volumes_from
+
+ if binds is not None:
+ self['Binds'] = convert_volume_binds(binds)
+
+ if port_bindings is not None:
+ self['PortBindings'] = convert_port_bindings(port_bindings)
+
+ if extra_hosts is not None:
+ if isinstance(extra_hosts, dict):
+ extra_hosts = [
+ '{0}:{1}'.format(k, v)
+ for k, v in sorted(six.iteritems(extra_hosts))
+ ]
+
+ self['ExtraHosts'] = extra_hosts
+
+ if links is not None:
+ self['Links'] = normalize_links(links)
+
+ if isinstance(lxc_conf, dict):
+ formatted = []
+ for k, v in six.iteritems(lxc_conf):
+ formatted.append({'Key': k, 'Value': str(v)})
+ lxc_conf = formatted
+
+ if lxc_conf is not None:
+ self['LxcConf'] = lxc_conf
+
+ if cgroup_parent is not None:
+ self['CgroupParent'] = cgroup_parent
+
+ if ulimits is not None:
+ if not isinstance(ulimits, list):
+ raise host_config_type_error('ulimits', ulimits, 'list')
+ self['Ulimits'] = []
+ for l in ulimits:
+ if not isinstance(l, Ulimit):
+ l = Ulimit(**l)
+ self['Ulimits'].append(l)
+
+ if log_config is not None:
+ if not isinstance(log_config, LogConfig):
+ if not isinstance(log_config, dict):
+ raise host_config_type_error(
+ 'log_config', log_config, 'LogConfig'
+ )
+ log_config = LogConfig(**log_config)
+
+ self['LogConfig'] = log_config
+
+ if cpu_quota:
+ if not isinstance(cpu_quota, int):
+ raise host_config_type_error('cpu_quota', cpu_quota, 'int')
+ if version_lt(version, '1.19'):
+ raise host_config_version_error('cpu_quota', '1.19')
+
+ self['CpuQuota'] = cpu_quota
+
+ if cpu_period:
+ if not isinstance(cpu_period, int):
+ raise host_config_type_error('cpu_period', cpu_period, 'int')
+ if version_lt(version, '1.19'):
+ raise host_config_version_error('cpu_period', '1.19')
+
+ self['CpuPeriod'] = cpu_period
+
+ if cpu_shares:
+ if version_lt(version, '1.18'):
+ raise host_config_version_error('cpu_shares', '1.18')
+
+ if not isinstance(cpu_shares, int):
+ raise host_config_type_error('cpu_shares', cpu_shares, 'int')
+
+ self['CpuShares'] = cpu_shares
+
+ if cpuset_cpus:
+ if version_lt(version, '1.18'):
+ raise host_config_version_error('cpuset_cpus', '1.18')
+
+ self['CpuSetCpus'] = cpuset_cpus
+
+ if blkio_weight:
+ if not isinstance(blkio_weight, int):
+ raise host_config_type_error(
+ 'blkio_weight', blkio_weight, 'int'
+ )
+ if version_lt(version, '1.22'):
+ raise host_config_version_error('blkio_weight', '1.22')
+ self["BlkioWeight"] = blkio_weight
+
+ if blkio_weight_device:
+ if not isinstance(blkio_weight_device, list):
+ raise host_config_type_error(
+ 'blkio_weight_device', blkio_weight_device, 'list'
+ )
+ if version_lt(version, '1.22'):
+ raise host_config_version_error('blkio_weight_device', '1.22')
+ self["BlkioWeightDevice"] = blkio_weight_device
+
+ if device_read_bps:
+ if not isinstance(device_read_bps, list):
+ raise host_config_type_error(
+ 'device_read_bps', device_read_bps, 'list'
+ )
+ if version_lt(version, '1.22'):
+ raise host_config_version_error('device_read_bps', '1.22')
+ self["BlkioDeviceReadBps"] = device_read_bps
+
+ if device_write_bps:
+ if not isinstance(device_write_bps, list):
+ raise host_config_type_error(
+ 'device_write_bps', device_write_bps, 'list'
+ )
+ if version_lt(version, '1.22'):
+ raise host_config_version_error('device_write_bps', '1.22')
+ self["BlkioDeviceWriteBps"] = device_write_bps
+
+ if device_read_iops:
+ if not isinstance(device_read_iops, list):
+ raise host_config_type_error(
+ 'device_read_iops', device_read_iops, 'list'
+ )
+ if version_lt(version, '1.22'):
+ raise host_config_version_error('device_read_iops', '1.22')
+ self["BlkioDeviceReadIOps"] = device_read_iops
+
+ if device_write_iops:
+ if not isinstance(device_write_iops, list):
+ raise host_config_type_error(
+ 'device_write_iops', device_write_iops, 'list'
+ )
+ if version_lt(version, '1.22'):
+ raise host_config_version_error('device_write_iops', '1.22')
+ self["BlkioDeviceWriteIOps"] = device_write_iops
+
+ if tmpfs:
+ if version_lt(version, '1.22'):
+ raise host_config_version_error('tmpfs', '1.22')
+ self["Tmpfs"] = convert_tmpfs_mounts(tmpfs)
+
+ if userns_mode:
+ if version_lt(version, '1.23'):
+ raise host_config_version_error('userns_mode', '1.23')
+
+ if userns_mode != "host":
+ raise host_config_value_error("userns_mode", userns_mode)
+ self['UsernsMode'] = userns_mode
+
+ if pids_limit:
+ if not isinstance(pids_limit, int):
+ raise host_config_type_error('pids_limit', pids_limit, 'int')
+ if version_lt(version, '1.23'):
+ raise host_config_version_error('pids_limit', '1.23')
+ self["PidsLimit"] = pids_limit
+
+ if isolation:
+ if not isinstance(isolation, six.string_types):
+ raise host_config_type_error('isolation', isolation, 'string')
+ if version_lt(version, '1.24'):
+ raise host_config_version_error('isolation', '1.24')
+ self['Isolation'] = isolation
+
+
+def host_config_type_error(param, param_value, expected):
+ error_msg = 'Invalid type for {0} param: expected {1} but found {2}'
+ return TypeError(error_msg.format(param, expected, type(param_value)))
+
+
+def host_config_version_error(param, version, less_than=True):
+ operator = '<' if less_than else '>'
+ error_msg = '{0} param is not supported in API versions {1} {2}'
+ return errors.InvalidVersion(error_msg.format(param, operator, version))
+
+
+def host_config_value_error(param, param_value):
+ error_msg = 'Invalid value for {0} param: {1}'
+ return ValueError(error_msg.format(param, param_value))
+
+
+class ContainerConfig(dict):
+ def __init__(
+ self, version, image, command, hostname=None, user=None, detach=False,
+ stdin_open=False, tty=False, mem_limit=None, ports=None, dns=None,
+ environment=None, volumes=None, volumes_from=None,
+ network_disabled=False, entrypoint=None, cpu_shares=None,
+ working_dir=None, domainname=None, memswap_limit=None, cpuset=None,
+ host_config=None, mac_address=None, labels=None, volume_driver=None,
+ stop_signal=None, networking_config=None, healthcheck=None,
+ ):
+ if isinstance(command, six.string_types):
+ command = split_command(command)
+
+ if isinstance(entrypoint, six.string_types):
+ entrypoint = split_command(entrypoint)
+
+ if isinstance(environment, dict):
+ environment = format_environment(environment)
+
+ if labels is not None and version_lt(version, '1.18'):
+ raise errors.InvalidVersion(
+ 'labels were only introduced in API version 1.18'
+ )
+
+ if cpuset is not None or cpu_shares is not None:
+ if version_gte(version, '1.18'):
+ warnings.warn(
+ 'The cpuset_cpus and cpu_shares options have been moved to'
+ ' host_config in API version 1.18, and will be removed',
+ DeprecationWarning
+ )
+
+ if stop_signal is not None and version_lt(version, '1.21'):
+ raise errors.InvalidVersion(
+ 'stop_signal was only introduced in API version 1.21'
+ )
+
+ if healthcheck is not None and version_lt(version, '1.24'):
+ raise errors.InvalidVersion(
+ 'Health options were only introduced in API version 1.24'
+ )
+
+ if version_lt(version, '1.19'):
+ if volume_driver is not None:
+ raise errors.InvalidVersion(
+ 'Volume drivers were only introduced in API version 1.19'
+ )
+ mem_limit = mem_limit if mem_limit is not None else 0
+ memswap_limit = memswap_limit if memswap_limit is not None else 0
+ else:
+ if mem_limit is not None:
+ raise errors.InvalidVersion(
+ 'mem_limit has been moved to host_config in API version'
+ ' 1.19'
+ )
+
+ if memswap_limit is not None:
+ raise errors.InvalidVersion(
+ 'memswap_limit has been moved to host_config in API '
+ 'version 1.19'
+ )
+
+ if isinstance(labels, list):
+ labels = dict((lbl, six.text_type('')) for lbl in labels)
+
+ if mem_limit is not None:
+ mem_limit = parse_bytes(mem_limit)
+
+ if memswap_limit is not None:
+ memswap_limit = parse_bytes(memswap_limit)
+
+ if isinstance(ports, list):
+ exposed_ports = {}
+ for port_definition in ports:
+ port = port_definition
+ proto = 'tcp'
+ if isinstance(port_definition, tuple):
+ if len(port_definition) == 2:
+ proto = port_definition[1]
+ port = port_definition[0]
+ exposed_ports['{0}/{1}'.format(port, proto)] = {}
+ ports = exposed_ports
+
+ if isinstance(volumes, six.string_types):
+ volumes = [volumes, ]
+
+ if isinstance(volumes, list):
+ volumes_dict = {}
+ for vol in volumes:
+ volumes_dict[vol] = {}
+ volumes = volumes_dict
+
+ if volumes_from:
+ if not isinstance(volumes_from, six.string_types):
+ volumes_from = ','.join(volumes_from)
+ else:
+ # Force None, an empty list or dict causes client.start to fail
+ volumes_from = None
+
+ if healthcheck and isinstance(healthcheck, dict):
+ healthcheck = Healthcheck(**healthcheck)
+
+ attach_stdin = False
+ attach_stdout = False
+ attach_stderr = False
+ stdin_once = False
+
+ if not detach:
+ attach_stdout = True
+ attach_stderr = True
+
+ if stdin_open:
+ attach_stdin = True
+ stdin_once = True
+
+ if version_gte(version, '1.10'):
+ message = ('{0!r} parameter has no effect on create_container().'
+ ' It has been moved to host_config')
+ if dns is not None:
+ raise errors.InvalidVersion(message.format('dns'))
+ if volumes_from is not None:
+ raise errors.InvalidVersion(message.format('volumes_from'))
+
+ self.update({
+ 'Hostname': hostname,
+ 'Domainname': domainname,
+ 'ExposedPorts': ports,
+ 'User': six.text_type(user) if user else None,
+ 'Tty': tty,
+ 'OpenStdin': stdin_open,
+ 'StdinOnce': stdin_once,
+ 'Memory': mem_limit,
+ 'AttachStdin': attach_stdin,
+ 'AttachStdout': attach_stdout,
+ 'AttachStderr': attach_stderr,
+ 'Env': environment,
+ 'Cmd': command,
+ 'Dns': dns,
+ 'Image': image,
+ 'Volumes': volumes,
+ 'VolumesFrom': volumes_from,
+ 'NetworkDisabled': network_disabled,
+ 'Entrypoint': entrypoint,
+ 'CpuShares': cpu_shares,
+ 'Cpuset': cpuset,
+ 'CpusetCpus': cpuset,
+ 'WorkingDir': working_dir,
+ 'MemorySwap': memswap_limit,
+ 'HostConfig': host_config,
+ 'NetworkingConfig': networking_config,
+ 'MacAddress': mac_address,
+ 'Labels': labels,
+ 'VolumeDriver': volume_driver,
+ 'StopSignal': stop_signal,
+ 'Healthcheck': healthcheck,
+ })
diff --git a/docker/types/networks.py b/docker/types/networks.py
new file mode 100644
index 0000000..a539ac0
--- /dev/null
+++ b/docker/types/networks.py
@@ -0,0 +1,104 @@
+from .. import errors
+from ..utils import normalize_links, version_lt
+
+
+class EndpointConfig(dict):
+ def __init__(self, version, aliases=None, links=None, ipv4_address=None,
+ ipv6_address=None, link_local_ips=None):
+ if version_lt(version, '1.22'):
+ raise errors.InvalidVersion(
+ 'Endpoint config is not supported for API version < 1.22'
+ )
+
+ if aliases:
+ self["Aliases"] = aliases
+
+ if links:
+ self["Links"] = normalize_links(links)
+
+ ipam_config = {}
+ if ipv4_address:
+ ipam_config['IPv4Address'] = ipv4_address
+
+ if ipv6_address:
+ ipam_config['IPv6Address'] = ipv6_address
+
+ if link_local_ips is not None:
+ if version_lt(version, '1.24'):
+ raise errors.InvalidVersion(
+ 'link_local_ips is not supported for API version < 1.24'
+ )
+ ipam_config['LinkLocalIPs'] = link_local_ips
+
+ if ipam_config:
+ self['IPAMConfig'] = ipam_config
+
+
+class NetworkingConfig(dict):
+ def __init__(self, endpoints_config=None):
+ if endpoints_config:
+ self["EndpointsConfig"] = endpoints_config
+
+
+class IPAMConfig(dict):
+ """
+ Create an IPAM (IP Address Management) config dictionary to be used with
+ :py:meth:`~docker.api.network.NetworkApiMixin.create_network`.
+
+ Args:
+
+ driver (str): The IPAM driver to use. Defaults to ``default``.
+ pool_configs (list): A list of pool configurations
+ (:py:class:`~docker.types.IPAMPool`). Defaults to empty list.
+
+ Example:
+
+ >>> ipam_config = docker.types.IPAMConfig(driver='default')
+ >>> network = client.create_network('network1', ipam=ipam_config)
+
+ """
+ def __init__(self, driver='default', pool_configs=None):
+ self.update({
+ 'Driver': driver,
+ 'Config': pool_configs or []
+ })
+
+
+class IPAMPool(dict):
+ """
+ Create an IPAM pool config dictionary to be added to the
+ ``pool_configs`` parameter of
+ :py:class:`~docker.types.IPAMConfig`.
+
+ Args:
+
+ subnet (str): Custom subnet for this IPAM pool using the CIDR
+ notation. Defaults to ``None``.
+ iprange (str): Custom IP range for endpoints in this IPAM pool using
+ the CIDR notation. Defaults to ``None``.
+ gateway (str): Custom IP address for the pool's gateway.
+ aux_addresses (dict): A dictionary of ``key -> ip_address``
+ relationships specifying auxiliary addresses that need to be
+ allocated by the IPAM driver.
+
+ Example:
+
+ >>> ipam_pool = docker.types.IPAMPool(
+ subnet='124.42.0.0/16',
+ iprange='124.42.0.0/24',
+ gateway='124.42.0.254',
+ aux_addresses={
+ 'reserved1': '124.42.1.1'
+ }
+ )
+ >>> ipam_config = docker.types.IPAMConfig(
+ pool_configs=[ipam_pool])
+ """
+ def __init__(self, subnet=None, iprange=None, gateway=None,
+ aux_addresses=None):
+ self.update({
+ 'Subnet': subnet,
+ 'IPRange': iprange,
+ 'Gateway': gateway,
+ 'AuxiliaryAddresses': aux_addresses
+ })
diff --git a/docker/types/services.py b/docker/types/services.py
index 9d5fa1b..82d1e60 100644
--- a/docker/types/services.py
+++ b/docker/types/services.py
@@ -1,6 +1,7 @@
import six
from .. import errors
+from ..utils import split_command
class TaskTemplate(dict):
@@ -10,19 +11,15 @@ class TaskTemplate(dict):
Args:
- * container_spec (dict): Container settings for containers started as part
- of this task. See the :py:class:`~docker.types.services.ContainerSpec`
- for details.
- * log_driver (dict): Log configuration for containers created as part of
- the service. See the :py:class:`~docker.types.services.DriverConfig`
- class for details.
- * resources (dict): Resource requirements which apply to each individual
- container created as part of the service. See the
- :py:class:`~docker.types.services.Resources` class for details.
- * restart_policy (dict): Specification for the restart policy which applies
- to containers created as part of this service. See the
- :py:class:`~docker.types.services.RestartPolicy` class for details.
- * placement (list): A list of constraints.
+ container_spec (ContainerSpec): Container settings for containers
+ started as part of this task.
+ log_driver (DriverConfig): Log configuration for containers created as
+ part of the service.
+ resources (Resources): Resource requirements which apply to each
+ 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.
"""
def __init__(self, container_spec, resources=None, restart_policy=None,
placement=None, log_driver=None):
@@ -58,27 +55,25 @@ class TaskTemplate(dict):
class ContainerSpec(dict):
"""
Describes the behavior of containers that are part of a task, and is used
- when declaring a :py:class:`~docker.types.services.TaskTemplate`.
+ when declaring a :py:class:`~docker.types.TaskTemplate`.
Args:
- * 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.
- * 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.services.Mount` class for details.
- * stop_grace_period (int): Amount of time to wait for the container to
- terminate before forcefully killing it.
+ 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.
+ 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.
+ stop_grace_period (int): Amount of time to wait for the container to
+ 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):
- from ..utils import split_command # FIXME: circular import
-
self['Image'] = image
if isinstance(command, six.string_types):
@@ -108,24 +103,24 @@ 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:`~docker.types.services.ContainerSpec`.
+ :py:class:`~docker.types.ContainerSpec`.
Args:
- * target (string): Container path.
- * source (string): Mount source (e.g. a volume name or a host path).
- * type (string): The mount type (``bind`` or ``volume``).
- Default: ``volume``.
- * read_only (bool): Whether the mount should be read-only.
- * propagation (string): A propagation mode with the value ``[r]private``,
- ``[r]shared``, or ``[r]slave``. Only valid for the ``bind`` type.
- * no_copy (bool): False if the volume should be populated with the data
- from the target. Default: ``False``. Only valid for the ``volume`` type.
- * labels (dict): User-defined name and labels for the volume. Only valid
- for the ``volume`` type.
- * driver_config (dict): Volume driver configuration.
- See the :py:class:`~docker.types.services.DriverConfig` class for
- details. Only valid for the ``volume`` type.
+ target (string): Container path.
+ source (string): Mount source (e.g. a volume name or a host path).
+ type (string): The mount type (``bind`` or ``volume``).
+ Default: ``volume``.
+ read_only (bool): Whether the mount should be read-only.
+ propagation (string): A propagation mode with the value ``[r]private``,
+ ``[r]shared``, or ``[r]slave``. Only valid for the ``bind`` type.
+ no_copy (bool): False if the volume should be populated with the data
+ from the target. Default: ``False``. Only valid for the ``volume``
+ type.
+ labels (dict): User-defined name and labels for the volume. Only valid
+ for the ``volume`` type.
+ driver_config (DriverConfig): Volume driver configuration. Only valid
+ for the ``volume`` type.
"""
def __init__(self, target, source, type='volume', read_only=False,
propagation=None, no_copy=False, labels=None,
@@ -183,14 +178,14 @@ class Mount(dict):
class Resources(dict):
"""
Configures resource allocation for containers when made part of a
- :py:class:`~docker.types.services.ContainerSpec`.
+ :py:class:`~docker.types.ContainerSpec`.
Args:
- * cpu_limit (int): CPU limit in units of 10^9 CPU shares.
- * mem_limit (int): Memory limit in Bytes.
- * cpu_reservation (int): CPU reservation in units of 10^9 CPU shares.
- * mem_reservation (int): Memory reservation in Bytes.
+ cpu_limit (int): CPU limit in units of 10^9 CPU shares.
+ mem_limit (int): Memory limit in Bytes.
+ cpu_reservation (int): CPU reservation in units of 10^9 CPU shares.
+ mem_reservation (int): Memory reservation in Bytes.
"""
def __init__(self, cpu_limit=None, mem_limit=None, cpu_reservation=None,
mem_reservation=None):
@@ -218,12 +213,12 @@ class UpdateConfig(dict):
Args:
- * parallelism (int): Maximum number of tasks to be updated in one iteration
- (0 means unlimited parallelism). Default: 0.
- * delay (int): Amount of time between updates.
- * failure_action (string): Action to take if an updated task fails to run,
- or stops running during the update. Acceptable values are ``continue``
- and ``pause``. Default: ``continue``
+ parallelism (int): Maximum number of tasks to be updated in one
+ iteration (0 means unlimited parallelism). Default: 0.
+ delay (int): Amount of time between updates.
+ failure_action (string): Action to take if an updated task fails to
+ run, or stops running during the update. Acceptable values are
+ ``continue`` and ``pause``. Default: ``continue``
"""
def __init__(self, parallelism=0, delay=None, failure_action='continue'):
self['Parallelism'] = parallelism
@@ -247,16 +242,18 @@ class RestartConditionTypesEnum(object):
class RestartPolicy(dict):
"""
- Used when creating a :py:class:`~docker.types.services.ContainerSpec`,
+ Used when creating a :py:class:`~docker.types.ContainerSpec`,
dictates whether a container should restart after stopping or failing.
- * condition (string): Condition for restart (``none``, ``on-failure``,
- or ``any``). Default: `none`.
- * delay (int): Delay between restart attempts. Default: 0
- * attempts (int): Maximum attempts to restart a given container before
- giving up. Default value is 0, which is ignored.
- * window (int): Time window used to evaluate the restart policy. Default
- value is 0, which is unbounded.
+ Args:
+
+ condition (string): Condition for restart (``none``, ``on-failure``,
+ or ``any``). Default: `none`.
+ delay (int): Delay between restart attempts. Default: 0
+ attempts (int): Maximum attempts to restart a given container before
+ giving up. Default value is 0, which is ignored.
+ window (int): Time window used to evaluate the restart policy. Default
+ value is 0, which is unbounded.
"""
condition_types = RestartConditionTypesEnum
@@ -277,14 +274,14 @@ class RestartPolicy(dict):
class DriverConfig(dict):
"""
Indicates which driver to use, as well as its configuration. Can be used
- as ``log_driver`` in a :py:class:`~docker.types.services.ContainerSpec`,
+ as ``log_driver`` in a :py:class:`~docker.types.ContainerSpec`,
and for the `driver_config` in a volume
- :py:class:`~docker.types.services.Mount`.
+ :py:class:`~docker.types.Mount`.
Args:
- * name (string): Name of the driver to use.
- * options (dict): Driver-specific options. Default: ``None``.
+ name (string): Name of the driver to use.
+ options (dict): Driver-specific options. Default: ``None``.
"""
def __init__(self, name, options=None):
self['Name'] = name
@@ -298,13 +295,13 @@ class EndpointSpec(dict):
Args:
- * mode (string): The mode of resolution to use for internal load balancing
- between tasks (``'vip'`` or ``'dnsrr'``). Defaults to ``'vip'`` if not
- provided.
- * ports (dict): Exposed ports that this service is accessible on from the
- outside, in the form of ``{ target_port: published_port }`` or
- ``{ target_port: (published_port, protocol) }``. Ports can only be
- provided if the ``vip`` resolution mode is used.
+ mode (string): The mode of resolution to use for internal load
+ balancing between tasks (``'vip'`` or ``'dnsrr'``). Defaults to
+ ``'vip'`` if not provided.
+ ports (dict): Exposed ports that this service is accessible on from the
+ outside, in the form of ``{ target_port: published_port }`` or
+ ``{ target_port: (published_port, protocol) }``. Ports can only be
+ provided if the ``vip`` resolution mode is used.
"""
def __init__(self, mode=None, ports=None):
if ports:
diff --git a/docker/utils/__init__.py b/docker/utils/__init__.py
index 5bd69b4..061c26e 100644
--- a/docker/utils/__init__.py
+++ b/docker/utils/__init__.py
@@ -3,12 +3,9 @@ from .utils import (
compare_version, convert_port_bindings, convert_volume_binds,
mkbuildcontext, tar, exclude_paths, parse_repository_tag, parse_host,
kwargs_from_env, convert_filters, datetime_to_timestamp,
- create_host_config, create_container_config, parse_bytes, ping_registry,
- parse_env_file, version_lt, version_gte, decode_json_header, split_command,
- create_ipam_config, create_ipam_pool, parse_devices, normalize_links,
- convert_service_networks,
+ create_host_config, parse_bytes, ping_registry, parse_env_file, version_lt,
+ version_gte, decode_json_header, split_command, create_ipam_config,
+ create_ipam_pool, parse_devices, normalize_links, convert_service_networks,
)
-from ..types import LogConfig, Ulimit
-from ..types import SwarmExternalCA, SwarmSpec
from .decorators import check_resource, minimum_version, update_headers
diff --git a/docker/utils/types.py b/docker/utils/types.py
deleted file mode 100644
index 8098c47..0000000
--- a/docker/utils/types.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# Compatibility module. See https://github.com/docker/docker-py/issues/1196
-
-import warnings
-
-from ..types import Ulimit, LogConfig # flake8: noqa
-
-warnings.warn('docker.utils.types is now docker.types', ImportWarning)
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
index a6ebf0f..4e5f454 100644
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -15,10 +15,8 @@ from fnmatch import fnmatch
import requests
import six
-from .. import constants
from .. import errors
from .. import tls
-from ..types import Ulimit, LogConfig, Healthcheck
if six.PY2:
from urllib import splitnport
@@ -37,72 +35,18 @@ BYTE_UNITS = {
}
-def create_ipam_pool(subnet=None, iprange=None, gateway=None,
- aux_addresses=None):
- """
- Create an IPAM pool config dictionary to be added to the
- ``pool_configs`` parameter of
- :py:meth:`~docker.utils.create_ipam_config`.
-
- Args:
-
- subnet (str): Custom subnet for this IPAM pool using the CIDR
- notation. Defaults to ``None``.
- iprange (str): Custom IP range for endpoints in this IPAM pool using
- the CIDR notation. Defaults to ``None``.
- gateway (str): Custom IP address for the pool's gateway.
- aux_addresses (dict): A dictionary of ``key -> ip_address``
- relationships specifying auxiliary addresses that need to be
- allocated by the IPAM driver.
-
- Returns:
- (dict) An IPAM pool config
-
- Example:
-
- >>> ipam_pool = docker.utils.create_ipam_pool(
- subnet='124.42.0.0/16',
- iprange='124.42.0.0/24',
- gateway='124.42.0.254',
- aux_addresses={
- 'reserved1': '124.42.1.1'
- }
- )
- >>> ipam_config = docker.utils.create_ipam_config(
- pool_configs=[ipam_pool])
- """
- return {
- 'Subnet': subnet,
- 'IPRange': iprange,
- 'Gateway': gateway,
- 'AuxiliaryAddresses': aux_addresses
- }
-
-
-def create_ipam_config(driver='default', pool_configs=None):
- """
- Create an IPAM (IP Address Management) config dictionary to be used with
- :py:meth:`~docker.api.network.NetworkApiMixin.create_network`.
-
- Args:
- driver (str): The IPAM driver to use. Defaults to ``default``.
- pool_configs (list): A list of pool configuration dictionaries as
- created by :py:meth:`~docker.utils.create_ipam_pool`. Defaults to
- empty list.
-
- Returns:
- (dict) An IPAM config.
-
- Example:
+def create_ipam_pool(*args, **kwargs):
+ raise errors.DeprecatedMethod(
+ 'utils.create_ipam_pool has been removed. Please use a '
+ 'docker.types.IPAMPool object instead.'
+ )
- >>> ipam_config = docker.utils.create_ipam_config(driver='default')
- >>> network = client.create_network('network1', ipam=ipam_config)
- """
- return {
- 'Driver': driver,
- 'Config': pool_configs or []
- }
+def create_ipam_config(*args, **kwargs):
+ raise errors.DeprecatedMethod(
+ 'utils.create_ipam_config has been removed. Please use a '
+ 'docker.types.IPAMConfig object instead.'
+ )
def mkbuildcontext(dockerfile):
@@ -669,338 +613,6 @@ def parse_bytes(s):
return s
-def host_config_type_error(param, param_value, expected):
- error_msg = 'Invalid type for {0} param: expected {1} but found {2}'
- return TypeError(error_msg.format(param, expected, type(param_value)))
-
-
-def host_config_version_error(param, version, less_than=True):
- operator = '<' if less_than else '>'
- error_msg = '{0} param is not supported in API versions {1} {2}'
- return errors.InvalidVersion(error_msg.format(param, operator, version))
-
-
-def host_config_value_error(param, param_value):
- error_msg = 'Invalid value for {0} param: {1}'
- return ValueError(error_msg.format(param, param_value))
-
-
-def create_host_config(binds=None, port_bindings=None, lxc_conf=None,
- publish_all_ports=False, links=None, privileged=False,
- dns=None, dns_search=None, volumes_from=None,
- network_mode=None, restart_policy=None, cap_add=None,
- cap_drop=None, devices=None, extra_hosts=None,
- read_only=None, pid_mode=None, ipc_mode=None,
- security_opt=None, ulimits=None, log_config=None,
- mem_limit=None, memswap_limit=None,
- mem_reservation=None, kernel_memory=None,
- mem_swappiness=None, cgroup_parent=None,
- group_add=None, cpu_quota=None,
- cpu_period=None, blkio_weight=None,
- blkio_weight_device=None, device_read_bps=None,
- device_write_bps=None, device_read_iops=None,
- device_write_iops=None, oom_kill_disable=False,
- shm_size=None, sysctls=None, version=None, tmpfs=None,
- oom_score_adj=None, dns_opt=None, cpu_shares=None,
- cpuset_cpus=None, userns_mode=None, pids_limit=None,
- isolation=None):
-
- host_config = {}
-
- if not version:
- warnings.warn(
- 'docker.utils.create_host_config() is deprecated. Please use '
- 'APIClient.create_host_config() instead.'
- )
- version = constants.DEFAULT_DOCKER_API_VERSION
-
- if mem_limit is not None:
- host_config['Memory'] = parse_bytes(mem_limit)
-
- if memswap_limit is not None:
- host_config['MemorySwap'] = parse_bytes(memswap_limit)
-
- if mem_reservation:
- if version_lt(version, '1.21'):
- raise host_config_version_error('mem_reservation', '1.21')
-
- host_config['MemoryReservation'] = parse_bytes(mem_reservation)
-
- if kernel_memory:
- if version_lt(version, '1.21'):
- raise host_config_version_error('kernel_memory', '1.21')
-
- host_config['KernelMemory'] = parse_bytes(kernel_memory)
-
- if mem_swappiness is not None:
- if version_lt(version, '1.20'):
- raise host_config_version_error('mem_swappiness', '1.20')
- if not isinstance(mem_swappiness, int):
- raise host_config_type_error(
- 'mem_swappiness', mem_swappiness, 'int'
- )
-
- host_config['MemorySwappiness'] = mem_swappiness
-
- if shm_size is not None:
- if isinstance(shm_size, six.string_types):
- shm_size = parse_bytes(shm_size)
-
- host_config['ShmSize'] = shm_size
-
- if pid_mode:
- if version_lt(version, '1.24') and pid_mode != 'host':
- raise host_config_value_error('pid_mode', pid_mode)
- host_config['PidMode'] = pid_mode
-
- if ipc_mode:
- host_config['IpcMode'] = ipc_mode
-
- if privileged:
- host_config['Privileged'] = privileged
-
- if oom_kill_disable:
- if version_lt(version, '1.20'):
- raise host_config_version_error('oom_kill_disable', '1.19')
-
- host_config['OomKillDisable'] = oom_kill_disable
-
- if oom_score_adj:
- if version_lt(version, '1.22'):
- raise host_config_version_error('oom_score_adj', '1.22')
- if not isinstance(oom_score_adj, int):
- raise host_config_type_error(
- 'oom_score_adj', oom_score_adj, 'int'
- )
- host_config['OomScoreAdj'] = oom_score_adj
-
- if publish_all_ports:
- host_config['PublishAllPorts'] = publish_all_ports
-
- if read_only is not None:
- host_config['ReadonlyRootfs'] = read_only
-
- if dns_search:
- host_config['DnsSearch'] = dns_search
-
- if network_mode:
- host_config['NetworkMode'] = network_mode
- elif network_mode is None and compare_version('1.19', version) > 0:
- host_config['NetworkMode'] = 'default'
-
- if restart_policy:
- if not isinstance(restart_policy, dict):
- raise host_config_type_error(
- 'restart_policy', restart_policy, 'dict'
- )
-
- host_config['RestartPolicy'] = restart_policy
-
- if cap_add:
- host_config['CapAdd'] = cap_add
-
- if cap_drop:
- host_config['CapDrop'] = cap_drop
-
- if devices:
- host_config['Devices'] = parse_devices(devices)
-
- if group_add:
- if version_lt(version, '1.20'):
- raise host_config_version_error('group_add', '1.20')
-
- host_config['GroupAdd'] = [six.text_type(grp) for grp in group_add]
-
- if dns is not None:
- host_config['Dns'] = dns
-
- if dns_opt is not None:
- if version_lt(version, '1.21'):
- raise host_config_version_error('dns_opt', '1.21')
-
- host_config['DnsOptions'] = dns_opt
-
- if security_opt is not None:
- if not isinstance(security_opt, list):
- raise host_config_type_error('security_opt', security_opt, 'list')
-
- host_config['SecurityOpt'] = security_opt
-
- if sysctls:
- if not isinstance(sysctls, dict):
- raise host_config_type_error('sysctls', sysctls, 'dict')
- host_config['Sysctls'] = {}
- for k, v in six.iteritems(sysctls):
- host_config['Sysctls'][k] = six.text_type(v)
-
- if volumes_from is not None:
- if isinstance(volumes_from, six.string_types):
- volumes_from = volumes_from.split(',')
-
- host_config['VolumesFrom'] = volumes_from
-
- if binds is not None:
- host_config['Binds'] = convert_volume_binds(binds)
-
- if port_bindings is not None:
- host_config['PortBindings'] = convert_port_bindings(port_bindings)
-
- if extra_hosts is not None:
- if isinstance(extra_hosts, dict):
- extra_hosts = [
- '{0}:{1}'.format(k, v)
- for k, v in sorted(six.iteritems(extra_hosts))
- ]
-
- host_config['ExtraHosts'] = extra_hosts
-
- if links is not None:
- host_config['Links'] = normalize_links(links)
-
- if isinstance(lxc_conf, dict):
- formatted = []
- for k, v in six.iteritems(lxc_conf):
- formatted.append({'Key': k, 'Value': str(v)})
- lxc_conf = formatted
-
- if lxc_conf is not None:
- host_config['LxcConf'] = lxc_conf
-
- if cgroup_parent is not None:
- host_config['CgroupParent'] = cgroup_parent
-
- if ulimits is not None:
- if not isinstance(ulimits, list):
- raise host_config_type_error('ulimits', ulimits, 'list')
- host_config['Ulimits'] = []
- for l in ulimits:
- if not isinstance(l, Ulimit):
- l = Ulimit(**l)
- host_config['Ulimits'].append(l)
-
- if log_config is not None:
- if not isinstance(log_config, LogConfig):
- if not isinstance(log_config, dict):
- raise host_config_type_error(
- 'log_config', log_config, 'LogConfig'
- )
- log_config = LogConfig(**log_config)
-
- host_config['LogConfig'] = log_config
-
- if cpu_quota:
- if not isinstance(cpu_quota, int):
- raise host_config_type_error('cpu_quota', cpu_quota, 'int')
- if version_lt(version, '1.19'):
- raise host_config_version_error('cpu_quota', '1.19')
-
- host_config['CpuQuota'] = cpu_quota
-
- if cpu_period:
- if not isinstance(cpu_period, int):
- raise host_config_type_error('cpu_period', cpu_period, 'int')
- if version_lt(version, '1.19'):
- raise host_config_version_error('cpu_period', '1.19')
-
- host_config['CpuPeriod'] = cpu_period
-
- if cpu_shares:
- if version_lt(version, '1.18'):
- raise host_config_version_error('cpu_shares', '1.18')
-
- if not isinstance(cpu_shares, int):
- raise host_config_type_error('cpu_shares', cpu_shares, 'int')
-
- host_config['CpuShares'] = cpu_shares
-
- if cpuset_cpus:
- if version_lt(version, '1.18'):
- raise host_config_version_error('cpuset_cpus', '1.18')
-
- host_config['CpuSetCpus'] = cpuset_cpus
-
- if blkio_weight:
- if not isinstance(blkio_weight, int):
- raise host_config_type_error('blkio_weight', blkio_weight, 'int')
- if version_lt(version, '1.22'):
- raise host_config_version_error('blkio_weight', '1.22')
- host_config["BlkioWeight"] = blkio_weight
-
- if blkio_weight_device:
- if not isinstance(blkio_weight_device, list):
- raise host_config_type_error(
- 'blkio_weight_device', blkio_weight_device, 'list'
- )
- if version_lt(version, '1.22'):
- raise host_config_version_error('blkio_weight_device', '1.22')
- host_config["BlkioWeightDevice"] = blkio_weight_device
-
- if device_read_bps:
- if not isinstance(device_read_bps, list):
- raise host_config_type_error(
- 'device_read_bps', device_read_bps, 'list'
- )
- if version_lt(version, '1.22'):
- raise host_config_version_error('device_read_bps', '1.22')
- host_config["BlkioDeviceReadBps"] = device_read_bps
-
- if device_write_bps:
- if not isinstance(device_write_bps, list):
- raise host_config_type_error(
- 'device_write_bps', device_write_bps, 'list'
- )
- if version_lt(version, '1.22'):
- raise host_config_version_error('device_write_bps', '1.22')
- host_config["BlkioDeviceWriteBps"] = device_write_bps
-
- if device_read_iops:
- if not isinstance(device_read_iops, list):
- raise host_config_type_error(
- 'device_read_iops', device_read_iops, 'list'
- )
- if version_lt(version, '1.22'):
- raise host_config_version_error('device_read_iops', '1.22')
- host_config["BlkioDeviceReadIOps"] = device_read_iops
-
- if device_write_iops:
- if not isinstance(device_write_iops, list):
- raise host_config_type_error(
- 'device_write_iops', device_write_iops, 'list'
- )
- if version_lt(version, '1.22'):
- raise host_config_version_error('device_write_iops', '1.22')
- host_config["BlkioDeviceWriteIOps"] = device_write_iops
-
- if tmpfs:
- if version_lt(version, '1.22'):
- raise host_config_version_error('tmpfs', '1.22')
- host_config["Tmpfs"] = convert_tmpfs_mounts(tmpfs)
-
- if userns_mode:
- if version_lt(version, '1.23'):
- raise host_config_version_error('userns_mode', '1.23')
-
- if userns_mode != "host":
- raise host_config_value_error("userns_mode", userns_mode)
- host_config['UsernsMode'] = userns_mode
-
- if pids_limit:
- if not isinstance(pids_limit, int):
- raise host_config_type_error('pids_limit', pids_limit, 'int')
- if version_lt(version, '1.23'):
- raise host_config_version_error('pids_limit', '1.23')
- host_config["PidsLimit"] = pids_limit
-
- if isolation:
- if not isinstance(isolation, six.string_types):
- raise host_config_type_error('isolation', isolation, 'string')
- if version_lt(version, '1.24'):
- raise host_config_version_error('isolation', '1.24')
- host_config['Isolation'] = isolation
-
- return host_config
-
-
def normalize_links(links):
if isinstance(links, dict):
links = six.iteritems(links)
@@ -1008,50 +620,6 @@ def normalize_links(links):
return ['{0}:{1}'.format(k, v) for k, v in sorted(links)]
-def create_networking_config(endpoints_config=None):
- networking_config = {}
-
- if endpoints_config:
- networking_config["EndpointsConfig"] = endpoints_config
-
- return networking_config
-
-
-def create_endpoint_config(version, aliases=None, links=None,
- ipv4_address=None, ipv6_address=None,
- link_local_ips=None):
- if version_lt(version, '1.22'):
- raise errors.InvalidVersion(
- 'Endpoint config is not supported for API version < 1.22'
- )
- endpoint_config = {}
-
- if aliases:
- endpoint_config["Aliases"] = aliases
-
- if links:
- endpoint_config["Links"] = normalize_links(links)
-
- ipam_config = {}
- if ipv4_address:
- ipam_config['IPv4Address'] = ipv4_address
-
- if ipv6_address:
- ipam_config['IPv6Address'] = ipv6_address
-
- if link_local_ips is not None:
- if version_lt(version, '1.24'):
- raise errors.InvalidVersion(
- 'link_local_ips is not supported for API version < 1.24'
- )
- ipam_config['LinkLocalIPs'] = link_local_ips
-
- if ipam_config:
- endpoint_config['IPAMConfig'] = ipam_config
-
- return endpoint_config
-
-
def parse_env_file(env_file):
"""
Reads a line-separated environment file.
@@ -1098,157 +666,8 @@ def format_environment(environment):
return [format_env(*var) for var in six.iteritems(environment)]
-def create_container_config(
- version, image, command, hostname=None, user=None, detach=False,
- stdin_open=False, tty=False, mem_limit=None, ports=None, environment=None,
- dns=None, volumes=None, volumes_from=None, network_disabled=False,
- entrypoint=None, cpu_shares=None, working_dir=None, domainname=None,
- memswap_limit=None, cpuset=None, host_config=None, mac_address=None,
- labels=None, volume_driver=None, stop_signal=None, networking_config=None,
- healthcheck=None,
-):
- if isinstance(command, six.string_types):
- command = split_command(command)
-
- if isinstance(entrypoint, six.string_types):
- entrypoint = split_command(entrypoint)
-
- if isinstance(environment, dict):
- environment = format_environment(environment)
-
- if labels is not None and compare_version('1.18', version) < 0:
- raise errors.InvalidVersion(
- 'labels were only introduced in API version 1.18'
- )
-
- if cpuset is not None or cpu_shares is not None:
- if version_gte(version, '1.18'):
- warnings.warn(
- 'The cpuset_cpus and cpu_shares options have been moved to '
- 'host_config in API version 1.18, and will be removed',
- DeprecationWarning
- )
-
- if stop_signal is not None and compare_version('1.21', version) < 0:
- raise errors.InvalidVersion(
- 'stop_signal was only introduced in API version 1.21'
- )
-
- if healthcheck is not None and version_lt(version, '1.24'):
- raise errors.InvalidVersion(
- 'Health options were only introduced in API version 1.24'
- )
-
- if compare_version('1.19', version) < 0:
- if volume_driver is not None:
- raise errors.InvalidVersion(
- 'Volume drivers were only introduced in API version 1.19'
- )
- mem_limit = mem_limit if mem_limit is not None else 0
- memswap_limit = memswap_limit if memswap_limit is not None else 0
- else:
- if mem_limit is not None:
- raise errors.InvalidVersion(
- 'mem_limit has been moved to host_config in API version 1.19'
- )
-
- if memswap_limit is not None:
- raise errors.InvalidVersion(
- 'memswap_limit has been moved to host_config in API '
- 'version 1.19'
- )
-
- if isinstance(labels, list):
- labels = dict((lbl, six.text_type('')) for lbl in labels)
-
- if mem_limit is not None:
- mem_limit = parse_bytes(mem_limit)
-
- if memswap_limit is not None:
- memswap_limit = parse_bytes(memswap_limit)
-
- if isinstance(ports, list):
- exposed_ports = {}
- for port_definition in ports:
- port = port_definition
- proto = 'tcp'
- if isinstance(port_definition, tuple):
- if len(port_definition) == 2:
- proto = port_definition[1]
- port = port_definition[0]
- exposed_ports['{0}/{1}'.format(port, proto)] = {}
- ports = exposed_ports
-
- if isinstance(volumes, six.string_types):
- volumes = [volumes, ]
-
- if isinstance(volumes, list):
- volumes_dict = {}
- for vol in volumes:
- volumes_dict[vol] = {}
- volumes = volumes_dict
-
- if volumes_from:
- if not isinstance(volumes_from, six.string_types):
- volumes_from = ','.join(volumes_from)
- else:
- # Force None, an empty list or dict causes client.start to fail
- volumes_from = None
-
- if healthcheck and isinstance(healthcheck, dict):
- healthcheck = Healthcheck(**healthcheck)
-
- attach_stdin = False
- attach_stdout = False
- attach_stderr = False
- stdin_once = False
-
- if not detach:
- attach_stdout = True
- attach_stderr = True
-
- if stdin_open:
- attach_stdin = True
- stdin_once = True
-
- if compare_version('1.10', version) >= 0:
- message = ('{0!r} parameter has no effect on create_container().'
- ' It has been moved to host_config')
- if dns is not None:
- raise errors.InvalidVersion(message.format('dns'))
- if volumes_from is not None:
- raise errors.InvalidVersion(message.format('volumes_from'))
-
- return {
- 'Hostname': hostname,
- 'Domainname': domainname,
- 'ExposedPorts': ports,
- 'User': six.text_type(user) if user else None,
- 'Tty': tty,
- 'OpenStdin': stdin_open,
- 'StdinOnce': stdin_once,
- 'Memory': mem_limit,
- 'AttachStdin': attach_stdin,
- 'AttachStdout': attach_stdout,
- 'AttachStderr': attach_stderr,
- 'Env': environment,
- 'Cmd': command,
- 'Dns': dns,
- 'Image': image,
- 'Volumes': volumes,
- 'VolumesFrom': volumes_from,
- 'NetworkDisabled': network_disabled,
- 'Entrypoint': entrypoint,
- 'CpuShares': cpu_shares,
- 'Cpuset': cpuset,
- 'CpusetCpus': cpuset,
- 'WorkingDir': working_dir,
- 'MemorySwap': memswap_limit,
- 'HostConfig': host_config,
- 'NetworkingConfig': networking_config,
- 'MacAddress': mac_address,
- 'Labels': labels,
- 'VolumeDriver': volume_driver,
- 'StopSignal': stop_signal,
- 'Healthcheck': healthcheck,
- }
+def create_host_config(self, *args, **kwargs):
+ raise errors.DeprecatedMethod(
+ 'utils.create_host_config has been removed. Please use a '
+ 'docker.types.HostConfig object instead.'
+ )
diff --git a/docs/api.rst b/docs/api.rst
index 97db839..5e59aa7 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -49,15 +49,6 @@ Networks
:members:
:undoc-members:
-Utilities
-~~~~~~~~~
-
-These functions are available under ``docker.utils`` to create arguments
-for :py:meth:`create_network`:
-
-.. autofunction:: docker.utils.create_ipam_config
-.. autofunction:: docker.utils.create_ipam_pool
-
Volumes
-------
@@ -107,3 +98,19 @@ The Docker daemon
.. autoclass:: DaemonApiMixin
:members:
:undoc-members:
+
+Configuration types
+-------------------
+
+.. py:module:: docker.types
+
+.. autoclass:: IPAMConfig
+.. autoclass:: IPAMPool
+.. autoclass:: ContainerSpec
+.. autoclass:: DriverConfig
+.. autoclass:: EndpointSpec
+.. autoclass:: Mount
+.. autoclass:: Resources
+.. autoclass:: RestartPolicy
+.. autoclass:: TaskTemplate
+.. autoclass:: UpdateConfig
diff --git a/tests/integration/api_container_test.py b/tests/integration/api_container_test.py
index f09e75a..bebadb7 100644
--- a/tests/integration/api_container_test.py
+++ b/tests/integration/api_container_test.py
@@ -255,7 +255,7 @@ class CreateContainerTest(BaseAPIIntegrationTest):
self.assertIn('1001', groups)
def test_valid_log_driver_and_log_opt(self):
- log_config = docker.utils.LogConfig(
+ log_config = docker.types.LogConfig(
type='json-file',
config={'max-file': '100'}
)
@@ -274,7 +274,7 @@ class CreateContainerTest(BaseAPIIntegrationTest):
self.assertEqual(container_log_config['Config'], log_config.config)
def test_invalid_log_driver_raises_exception(self):
- log_config = docker.utils.LogConfig(
+ log_config = docker.types.LogConfig(
type='asdf-nope',
config={}
)
@@ -292,7 +292,7 @@ class CreateContainerTest(BaseAPIIntegrationTest):
assert excinfo.value.explanation == expected_msg
def test_valid_no_log_driver_specified(self):
- log_config = docker.utils.LogConfig(
+ log_config = docker.types.LogConfig(
type="",
config={'max-file': '100'}
)
@@ -311,7 +311,7 @@ class CreateContainerTest(BaseAPIIntegrationTest):
self.assertEqual(container_log_config['Config'], log_config.config)
def test_valid_no_config_specified(self):
- log_config = docker.utils.LogConfig(
+ log_config = docker.types.LogConfig(
type="json-file",
config=None
)
diff --git a/tests/integration/api_network_test.py b/tests/integration/api_network_test.py
index 092a12c..b1ac52c 100644
--- a/tests/integration/api_network_test.py
+++ b/tests/integration/api_network_test.py
@@ -1,6 +1,5 @@
import docker
-from docker.utils import create_ipam_config
-from docker.utils import create_ipam_pool
+from docker.types import IPAMConfig, IPAMPool
import pytest
from ..helpers import random_name, requires_api_version
@@ -45,10 +44,10 @@ class TestNetworks(BaseAPIIntegrationTest):
@requires_api_version('1.21')
def test_create_network_with_ipam_config(self):
_, net_id = self.create_network(
- ipam=create_ipam_config(
+ ipam=IPAMConfig(
driver='default',
pool_configs=[
- create_ipam_pool(
+ IPAMPool(
subnet="172.28.0.0/16",
iprange="172.28.5.0/24",
gateway="172.28.5.254",
@@ -217,9 +216,9 @@ class TestNetworks(BaseAPIIntegrationTest):
@requires_api_version('1.22')
def test_create_with_ipv4_address(self):
net_name, net_id = self.create_network(
- ipam=create_ipam_config(
+ ipam=IPAMConfig(
driver='default',
- pool_configs=[create_ipam_pool(subnet="132.124.0.0/16")],
+ pool_configs=[IPAMPool(subnet="132.124.0.0/16")],
),
)
container = self.client.create_container(
@@ -246,9 +245,9 @@ class TestNetworks(BaseAPIIntegrationTest):
@requires_api_version('1.22')
def test_create_with_ipv6_address(self):
net_name, net_id = self.create_network(
- ipam=create_ipam_config(
+ ipam=IPAMConfig(
driver='default',
- pool_configs=[create_ipam_pool(subnet="2001:389::1/64")],
+ pool_configs=[IPAMPool(subnet="2001:389::1/64")],
),
)
container = self.client.create_container(
@@ -353,10 +352,10 @@ class TestNetworks(BaseAPIIntegrationTest):
@requires_api_version('1.22')
def test_connect_with_ipv4_address(self):
net_name, net_id = self.create_network(
- ipam=create_ipam_config(
+ ipam=IPAMConfig(
driver='default',
pool_configs=[
- create_ipam_pool(
+ IPAMPool(
subnet="172.28.0.0/16", iprange="172.28.5.0/24",
gateway="172.28.5.254"
)
@@ -381,10 +380,10 @@ class TestNetworks(BaseAPIIntegrationTest):
@requires_api_version('1.22')
def test_connect_with_ipv6_address(self):
net_name, net_id = self.create_network(
- ipam=create_ipam_config(
+ ipam=IPAMConfig(
driver='default',
pool_configs=[
- create_ipam_pool(
+ IPAMPool(
subnet="2001:389::1/64", iprange="2001:389::0/96",
gateway="2001:389::ffff"
)
diff --git a/tests/unit/api_network_test.py b/tests/unit/api_network_test.py
index 8e09c67..037edb5 100644
--- a/tests/unit/api_network_test.py
+++ b/tests/unit/api_network_test.py
@@ -4,7 +4,7 @@ import six
from .api_test import BaseAPIClientTest, url_prefix, response
from ..helpers import requires_api_version
-from docker.utils import create_ipam_config, create_ipam_pool
+from docker.types import IPAMConfig, IPAMPool
try:
from unittest import mock
@@ -81,9 +81,9 @@ class NetworkTest(BaseAPIClientTest):
json.loads(post.call_args[1]['data']),
{"Name": "foo", "Driver": "bridge", "Options": opts})
- ipam_pool_config = create_ipam_pool(subnet="192.168.52.0/24",
- gateway="192.168.52.254")
- ipam_config = create_ipam_config(pool_configs=[ipam_pool_config])
+ ipam_pool_config = IPAMPool(subnet="192.168.52.0/24",
+ gateway="192.168.52.254")
+ ipam_config = IPAMConfig(pool_configs=[ipam_pool_config])
self.client.create_network("bar", driver="bridge",
ipam=ipam_config)
diff --git a/tests/unit/dockertypes_test.py b/tests/unit/dockertypes_test.py
new file mode 100644
index 0000000..2480b9e
--- /dev/null
+++ b/tests/unit/dockertypes_test.py
@@ -0,0 +1,255 @@
+# -*- coding: utf-8 -*-
+
+import unittest
+
+import pytest
+
+from docker.constants import DEFAULT_DOCKER_API_VERSION
+from docker.errors import InvalidVersion
+from docker.types import (
+ EndpointConfig, HostConfig, IPAMConfig, IPAMPool, LogConfig, Ulimit,
+)
+
+
+def create_host_config(*args, **kwargs):
+ return HostConfig(*args, **kwargs)
+
+
+class HostConfigTest(unittest.TestCase):
+ def test_create_host_config_no_options(self):
+ config = create_host_config(version='1.19')
+ self.assertFalse('NetworkMode' in config)
+
+ def test_create_host_config_no_options_newer_api_version(self):
+ config = create_host_config(version='1.20')
+ self.assertEqual(config['NetworkMode'], 'default')
+
+ def test_create_host_config_invalid_cpu_cfs_types(self):
+ with pytest.raises(TypeError):
+ create_host_config(version='1.20', cpu_quota='0')
+
+ with pytest.raises(TypeError):
+ create_host_config(version='1.20', cpu_period='0')
+
+ with pytest.raises(TypeError):
+ create_host_config(version='1.20', cpu_quota=23.11)
+
+ with pytest.raises(TypeError):
+ create_host_config(version='1.20', cpu_period=1999.0)
+
+ def test_create_host_config_with_cpu_quota(self):
+ config = create_host_config(version='1.20', cpu_quota=1999)
+ self.assertEqual(config.get('CpuQuota'), 1999)
+
+ def test_create_host_config_with_cpu_period(self):
+ config = create_host_config(version='1.20', cpu_period=1999)
+ self.assertEqual(config.get('CpuPeriod'), 1999)
+
+ def test_create_host_config_with_blkio_constraints(self):
+ blkio_rate = [{"Path": "/dev/sda", "Rate": 1000}]
+ config = create_host_config(version='1.22',
+ blkio_weight=1999,
+ blkio_weight_device=blkio_rate,
+ device_read_bps=blkio_rate,
+ device_write_bps=blkio_rate,
+ device_read_iops=blkio_rate,
+ device_write_iops=blkio_rate)
+
+ self.assertEqual(config.get('BlkioWeight'), 1999)
+ self.assertTrue(config.get('BlkioWeightDevice') is blkio_rate)
+ self.assertTrue(config.get('BlkioDeviceReadBps') is blkio_rate)
+ self.assertTrue(config.get('BlkioDeviceWriteBps') is blkio_rate)
+ self.assertTrue(config.get('BlkioDeviceReadIOps') is blkio_rate)
+ self.assertTrue(config.get('BlkioDeviceWriteIOps') is blkio_rate)
+ self.assertEqual(blkio_rate[0]['Path'], "/dev/sda")
+ self.assertEqual(blkio_rate[0]['Rate'], 1000)
+
+ def test_create_host_config_with_shm_size(self):
+ config = create_host_config(version='1.22', shm_size=67108864)
+ self.assertEqual(config.get('ShmSize'), 67108864)
+
+ def test_create_host_config_with_shm_size_in_mb(self):
+ config = create_host_config(version='1.22', shm_size='64M')
+ self.assertEqual(config.get('ShmSize'), 67108864)
+
+ def test_create_host_config_with_oom_kill_disable(self):
+ config = create_host_config(version='1.20', oom_kill_disable=True)
+ self.assertEqual(config.get('OomKillDisable'), True)
+ self.assertRaises(
+ InvalidVersion, lambda: create_host_config(version='1.18.3',
+ oom_kill_disable=True))
+
+ def test_create_host_config_with_userns_mode(self):
+ config = create_host_config(version='1.23', userns_mode='host')
+ self.assertEqual(config.get('UsernsMode'), 'host')
+ self.assertRaises(
+ InvalidVersion, lambda: create_host_config(version='1.22',
+ userns_mode='host'))
+ self.assertRaises(
+ ValueError, lambda: create_host_config(version='1.23',
+ userns_mode='host12'))
+
+ def test_create_host_config_with_oom_score_adj(self):
+ config = create_host_config(version='1.22', oom_score_adj=100)
+ self.assertEqual(config.get('OomScoreAdj'), 100)
+ self.assertRaises(
+ InvalidVersion, lambda: create_host_config(version='1.21',
+ oom_score_adj=100))
+ self.assertRaises(
+ TypeError, lambda: create_host_config(version='1.22',
+ oom_score_adj='100'))
+
+ def test_create_host_config_with_dns_opt(self):
+
+ tested_opts = ['use-vc', 'no-tld-query']
+ config = create_host_config(version='1.21', dns_opt=tested_opts)
+ dns_opts = config.get('DnsOptions')
+
+ self.assertTrue('use-vc' in dns_opts)
+ self.assertTrue('no-tld-query' in dns_opts)
+
+ self.assertRaises(
+ InvalidVersion, lambda: create_host_config(version='1.20',
+ dns_opt=tested_opts))
+
+ def test_create_host_config_with_mem_reservation(self):
+ config = create_host_config(version='1.21', mem_reservation=67108864)
+ self.assertEqual(config.get('MemoryReservation'), 67108864)
+ self.assertRaises(
+ InvalidVersion, lambda: create_host_config(
+ version='1.20', mem_reservation=67108864))
+
+ def test_create_host_config_with_kernel_memory(self):
+ config = create_host_config(version='1.21', kernel_memory=67108864)
+ self.assertEqual(config.get('KernelMemory'), 67108864)
+ self.assertRaises(
+ InvalidVersion, lambda: create_host_config(
+ version='1.20', kernel_memory=67108864))
+
+ def test_create_host_config_with_pids_limit(self):
+ config = create_host_config(version='1.23', pids_limit=1024)
+ self.assertEqual(config.get('PidsLimit'), 1024)
+
+ with pytest.raises(InvalidVersion):
+ create_host_config(version='1.22', pids_limit=1024)
+ with pytest.raises(TypeError):
+ create_host_config(version='1.23', pids_limit='1024')
+
+ def test_create_host_config_with_isolation(self):
+ config = create_host_config(version='1.24', isolation='hyperv')
+ self.assertEqual(config.get('Isolation'), 'hyperv')
+
+ with pytest.raises(InvalidVersion):
+ create_host_config(version='1.23', isolation='hyperv')
+ with pytest.raises(TypeError):
+ create_host_config(
+ version='1.24', isolation={'isolation': 'hyperv'}
+ )
+
+ def test_create_host_config_pid_mode(self):
+ with pytest.raises(ValueError):
+ create_host_config(version='1.23', pid_mode='baccab125')
+
+ config = create_host_config(version='1.23', pid_mode='host')
+ assert config.get('PidMode') == 'host'
+ config = create_host_config(version='1.24', pid_mode='baccab125')
+ assert config.get('PidMode') == 'baccab125'
+
+ def test_create_host_config_invalid_mem_swappiness(self):
+ with pytest.raises(TypeError):
+ create_host_config(version='1.24', mem_swappiness='40')
+
+
+class UlimitTest(unittest.TestCase):
+ def test_create_host_config_dict_ulimit(self):
+ ulimit_dct = {'name': 'nofile', 'soft': 8096}
+ config = create_host_config(
+ ulimits=[ulimit_dct], version=DEFAULT_DOCKER_API_VERSION
+ )
+ self.assertIn('Ulimits', config)
+ self.assertEqual(len(config['Ulimits']), 1)
+ ulimit_obj = config['Ulimits'][0]
+ self.assertTrue(isinstance(ulimit_obj, Ulimit))
+ self.assertEqual(ulimit_obj.name, ulimit_dct['name'])
+ self.assertEqual(ulimit_obj.soft, ulimit_dct['soft'])
+ self.assertEqual(ulimit_obj['Soft'], ulimit_obj.soft)
+
+ def test_create_host_config_dict_ulimit_capitals(self):
+ ulimit_dct = {'Name': 'nofile', 'Soft': 8096, 'Hard': 8096 * 4}
+ config = create_host_config(
+ ulimits=[ulimit_dct], version=DEFAULT_DOCKER_API_VERSION
+ )
+ self.assertIn('Ulimits', config)
+ self.assertEqual(len(config['Ulimits']), 1)
+ ulimit_obj = config['Ulimits'][0]
+ self.assertTrue(isinstance(ulimit_obj, Ulimit))
+ self.assertEqual(ulimit_obj.name, ulimit_dct['Name'])
+ self.assertEqual(ulimit_obj.soft, ulimit_dct['Soft'])
+ self.assertEqual(ulimit_obj.hard, ulimit_dct['Hard'])
+ self.assertEqual(ulimit_obj['Soft'], ulimit_obj.soft)
+
+ def test_create_host_config_obj_ulimit(self):
+ ulimit_dct = Ulimit(name='nofile', soft=8096)
+ config = create_host_config(
+ ulimits=[ulimit_dct], version=DEFAULT_DOCKER_API_VERSION
+ )
+ self.assertIn('Ulimits', config)
+ self.assertEqual(len(config['Ulimits']), 1)
+ ulimit_obj = config['Ulimits'][0]
+ self.assertTrue(isinstance(ulimit_obj, Ulimit))
+ self.assertEqual(ulimit_obj, ulimit_dct)
+
+ def test_ulimit_invalid_type(self):
+ self.assertRaises(ValueError, lambda: Ulimit(name=None))
+ self.assertRaises(ValueError, lambda: Ulimit(name='hello', soft='123'))
+ self.assertRaises(ValueError, lambda: Ulimit(name='hello', hard='456'))
+
+
+class LogConfigTest(unittest.TestCase):
+ def test_create_host_config_dict_logconfig(self):
+ dct = {'type': LogConfig.types.SYSLOG, 'config': {'key1': 'val1'}}
+ config = create_host_config(
+ version=DEFAULT_DOCKER_API_VERSION, log_config=dct
+ )
+ self.assertIn('LogConfig', config)
+ self.assertTrue(isinstance(config['LogConfig'], LogConfig))
+ self.assertEqual(dct['type'], config['LogConfig'].type)
+
+ def test_create_host_config_obj_logconfig(self):
+ obj = LogConfig(type=LogConfig.types.SYSLOG, config={'key1': 'val1'})
+ config = create_host_config(
+ version=DEFAULT_DOCKER_API_VERSION, log_config=obj
+ )
+ self.assertIn('LogConfig', config)
+ self.assertTrue(isinstance(config['LogConfig'], LogConfig))
+ self.assertEqual(obj, config['LogConfig'])
+
+ def test_logconfig_invalid_config_type(self):
+ with pytest.raises(ValueError):
+ LogConfig(type=LogConfig.types.JSON, config='helloworld')
+
+
+class EndpointConfigTest(unittest.TestCase):
+ def test_create_endpoint_config_with_aliases(self):
+ config = EndpointConfig(version='1.22', aliases=['foo', 'bar'])
+ assert config == {'Aliases': ['foo', 'bar']}
+
+ with pytest.raises(InvalidVersion):
+ EndpointConfig(version='1.21', aliases=['foo', 'bar'])
+
+
+class IPAMConfigTest(unittest.TestCase):
+ def test_create_ipam_config(self):
+ ipam_pool = IPAMPool(subnet='192.168.52.0/24',
+ gateway='192.168.52.254')
+
+ ipam_config = IPAMConfig(pool_configs=[ipam_pool])
+ self.assertEqual(ipam_config, {
+ 'Driver': 'default',
+ 'Config': [{
+ 'Subnet': '192.168.52.0/24',
+ 'Gateway': '192.168.52.254',
+ 'AuxiliaryAddresses': None,
+ 'IPRange': None,
+ }]
+ })
diff --git a/tests/unit/utils_test.py b/tests/unit/utils_test.py
index f69c62c..743d076 100644
--- a/tests/unit/utils_test.py
+++ b/tests/unit/utils_test.py
@@ -14,19 +14,17 @@ import pytest
import six
from docker.api.client import APIClient
-from docker.constants import DEFAULT_DOCKER_API_VERSION, IS_WINDOWS_PLATFORM
-from docker.errors import DockerException, InvalidVersion
+from docker.constants import IS_WINDOWS_PLATFORM
+from docker.errors import DockerException
from docker.utils import (
parse_repository_tag, parse_host, convert_filters, kwargs_from_env,
- create_host_config, Ulimit, LogConfig, parse_bytes, parse_env_file,
- exclude_paths, convert_volume_binds, decode_json_header, tar,
- split_command, create_ipam_config, create_ipam_pool, parse_devices,
- update_headers
+ parse_bytes, parse_env_file, exclude_paths, convert_volume_binds,
+ decode_json_header, tar, split_command, parse_devices, update_headers,
)
from docker.utils.ports import build_port_bindings, split_port
from docker.utils.utils import (
- create_endpoint_config, format_environment, should_check_directory
+ format_environment, should_check_directory
)
from ..helpers import make_tree
@@ -69,227 +67,6 @@ class DecoratorsTest(unittest.TestCase):
}
-class HostConfigTest(unittest.TestCase):
- def test_create_host_config_no_options(self):
- config = create_host_config(version='1.19')
- self.assertFalse('NetworkMode' in config)
-
- def test_create_host_config_no_options_newer_api_version(self):
- config = create_host_config(version='1.20')
- self.assertEqual(config['NetworkMode'], 'default')
-
- def test_create_host_config_invalid_cpu_cfs_types(self):
- with pytest.raises(TypeError):
- create_host_config(version='1.20', cpu_quota='0')
-
- with pytest.raises(TypeError):
- create_host_config(version='1.20', cpu_period='0')
-
- with pytest.raises(TypeError):
- create_host_config(version='1.20', cpu_quota=23.11)
-
- with pytest.raises(TypeError):
- create_host_config(version='1.20', cpu_period=1999.0)
-
- def test_create_host_config_with_cpu_quota(self):
- config = create_host_config(version='1.20', cpu_quota=1999)
- self.assertEqual(config.get('CpuQuota'), 1999)
-
- def test_create_host_config_with_cpu_period(self):
- config = create_host_config(version='1.20', cpu_period=1999)
- self.assertEqual(config.get('CpuPeriod'), 1999)
-
- def test_create_host_config_with_blkio_constraints(self):
- blkio_rate = [{"Path": "/dev/sda", "Rate": 1000}]
- config = create_host_config(version='1.22',
- blkio_weight=1999,
- blkio_weight_device=blkio_rate,
- device_read_bps=blkio_rate,
- device_write_bps=blkio_rate,
- device_read_iops=blkio_rate,
- device_write_iops=blkio_rate)
-
- self.assertEqual(config.get('BlkioWeight'), 1999)
- self.assertTrue(config.get('BlkioWeightDevice') is blkio_rate)
- self.assertTrue(config.get('BlkioDeviceReadBps') is blkio_rate)
- self.assertTrue(config.get('BlkioDeviceWriteBps') is blkio_rate)
- self.assertTrue(config.get('BlkioDeviceReadIOps') is blkio_rate)
- self.assertTrue(config.get('BlkioDeviceWriteIOps') is blkio_rate)
- self.assertEqual(blkio_rate[0]['Path'], "/dev/sda")
- self.assertEqual(blkio_rate[0]['Rate'], 1000)
-
- def test_create_host_config_with_shm_size(self):
- config = create_host_config(version='1.22', shm_size=67108864)
- self.assertEqual(config.get('ShmSize'), 67108864)
-
- def test_create_host_config_with_shm_size_in_mb(self):
- config = create_host_config(version='1.22', shm_size='64M')
- self.assertEqual(config.get('ShmSize'), 67108864)
-
- def test_create_host_config_with_oom_kill_disable(self):
- config = create_host_config(version='1.20', oom_kill_disable=True)
- self.assertEqual(config.get('OomKillDisable'), True)
- self.assertRaises(
- InvalidVersion, lambda: create_host_config(version='1.18.3',
- oom_kill_disable=True))
-
- def test_create_host_config_with_userns_mode(self):
- config = create_host_config(version='1.23', userns_mode='host')
- self.assertEqual(config.get('UsernsMode'), 'host')
- self.assertRaises(
- InvalidVersion, lambda: create_host_config(version='1.22',
- userns_mode='host'))
- self.assertRaises(
- ValueError, lambda: create_host_config(version='1.23',
- userns_mode='host12'))
-
- def test_create_host_config_with_oom_score_adj(self):
- config = create_host_config(version='1.22', oom_score_adj=100)
- self.assertEqual(config.get('OomScoreAdj'), 100)
- self.assertRaises(
- InvalidVersion, lambda: create_host_config(version='1.21',
- oom_score_adj=100))
- self.assertRaises(
- TypeError, lambda: create_host_config(version='1.22',
- oom_score_adj='100'))
-
- def test_create_host_config_with_dns_opt(self):
-
- tested_opts = ['use-vc', 'no-tld-query']
- config = create_host_config(version='1.21', dns_opt=tested_opts)
- dns_opts = config.get('DnsOptions')
-
- self.assertTrue('use-vc' in dns_opts)
- self.assertTrue('no-tld-query' in dns_opts)
-
- self.assertRaises(
- InvalidVersion, lambda: create_host_config(version='1.20',
- dns_opt=tested_opts))
-
- def test_create_endpoint_config_with_aliases(self):
- config = create_endpoint_config(version='1.22', aliases=['foo', 'bar'])
- assert config == {'Aliases': ['foo', 'bar']}
-
- with pytest.raises(InvalidVersion):
- create_endpoint_config(version='1.21', aliases=['foo', 'bar'])
-
- def test_create_host_config_with_mem_reservation(self):
- config = create_host_config(version='1.21', mem_reservation=67108864)
- self.assertEqual(config.get('MemoryReservation'), 67108864)
- self.assertRaises(
- InvalidVersion, lambda: create_host_config(
- version='1.20', mem_reservation=67108864))
-
- def test_create_host_config_with_kernel_memory(self):
- config = create_host_config(version='1.21', kernel_memory=67108864)
- self.assertEqual(config.get('KernelMemory'), 67108864)
- self.assertRaises(
- InvalidVersion, lambda: create_host_config(
- version='1.20', kernel_memory=67108864))
-
- def test_create_host_config_with_pids_limit(self):
- config = create_host_config(version='1.23', pids_limit=1024)
- self.assertEqual(config.get('PidsLimit'), 1024)
-
- with pytest.raises(InvalidVersion):
- create_host_config(version='1.22', pids_limit=1024)
- with pytest.raises(TypeError):
- create_host_config(version='1.23', pids_limit='1024')
-
- def test_create_host_config_with_isolation(self):
- config = create_host_config(version='1.24', isolation='hyperv')
- self.assertEqual(config.get('Isolation'), 'hyperv')
-
- with pytest.raises(InvalidVersion):
- create_host_config(version='1.23', isolation='hyperv')
- with pytest.raises(TypeError):
- create_host_config(
- version='1.24', isolation={'isolation': 'hyperv'}
- )
-
- def test_create_host_config_pid_mode(self):
- with pytest.raises(ValueError):
- create_host_config(version='1.23', pid_mode='baccab125')
-
- config = create_host_config(version='1.23', pid_mode='host')
- assert config.get('PidMode') == 'host'
- config = create_host_config(version='1.24', pid_mode='baccab125')
- assert config.get('PidMode') == 'baccab125'
-
- def test_create_host_config_invalid_mem_swappiness(self):
- with pytest.raises(TypeError):
- create_host_config(version='1.24', mem_swappiness='40')
-
-
-class UlimitTest(unittest.TestCase):
- def test_create_host_config_dict_ulimit(self):
- ulimit_dct = {'name': 'nofile', 'soft': 8096}
- config = create_host_config(
- ulimits=[ulimit_dct], version=DEFAULT_DOCKER_API_VERSION
- )
- self.assertIn('Ulimits', config)
- self.assertEqual(len(config['Ulimits']), 1)
- ulimit_obj = config['Ulimits'][0]
- self.assertTrue(isinstance(ulimit_obj, Ulimit))
- self.assertEqual(ulimit_obj.name, ulimit_dct['name'])
- self.assertEqual(ulimit_obj.soft, ulimit_dct['soft'])
- self.assertEqual(ulimit_obj['Soft'], ulimit_obj.soft)
-
- def test_create_host_config_dict_ulimit_capitals(self):
- ulimit_dct = {'Name': 'nofile', 'Soft': 8096, 'Hard': 8096 * 4}
- config = create_host_config(
- ulimits=[ulimit_dct], version=DEFAULT_DOCKER_API_VERSION
- )
- self.assertIn('Ulimits', config)
- self.assertEqual(len(config['Ulimits']), 1)
- ulimit_obj = config['Ulimits'][0]
- self.assertTrue(isinstance(ulimit_obj, Ulimit))
- self.assertEqual(ulimit_obj.name, ulimit_dct['Name'])
- self.assertEqual(ulimit_obj.soft, ulimit_dct['Soft'])
- self.assertEqual(ulimit_obj.hard, ulimit_dct['Hard'])
- self.assertEqual(ulimit_obj['Soft'], ulimit_obj.soft)
-
- def test_create_host_config_obj_ulimit(self):
- ulimit_dct = Ulimit(name='nofile', soft=8096)
- config = create_host_config(
- ulimits=[ulimit_dct], version=DEFAULT_DOCKER_API_VERSION
- )
- self.assertIn('Ulimits', config)
- self.assertEqual(len(config['Ulimits']), 1)
- ulimit_obj = config['Ulimits'][0]
- self.assertTrue(isinstance(ulimit_obj, Ulimit))
- self.assertEqual(ulimit_obj, ulimit_dct)
-
- def test_ulimit_invalid_type(self):
- self.assertRaises(ValueError, lambda: Ulimit(name=None))
- self.assertRaises(ValueError, lambda: Ulimit(name='hello', soft='123'))
- self.assertRaises(ValueError, lambda: Ulimit(name='hello', hard='456'))
-
-
-class LogConfigTest(unittest.TestCase):
- def test_create_host_config_dict_logconfig(self):
- dct = {'type': LogConfig.types.SYSLOG, 'config': {'key1': 'val1'}}
- config = create_host_config(
- version=DEFAULT_DOCKER_API_VERSION, log_config=dct
- )
- self.assertIn('LogConfig', config)
- self.assertTrue(isinstance(config['LogConfig'], LogConfig))
- self.assertEqual(dct['type'], config['LogConfig'].type)
-
- def test_create_host_config_obj_logconfig(self):
- obj = LogConfig(type=LogConfig.types.SYSLOG, config={'key1': 'val1'})
- config = create_host_config(
- version=DEFAULT_DOCKER_API_VERSION, log_config=obj
- )
- self.assertIn('LogConfig', config)
- self.assertTrue(isinstance(config['LogConfig'], LogConfig))
- self.assertEqual(obj, config['LogConfig'])
-
- def test_logconfig_invalid_config_type(self):
- with pytest.raises(ValueError):
- LogConfig(type=LogConfig.types.JSON, config='helloworld')
-
-
class KwargsFromEnvTest(unittest.TestCase):
def setUp(self):
self.os_environ = os.environ.copy()
@@ -711,21 +488,6 @@ class UtilsTest(unittest.TestCase):
decoded_data = decode_json_header(data)
self.assertEqual(obj, decoded_data)
- def test_create_ipam_config(self):
- ipam_pool = create_ipam_pool(subnet='192.168.52.0/24',
- gateway='192.168.52.254')
-
- ipam_config = create_ipam_config(pool_configs=[ipam_pool])
- self.assertEqual(ipam_config, {
- 'Driver': 'default',
- 'Config': [{
- 'Subnet': '192.168.52.0/24',
- 'Gateway': '192.168.52.254',
- 'AuxiliaryAddresses': None,
- 'IPRange': None,
- }]
- })
-
class SplitCommandTest(unittest.TestCase):
def test_split_command_with_unicode(self):