diff options
author | Joffrey F <joffrey@docker.com> | 2016-08-01 13:59:52 -0700 |
---|---|---|
committer | Joffrey F <joffrey@docker.com> | 2016-08-03 16:58:26 -0700 |
commit | 07563cfe3f565b57b955455d2ce2b350ed34883b (patch) | |
tree | 7927de42b8fa276ff6ef275e94135e2b76fa9ab3 | |
parent | 9fdc8d476dfad2dad20ded9a1a471a225dc398aa (diff) | |
download | docker-py-07563cfe3f565b57b955455d2ce2b350ed34883b.tar.gz |
Update swarm methods to include newly added parameters
Rename swarm methods to be more explicit
Utility methods / types to create swarm spec objects
Integration tests
Signed-off-by: Joffrey F <joffrey@docker.com>
-rw-r--r-- | docker/api/swarm.py | 43 | ||||
-rw-r--r-- | docker/constants.py | 2 | ||||
-rw-r--r-- | docker/utils/__init__.py | 5 | ||||
-rw-r--r-- | docker/utils/types.py | 49 | ||||
-rw-r--r-- | tests/integration/swarm_test.py | 55 |
5 files changed, 136 insertions, 18 deletions
diff --git a/docker/api/swarm.py b/docker/api/swarm.py index be3eae4..bc2179c 100644 --- a/docker/api/swarm.py +++ b/docker/api/swarm.py @@ -4,38 +4,49 @@ log = logging.getLogger(__name__) class SwarmApiMixin(object): - @utils.minimum_version('1.24') - def swarm(self): - url = self._url('/swarm') - return self._result(self._get(url), True) + + def create_swarm_spec(self, *args, **kwargs): + return utils.SwarmSpec(*args, **kwargs) @utils.minimum_version('1.24') - def swarm_init(self, listen_addr, force_new_cluster=False, - swarm_opts=None): + def init_swarm(self, advertise_addr, listen_addr='0.0.0.0:2377', + force_new_cluster=False, swarm_spec=None): url = self._url('/swarm/init') - if swarm_opts is not None and not isinstance(swarm_opts, dict): - raise TypeError('swarm_opts must be a dictionary') + if swarm_spec is not None and not isinstance(swarm_spec, dict): + raise TypeError('swarm_spec must be a dictionary') data = { + 'AdvertiseAddr': advertise_addr, 'ListenAddr': listen_addr, 'ForceNewCluster': force_new_cluster, - 'Spec': swarm_opts + 'Spec': swarm_spec, } - return self._result(self._post_json(url, data=data), True) + response = self._post_json(url, data=data) + self._raise_for_status(response) + return True + + @utils.minimum_version('1.24') + def inspect_swarm(self): + url = self._url('/swarm') + return self._result(self._get(url), True) @utils.minimum_version('1.24') - def swarm_join(self, remote_address, listen_address=None, + def join_swarm(self, remote_addresses, listen_address=None, secret=None, ca_cert_hash=None, manager=False): data = { - "RemoteAddr": remote_address, + "RemoteAddrs": remote_addresses, "ListenAddr": listen_address, "Secret": secret, "CACertHash": ca_cert_hash, "Manager": manager } - url = self._url('/swarm/join', ) - return self._result(self._post_json(url, data=data), True) + url = self._url('/swarm/join') + response = self._post_json(url, data=data) + self._raise_for_status(response) + return True @utils.minimum_version('1.24') - def swarm_leave(self): + def leave_swarm(self, force=False): url = self._url('/swarm/leave') - return self._result(self._post(url)) + response = self._post(url, params={'force': force}) + self._raise_for_status(response) + return True diff --git a/docker/constants.py b/docker/constants.py index 904d50e..cf5a39a 100644 --- a/docker/constants.py +++ b/docker/constants.py @@ -1,7 +1,7 @@ import sys from .version import version -DEFAULT_DOCKER_API_VERSION = '1.22' +DEFAULT_DOCKER_API_VERSION = '1.24' DEFAULT_TIMEOUT_SECONDS = 60 STREAM_HEADER_SIZE_BYTES = 8 CONTAINER_LIMITS_KEYS = [ diff --git a/docker/utils/__init__.py b/docker/utils/__init__.py index 41df004..c02adea 100644 --- a/docker/utils/__init__.py +++ b/docker/utils/__init__.py @@ -8,5 +8,8 @@ from .utils import ( create_ipam_config, create_ipam_pool, parse_devices, normalize_links, ) -from .types import Ulimit, LogConfig +from .types import LogConfig, Ulimit +from .types import ( + SwarmAcceptancePolicy, SwarmExternalCA, SwarmSpec, +) from .decorators import check_resource, minimum_version, update_headers diff --git a/docker/utils/types.py b/docker/utils/types.py index ea9f06d..b970114 100644 --- a/docker/utils/types.py +++ b/docker/utils/types.py @@ -94,3 +94,52 @@ class Ulimit(DictType): @hard.setter def hard(self, value): self['Hard'] = value + + +class SwarmSpec(DictType): + def __init__(self, policies=None, 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): + if policies is not None: + self['AcceptancePolicy'] = {'Policies': policies} + 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 + } + + +class SwarmAcceptancePolicy(DictType): + def __init__(self, role, auto_accept=False, secret=None): + self['Role'] = role.upper() + self['Autoaccept'] = auto_accept + if secret is not None: + self['Secret'] = secret + + +class SwarmExternalCA(DictType): + def __init__(self, url, protocol=None, options=None): + self['URL'] = url + self['Protocol'] = protocol + self['Options'] = options diff --git a/tests/integration/swarm_test.py b/tests/integration/swarm_test.py new file mode 100644 index 0000000..734d470 --- /dev/null +++ b/tests/integration/swarm_test.py @@ -0,0 +1,55 @@ +import docker +import pytest + +from ..base import requires_api_version +from .. import helpers + + +BUSYBOX = helpers.BUSYBOX + + +class SwarmTest(helpers.BaseTestCase): + def setUp(self): + super(SwarmTest, self).setUp() + try: + self.client.leave_swarm(force=True) + except docker.errors.APIError: + pass + + def tearDown(self): + super(SwarmTest, self).tearDown() + try: + self.client.leave_swarm(force=True) + except docker.errors.APIError: + pass + + @requires_api_version('1.24') + def test_init_swarm_simple(self): + assert self.client.init_swarm('eth0') + + @requires_api_version('1.24') + def test_init_swarm_force_new_cluster(self): + pytest.skip('Test stalls the engine on 1.12') + + assert self.client.init_swarm('eth0') + version_1 = self.client.inspect_swarm()['Version']['Index'] + assert self.client.init_swarm('eth0', force_new_cluster=True) + version_2 = self.client.inspect_swarm()['Version']['Index'] + assert version_2 != version_1 + + @requires_api_version('1.24') + def test_init_already_in_cluster(self): + assert self.client.init_swarm('eth0') + with pytest.raises(docker.errors.APIError): + self.client.init_swarm('eth0') + + @requires_api_version('1.24') + def test_leave_swarm(self): + assert self.client.init_swarm('eth0') + with pytest.raises(docker.errors.APIError) as exc_info: + self.client.leave_swarm() + exc_info.value.response.status_code == 500 + assert self.client.leave_swarm(force=True) + with pytest.raises(docker.errors.APIError) as exc_info: + self.client.inspect_swarm() + exc_info.value.response.status_code == 406 |