diff --git a/README.md b/README.md index 5a82ce2..aea721e 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,19 @@ The authentication can be configured in the following ways: export DOCKERCLOUD_USER=username export DOCKERCLOUD_APIKEY=apikey +## Namespace + +To support teams and orgs, you can specify the namespace in the following ways: + +* Set it in the Python code: + + import dockercloud + dockercloud.namespace = "yourteam" + +* Set it in the environment variable: + + export DOCKERCLOUD_NAMESPACE=yourteam + ## Errors Errors in the HTTP API will be returned with status codes in the 4xx and 5xx ranges. diff --git a/dockercloud/__init__.py b/dockercloud/__init__.py index 1fd6d63..12bb373 100644 --- a/dockercloud/__init__.py +++ b/dockercloud/__init__.py @@ -25,7 +25,7 @@ from dockercloud.api.utils import Utils from dockercloud.api.events import Events from dockercloud.api.nodeaz import AZ -__version__ = '1.0.5' +__version__ = '1.0.6' dockercloud_auth = os.environ.get('DOCKERCLOUD_AUTH') basic_auth = auth.load_from_file("~/.docker/config.json") @@ -38,6 +38,8 @@ if os.environ.get('DOCKERCLOUD_USER') and os.environ.get('DOCKERCLOUD_APIKEY'): rest_host = os.environ.get("DOCKERCLOUD_REST_HOST") or 'https://cloud.docker.com/' stream_host = os.environ.get("DOCKERCLOUD_STREAM_HOST") or 'wss://ws.cloud.docker.com/' +namespace = os.environ.get('DOCKERCLOUD_NAMESPACE') + user_agent = None logging.basicConfig() diff --git a/dockercloud/api/base.py b/dockercloud/api/base.py index c6bf3d0..e9feeed 100644 --- a/dockercloud/api/base.py +++ b/dockercloud/api/base.py @@ -22,6 +22,7 @@ class BasicObject(object): class Restful(BasicObject): _detail_uri = None + namespaced = True def __init__(self, **kwargs): """Simply reflect all the values in kwargs""" @@ -58,7 +59,11 @@ class Restful(BasicObject): assert subsystem, "Subsystem not specified for %s" % self.__class__.__name__ for k, v in list(dict.items()): setattr(self, k, v) - self._detail_uri = "/".join(["api", subsystem, self._api_version, endpoint.strip("/"), self.pk]) + if self.namespaced and dockercloud.namespace: + self._detail_uri = "/".join(["api", subsystem, self._api_version, dockercloud.namespace, + endpoint.strip("/"), self.pk]) + else: + self._detail_uri = "/".join(["api", subsystem, self._api_version, endpoint.strip("/"), self.pk]) self.__setchanges__([]) @property @@ -126,7 +131,10 @@ class Immutable(Restful): subsystem = getattr(cls, 'subsystem', None) assert endpoint, "Endpoint not specified for %s" % cls.__name__ assert subsystem, "Subsystem not specified for %s" % cls.__name__ - detail_uri = "/".join(["api", subsystem, cls._api_version, endpoint.strip("/"), pk]) + if cls.namespaced and dockercloud.namespace: + detail_uri = "/".join(["api", subsystem, cls._api_version, dockercloud.namespace, endpoint.strip("/"), pk]) + else: + detail_uri = "/".join(["api", subsystem, cls._api_version, endpoint.strip("/"), pk]) json = send_request('GET', detail_uri) if json: instance = cls() @@ -141,7 +149,10 @@ class Immutable(Restful): assert endpoint, "Endpoint not specified for %s" % cls.__name__ assert subsystem, "Subsystem not specified for %s" % cls.__name__ - detail_uri = "/".join(["api", subsystem, cls._api_version, endpoint.strip("/")]) + if cls.namespaced and dockercloud.namespace: + detail_uri = "/".join(["api", subsystem, cls._api_version, dockercloud.namespace, endpoint.strip("/")]) + else: + detail_uri = "/".join(["api", subsystem, cls._api_version, endpoint.strip("/")]) objects = [] while True: if limit and len(objects) >= limit: @@ -219,7 +230,10 @@ class Mutable(Immutable): # Figure out whether we should do a create or update if not self._detail_uri: action = "POST" - path = "/".join(["api", subsystem, self._api_version, endpoint.lstrip("/")]) + if cls.namespaced and dockercloud.namespace: + path = "/".join(["api", subsystem, self._api_version, dockercloud.namespace, endpoint.lstrip("/")]) + else: + path = "/".join(["api", subsystem, self._api_version, endpoint.lstrip("/")]) else: action = "PATCH" path = self._detail_uri @@ -316,7 +330,12 @@ class StreamingLog(StreamingAPI): endpoint = "%s/%s/logs/?follow=%s" % (resource, uuid, str(follow).lower()) if tail: endpoint = "%s&tail=%d" % (endpoint, tail) - url = "/".join([dockercloud.stream_host.rstrip("/"), "api", subsystem, self._api_version, endpoint.lstrip("/")]) + if dockercloud.namespace: + url = "/".join([dockercloud.stream_host.rstrip("/"), "api", subsystem, self._api_version, + dockercloud.namespace, endpoint.lstrip("/")]) + else: + url = "/".join([dockercloud.stream_host.rstrip("/"), "api", subsystem, self._api_version, + endpoint.lstrip("/")]) super(self.__class__, self).__init__(url) @staticmethod @@ -335,7 +354,11 @@ class StreamingLog(StreamingAPI): class Exec(StreamingAPI): def __init__(self, uuid, cmd='sh'): endpoint = "container/%s/exec/?command=%s" % (uuid, urllib.quote_plus(cmd)) - url = "/".join([dockercloud.stream_host.rstrip("/"), "api", "app", self._api_version, endpoint.lstrip("/")]) + if dockercloud.namespace: + url = "/".join([dockercloud.stream_host.rstrip("/"), "api", "app", self._api_version, + dockercloud.namespace, endpoint.lstrip("/")]) + else: + url = "/".join([dockercloud.stream_host.rstrip("/"), "api", "app", self._api_version, endpoint.lstrip("/")]) super(self.__class__, self).__init__(url) @staticmethod diff --git a/dockercloud/api/events.py b/dockercloud/api/events.py index f84e4aa..06f989a 100644 --- a/dockercloud/api/events.py +++ b/dockercloud/api/events.py @@ -15,7 +15,12 @@ logger = logging.getLogger("python-dockercloud") class Events(StreamingAPI): def __init__(self): endpoint = "events" - url = "/".join([dockercloud.stream_host.rstrip("/"), "api", "audit", self._api_version, endpoint.lstrip("/")]) + if dockercloud.namespace: + url = "/".join([dockercloud.stream_host.rstrip("/"), "api", "audit", self._api_version, + dockercloud.namespace, endpoint.lstrip("/")]) + else: + url = "/".join([dockercloud.stream_host.rstrip("/"), "api", "audit", self._api_version, + endpoint.lstrip("/")]) self.invaid_auth_headers = set() self.auth_error = "" super(self.__class__, self).__init__(url) diff --git a/dockercloud/api/nodeaz.py b/dockercloud/api/nodeaz.py index abc4d5f..25a153b 100644 --- a/dockercloud/api/nodeaz.py +++ b/dockercloud/api/nodeaz.py @@ -6,6 +6,7 @@ from .base import Immutable class AZ(Immutable): subsystem = "infra" endpoint = "/az" + namespaced = False @classmethod def _pk_key(cls): diff --git a/dockercloud/api/nodeprovider.py b/dockercloud/api/nodeprovider.py index 3d31f06..168bc72 100644 --- a/dockercloud/api/nodeprovider.py +++ b/dockercloud/api/nodeprovider.py @@ -6,6 +6,7 @@ from .base import Immutable class Provider(Immutable): subsystem = "infra" endpoint = "/provider" + namespaced = False @classmethod def _pk_key(cls): diff --git a/dockercloud/api/noderegion.py b/dockercloud/api/noderegion.py index 4d10bed..9a9c97a 100644 --- a/dockercloud/api/noderegion.py +++ b/dockercloud/api/noderegion.py @@ -6,6 +6,7 @@ from .base import Immutable class Region(Immutable): subsystem = "infra" endpoint = "/region" + namespaced = False @classmethod def _pk_key(cls): diff --git a/dockercloud/api/nodetype.py b/dockercloud/api/nodetype.py index 5274aaf..7416d63 100644 --- a/dockercloud/api/nodetype.py +++ b/dockercloud/api/nodetype.py @@ -6,6 +6,7 @@ from .base import Immutable class NodeType(Immutable): subsystem = "infra" endpoint = "/nodetype" + namespaced = False @classmethod def _pk_key(cls):