From 82445764e0499f605ec6222ce3341511436e96b3 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Thu, 9 Aug 2018 16:41:25 -0700 Subject: Add support for RollbackConfig Signed-off-by: Joffrey F --- docker/api/service.py | 34 +++++++++++++++++++++++++++++----- docker/models/services.py | 2 ++ docker/types/__init__.py | 4 ++-- docker/types/services.py | 24 ++++++++++++++++++++++++ tests/integration/api_service_test.py | 21 +++++++++++++++++++++ 5 files changed, 78 insertions(+), 7 deletions(-) diff --git a/docker/api/service.py b/docker/api/service.py index 1dbe269..8b956b6 100644 --- a/docker/api/service.py +++ b/docker/api/service.py @@ -2,7 +2,8 @@ from .. import auth, errors, utils from ..types import ServiceMode -def _check_api_features(version, task_template, update_config, endpoint_spec): +def _check_api_features(version, task_template, update_config, endpoint_spec, + rollback_config): def raise_version_error(param, min_version): raise errors.InvalidVersion( @@ -28,6 +29,14 @@ def _check_api_features(version, task_template, update_config, endpoint_spec): if 'Order' in update_config: raise_version_error('UpdateConfig.order', '1.29') + if rollback_config is not None: + if utils.version_lt(version, '1.28'): + raise_version_error('rollback_config', '1.28') + + if utils.version_lt(version, '1.29'): + if 'Order' in update_config: + raise_version_error('RollbackConfig.order', '1.29') + if endpoint_spec is not None: if utils.version_lt(version, '1.32') and 'Ports' in endpoint_spec: if any(p.get('PublishMode') for p in endpoint_spec['Ports']): @@ -105,7 +114,7 @@ class ServiceApiMixin(object): def create_service( self, task_template, name=None, labels=None, mode=None, update_config=None, networks=None, endpoint_config=None, - endpoint_spec=None + endpoint_spec=None, rollback_config=None ): """ Create a service. @@ -120,6 +129,8 @@ class ServiceApiMixin(object): or global). Defaults to replicated. update_config (UpdateConfig): Specification for the update strategy of the service. Default: ``None`` + rollback_config (RollbackConfig): Specification for the rollback + strategy of the service. Default: ``None`` networks (:py:class:`list`): List of network names or IDs to attach the service to. Default: ``None``. endpoint_spec (EndpointSpec): Properties that can be configured to @@ -135,7 +146,8 @@ class ServiceApiMixin(object): """ _check_api_features( - self._version, task_template, update_config, endpoint_spec + self._version, task_template, update_config, endpoint_spec, + rollback_config ) url = self._url('/services/create') @@ -166,6 +178,9 @@ class ServiceApiMixin(object): if update_config is not None: data['UpdateConfig'] = update_config + if rollback_config is not None: + data['RollbackConfig'] = rollback_config + return self._result( self._post_json(url, data=data, headers=headers), True ) @@ -342,7 +357,8 @@ class ServiceApiMixin(object): def update_service(self, service, version, task_template=None, name=None, labels=None, mode=None, update_config=None, networks=None, endpoint_config=None, - endpoint_spec=None, fetch_current_spec=False): + endpoint_spec=None, fetch_current_spec=False, + rollback_config=None): """ Update a service. @@ -360,6 +376,8 @@ class ServiceApiMixin(object): or global). Defaults to replicated. update_config (UpdateConfig): Specification for the update strategy of the service. Default: ``None``. + rollback_config (RollbackConfig): Specification for the rollback + strategy of the service. Default: ``None`` networks (:py:class:`list`): List of network names or IDs to attach the service to. Default: ``None``. endpoint_spec (EndpointSpec): Properties that can be configured to @@ -376,7 +394,8 @@ class ServiceApiMixin(object): """ _check_api_features( - self._version, task_template, update_config, endpoint_spec + self._version, task_template, update_config, endpoint_spec, + rollback_config ) if fetch_current_spec: @@ -422,6 +441,11 @@ class ServiceApiMixin(object): else: data['UpdateConfig'] = current.get('UpdateConfig') + if rollback_config is not None: + data['RollbackConfig'] = rollback_config + else: + data['RollbackConfig'] = current.get('RollbackConfig') + if networks is not None: converted_networks = utils.convert_service_networks(networks) if utils.version_lt(self._version, '1.25'): diff --git a/docker/models/services.py b/docker/models/services.py index 7fbd165..fa029f3 100644 --- a/docker/models/services.py +++ b/docker/models/services.py @@ -183,6 +183,8 @@ class ServiceCollection(Collection): containers to terminate before forcefully killing them. update_config (UpdateConfig): Specification for the update strategy of the service. Default: ``None`` + rollback_config (RollbackConfig): Specification for the rollback + strategy of the service. Default: ``None`` user (str): User to run commands as. workdir (str): Working directory for commands to run. tty (boolean): Whether a pseudo-TTY should be allocated. diff --git a/docker/types/__init__.py b/docker/types/__init__.py index 0b0d847..6451233 100644 --- a/docker/types/__init__.py +++ b/docker/types/__init__.py @@ -5,7 +5,7 @@ from .healthcheck import Healthcheck from .networks import EndpointConfig, IPAMConfig, IPAMPool, NetworkingConfig from .services import ( ConfigReference, ContainerSpec, DNSConfig, DriverConfig, EndpointSpec, - Mount, Placement, Privileges, Resources, RestartPolicy, SecretReference, - ServiceMode, TaskTemplate, UpdateConfig + Mount, Placement, Privileges, Resources, RestartPolicy, RollbackConfig, + SecretReference, ServiceMode, TaskTemplate, UpdateConfig ) from .swarm import SwarmSpec, SwarmExternalCA diff --git a/docker/types/services.py b/docker/types/services.py index a883f3f..c66d41a 100644 --- a/docker/types/services.py +++ b/docker/types/services.py @@ -414,6 +414,30 @@ class UpdateConfig(dict): self['Order'] = order +class RollbackConfig(UpdateConfig): + """ + Used to specify the way containe rollbacks should be performed by a service + + Args: + parallelism (int): Maximum number of tasks to be rolled back in one + iteration (0 means unlimited parallelism). Default: 0 + delay (int): Amount of time between rollbacks, in nanoseconds. + failure_action (string): Action to take if a rolled back task fails to + run, or stops running during the rollback. Acceptable values are + ``continue``, ``pause`` or ``rollback``. + Default: ``continue`` + monitor (int): Amount of time to monitor each rolled back task for + failures, in nanoseconds. + max_failure_ratio (float): The fraction of tasks that may fail during + a rollback before the failure action is invoked, specified as a + floating point number between 0 and 1. Default: 0 + order (string): Specifies the order of operations when rolling out a + rolled back task. Either ``start_first`` or ``stop_first`` are + accepted. + """ + pass + + class RestartConditionTypesEnum(object): _values = ( 'none', diff --git a/tests/integration/api_service_test.py b/tests/integration/api_service_test.py index ba2ed91..a53ca1c 100644 --- a/tests/integration/api_service_test.py +++ b/tests/integration/api_service_test.py @@ -312,6 +312,27 @@ class ServiceTest(BaseAPIIntegrationTest): assert update_config['Monitor'] == uc['Monitor'] assert update_config['MaxFailureRatio'] == uc['MaxFailureRatio'] + @requires_api_version('1.28') + def test_create_service_with_rollback_config(self): + container_spec = docker.types.ContainerSpec(BUSYBOX, ['true']) + task_tmpl = docker.types.TaskTemplate(container_spec) + rollback_cfg = docker.types.RollbackConfig( + parallelism=10, delay=5, failure_action='pause', + monitor=300000000, max_failure_ratio=0.4 + ) + name = self.get_service_name() + svc_id = self.client.create_service( + task_tmpl, rollback_config=rollback_cfg, name=name + ) + svc_info = self.client.inspect_service(svc_id) + assert 'RollbackConfig' in svc_info['Spec'] + rc = svc_info['Spec']['RollbackConfig'] + assert rollback_cfg['Parallelism'] == rc['Parallelism'] + assert rollback_cfg['Delay'] == rc['Delay'] + assert rollback_cfg['FailureAction'] == rc['FailureAction'] + assert rollback_cfg['Monitor'] == rc['Monitor'] + assert rollback_cfg['MaxFailureRatio'] == rc['MaxFailureRatio'] + def test_create_service_with_restart_policy(self): container_spec = docker.types.ContainerSpec(BUSYBOX, ['true']) policy = docker.types.RestartPolicy( -- cgit v1.2.1