summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoffrey F <joffrey@docker.com>2016-08-19 16:46:21 -0700
committerJoffrey F <joffrey@docker.com>2016-08-19 16:51:13 -0700
commit97094e4ea303b59ce05869132cf639305a6d947a (patch)
tree2d07d95e8df436a6c642d0a195eafc16bd04bf6a
parent02e99e4967bebb116a7d9d650647df912c608297 (diff)
downloaddocker-py-97094e4ea303b59ce05869132cf639305a6d947a.tar.gz
New docker.types subpackage containing advanced config dictionary types
Tests and docs updated to match docker.utils.types has been moved to docker.types Signed-off-by: Joffrey F <joffrey@docker.com>
-rw-r--r--docker/api/__init__.py5
-rw-r--r--docker/api/service.py187
-rw-r--r--docker/types/__init__.py7
-rw-r--r--docker/types/base.py7
-rw-r--r--docker/types/containers.py (renamed from docker/utils/types.py)50
-rw-r--r--docker/types/services.py176
-rw-r--r--docker/types/swarm.py40
-rw-r--r--docker/utils/__init__.py6
-rw-r--r--docker/utils/utils.py2
-rw-r--r--docs/swarm.md8
-rw-r--r--setup.py3
-rw-r--r--tests/integration/service_test.py96
12 files changed, 345 insertions, 242 deletions
diff --git a/docker/api/__init__.py b/docker/api/__init__.py
index 3c74677..bc7e93c 100644
--- a/docker/api/__init__.py
+++ b/docker/api/__init__.py
@@ -5,9 +5,6 @@ from .daemon import DaemonApiMixin
from .exec_api import ExecApiMixin
from .image import ImageApiMixin
from .network import NetworkApiMixin
-from .service import (
- ServiceApiMixin, TaskTemplate, ContainerSpec, Mount, Resources,
- RestartPolicy, UpdateConfig
-)
+from .service import ServiceApiMixin
from .swarm import SwarmApiMixin
from .volume import VolumeApiMixin
diff --git a/docker/api/service.py b/docker/api/service.py
index db19ae5..c62e494 100644
--- a/docker/api/service.py
+++ b/docker/api/service.py
@@ -1,28 +1,17 @@
-import six
-
-from .. import errors
from .. import utils
class ServiceApiMixin(object):
@utils.minimum_version('1.24')
- def services(self, filters=None):
- params = {
- 'filters': utils.convert_filters(filters) if filters else None
- }
- url = self._url('/services')
- return self._result(self._get(url, params=params), True)
-
- @utils.minimum_version('1.24')
def create_service(
- self, task_config, name=None, labels=None, mode=None,
+ self, task_template, name=None, labels=None, mode=None,
update_config=None, networks=None, endpoint_config=None
):
url = self._url('/services/create')
data = {
'Name': name,
'Labels': labels,
- 'TaskTemplate': task_config,
+ 'TaskTemplate': task_template,
'Mode': mode,
'UpdateConfig': update_config,
'Networks': networks,
@@ -45,6 +34,14 @@ class ServiceApiMixin(object):
return True
@utils.minimum_version('1.24')
+ def services(self, filters=None):
+ params = {
+ 'filters': utils.convert_filters(filters) if filters else None
+ }
+ url = self._url('/services')
+ return self._result(self._get(url, params=params), True)
+
+ @utils.minimum_version('1.24')
@utils.check_resource
def update_service(self, service, version, task_template=None, name=None,
labels=None, mode=None, update_config=None,
@@ -69,167 +66,3 @@ class ServiceApiMixin(object):
resp = self._post_json(url, data=data, params={'version': version})
self._raise_for_status(resp)
return True
-
-
-class TaskTemplate(dict):
- def __init__(self, container_spec, resources=None, restart_policy=None,
- placement=None, log_driver=None):
- self['ContainerSpec'] = container_spec
- if resources:
- self['Resources'] = resources
- if restart_policy:
- self['RestartPolicy'] = restart_policy
- if placement:
- self['Placement'] = placement
- if log_driver:
- self['LogDriver'] = log_driver
-
- @property
- def container_spec(self):
- return self.get('ContainerSpec')
-
- @property
- def resources(self):
- return self.get('Resources')
-
- @property
- def restart_policy(self):
- return self.get('RestartPolicy')
-
- @property
- def placement(self):
- return self.get('Placement')
-
-
-class ContainerSpec(dict):
- def __init__(self, image, command=None, args=None, env=None, workdir=None,
- user=None, labels=None, mounts=None, stop_grace_period=None):
- self['Image'] = image
- self['Command'] = command
- self['Args'] = args
-
- if env is not None:
- self['Env'] = env
- if workdir is not None:
- self['Dir'] = workdir
- if user is not None:
- self['User'] = user
- if labels is not None:
- self['Labels'] = labels
- if mounts is not None:
- for mount in mounts:
- if isinstance(mount, six.string_types):
- mounts.append(Mount.parse_mount_string(mount))
- mounts.remove(mount)
- self['Mounts'] = mounts
- if stop_grace_period is not None:
- self['StopGracePeriod'] = stop_grace_period
-
-
-class Mount(dict):
- def __init__(self, target, source, type='volume', read_only=False,
- propagation=None, no_copy=False, labels=None,
- driver_config=None):
- self['Target'] = target
- self['Source'] = source
- if type not in ('bind', 'volume'):
- raise errors.DockerError(
- 'Only acceptable mount types are `bind` and `volume`.'
- )
- self['Type'] = type
-
- if type == 'bind':
- if propagation is not None:
- self['BindOptions'] = {
- 'Propagation': propagation
- }
- if any(labels, driver_config, no_copy):
- raise errors.DockerError(
- 'Mount type is binding but volume options have been '
- 'provided.'
- )
- else:
- volume_opts = {}
- if no_copy:
- volume_opts['NoCopy'] = True
- if labels:
- volume_opts['Labels'] = labels
- if driver_config:
- volume_opts['driver_config'] = driver_config
- if volume_opts:
- self['VolumeOptions'] = volume_opts
- if propagation:
- raise errors.DockerError(
- 'Mount type is volume but `propagation` argument has been '
- 'provided.'
- )
-
- @classmethod
- def parse_mount_string(cls, string):
- parts = string.split(':')
- if len(parts) > 3:
- raise errors.DockerError(
- 'Invalid mount format "{0}"'.format(string)
- )
- if len(parts) == 1:
- return cls(target=parts[0])
- else:
- target = parts[1]
- source = parts[0]
- read_only = not (len(parts) == 3 or parts[2] == 'ro')
- return cls(target, source, read_only=read_only)
-
-
-class Resources(dict):
- def __init__(self, cpu_limit=None, mem_limit=None, cpu_reservation=None,
- mem_reservation=None):
- limits = {}
- reservation = {}
- if cpu_limit is not None:
- limits['NanoCPUs'] = cpu_limit
- if mem_limit is not None:
- limits['MemoryBytes'] = mem_limit
- if cpu_reservation is not None:
- reservation['NanoCPUs'] = cpu_reservation
- if mem_reservation is not None:
- reservation['MemoryBytes'] = mem_reservation
-
- self['Limits'] = limits
- self['Reservation'] = reservation
-
-
-class UpdateConfig(dict):
- def __init__(self, parallelism=0, delay=None, failure_action='continue'):
- self['Parallelism'] = parallelism
- if delay is not None:
- self['Delay'] = delay
- if failure_action not in ('pause', 'continue'):
- raise errors.DockerError(
- 'failure_action must be either `pause` or `continue`.'
- )
- self['FailureAction'] = failure_action
-
-
-class RestartConditionTypesEnum(object):
- _values = (
- 'none',
- 'on_failure',
- 'any',
- )
- NONE, ON_FAILURE, ANY = _values
-
-
-class RestartPolicy(dict):
- condition_types = RestartConditionTypesEnum
-
- def __init__(self, condition=RestartConditionTypesEnum.NONE, delay=0,
- attempts=0, window=0):
- if condition not in self.condition_types._values:
- raise TypeError(
- 'Invalid RestartPolicy condition {0}'.format(condition)
- )
-
- self['Condition'] = condition
- self['Delay'] = delay
- self['Attempts'] = attempts
- self['Window'] = window
diff --git a/docker/types/__init__.py b/docker/types/__init__.py
new file mode 100644
index 0000000..46f10d8
--- /dev/null
+++ b/docker/types/__init__.py
@@ -0,0 +1,7 @@
+# flake8: noqa
+from .containers import LogConfig, Ulimit
+from .services import (
+ ContainerSpec, LogDriver, Mount, Resources, RestartPolicy, TaskTemplate,
+ UpdateConfig
+)
+from .swarm import SwarmSpec, SwarmExternalCA
diff --git a/docker/types/base.py b/docker/types/base.py
new file mode 100644
index 0000000..6891062
--- /dev/null
+++ b/docker/types/base.py
@@ -0,0 +1,7 @@
+import six
+
+
+class DictType(dict):
+ def __init__(self, init):
+ for k, v in six.iteritems(init):
+ self[k] = v
diff --git a/docker/utils/types.py b/docker/types/containers.py
index d778b90..40a44ca 100644
--- a/docker/utils/types.py
+++ b/docker/types/containers.py
@@ -1,5 +1,7 @@
import six
+from .base import DictType
+
class LogConfigTypesEnum(object):
_values = (
@@ -13,12 +15,6 @@ class LogConfigTypesEnum(object):
JSON, SYSLOG, JOURNALD, GELF, FLUENTD, NONE = _values
-class DictType(dict):
- def __init__(self, init):
- for k, v in six.iteritems(init):
- self[k] = v
-
-
class LogConfig(DictType):
types = LogConfigTypesEnum
@@ -94,45 +90,3 @@ class Ulimit(DictType):
@hard.setter
def hard(self, value):
self['Hard'] = value
-
-
-class SwarmSpec(DictType):
- def __init__(self, task_history_retention_limit=None,
- snapshot_interval=None, keep_old_snapshots=None,
- log_entries_for_slow_followers=None, heartbeat_tick=None,
- election_tick=None, dispatcher_heartbeat_period=None,
- node_cert_expiry=None, external_ca=None, name=None):
- if task_history_retention_limit is not None:
- self['Orchestration'] = {
- 'TaskHistoryRetentionLimit': task_history_retention_limit
- }
- if any([snapshot_interval, keep_old_snapshots,
- log_entries_for_slow_followers, heartbeat_tick, election_tick]):
- self['Raft'] = {
- 'SnapshotInterval': snapshot_interval,
- 'KeepOldSnapshots': keep_old_snapshots,
- 'LogEntriesForSlowFollowers': log_entries_for_slow_followers,
- 'HeartbeatTick': heartbeat_tick,
- 'ElectionTick': election_tick
- }
-
- if dispatcher_heartbeat_period:
- self['Dispatcher'] = {
- 'HeartbeatPeriod': dispatcher_heartbeat_period
- }
-
- if node_cert_expiry or external_ca:
- self['CAConfig'] = {
- 'NodeCertExpiry': node_cert_expiry,
- 'ExternalCA': external_ca
- }
-
- if name is not None:
- self['Name'] = name
-
-
-class SwarmExternalCA(DictType):
- def __init__(self, url, protocol=None, options=None):
- self['URL'] = url
- self['Protocol'] = protocol
- self['Options'] = options
diff --git a/docker/types/services.py b/docker/types/services.py
new file mode 100644
index 0000000..6a17e93
--- /dev/null
+++ b/docker/types/services.py
@@ -0,0 +1,176 @@
+import six
+
+from .. import errors
+
+
+class TaskTemplate(dict):
+ def __init__(self, container_spec, resources=None, restart_policy=None,
+ placement=None, log_driver=None):
+ self['ContainerSpec'] = container_spec
+ if resources:
+ self['Resources'] = resources
+ if restart_policy:
+ self['RestartPolicy'] = restart_policy
+ if placement:
+ self['Placement'] = placement
+ if log_driver:
+ self['LogDriver'] = log_driver
+
+ @property
+ def container_spec(self):
+ return self.get('ContainerSpec')
+
+ @property
+ def resources(self):
+ return self.get('Resources')
+
+ @property
+ def restart_policy(self):
+ return self.get('RestartPolicy')
+
+ @property
+ def placement(self):
+ return self.get('Placement')
+
+
+class ContainerSpec(dict):
+ def __init__(self, image, command=None, args=None, env=None, workdir=None,
+ user=None, labels=None, mounts=None, stop_grace_period=None):
+ self['Image'] = image
+ self['Command'] = command
+ self['Args'] = args
+
+ if env is not None:
+ self['Env'] = env
+ if workdir is not None:
+ self['Dir'] = workdir
+ if user is not None:
+ self['User'] = user
+ if labels is not None:
+ self['Labels'] = labels
+ if mounts is not None:
+ for mount in mounts:
+ if isinstance(mount, six.string_types):
+ mounts.append(Mount.parse_mount_string(mount))
+ mounts.remove(mount)
+ self['Mounts'] = mounts
+ if stop_grace_period is not None:
+ self['StopGracePeriod'] = stop_grace_period
+
+
+class Mount(dict):
+ def __init__(self, target, source, type='volume', read_only=False,
+ propagation=None, no_copy=False, labels=None,
+ driver_config=None):
+ self['Target'] = target
+ self['Source'] = source
+ if type not in ('bind', 'volume'):
+ raise errors.DockerError(
+ 'Only acceptable mount types are `bind` and `volume`.'
+ )
+ self['Type'] = type
+
+ if type == 'bind':
+ if propagation is not None:
+ self['BindOptions'] = {
+ 'Propagation': propagation
+ }
+ if any(labels, driver_config, no_copy):
+ raise errors.DockerError(
+ 'Mount type is binding but volume options have been '
+ 'provided.'
+ )
+ else:
+ volume_opts = {}
+ if no_copy:
+ volume_opts['NoCopy'] = True
+ if labels:
+ volume_opts['Labels'] = labels
+ if driver_config:
+ volume_opts['driver_config'] = driver_config
+ if volume_opts:
+ self['VolumeOptions'] = volume_opts
+ if propagation:
+ raise errors.DockerError(
+ 'Mount type is volume but `propagation` argument has been '
+ 'provided.'
+ )
+
+ @classmethod
+ def parse_mount_string(cls, string):
+ parts = string.split(':')
+ if len(parts) > 3:
+ raise errors.DockerError(
+ 'Invalid mount format "{0}"'.format(string)
+ )
+ if len(parts) == 1:
+ return cls(target=parts[0])
+ else:
+ target = parts[1]
+ source = parts[0]
+ read_only = not (len(parts) == 3 or parts[2] == 'ro')
+ return cls(target, source, read_only=read_only)
+
+
+class Resources(dict):
+ def __init__(self, cpu_limit=None, mem_limit=None, cpu_reservation=None,
+ mem_reservation=None):
+ limits = {}
+ reservation = {}
+ if cpu_limit is not None:
+ limits['NanoCPUs'] = cpu_limit
+ if mem_limit is not None:
+ limits['MemoryBytes'] = mem_limit
+ if cpu_reservation is not None:
+ reservation['NanoCPUs'] = cpu_reservation
+ if mem_reservation is not None:
+ reservation['MemoryBytes'] = mem_reservation
+
+ if limits:
+ self['Limits'] = limits
+ if reservation:
+ self['Reservations'] = reservation
+
+
+class UpdateConfig(dict):
+ def __init__(self, parallelism=0, delay=None, failure_action='continue'):
+ self['Parallelism'] = parallelism
+ if delay is not None:
+ self['Delay'] = delay
+ if failure_action not in ('pause', 'continue'):
+ raise errors.DockerError(
+ 'failure_action must be either `pause` or `continue`.'
+ )
+ self['FailureAction'] = failure_action
+
+
+class RestartConditionTypesEnum(object):
+ _values = (
+ 'none',
+ 'on_failure',
+ 'any',
+ )
+ NONE, ON_FAILURE, ANY = _values
+
+
+class RestartPolicy(dict):
+ condition_types = RestartConditionTypesEnum
+
+ def __init__(self, condition=RestartConditionTypesEnum.NONE, delay=0,
+ max_attempts=0, window=0):
+ if condition not in self.condition_types._values:
+ raise TypeError(
+ 'Invalid RestartPolicy condition {0}'.format(condition)
+ )
+
+ self['Condition'] = condition
+ self['Delay'] = delay
+ self['MaxAttempts'] = max_attempts
+ self['Window'] = window
+
+
+class LogDriver(dict):
+ def __init__(self, name, options=None):
+ self['Name'] = name
+ if options:
+ self['Options'] = options
diff --git a/docker/types/swarm.py b/docker/types/swarm.py
new file mode 100644
index 0000000..865fde6
--- /dev/null
+++ b/docker/types/swarm.py
@@ -0,0 +1,40 @@
+class SwarmSpec(dict):
+ def __init__(self, task_history_retention_limit=None,
+ snapshot_interval=None, keep_old_snapshots=None,
+ log_entries_for_slow_followers=None, heartbeat_tick=None,
+ election_tick=None, dispatcher_heartbeat_period=None,
+ node_cert_expiry=None, external_ca=None, name=None):
+ if task_history_retention_limit is not None:
+ self['Orchestration'] = {
+ 'TaskHistoryRetentionLimit': task_history_retention_limit
+ }
+ if any([snapshot_interval, keep_old_snapshots,
+ log_entries_for_slow_followers, heartbeat_tick, election_tick]):
+ self['Raft'] = {
+ 'SnapshotInterval': snapshot_interval,
+ 'KeepOldSnapshots': keep_old_snapshots,
+ 'LogEntriesForSlowFollowers': log_entries_for_slow_followers,
+ 'HeartbeatTick': heartbeat_tick,
+ 'ElectionTick': election_tick
+ }
+
+ if dispatcher_heartbeat_period:
+ self['Dispatcher'] = {
+ 'HeartbeatPeriod': dispatcher_heartbeat_period
+ }
+
+ if node_cert_expiry or external_ca:
+ self['CAConfig'] = {
+ 'NodeCertExpiry': node_cert_expiry,
+ 'ExternalCA': external_ca
+ }
+
+ if name is not None:
+ self['Name'] = name
+
+
+class SwarmExternalCA(dict):
+ def __init__(self, url, protocol=None, options=None):
+ self['URL'] = url
+ self['Protocol'] = protocol
+ self['Options'] = options
diff --git a/docker/utils/__init__.py b/docker/utils/__init__.py
index 35acc77..4bb3876 100644
--- a/docker/utils/__init__.py
+++ b/docker/utils/__init__.py
@@ -8,8 +8,6 @@ from .utils import (
create_ipam_config, create_ipam_pool, parse_devices, normalize_links,
)
-from .types import LogConfig, Ulimit
-from .types import (
- SwarmExternalCA, SwarmSpec,
-)
+from ..types import LogConfig, Ulimit
+from ..types import SwarmExternalCA, SwarmSpec
from .decorators import check_resource, minimum_version, update_headers
diff --git a/docker/utils/utils.py b/docker/utils/utils.py
index 00a7af1..bea436a 100644
--- a/docker/utils/utils.py
+++ b/docker/utils/utils.py
@@ -31,7 +31,7 @@ import six
from .. import constants
from .. import errors
from .. import tls
-from .types import Ulimit, LogConfig
+from ..types import Ulimit, LogConfig
if six.PY2:
from urllib import splitnport
diff --git a/docs/swarm.md b/docs/swarm.md
index 0cd015a..3cc44f8 100644
--- a/docs/swarm.md
+++ b/docs/swarm.md
@@ -95,7 +95,7 @@ Initialize a new Swarm using the current connected engine as the first node.
#### Client.create_swarm_spec
-Create a `docker.utils.SwarmSpec` instance that can be used as the `swarm_spec`
+Create a `docker.types.SwarmSpec` instance that can be used as the `swarm_spec`
argument in `Client.init_swarm`.
**Params:**
@@ -113,12 +113,12 @@ argument in `Client.init_swarm`.
heartbeat to the dispatcher.
* node_cert_expiry (int): Automatic expiry for nodes certificates.
* external_ca (dict): Configuration for forwarding signing requests to an
- external certificate authority. Use `docker.utils.SwarmExternalCA`.
+ external certificate authority. Use `docker.types.SwarmExternalCA`.
* name (string): Swarm's name
-**Returns:** `docker.utils.SwarmSpec` instance.
+**Returns:** `docker.types.SwarmSpec` instance.
-#### docker.utils.SwarmExternalCA
+#### docker.types.SwarmExternalCA
Create a configuration dictionary for the `external_ca` argument in a
`SwarmSpec`.
diff --git a/setup.py b/setup.py
index 85a4499..c809321 100644
--- a/setup.py
+++ b/setup.py
@@ -36,7 +36,8 @@ setup(
url='https://github.com/docker/docker-py/',
packages=[
'docker', 'docker.api', 'docker.auth', 'docker.transport',
- 'docker.utils', 'docker.utils.ports', 'docker.ssladapter'
+ 'docker.utils', 'docker.utils.ports', 'docker.ssladapter',
+ 'docker.types',
],
install_requires=requirements,
tests_require=test_requirements,
diff --git a/tests/integration/service_test.py b/tests/integration/service_test.py
index 0010986..fda62b3 100644
--- a/tests/integration/service_test.py
+++ b/tests/integration/service_test.py
@@ -40,8 +40,10 @@ class ServiceTest(helpers.BaseTestCase):
else:
name = self.get_service_name()
- container_spec = docker.api.ContainerSpec('busybox', ['echo', 'hello'])
- task_tmpl = docker.api.TaskTemplate(container_spec)
+ container_spec = docker.types.ContainerSpec(
+ 'busybox', ['echo', 'hello']
+ )
+ task_tmpl = docker.types.TaskTemplate(container_spec)
return name, self.client.create_service(task_tmpl, name=name)
@requires_api_version('1.24')
@@ -74,7 +76,7 @@ class ServiceTest(helpers.BaseTestCase):
test_services = self.client.services(filters={'name': 'dockerpytest_'})
assert len(test_services) == 0
- def test_rempve_service_by_name(self):
+ def test_remove_service_by_name(self):
svc_name, svc_id = self.create_simple_service()
assert self.client.remove_service(svc_name)
test_services = self.client.services(filters={'name': 'dockerpytest_'})
@@ -87,6 +89,94 @@ class ServiceTest(helpers.BaseTestCase):
assert len(services) == 1
assert services[0]['ID'] == svc_id['ID']
+ def test_create_service_custom_log_driver(self):
+ container_spec = docker.types.ContainerSpec(
+ 'busybox', ['echo', 'hello']
+ )
+ log_cfg = docker.types.LogDriver('none')
+ task_tmpl = docker.types.TaskTemplate(
+ container_spec, log_driver=log_cfg
+ )
+ name = self.get_service_name()
+ svc_id = self.client.create_service(task_tmpl, name=name)
+ svc_info = self.client.inspect_service(svc_id)
+ assert 'TaskTemplate' in svc_info['Spec']
+ res_template = svc_info['Spec']['TaskTemplate']
+ assert 'LogDriver' in res_template
+ assert 'Name' in res_template['LogDriver']
+ assert res_template['LogDriver']['Name'] == 'none'
+
+ def test_create_service_with_volume_mount(self):
+ vol_name = self.get_service_name()
+ container_spec = docker.types.ContainerSpec(
+ 'busybox', ['ls'],
+ mounts=[
+ docker.types.Mount(target='/test', source=vol_name)
+ ]
+ )
+ self.tmp_volumes.append(vol_name)
+ task_tmpl = docker.types.TaskTemplate(container_spec)
+ name = self.get_service_name()
+ svc_id = self.client.create_service(task_tmpl, name=name)
+ svc_info = self.client.inspect_service(svc_id)
+ assert 'ContainerSpec' in svc_info['Spec']['TaskTemplate']
+ cspec = svc_info['Spec']['TaskTemplate']['ContainerSpec']
+ assert 'Mounts' in cspec
+ assert len(cspec['Mounts']) == 1
+ mount = cspec['Mounts'][0]
+ assert mount['Target'] == '/test'
+ assert mount['Source'] == vol_name
+ assert mount['Type'] == 'volume'
+
+ def test_create_service_with_resources_constraints(self):
+ container_spec = docker.types.ContainerSpec('busybox', ['true'])
+ resources = docker.types.Resources(
+ cpu_limit=4000000, mem_limit=3 * 1024 * 1024 * 1024,
+ cpu_reservation=3500000, mem_reservation=2 * 1024 * 1024 * 1024
+ )
+ task_tmpl = docker.types.TaskTemplate(
+ container_spec, resources=resources
+ )
+ name = self.get_service_name()
+ svc_id = self.client.create_service(task_tmpl, name=name)
+ svc_info = self.client.inspect_service(svc_id)
+ assert 'TaskTemplate' in svc_info['Spec']
+ res_template = svc_info['Spec']['TaskTemplate']
+ assert 'Resources' in res_template
+ assert res_template['Resources']['Limits'] == resources['Limits']
+ assert res_template['Resources']['Reservations'] == resources[
+ 'Reservations'
+ ]
+
+ def test_create_service_with_update_config(self):
+ container_spec = docker.types.ContainerSpec('busybox', ['true'])
+ task_tmpl = docker.types.TaskTemplate(container_spec)
+ update_config = docker.types.UpdateConfig(
+ parallelism=10, delay=5, failure_action='pause'
+ )
+ name = self.get_service_name()
+ svc_id = self.client.create_service(
+ task_tmpl, update_config=update_config, name=name
+ )
+ svc_info = self.client.inspect_service(svc_id)
+ assert 'UpdateConfig' in svc_info['Spec']
+ assert update_config == svc_info['Spec']['UpdateConfig']
+
+ def test_create_service_with_restart_policy(self):
+ container_spec = docker.types.ContainerSpec('busybox', ['true'])
+ policy = docker.types.RestartPolicy(
+ docker.types.RestartPolicy.condition_types.ANY,
+ delay=5, max_attempts=5
+ )
+ task_tmpl = docker.types.TaskTemplate(
+ container_spec, restart_policy=policy
+ )
+ name = self.get_service_name()
+ svc_id = self.client.create_service(task_tmpl, name=name)
+ svc_info = self.client.inspect_service(svc_id)
+ assert 'RestartPolicy' in svc_info['Spec']['TaskTemplate']
+ assert policy == svc_info['Spec']['TaskTemplate']['RestartPolicy']
+
def test_update_service_name(self):
name, svc_id = self.create_simple_service()
svc_info = self.client.inspect_service(svc_id)