Kaydet (Commit) 32392b7d authored tarafından Joffrey F's avatar Joffrey F Kaydeden (comit) GitHub

Merge pull request #1390 from docker/1388-format-service-mode

Convert mode argument to valid structure in create_service
import warnings
from .. import auth, errors, utils
from ..types import ServiceMode
class ServiceApiMixin(object):
......@@ -18,8 +19,8 @@ class ServiceApiMixin(object):
name (string): User-defined name for the service. Optional.
labels (dict): A map of labels to associate with the service.
Optional.
mode (string): Scheduling mode for the service (``replicated`` or
``global``). Defaults to ``replicated``.
mode (ServiceMode): Scheduling mode for the service (replicated
or global). Defaults to replicated.
update_config (UpdateConfig): Specification for the update strategy
of the service. Default: ``None``
networks (:py:class:`list`): List of network names or IDs to attach
......@@ -49,6 +50,9 @@ class ServiceApiMixin(object):
raise errors.DockerException(
'Missing mandatory Image key in ContainerSpec'
)
if mode and not isinstance(mode, dict):
mode = ServiceMode(mode)
registry, repo_name = auth.resolve_repository_name(image)
auth_header = auth.get_config_header(self, registry)
if auth_header:
......@@ -191,8 +195,8 @@ class ServiceApiMixin(object):
name (string): New name for the service. Optional.
labels (dict): A map of labels to associate with the service.
Optional.
mode (string): Scheduling mode for the service (``replicated`` or
``global``). Defaults to ``replicated``.
mode (ServiceMode): Scheduling mode for the service (replicated
or global). Defaults to replicated.
update_config (UpdateConfig): Specification for the update strategy
of the service. Default: ``None``.
networks (:py:class:`list`): List of network names or IDs to attach
......@@ -222,6 +226,8 @@ class ServiceApiMixin(object):
if labels is not None:
data['Labels'] = labels
if mode is not None:
if not isinstance(mode, dict):
mode = ServiceMode(mode)
data['Mode'] = mode
if task_template is not None:
image = task_template.get('ContainerSpec', {}).get('Image', None)
......
......@@ -4,6 +4,6 @@ from .healthcheck import Healthcheck
from .networks import EndpointConfig, IPAMConfig, IPAMPool, NetworkingConfig
from .services import (
ContainerSpec, DriverConfig, EndpointSpec, Mount, Resources, RestartPolicy,
TaskTemplate, UpdateConfig
ServiceMode, TaskTemplate, UpdateConfig
)
from .swarm import SwarmSpec, SwarmExternalCA
......@@ -348,3 +348,38 @@ def convert_service_ports(ports):
result.append(port_spec)
return result
class ServiceMode(dict):
"""
Indicate whether a service should be deployed as a replicated or global
service, and associated parameters
Args:
mode (string): Can be either ``replicated`` or ``global``
replicas (int): Number of replicas. For replicated services only.
"""
def __init__(self, mode, replicas=None):
if mode not in ('replicated', 'global'):
raise errors.InvalidArgument(
'mode must be either "replicated" or "global"'
)
if mode != 'replicated' and replicas is not None:
raise errors.InvalidArgument(
'replicas can only be used for replicated mode'
)
self[mode] = {}
if replicas:
self[mode]['Replicas'] = replicas
@property
def mode(self):
if 'global' in self:
return 'global'
return 'replicated'
@property
def replicas(self):
if self.mode != 'replicated':
return None
return self['replicated'].get('Replicas')
......@@ -110,5 +110,6 @@ Configuration types
.. autoclass:: Mount
.. autoclass:: Resources
.. autoclass:: RestartPolicy
.. autoclass:: ServiceMode
.. autoclass:: TaskTemplate
.. autoclass:: UpdateConfig
......@@ -251,3 +251,31 @@ class ServiceTest(BaseAPIIntegrationTest):
con_spec = svc_info['Spec']['TaskTemplate']['ContainerSpec']
assert 'Env' in con_spec
assert con_spec['Env'] == ['DOCKER_PY_TEST=1']
def test_create_service_global_mode(self):
container_spec = docker.types.ContainerSpec(
'busybox', ['echo', 'hello']
)
task_tmpl = docker.types.TaskTemplate(container_spec)
name = self.get_service_name()
svc_id = self.client.create_service(
task_tmpl, name=name, mode='global'
)
svc_info = self.client.inspect_service(svc_id)
assert 'Mode' in svc_info['Spec']
assert 'Global' in svc_info['Spec']['Mode']
def test_create_service_replicated_mode(self):
container_spec = docker.types.ContainerSpec(
'busybox', ['echo', 'hello']
)
task_tmpl = docker.types.TaskTemplate(container_spec)
name = self.get_service_name()
svc_id = self.client.create_service(
task_tmpl, name=name,
mode=docker.types.ServiceMode('replicated', 5)
)
svc_info = self.client.inspect_service(svc_id)
assert 'Mode' in svc_info['Spec']
assert 'Replicated' in svc_info['Spec']['Mode']
assert svc_info['Spec']['Mode']['Replicated'] == {'Replicas': 5}
......@@ -7,7 +7,8 @@ import pytest
from docker.constants import DEFAULT_DOCKER_API_VERSION
from docker.errors import InvalidArgument, InvalidVersion
from docker.types import (
EndpointConfig, HostConfig, IPAMConfig, IPAMPool, LogConfig, Mount, Ulimit,
EndpointConfig, HostConfig, IPAMConfig, IPAMPool, LogConfig, Mount,
ServiceMode, Ulimit,
)
try:
......@@ -260,7 +261,35 @@ class IPAMConfigTest(unittest.TestCase):
})
class TestMounts(unittest.TestCase):
class ServiceModeTest(unittest.TestCase):
def test_replicated_simple(self):
mode = ServiceMode('replicated')
assert mode == {'replicated': {}}
assert mode.mode == 'replicated'
assert mode.replicas is None
def test_global_simple(self):
mode = ServiceMode('global')
assert mode == {'global': {}}
assert mode.mode == 'global'
assert mode.replicas is None
def test_global_replicas_error(self):
with pytest.raises(InvalidArgument):
ServiceMode('global', 21)
def test_replicated_replicas(self):
mode = ServiceMode('replicated', 21)
assert mode == {'replicated': {'Replicas': 21}}
assert mode.mode == 'replicated'
assert mode.replicas == 21
def test_invalid_mode(self):
with pytest.raises(InvalidArgument):
ServiceMode('foobar')
class MountTest(unittest.TestCase):
def test_parse_mount_string_ro(self):
mount = Mount.parse_mount_string("/foo/bar:/baz:ro")
assert mount['Source'] == "/foo/bar"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment