diff --git a/dockercloud/__init__.py b/dockercloud/__init__.py index 8bb6aa5..4d8b5cf 100644 --- a/dockercloud/__init__.py +++ b/dockercloud/__init__.py @@ -23,6 +23,7 @@ from dockercloud.api.exceptions import ApiError, AuthError, ObjectNotFound, NonUniqueIdentifier from dockercloud.api.utils import Utils from dockercloud.api.events import Events +from dockercloud.api.swarm import Swarm from dockercloud.api.nodeaz import AZ __version__ = '1.0.9' diff --git a/dockercloud/api/http.py b/dockercloud/api/http.py index f1d0696..01f49b4 100644 --- a/dockercloud/api/http.py +++ b/dockercloud/api/http.py @@ -69,7 +69,7 @@ def send_request(method, path, inject_header=True, **kwargs): # Try to parse the response. try: json = response.json() - if response.headers and inject_header: + if response.headers and inject_header and response.headers.get("X-DockerCloud-Action-URI"): json["dockercloud_action_uri"] = response.headers.get("X-DockerCloud-Action-URI", "") except TypeError: raise ApiError("JSON Parse Error (%s %s). Response: %s" % (method, url, response.text)) diff --git a/dockercloud/api/swarm.py b/dockercloud/api/swarm.py new file mode 100644 index 0000000..34a0ea1 --- /dev/null +++ b/dockercloud/api/swarm.py @@ -0,0 +1,8 @@ +from __future__ import absolute_import + +from .base import Mutable + + +class Swarm(Mutable): + subsystem = "infra" + endpoint = "/swarm" diff --git a/dockercloud/api/utils.py b/dockercloud/api/utils.py index 20d5c68..64a0c03 100644 --- a/dockercloud/api/utils.py +++ b/dockercloud/api/utils.py @@ -9,6 +9,7 @@ from .nodecluster import NodeCluster from .service import Service from .stack import Stack +from .swarm import Swarm def is_uuid4(identifier): @@ -191,3 +192,30 @@ def fetch_remote_action(identifier, raise_exceptions=True): if not raise_exceptions: return e raise e + + @staticmethod + def fetch_remote_swarm(identifier, raise_exceptions=True): + try: + terms = identifier.split("/", 1) + if len(terms) == 1: + namespace = "" + name = identifier + else: + namespace = terms[0] + name = terms[1] + try: + return Swarm.fetch(name, namespace=namespace) + except Exception: + objects_same_identifier = Swarm.list(name__startswith=name, namespace=namespace) + if len(objects_same_identifier) == 1: + swarm_id = objects_same_identifier[0].swarm_id + return Swarm.fetch(swarm_id, namespace=namespace) + elif len(objects_same_identifier) == 0: + raise ObjectNotFound("Cannot find a swarm cluster with name '%s'" % identifier) + raise NonUniqueIdentifier( + "More than one action has the same identifier, please use swarm id") + + except (NonUniqueIdentifier, ObjectNotFound) as e: + if not raise_exceptions: + return e + raise e diff --git a/tests/test_utils.py b/tests/test_utils.py index ca9140f..d4e8ee8 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -222,3 +222,34 @@ def test_fetch_remote_nodecluster(self, mock_fetch, mock_list): mock_list.side_effect = [ApiError, ApiError] self.assertRaises(ApiError, dockercloud.Utils.fetch_remote_nodecluster, 'uuid_or_name', True) self.assertRaises(ApiError, dockercloud.Utils.fetch_remote_nodecluster, 'uuid_or_name', False) + + @mock.patch('dockercloud.Swarm.list') + @mock.patch('dockercloud.Swarm.fetch') + def test_fetch_remote_swarm(self, mock_fetch, mock_list): + # test the identifier w/ and w/o namespace + mock_fetch.return_value = 'returned' + self.assertEqual(dockercloud.Utils.fetch_remote_swarm('abc', True), 'returned') + mock_fetch.assert_called_with("abc", namespace="") + + self.assertEqual(dockercloud.Utils.fetch_remote_swarm('tifayuki/abc', True), 'returned') + mock_fetch.assert_called_with("abc", namespace="tifayuki") + + self.assertEqual(dockercloud.Utils.fetch_remote_swarm('tifayuki/abc/xyz', True), 'returned') + mock_fetch.assert_called_with("abc/xyz", namespace="tifayuki") + + # test no swarm found + mock_fetch.side_effect = ObjectNotFound + self.assertRaises(ObjectNotFound, dockercloud.Utils.fetch_remote_swarm, 'swarm_id', True) + self.assertIsInstance(dockercloud.Utils.fetch_remote_swarm('swarm_id', False), ObjectNotFound) + + # test multi-swarm found + mock_fetch.return_value = 'returned' + mock_list.side_effect = [['swarm1', 'swarm2'], []] + self.assertRaises(NonUniqueIdentifier, dockercloud.Utils.fetch_remote_swarm, 'swarm_id', True) + mock_list.side_effect = [['swarm1', 'swarm2'], []] + self.assertIsInstance(dockercloud.Utils.fetch_remote_swarm('swarm_id', False), NonUniqueIdentifier) + + # test api error + mock_list.side_effect = [ApiError, ApiError] + self.assertRaises(ApiError, dockercloud.Utils.fetch_remote_swarm, 'swarm_id', True) + self.assertRaises(ApiError, dockercloud.Utils.fetch_remote_swarm, 'swarm_id', False)