Compare commits

..

No commits in common. "master" and "v1.0.5" have entirely different histories.

11 changed files with 21 additions and 86 deletions

View file

@ -1,7 +1,5 @@
FROM python:2.7.11-alpine FROM python:2.7.11-alpine
RUN apk update && apk add ca-certificates
ADD . /sdk ADD . /sdk
WORKDIR sdk WORKDIR sdk
RUN python setup.py install RUN python setup.py install

View file

@ -29,19 +29,6 @@ The authentication can be configured in the following ways:
export DOCKERCLOUD_USER=username export DOCKERCLOUD_USER=username
export DOCKERCLOUD_APIKEY=apikey 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
Errors in the HTTP API will be returned with status codes in the 4xx and 5xx ranges. Errors in the HTTP API will be returned with status codes in the 4xx and 5xx ranges.

View file

@ -25,7 +25,7 @@ from dockercloud.api.utils import Utils
from dockercloud.api.events import Events from dockercloud.api.events import Events
from dockercloud.api.nodeaz import AZ from dockercloud.api.nodeaz import AZ
__version__ = '1.0.9' __version__ = '1.0.5'
dockercloud_auth = os.environ.get('DOCKERCLOUD_AUTH') dockercloud_auth = os.environ.get('DOCKERCLOUD_AUTH')
basic_auth = auth.load_from_file("~/.docker/config.json") basic_auth = auth.load_from_file("~/.docker/config.json")
@ -38,8 +38,6 @@ 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/' 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/' stream_host = os.environ.get("DOCKERCLOUD_STREAM_HOST") or 'wss://ws.cloud.docker.com/'
namespace = os.environ.get('DOCKERCLOUD_NAMESPACE')
user_agent = None user_agent = None
logging.basicConfig() logging.basicConfig()

View file

@ -6,7 +6,6 @@ from .base import Immutable, StreamingLog
class Action(Immutable): class Action(Immutable):
subsystem = 'audit' subsystem = 'audit'
endpoint = "/action" endpoint = "/action"
namespaced = False
@classmethod @classmethod
def _pk_key(cls): def _pk_key(cls):

View file

@ -3,14 +3,12 @@ from __future__ import absolute_import
import base64 import base64
import json import json
import os import os
import subprocess
from requests.auth import HTTPBasicAuth from requests.auth import HTTPBasicAuth
import dockercloud import dockercloud
from .http import send_request from .http import send_request
HUB_INDEX = "https://index.docker.io/v1/"
def authenticate(username, password): def authenticate(username, password):
verify_credential(username, password) verify_credential(username, password)
@ -45,29 +43,11 @@ def load_from_file(f="~/.docker/config.json"):
try: try:
with open(os.path.expanduser(f)) as config_file: with open(os.path.expanduser(f)) as config_file:
data = json.load(config_file) data = json.load(config_file)
except:
return data.get("auths", {}).get("https://index.docker.io/v1/", {}).get("auth", None)
except Exception:
return None return None
creds_store = data.get("credsStore", None)
if creds_store:
try:
cmd = "docker-credential-" + creds_store
p = subprocess.Popen([cmd, 'get'], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT)
out = p.communicate(input=HUB_INDEX)[0]
except:
raise dockercloud.AuthError('error getting credentials - err: exec: "%s": executable file not found in $PATH, out: ``' % cmd)
try:
credential = json.loads(out)
username = credential.get("Username")
password = credential.get("Secret")
return base64.b64encode("%s:%s" % (username, password))
except:
return None
else:
return data.get("auths", {}).get(HUB_INDEX, {}).get("auth", None)
def get_auth_header(): def get_auth_header():
try: try:

View file

@ -22,7 +22,6 @@ class BasicObject(object):
class Restful(BasicObject): class Restful(BasicObject):
_detail_uri = None _detail_uri = None
namespaced = True
def __init__(self, **kwargs): def __init__(self, **kwargs):
"""Simply reflect all the values in kwargs""" """Simply reflect all the values in kwargs"""
@ -59,10 +58,6 @@ class Restful(BasicObject):
assert subsystem, "Subsystem not specified for %s" % self.__class__.__name__ assert subsystem, "Subsystem not specified for %s" % self.__class__.__name__
for k, v in list(dict.items()): for k, v in list(dict.items()):
setattr(self, k, v) setattr(self, k, v)
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._detail_uri = "/".join(["api", subsystem, self._api_version, endpoint.strip("/"), self.pk])
self.__setchanges__([]) self.__setchanges__([])
@ -131,9 +126,6 @@ class Immutable(Restful):
subsystem = getattr(cls, 'subsystem', None) subsystem = getattr(cls, 'subsystem', None)
assert endpoint, "Endpoint not specified for %s" % cls.__name__ assert endpoint, "Endpoint not specified for %s" % cls.__name__
assert subsystem, "Subsystem not specified for %s" % cls.__name__ assert subsystem, "Subsystem not specified for %s" % cls.__name__
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]) detail_uri = "/".join(["api", subsystem, cls._api_version, endpoint.strip("/"), pk])
json = send_request('GET', detail_uri) json = send_request('GET', detail_uri)
if json: if json:
@ -149,9 +141,6 @@ class Immutable(Restful):
assert endpoint, "Endpoint not specified for %s" % cls.__name__ assert endpoint, "Endpoint not specified for %s" % cls.__name__
assert subsystem, "Subsystem not specified for %s" % cls.__name__ assert subsystem, "Subsystem not specified for %s" % cls.__name__
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("/")]) detail_uri = "/".join(["api", subsystem, cls._api_version, endpoint.strip("/")])
objects = [] objects = []
while True: while True:
@ -230,9 +219,6 @@ class Mutable(Immutable):
# Figure out whether we should do a create or update # Figure out whether we should do a create or update
if not self._detail_uri: if not self._detail_uri:
action = "POST" action = "POST"
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("/")]) path = "/".join(["api", subsystem, self._api_version, endpoint.lstrip("/")])
else: else:
action = "PATCH" action = "PATCH"
@ -267,7 +253,11 @@ class Triggerable(BasicObject):
class StreamingAPI(BasicObject): class StreamingAPI(BasicObject):
def __init__(self, url): def __init__(self, url):
self._ws_init(url)
def _ws_init(self, url):
self.url = url self.url = url
user_agent = 'python-dockercloud/%s' % dockercloud.__version__ user_agent = 'python-dockercloud/%s' % dockercloud.__version__
if dockercloud.user_agent: if dockercloud.user_agent:
user_agent = "%s %s" % (dockercloud.user_agent, user_agent) user_agent = "%s %s" % (dockercloud.user_agent, user_agent)
@ -326,12 +316,7 @@ class StreamingLog(StreamingAPI):
endpoint = "%s/%s/logs/?follow=%s" % (resource, uuid, str(follow).lower()) endpoint = "%s/%s/logs/?follow=%s" % (resource, uuid, str(follow).lower())
if tail: if tail:
endpoint = "%s&tail=%d" % (endpoint, tail) endpoint = "%s&tail=%d" % (endpoint, tail)
if dockercloud.namespace: url = "/".join([dockercloud.stream_host.rstrip("/"), "api", subsystem, self._api_version, endpoint.lstrip("/")])
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) super(self.__class__, self).__init__(url)
@staticmethod @staticmethod
@ -350,10 +335,6 @@ class StreamingLog(StreamingAPI):
class Exec(StreamingAPI): class Exec(StreamingAPI):
def __init__(self, uuid, cmd='sh'): def __init__(self, uuid, cmd='sh'):
endpoint = "container/%s/exec/?command=%s" % (uuid, urllib.quote_plus(cmd)) endpoint = "container/%s/exec/?command=%s" % (uuid, urllib.quote_plus(cmd))
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("/")]) url = "/".join([dockercloud.stream_host.rstrip("/"), "api", "app", self._api_version, endpoint.lstrip("/")])
super(self.__class__, self).__init__(url) super(self.__class__, self).__init__(url)

View file

@ -15,12 +15,9 @@ logger = logging.getLogger("python-dockercloud")
class Events(StreamingAPI): class Events(StreamingAPI):
def __init__(self): def __init__(self):
endpoint = "events" endpoint = "events"
if dockercloud.namespace: url = "/".join([dockercloud.stream_host.rstrip("/"), "api", "audit", self._api_version, endpoint.lstrip("/")])
url = "/".join([dockercloud.stream_host.rstrip("/"), "api", "audit", self._api_version, self.invaid_auth_headers = set()
dockercloud.namespace, endpoint.lstrip("/")]) self.auth_error = ""
else:
url = "/".join([dockercloud.stream_host.rstrip("/"), "api", "audit", self._api_version,
endpoint.lstrip("/")])
super(self.__class__, self).__init__(url) super(self.__class__, self).__init__(url)
def _on_message(self, ws, message): def _on_message(self, ws, message):
@ -37,16 +34,15 @@ class Events(StreamingAPI):
def _on_error(self, ws, e): def _on_error(self, ws, e):
if isinstance(e, websocket._exceptions.WebSocketBadStatusException) and getattr(e, "status_code") == 401: if isinstance(e, websocket._exceptions.WebSocketBadStatusException) and getattr(e, "status_code") == 401:
self.auth_error = True self.auth_error = "Not Authorized"
self.invaid_auth_headers.add(str(dockercloud.auth.get_auth_header()))
super(self.__class__, self)._on_error(ws, e) super(self.__class__, self)._on_error(ws, e)
def run_forever(self, *args, **kwargs): def run_forever(self, *args, **kwargs):
while True: while True:
if self.auth_error: if str(dockercloud.auth.get_auth_header()) in self.invaid_auth_headers:
self.auth_error = False raise AuthError(self.auth_error)
raise AuthError("Not Authorized")
ws = websocket.WebSocketApp(self.url, header=self.header, ws = websocket.WebSocketApp(self.url, header=self.header,
on_open=self._on_open, on_open=self._on_open,
on_message=self._on_message, on_message=self._on_message,

View file

@ -6,7 +6,6 @@ from .base import Immutable
class AZ(Immutable): class AZ(Immutable):
subsystem = "infra" subsystem = "infra"
endpoint = "/az" endpoint = "/az"
namespaced = False
@classmethod @classmethod
def _pk_key(cls): def _pk_key(cls):

View file

@ -6,7 +6,6 @@ from .base import Immutable
class Provider(Immutable): class Provider(Immutable):
subsystem = "infra" subsystem = "infra"
endpoint = "/provider" endpoint = "/provider"
namespaced = False
@classmethod @classmethod
def _pk_key(cls): def _pk_key(cls):

View file

@ -6,7 +6,6 @@ from .base import Immutable
class Region(Immutable): class Region(Immutable):
subsystem = "infra" subsystem = "infra"
endpoint = "/region" endpoint = "/region"
namespaced = False
@classmethod @classmethod
def _pk_key(cls): def _pk_key(cls):

View file

@ -6,7 +6,6 @@ from .base import Immutable
class NodeType(Immutable): class NodeType(Immutable):
subsystem = "infra" subsystem = "infra"
endpoint = "/nodetype" endpoint = "/nodetype"
namespaced = False
@classmethod @classmethod
def _pk_key(cls): def _pk_key(cls):