From aa3c4f026d435af98391568c30998414fe2baedf Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Tue, 7 Nov 2017 15:19:07 -0800 Subject: [PATCH 1/2] Add unlock_swarm and get_unlock_key to APIClient Signed-off-by: Joffrey F --- docker/api/swarm.py | 50 ++++++++++++++++++++++++++++- tests/integration/api_swarm_test.py | 10 ++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/docker/api/swarm.py b/docker/api/swarm.py index 576fd79bf8..26ec75a913 100644 --- a/docker/api/swarm.py +++ b/docker/api/swarm.py @@ -1,7 +1,9 @@ import logging from six.moves import http_client +from .. import errors from .. import types from .. import utils + log = logging.getLogger(__name__) @@ -68,6 +70,16 @@ def create_swarm_spec(self, *args, **kwargs): kwargs['external_cas'] = [ext_ca] return types.SwarmSpec(self._version, *args, **kwargs) + @utils.minimum_version('1.24') + def get_unlock_key(self): + """ + Get the unlock key for this Swarm manager. + + Returns: + A ``dict`` containing an ``UnlockKey`` member + """ + return self._result(self._get(self._url('/swarm/unlockkey')), True) + @utils.minimum_version('1.24') def init_swarm(self, advertise_addr=None, listen_addr='0.0.0.0:2377', force_new_cluster=False, swarm_spec=None): @@ -270,10 +282,46 @@ def remove_node(self, node_id, force=False): self._raise_for_status(res) return True + @utils.minimum_version('1.24') + def unlock_swarm(self, key): + """ + Unlock a locked swarm. + + Args: + key (string): The unlock key as provided by + :py:meth:`get_unlock_key` + + Raises: + :py:class:`docker.errors.InvalidArgument` + If the key argument is in an incompatible format + + :py:class:`docker.errors.APIError` + If the server returns an error. + + Returns: + `True` if the request was successful. + + Example: + + >>> key = client.get_unlock_key() + >>> client.unlock_node(key) + + """ + if isinstance(key, dict): + if 'UnlockKey' not in key: + raise errors.InvalidArgument('Invalid unlock key format') + else: + key = {'UnlockKey': key} + + url = self._url('/swarm/unlock') + res = self._post_json(url, data=key) + self._raise_for_status(res) + return True + @utils.minimum_version('1.24') def update_node(self, node_id, version, node_spec=None): """ - Update the Node's configuration + Update the node's configuration Args: diff --git a/tests/integration/api_swarm_test.py b/tests/integration/api_swarm_test.py index 34b0879ce4..1a945c5f25 100644 --- a/tests/integration/api_swarm_test.py +++ b/tests/integration/api_swarm_test.py @@ -13,6 +13,13 @@ def setUp(self): def tearDown(self): super(SwarmTest, self).tearDown() + try: + unlock_key = self.client.get_unlock_key() + if unlock_key.get('UnlockKey'): + self.unlock_swarm(unlock_key) + except docker.errors.APIError: + pass + force_leave_swarm(self.client) @requires_api_version('1.24') @@ -70,6 +77,9 @@ def test_init_swarm_with_autolock_managers(self): swarm_info['Spec']['EncryptionConfig']['AutoLockManagers'] is True ) + unlock_key = self.get_unlock_key() + assert unlock_key.get('UnlockKey') + @requires_api_version('1.25') @pytest.mark.xfail( reason="This doesn't seem to be taken into account by the engine" From 3bd053a4b703156e5e1f66e3e1b4c72beada2b33 Mon Sep 17 00:00:00 2001 From: Joffrey F Date: Tue, 7 Nov 2017 15:29:53 -0800 Subject: [PATCH 2/2] Add unlock methods to Swarm model Signed-off-by: Joffrey F --- docker/models/swarm.py | 8 ++++++++ docs/swarm.rst | 2 ++ tests/integration/api_swarm_test.py | 11 ++++++----- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/docker/models/swarm.py b/docker/models/swarm.py index 5a253c57b5..7396e730d7 100644 --- a/docker/models/swarm.py +++ b/docker/models/swarm.py @@ -29,6 +29,10 @@ def version(self): """ return self.attrs.get('Version').get('Index') + def get_unlock_key(self): + return self.client.api.get_unlock_key() + get_unlock_key.__doc__ = APIClient.get_unlock_key.__doc__ + def init(self, advertise_addr=None, listen_addr='0.0.0.0:2377', force_new_cluster=False, **kwargs): """ @@ -128,6 +132,10 @@ def reload(self): """ self.attrs = self.client.api.inspect_swarm() + def unlock(self, key): + return self.client.api.unlock_swarm(key) + unlock.__doc__ = APIClient.unlock_swarm.__doc__ + def update(self, rotate_worker_token=False, rotate_manager_token=False, **kwargs): """ diff --git a/docs/swarm.rst b/docs/swarm.rst index 0c21bae1ab..cab9def70a 100644 --- a/docs/swarm.rst +++ b/docs/swarm.rst @@ -12,9 +12,11 @@ These methods are available on ``client.swarm``: .. rst-class:: hide-signature .. py:class:: Swarm + .. automethod:: get_unlock_key() .. automethod:: init() .. automethod:: join() .. automethod:: leave() + .. automethod:: unlock() .. automethod:: update() .. automethod:: reload() diff --git a/tests/integration/api_swarm_test.py b/tests/integration/api_swarm_test.py index 1a945c5f25..56b0129679 100644 --- a/tests/integration/api_swarm_test.py +++ b/tests/integration/api_swarm_test.py @@ -10,13 +10,13 @@ class SwarmTest(BaseAPIIntegrationTest): def setUp(self): super(SwarmTest, self).setUp() force_leave_swarm(self.client) + self._unlock_key = None def tearDown(self): super(SwarmTest, self).tearDown() try: - unlock_key = self.client.get_unlock_key() - if unlock_key.get('UnlockKey'): - self.unlock_swarm(unlock_key) + if self._unlock_key: + self.client.unlock_swarm(self._unlock_key) except docker.errors.APIError: pass @@ -71,14 +71,15 @@ def test_init_swarm_with_ca_config(self): def test_init_swarm_with_autolock_managers(self): spec = self.client.create_swarm_spec(autolock_managers=True) assert self.init_swarm(swarm_spec=spec) + # save unlock key for tearDown + self._unlock_key = self.client.get_unlock_key() swarm_info = self.client.inspect_swarm() assert ( swarm_info['Spec']['EncryptionConfig']['AutoLockManagers'] is True ) - unlock_key = self.get_unlock_key() - assert unlock_key.get('UnlockKey') + assert self._unlock_key.get('UnlockKey') @requires_api_version('1.25') @pytest.mark.xfail(