mirror of https://github.com/docker/docker-py.git
Add support for configs management
Signed-off-by: Joffrey F <joffrey@docker.com>
This commit is contained in:
parent
cd47a1f9f5
commit
b1301637cf
|
@ -9,6 +9,7 @@ import six
|
||||||
import websocket
|
import websocket
|
||||||
|
|
||||||
from .build import BuildApiMixin
|
from .build import BuildApiMixin
|
||||||
|
from .config import ConfigApiMixin
|
||||||
from .container import ContainerApiMixin
|
from .container import ContainerApiMixin
|
||||||
from .daemon import DaemonApiMixin
|
from .daemon import DaemonApiMixin
|
||||||
from .exec_api import ExecApiMixin
|
from .exec_api import ExecApiMixin
|
||||||
|
@ -43,6 +44,7 @@ except ImportError:
|
||||||
class APIClient(
|
class APIClient(
|
||||||
requests.Session,
|
requests.Session,
|
||||||
BuildApiMixin,
|
BuildApiMixin,
|
||||||
|
ConfigApiMixin,
|
||||||
ContainerApiMixin,
|
ContainerApiMixin,
|
||||||
DaemonApiMixin,
|
DaemonApiMixin,
|
||||||
ExecApiMixin,
|
ExecApiMixin,
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
import base64
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
from .. import utils
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigApiMixin(object):
|
||||||
|
@utils.minimum_version('1.25')
|
||||||
|
def create_config(self, name, data, labels=None):
|
||||||
|
"""
|
||||||
|
Create a config
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (string): Name of the config
|
||||||
|
data (bytes): Config data to be stored
|
||||||
|
labels (dict): A mapping of labels to assign to the config
|
||||||
|
|
||||||
|
Returns (dict): ID of the newly created config
|
||||||
|
"""
|
||||||
|
if not isinstance(data, bytes):
|
||||||
|
data = data.encode('utf-8')
|
||||||
|
|
||||||
|
data = base64.b64encode(data)
|
||||||
|
if six.PY3:
|
||||||
|
data = data.decode('ascii')
|
||||||
|
body = {
|
||||||
|
'Data': data,
|
||||||
|
'Name': name,
|
||||||
|
'Labels': labels
|
||||||
|
}
|
||||||
|
|
||||||
|
url = self._url('/configs/create')
|
||||||
|
return self._result(
|
||||||
|
self._post_json(url, data=body), True
|
||||||
|
)
|
||||||
|
|
||||||
|
@utils.minimum_version('1.25')
|
||||||
|
@utils.check_resource('id')
|
||||||
|
def inspect_config(self, id):
|
||||||
|
"""
|
||||||
|
Retrieve config metadata
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (string): Full ID of the config to remove
|
||||||
|
|
||||||
|
Returns (dict): A dictionary of metadata
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:py:class:`docker.errors.NotFound`
|
||||||
|
if no config with that ID exists
|
||||||
|
"""
|
||||||
|
url = self._url('/configs/{0}', id)
|
||||||
|
return self._result(self._get(url), True)
|
||||||
|
|
||||||
|
@utils.minimum_version('1.25')
|
||||||
|
@utils.check_resource('id')
|
||||||
|
def remove_config(self, id):
|
||||||
|
"""
|
||||||
|
Remove a config
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (string): Full ID of the config to remove
|
||||||
|
|
||||||
|
Returns (boolean): True if successful
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:py:class:`docker.errors.NotFound`
|
||||||
|
if no config with that ID exists
|
||||||
|
"""
|
||||||
|
url = self._url('/configs/{0}', id)
|
||||||
|
res = self._delete(url)
|
||||||
|
self._raise_for_status(res)
|
||||||
|
return True
|
||||||
|
|
||||||
|
@utils.minimum_version('1.25')
|
||||||
|
def configs(self, filters=None):
|
||||||
|
"""
|
||||||
|
List configs
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filters (dict): A map of filters to process on the configs
|
||||||
|
list. Available filters: ``names``
|
||||||
|
|
||||||
|
Returns (list): A list of configs
|
||||||
|
"""
|
||||||
|
url = self._url('/configs')
|
||||||
|
params = {}
|
||||||
|
if filters:
|
||||||
|
params['filters'] = utils.convert_filters(filters)
|
||||||
|
return self._result(self._get(url, params=params), True)
|
|
@ -1,5 +1,6 @@
|
||||||
from .api.client import APIClient
|
from .api.client import APIClient
|
||||||
from .constants import DEFAULT_TIMEOUT_SECONDS
|
from .constants import DEFAULT_TIMEOUT_SECONDS
|
||||||
|
from .models.configs import ConfigCollection
|
||||||
from .models.containers import ContainerCollection
|
from .models.containers import ContainerCollection
|
||||||
from .models.images import ImageCollection
|
from .models.images import ImageCollection
|
||||||
from .models.networks import NetworkCollection
|
from .models.networks import NetworkCollection
|
||||||
|
@ -80,6 +81,14 @@ class DockerClient(object):
|
||||||
**kwargs_from_env(**kwargs))
|
**kwargs_from_env(**kwargs))
|
||||||
|
|
||||||
# Resources
|
# Resources
|
||||||
|
@property
|
||||||
|
def configs(self):
|
||||||
|
"""
|
||||||
|
An object for managing configs on the server. See the
|
||||||
|
:doc:`configs documentation <configs>` for full details.
|
||||||
|
"""
|
||||||
|
return ConfigCollection(client=self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def containers(self):
|
def containers(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
from ..api import APIClient
|
||||||
|
from .resource import Model, Collection
|
||||||
|
|
||||||
|
|
||||||
|
class Config(Model):
|
||||||
|
"""A config."""
|
||||||
|
id_attribute = 'ID'
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<%s: '%s'>" % (self.__class__.__name__, self.name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self.attrs['Spec']['Name']
|
||||||
|
|
||||||
|
def remove(self):
|
||||||
|
"""
|
||||||
|
Remove this config.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:py:class:`docker.errors.APIError`
|
||||||
|
If config failed to remove.
|
||||||
|
"""
|
||||||
|
return self.client.api.remove_config(self.id)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigCollection(Collection):
|
||||||
|
"""Configs on the Docker server."""
|
||||||
|
model = Config
|
||||||
|
|
||||||
|
def create(self, **kwargs):
|
||||||
|
obj = self.client.api.create_config(**kwargs)
|
||||||
|
return self.prepare_model(obj)
|
||||||
|
create.__doc__ = APIClient.create_config.__doc__
|
||||||
|
|
||||||
|
def get(self, config_id):
|
||||||
|
"""
|
||||||
|
Get a config.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_id (str): Config ID.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(:py:class:`Config`): The config.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:py:class:`docker.errors.NotFound`
|
||||||
|
If the config does not exist.
|
||||||
|
:py:class:`docker.errors.APIError`
|
||||||
|
If the server returns an error.
|
||||||
|
"""
|
||||||
|
return self.prepare_model(self.client.api.inspect_config(config_id))
|
||||||
|
|
||||||
|
def list(self, **kwargs):
|
||||||
|
"""
|
||||||
|
List configs. Similar to the ``docker config ls`` command.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filters (dict): Server-side list filtering options.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(list of :py:class:`Config`): The configs.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:py:class:`docker.errors.APIError`
|
||||||
|
If the server returns an error.
|
||||||
|
"""
|
||||||
|
resp = self.client.api.configs(**kwargs)
|
||||||
|
return [self.prepare_model(obj) for obj in resp]
|
10
docs/api.rst
10
docs/api.rst
|
@ -9,6 +9,16 @@ It's possible to use :py:class:`APIClient` directly. Some basic things (e.g. run
|
||||||
|
|
||||||
.. autoclass:: docker.api.client.APIClient
|
.. autoclass:: docker.api.client.APIClient
|
||||||
|
|
||||||
|
Configs
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. py:module:: docker.api.config
|
||||||
|
|
||||||
|
.. rst-class:: hide-signature
|
||||||
|
.. autoclass:: ConfigApiMixin
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
|
||||||
Containers
|
Containers
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ Client reference
|
||||||
|
|
||||||
.. autoclass:: DockerClient()
|
.. autoclass:: DockerClient()
|
||||||
|
|
||||||
|
.. autoattribute:: configs
|
||||||
.. autoattribute:: containers
|
.. autoattribute:: containers
|
||||||
.. autoattribute:: images
|
.. autoattribute:: images
|
||||||
.. autoattribute:: networks
|
.. autoattribute:: networks
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
Configs
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. py:module:: docker.models.configs
|
||||||
|
|
||||||
|
Manage configs on the server.
|
||||||
|
|
||||||
|
Methods available on ``client.configs``:
|
||||||
|
|
||||||
|
.. rst-class:: hide-signature
|
||||||
|
.. py:class:: ConfigCollection
|
||||||
|
|
||||||
|
.. automethod:: create
|
||||||
|
.. automethod:: get
|
||||||
|
.. automethod:: list
|
||||||
|
|
||||||
|
|
||||||
|
Config objects
|
||||||
|
--------------
|
||||||
|
|
||||||
|
.. autoclass:: Config()
|
||||||
|
|
||||||
|
.. autoattribute:: id
|
||||||
|
.. autoattribute:: name
|
||||||
|
.. py:attribute:: attrs
|
||||||
|
|
||||||
|
The raw representation of this object from the server.
|
||||||
|
|
||||||
|
.. automethod:: reload
|
||||||
|
.. automethod:: remove
|
|
@ -80,6 +80,7 @@ That's just a taste of what you can do with the Docker SDK for Python. For more,
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
client
|
client
|
||||||
|
configs
|
||||||
containers
|
containers
|
||||||
images
|
images
|
||||||
networks
|
networks
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import docker
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from ..helpers import force_leave_swarm, requires_api_version
|
||||||
|
from .base import BaseAPIIntegrationTest
|
||||||
|
|
||||||
|
|
||||||
|
@requires_api_version('1.30')
|
||||||
|
class ConfigAPITest(BaseAPIIntegrationTest):
|
||||||
|
def setUp(self):
|
||||||
|
super(ConfigAPITest, self).setUp()
|
||||||
|
self.init_swarm()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(ConfigAPITest, self).tearDown()
|
||||||
|
force_leave_swarm(self.client)
|
||||||
|
|
||||||
|
def test_create_config(self):
|
||||||
|
config_id = self.client.create_config(
|
||||||
|
'favorite_character', 'sakuya izayoi'
|
||||||
|
)
|
||||||
|
self.tmp_configs.append(config_id)
|
||||||
|
assert 'ID' in config_id
|
||||||
|
data = self.client.inspect_config(config_id)
|
||||||
|
assert data['Spec']['Name'] == 'favorite_character'
|
||||||
|
|
||||||
|
def test_create_config_unicode_data(self):
|
||||||
|
config_id = self.client.create_config(
|
||||||
|
'favorite_character', u'いざよいさくや'
|
||||||
|
)
|
||||||
|
self.tmp_configs.append(config_id)
|
||||||
|
assert 'ID' in config_id
|
||||||
|
data = self.client.inspect_config(config_id)
|
||||||
|
assert data['Spec']['Name'] == 'favorite_character'
|
||||||
|
|
||||||
|
def test_inspect_config(self):
|
||||||
|
config_name = 'favorite_character'
|
||||||
|
config_id = self.client.create_config(
|
||||||
|
config_name, 'sakuya izayoi'
|
||||||
|
)
|
||||||
|
self.tmp_configs.append(config_id)
|
||||||
|
data = self.client.inspect_config(config_id)
|
||||||
|
assert data['Spec']['Name'] == config_name
|
||||||
|
assert 'ID' in data
|
||||||
|
assert 'Version' in data
|
||||||
|
|
||||||
|
def test_remove_config(self):
|
||||||
|
config_name = 'favorite_character'
|
||||||
|
config_id = self.client.create_config(
|
||||||
|
config_name, 'sakuya izayoi'
|
||||||
|
)
|
||||||
|
self.tmp_configs.append(config_id)
|
||||||
|
|
||||||
|
assert self.client.remove_config(config_id)
|
||||||
|
with pytest.raises(docker.errors.NotFound):
|
||||||
|
self.client.inspect_config(config_id)
|
||||||
|
|
||||||
|
def test_list_configs(self):
|
||||||
|
config_name = 'favorite_character'
|
||||||
|
config_id = self.client.create_config(
|
||||||
|
config_name, 'sakuya izayoi'
|
||||||
|
)
|
||||||
|
self.tmp_configs.append(config_id)
|
||||||
|
|
||||||
|
data = self.client.configs(filters={'name': ['favorite_character']})
|
||||||
|
assert len(data) == 1
|
||||||
|
assert data[0]['ID'] == config_id['ID']
|
|
@ -473,7 +473,7 @@ class ServiceTest(BaseAPIIntegrationTest):
|
||||||
secret_data = u'東方花映塚'
|
secret_data = u'東方花映塚'
|
||||||
secret_id = self.client.create_secret(secret_name, secret_data)
|
secret_id = self.client.create_secret(secret_name, secret_data)
|
||||||
self.tmp_secrets.append(secret_id)
|
self.tmp_secrets.append(secret_id)
|
||||||
secret_ref = docker.types.SecretReference(secret_id, secret_name)
|
secret_ref = docker.types.ConfigReference(secret_id, secret_name)
|
||||||
container_spec = docker.types.ContainerSpec(
|
container_spec = docker.types.ContainerSpec(
|
||||||
'busybox', ['sleep', '999'], secrets=[secret_ref]
|
'busybox', ['sleep', '999'], secrets=[secret_ref]
|
||||||
)
|
)
|
||||||
|
@ -481,8 +481,8 @@ class ServiceTest(BaseAPIIntegrationTest):
|
||||||
name = self.get_service_name()
|
name = self.get_service_name()
|
||||||
svc_id = self.client.create_service(task_tmpl, name=name)
|
svc_id = self.client.create_service(task_tmpl, name=name)
|
||||||
svc_info = self.client.inspect_service(svc_id)
|
svc_info = self.client.inspect_service(svc_id)
|
||||||
assert 'Secrets' in svc_info['Spec']['TaskTemplate']['ContainerSpec']
|
assert 'Configs' in svc_info['Spec']['TaskTemplate']['ContainerSpec']
|
||||||
secrets = svc_info['Spec']['TaskTemplate']['ContainerSpec']['Secrets']
|
secrets = svc_info['Spec']['TaskTemplate']['ContainerSpec']['Configs']
|
||||||
assert secrets[0] == secret_ref
|
assert secrets[0] == secret_ref
|
||||||
|
|
||||||
container = self.get_service_container(name)
|
container = self.get_service_container(name)
|
||||||
|
@ -493,3 +493,175 @@ class ServiceTest(BaseAPIIntegrationTest):
|
||||||
container_secret = self.client.exec_start(exec_id)
|
container_secret = self.client.exec_start(exec_id)
|
||||||
container_secret = container_secret.decode('utf-8')
|
container_secret = container_secret.decode('utf-8')
|
||||||
assert container_secret == secret_data
|
assert container_secret == secret_data
|
||||||
|
|
||||||
|
@requires_api_version('1.25')
|
||||||
|
def test_create_service_with_config(self):
|
||||||
|
config_name = 'favorite_touhou'
|
||||||
|
config_data = b'phantasmagoria of flower view'
|
||||||
|
config_id = self.client.create_config(config_name, config_data)
|
||||||
|
self.tmp_configs.append(config_id)
|
||||||
|
config_ref = docker.types.ConfigReference(config_id, config_name)
|
||||||
|
container_spec = docker.types.ContainerSpec(
|
||||||
|
'busybox', ['sleep', '999'], configs=[config_ref]
|
||||||
|
)
|
||||||
|
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 'Configs' in svc_info['Spec']['TaskTemplate']['ContainerSpec']
|
||||||
|
configs = svc_info['Spec']['TaskTemplate']['ContainerSpec']['Configs']
|
||||||
|
assert configs[0] == config_ref
|
||||||
|
|
||||||
|
container = self.get_service_container(name)
|
||||||
|
assert container is not None
|
||||||
|
exec_id = self.client.exec_create(
|
||||||
|
container, 'cat /run/configs/{0}'.format(config_name)
|
||||||
|
)
|
||||||
|
assert self.client.exec_start(exec_id) == config_data
|
||||||
|
|
||||||
|
@requires_api_version('1.25')
|
||||||
|
def test_create_service_with_unicode_config(self):
|
||||||
|
config_name = 'favorite_touhou'
|
||||||
|
config_data = u'東方花映塚'
|
||||||
|
config_id = self.client.create_config(config_name, config_data)
|
||||||
|
self.tmp_configs.append(config_id)
|
||||||
|
config_ref = docker.types.ConfigReference(config_id, config_name)
|
||||||
|
container_spec = docker.types.ContainerSpec(
|
||||||
|
'busybox', ['sleep', '999'], configs=[config_ref]
|
||||||
|
)
|
||||||
|
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 'Configs' in svc_info['Spec']['TaskTemplate']['ContainerSpec']
|
||||||
|
configs = svc_info['Spec']['TaskTemplate']['ContainerSpec']['Configs']
|
||||||
|
assert configs[0] == config_ref
|
||||||
|
|
||||||
|
container = self.get_service_container(name)
|
||||||
|
assert container is not None
|
||||||
|
exec_id = self.client.exec_create(
|
||||||
|
container, 'cat /run/configs/{0}'.format(config_name)
|
||||||
|
)
|
||||||
|
container_config = self.client.exec_start(exec_id)
|
||||||
|
container_config = container_config.decode('utf-8')
|
||||||
|
assert container_config == config_data
|
||||||
|
|
||||||
|
@requires_api_version('1.25')
|
||||||
|
def test_create_service_with_hosts(self):
|
||||||
|
container_spec = docker.types.ContainerSpec(
|
||||||
|
'busybox', ['sleep', '999'], hosts={
|
||||||
|
'foobar': '127.0.0.1',
|
||||||
|
'baz': '8.8.8.8',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
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 'Hosts' in svc_info['Spec']['TaskTemplate']['ContainerSpec']
|
||||||
|
hosts = svc_info['Spec']['TaskTemplate']['ContainerSpec']['Hosts']
|
||||||
|
assert len(hosts) == 2
|
||||||
|
assert 'foobar:127.0.0.1' in hosts
|
||||||
|
assert 'baz:8.8.8.8' in hosts
|
||||||
|
|
||||||
|
@requires_api_version('1.25')
|
||||||
|
def test_create_service_with_hostname(self):
|
||||||
|
container_spec = docker.types.ContainerSpec(
|
||||||
|
'busybox', ['sleep', '999'], hostname='foobar.baz.com'
|
||||||
|
)
|
||||||
|
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 'Hostname' in svc_info['Spec']['TaskTemplate']['ContainerSpec']
|
||||||
|
assert (
|
||||||
|
svc_info['Spec']['TaskTemplate']['ContainerSpec']['Hostname'] ==
|
||||||
|
'foobar.baz.com'
|
||||||
|
)
|
||||||
|
|
||||||
|
@requires_api_version('1.25')
|
||||||
|
def test_create_service_with_groups(self):
|
||||||
|
container_spec = docker.types.ContainerSpec(
|
||||||
|
'busybox', ['sleep', '999'], groups=['shrinemaidens', 'youkais']
|
||||||
|
)
|
||||||
|
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 'Groups' in svc_info['Spec']['TaskTemplate']['ContainerSpec']
|
||||||
|
groups = svc_info['Spec']['TaskTemplate']['ContainerSpec']['Groups']
|
||||||
|
assert len(groups) == 2
|
||||||
|
assert 'shrinemaidens' in groups
|
||||||
|
assert 'youkais' in groups
|
||||||
|
|
||||||
|
@requires_api_version('1.25')
|
||||||
|
def test_create_service_with_dns_config(self):
|
||||||
|
dns_config = docker.types.DNSConfig(
|
||||||
|
nameservers=['8.8.8.8', '8.8.4.4'],
|
||||||
|
search=['local'], options=['debug']
|
||||||
|
)
|
||||||
|
container_spec = docker.types.ContainerSpec(
|
||||||
|
BUSYBOX, ['sleep', '999'], dns_config=dns_config
|
||||||
|
)
|
||||||
|
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 'DNSConfig' in svc_info['Spec']['TaskTemplate']['ContainerSpec']
|
||||||
|
assert (
|
||||||
|
dns_config ==
|
||||||
|
svc_info['Spec']['TaskTemplate']['ContainerSpec']['DNSConfig']
|
||||||
|
)
|
||||||
|
|
||||||
|
@requires_api_version('1.25')
|
||||||
|
def test_create_service_with_healthcheck(self):
|
||||||
|
second = 1000000000
|
||||||
|
hc = docker.types.Healthcheck(
|
||||||
|
test='true', retries=3, timeout=1 * second,
|
||||||
|
start_period=3 * second, interval=second / 2,
|
||||||
|
)
|
||||||
|
container_spec = docker.types.ContainerSpec(
|
||||||
|
BUSYBOX, ['sleep', '999'], healthcheck=hc
|
||||||
|
)
|
||||||
|
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 (
|
||||||
|
'Healthcheck' in svc_info['Spec']['TaskTemplate']['ContainerSpec']
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
hc ==
|
||||||
|
svc_info['Spec']['TaskTemplate']['ContainerSpec']['Healthcheck']
|
||||||
|
)
|
||||||
|
|
||||||
|
@requires_api_version('1.28')
|
||||||
|
def test_create_service_with_readonly(self):
|
||||||
|
container_spec = docker.types.ContainerSpec(
|
||||||
|
BUSYBOX, ['sleep', '999'], read_only=True
|
||||||
|
)
|
||||||
|
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 (
|
||||||
|
'ReadOnly' in svc_info['Spec']['TaskTemplate']['ContainerSpec']
|
||||||
|
)
|
||||||
|
assert svc_info['Spec']['TaskTemplate']['ContainerSpec']['ReadOnly']
|
||||||
|
|
||||||
|
@requires_api_version('1.28')
|
||||||
|
def test_create_service_with_stop_signal(self):
|
||||||
|
container_spec = docker.types.ContainerSpec(
|
||||||
|
BUSYBOX, ['sleep', '999'], stop_signal='SIGINT'
|
||||||
|
)
|
||||||
|
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 (
|
||||||
|
'StopSignal' in svc_info['Spec']['TaskTemplate']['ContainerSpec']
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
svc_info['Spec']['TaskTemplate']['ContainerSpec']['StopSignal'] ==
|
||||||
|
'SIGINT'
|
||||||
|
)
|
||||||
|
|
|
@ -29,6 +29,7 @@ class BaseIntegrationTest(unittest.TestCase):
|
||||||
self.tmp_networks = []
|
self.tmp_networks = []
|
||||||
self.tmp_plugins = []
|
self.tmp_plugins = []
|
||||||
self.tmp_secrets = []
|
self.tmp_secrets = []
|
||||||
|
self.tmp_configs = []
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
client = docker.from_env(version=TEST_API_VERSION)
|
client = docker.from_env(version=TEST_API_VERSION)
|
||||||
|
@ -59,6 +60,12 @@ class BaseIntegrationTest(unittest.TestCase):
|
||||||
except docker.errors.APIError:
|
except docker.errors.APIError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
for config in self.tmp_configs:
|
||||||
|
try:
|
||||||
|
client.api.remove_config(config)
|
||||||
|
except docker.errors.APIError:
|
||||||
|
pass
|
||||||
|
|
||||||
for folder in self.tmp_folders:
|
for folder in self.tmp_folders:
|
||||||
shutil.rmtree(folder)
|
shutil.rmtree(folder)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue