Kaydet (Commit) b2d08e64 authored tarafından Viktor Adam's avatar Viktor Adam

Service model update changes

Signed-off-by: 's avatarViktor Adam <rycus86@gmail.com>
üst 6e5eb2eb
......@@ -363,8 +363,15 @@ class ServiceApiMixin(object):
data = {}
headers = {}
data['Name'] = name if name is not None else current.get('Name')
data['Labels'] = labels if labels is not None else current.get('Labels')
if name is not None:
data['Name'] = name
else:
data['Name'] = current.get('Name')
if labels is not None:
data['Labels'] = labels
else:
data['Labels'] = current.get('Labels')
if mode is not None:
if not isinstance(mode, dict):
......@@ -373,23 +380,17 @@ class ServiceApiMixin(object):
else:
data['Mode'] = current.get('Mode')
merged_task_template = current.get('TaskTemplate', {})
if task_template is not None:
for task_template_key, task_template_value in task_template.items():
if task_template_key == 'ContainerSpec':
if 'ContainerSpec' not in merged_task_template:
merged_task_template['ContainerSpec'] = {}
for container_spec_key, container_spec_value in task_template['ContainerSpec'].items():
merged_task_template['ContainerSpec'][container_spec_key] = container_spec_value
else:
merged_task_template[task_template_key] = task_template_value
image = merged_task_template.get('ContainerSpec', {}).get('Image', None)
if image is not None:
registry, repo_name = auth.resolve_repository_name(image)
auth_header = auth.get_config_header(self, registry)
if auth_header:
headers['X-Registry-Auth'] = auth_header
data['TaskTemplate'] = merged_task_template
data['TaskTemplate'] = self._merge_task_template(
current.get('TaskTemplate', {}), task_template
)
container_spec = data['TaskTemplate'].get('ContainerSpec', {})
image = container_spec.get('Image', None)
if image is not None:
registry, repo_name = auth.resolve_repository_name(image)
auth_header = auth.get_config_header(self, registry)
if auth_header:
headers['X-Registry-Auth'] = auth_header
if update_config is not None:
data['UpdateConfig'] = update_config
......@@ -397,11 +398,15 @@ class ServiceApiMixin(object):
data['UpdateConfig'] = current.get('UpdateConfig')
if networks is not None:
data['TaskTemplate']['Networks'] = utils.convert_service_networks(networks)
else:
existing_networks = current.get('TaskTemplate', {}).get('Networks') or current.get('Networks')
if existing_networks is not None:
data['TaskTemplate']['Networks'] = existing_networks
converted_networks = utils.convert_service_networks(networks)
data['TaskTemplate']['Networks'] = converted_networks
elif data['TaskTemplate'].get('Networks') is None:
current_task_template = current.get('TaskTemplate', {})
current_networks = current_task_template.get('Networks')
if current_networks is None:
current_networks = current.get('Networks')
if current_networks is not None:
data['TaskTemplate']['Networks'] = current_networks
if endpoint_spec is not None:
data['EndpointSpec'] = endpoint_spec
......@@ -413,3 +418,17 @@ class ServiceApiMixin(object):
)
self._raise_for_status(resp)
return True
@staticmethod
def _merge_task_template(current, override):
merged = current.copy()
if override is not None:
for ts_key, ts_value in override.items():
if ts_key == 'ContainerSpec':
if 'ContainerSpec' not in merged:
merged['ContainerSpec'] = {}
for cs_key, cs_value in override['ContainerSpec'].items():
merged['ContainerSpec'][cs_key] = cs_value
else:
merged[ts_key] = ts_value
return merged
......@@ -251,6 +251,7 @@ CONTAINER_SPEC_KWARGS = [
# kwargs to copy straight over to TaskTemplate
TASK_TEMPLATE_KWARGS = [
'networks',
'resources',
'restart_policy',
]
......@@ -261,7 +262,6 @@ CREATE_SERVICE_KWARGS = [
'labels',
'mode',
'update_config',
'networks',
'endpoint_spec',
]
......@@ -295,6 +295,15 @@ def _get_create_service_kwargs(func_name, kwargs):
'Options': kwargs.pop('log_driver_options', {})
}
if func_name == 'update':
if 'force_update' in kwargs:
task_template_kwargs['force_update'] = kwargs.pop('force_update')
# use the current spec by default if updating the service
# through the model
use_current_spec = kwargs.pop('use_current_spec', True)
create_kwargs['use_current_spec'] = use_current_spec
# All kwargs should have been consumed by this point, so raise
# error if any are left
if kwargs:
......
......@@ -4,7 +4,7 @@ from .. import errors
from ..constants import IS_WINDOWS_PLATFORM
from ..utils import (
check_resource, format_environment, format_extra_hosts, parse_bytes,
split_command,
split_command, convert_service_networks,
)
......@@ -26,11 +26,14 @@ class TaskTemplate(dict):
placement (Placement): Placement instructions for the scheduler.
If a list is passed instead, it is assumed to be a list of
constraints as part of a :py:class:`Placement` object.
networks (:py:class:`list`): List of network names or IDs to attach
the containers to.
force_update (int): A counter that triggers an update even if no
relevant parameters have been changed.
"""
def __init__(self, container_spec, resources=None, restart_policy=None,
placement=None, log_driver=None, force_update=None):
placement=None, log_driver=None, networks=None,
force_update=None):
self['ContainerSpec'] = container_spec
if resources:
self['Resources'] = resources
......@@ -42,6 +45,8 @@ class TaskTemplate(dict):
self['Placement'] = placement
if log_driver:
self['LogDriver'] = log_driver
if networks:
self['Networks'] = convert_service_networks(networks)
if force_update is not None:
if not isinstance(force_update, int):
......
import unittest
import docker
import pytest
from .. import helpers
from .base import TEST_API_VERSION
......@@ -36,6 +35,25 @@ class ServiceTest(unittest.TestCase):
assert "alpine" in container_spec['Image']
assert container_spec['Labels'] == {'container': 'label'}
def test_create_with_network(self):
client = docker.from_env(version=TEST_API_VERSION)
name = helpers.random_name()
network = client.networks.create(
helpers.random_name(), driver='overlay'
)
service = client.services.create(
# create arguments
name=name,
# ContainerSpec arguments
image="alpine",
command="sleep 300",
networks=[network.id]
)
assert 'Networks' in service.attrs['Spec']['TaskTemplate']
networks = service.attrs['Spec']['TaskTemplate']['Networks']
assert len(networks) == 1
assert networks[0]['Target'] == network.id
def test_get(self):
client = docker.from_env(version=TEST_API_VERSION)
name = helpers.random_name()
......@@ -82,7 +100,6 @@ class ServiceTest(unittest.TestCase):
assert len(tasks) == 1
assert tasks[0]['ServiceID'] == service2.id
@pytest.mark.skip(reason="Makes Swarm unstable?")
def test_update(self):
client = docker.from_env(version=TEST_API_VERSION)
service = client.services.create(
......@@ -101,3 +118,105 @@ class ServiceTest(unittest.TestCase):
service.reload()
container_spec = service.attrs['Spec']['TaskTemplate']['ContainerSpec']
assert container_spec['Command'] == ["sleep", "600"]
def test_update_retains_service_labels(self):
client = docker.from_env(version=TEST_API_VERSION)
service = client.services.create(
# create arguments
name=helpers.random_name(),
labels={'service.label': 'SampleLabel'},
# ContainerSpec arguments
image="alpine",
command="sleep 300"
)
service.update(
# create argument
name=service.name,
# ContainerSpec argument
command="sleep 600"
)
service.reload()
labels = service.attrs['Spec']['Labels']
assert labels == {'service.label': 'SampleLabel'}
def test_update_retains_container_labels(self):
client = docker.from_env(version=TEST_API_VERSION)
service = client.services.create(
# create arguments
name=helpers.random_name(),
# ContainerSpec arguments
image="alpine",
command="sleep 300",
container_labels={'container.label': 'SampleLabel'}
)
service.update(
# create argument
name=service.name,
# ContainerSpec argument
command="sleep 600"
)
service.reload()
container_spec = service.attrs['Spec']['TaskTemplate']['ContainerSpec']
assert container_spec['Labels'] == {'container.label': 'SampleLabel'}
def test_update_remove_service_labels(self):
client = docker.from_env(version=TEST_API_VERSION)
service = client.services.create(
# create arguments
name=helpers.random_name(),
labels={'service.label': 'SampleLabel'},
# ContainerSpec arguments
image="alpine",
command="sleep 300"
)
service.update(
# create argument
name=service.name,
labels={},
# ContainerSpec argument
command="sleep 600"
)
service.reload()
assert not service.attrs['Spec'].get('Labels')
def test_scale_service(self):
client = docker.from_env(version=TEST_API_VERSION)
service = client.services.create(
# create arguments
name=helpers.random_name(),
# ContainerSpec arguments
image="alpine",
command="sleep 300"
)
assert len(service.tasks()) == 1
service.update(
# create argument
name=service.name,
mode=docker.types.ServiceMode('replicated', replicas=2),
# ContainerSpec argument
command="sleep 600"
)
service.reload()
assert len(service.tasks()) >= 2
@helpers.requires_api_version('1.25')
def test_restart_service(self):
client = docker.from_env(version=TEST_API_VERSION)
service = client.services.create(
# create arguments
name=helpers.random_name(),
# ContainerSpec arguments
image="alpine",
command="sleep 300"
)
initial_version = service.version
service.update(
# create argument
name=service.name,
# task template argument
force_update=10,
# ContainerSpec argument
command="sleep 600"
)
service.reload()
assert service.version > initial_version
......@@ -35,18 +35,18 @@ class CreateServiceKwargsTest(unittest.TestCase):
'labels': {'key': 'value'},
'mode': 'global',
'update_config': {'update': 'config'},
'networks': ['somenet'],
'endpoint_spec': {'blah': 'blah'},
}
assert set(task_template.keys()) == set([
'ContainerSpec', 'Resources', 'RestartPolicy', 'Placement',
'LogDriver'
'LogDriver', 'Networks'
])
assert task_template['Placement'] == {'Constraints': ['foo=bar']}
assert task_template['LogDriver'] == {
'Name': 'logdriver',
'Options': {'foo': 'bar'}
}
assert task_template['Networks'] == [{'Target': 'somenet'}]
assert set(task_template['ContainerSpec'].keys()) == set([
'Image', 'Command', 'Args', 'Hostname', 'Env', 'Dir', 'User',
'Labels', 'Mounts', 'StopGracePeriod'
......
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