mirror of
https://github.com/docker/compose.git
synced 2025-07-23 05:34:36 +02:00
parent
80991f1521
commit
5166b2c1a8
@ -15,4 +15,4 @@
|
|||||||
__title__ = 'docker-py'
|
__title__ = 'docker-py'
|
||||||
__version__ = '0.3.0'
|
__version__ = '0.3.0'
|
||||||
|
|
||||||
from .client import Client, APIError # flake8: noqa
|
from .client import Client # flake8: noqa
|
||||||
|
@ -20,6 +20,7 @@ import os
|
|||||||
from fig.packages import six
|
from fig.packages import six
|
||||||
|
|
||||||
from ..utils import utils
|
from ..utils import utils
|
||||||
|
from .. import errors
|
||||||
|
|
||||||
INDEX_URL = 'https://index.docker.io/v1/'
|
INDEX_URL = 'https://index.docker.io/v1/'
|
||||||
DOCKER_CONFIG_FILENAME = '.dockercfg'
|
DOCKER_CONFIG_FILENAME = '.dockercfg'
|
||||||
@ -45,18 +46,19 @@ def expand_registry_url(hostname):
|
|||||||
|
|
||||||
def resolve_repository_name(repo_name):
|
def resolve_repository_name(repo_name):
|
||||||
if '://' in repo_name:
|
if '://' in repo_name:
|
||||||
raise ValueError('Repository name cannot contain a '
|
raise errors.InvalidRepository(
|
||||||
'scheme ({0})'.format(repo_name))
|
'Repository name cannot contain a scheme ({0})'.format(repo_name))
|
||||||
parts = repo_name.split('/', 1)
|
parts = repo_name.split('/', 1)
|
||||||
if '.' not in parts[0] and ':' not in parts[0] and parts[0] != 'localhost':
|
if '.' not in parts[0] and ':' not in parts[0] and parts[0] != 'localhost':
|
||||||
# This is a docker index repo (ex: foo/bar or ubuntu)
|
# This is a docker index repo (ex: foo/bar or ubuntu)
|
||||||
return INDEX_URL, repo_name
|
return INDEX_URL, repo_name
|
||||||
if len(parts) < 2:
|
if len(parts) < 2:
|
||||||
raise ValueError('Invalid repository name ({0})'.format(repo_name))
|
raise errors.InvalidRepository(
|
||||||
|
'Invalid repository name ({0})'.format(repo_name))
|
||||||
|
|
||||||
if 'index.docker.io' in parts[0]:
|
if 'index.docker.io' in parts[0]:
|
||||||
raise ValueError('Invalid repository name,'
|
raise errors.InvalidRepository(
|
||||||
'try "{0}" instead'.format(parts[1]))
|
'Invalid repository name, try "{0}" instead'.format(parts[1]))
|
||||||
|
|
||||||
return expand_registry_url(parts[0]), parts[1]
|
return expand_registry_url(parts[0]), parts[1]
|
||||||
|
|
||||||
@ -147,7 +149,8 @@ def load_config(root=None):
|
|||||||
data.append(line.strip().split(' = ')[1])
|
data.append(line.strip().split(' = ')[1])
|
||||||
if len(data) < 2:
|
if len(data) < 2:
|
||||||
# Not enough data
|
# Not enough data
|
||||||
raise Exception('Invalid or empty configuration file!')
|
raise errors.InvalidConfigFile(
|
||||||
|
'Invalid or empty configuration file!')
|
||||||
|
|
||||||
username, password = decode_auth(data[0])
|
username, password = decode_auth(data[0])
|
||||||
conf[INDEX_URL] = {
|
conf[INDEX_URL] = {
|
||||||
|
@ -24,6 +24,7 @@ from fig.packages import six
|
|||||||
from .auth import auth
|
from .auth import auth
|
||||||
from .unixconn import unixconn
|
from .unixconn import unixconn
|
||||||
from .utils import utils
|
from .utils import utils
|
||||||
|
from . import errors
|
||||||
|
|
||||||
if not six.PY3:
|
if not six.PY3:
|
||||||
import websocket
|
import websocket
|
||||||
@ -33,41 +34,6 @@ DEFAULT_TIMEOUT_SECONDS = 60
|
|||||||
STREAM_HEADER_SIZE_BYTES = 8
|
STREAM_HEADER_SIZE_BYTES = 8
|
||||||
|
|
||||||
|
|
||||||
class APIError(requests.exceptions.HTTPError):
|
|
||||||
def __init__(self, message, response, explanation=None):
|
|
||||||
# requests 1.2 supports response as a keyword argument, but
|
|
||||||
# requests 1.1 doesn't
|
|
||||||
super(APIError, self).__init__(message)
|
|
||||||
self.response = response
|
|
||||||
|
|
||||||
self.explanation = explanation
|
|
||||||
|
|
||||||
if self.explanation is None and response.content:
|
|
||||||
self.explanation = response.content.strip()
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
message = super(APIError, self).__str__()
|
|
||||||
|
|
||||||
if self.is_client_error():
|
|
||||||
message = '%s Client Error: %s' % (
|
|
||||||
self.response.status_code, self.response.reason)
|
|
||||||
|
|
||||||
elif self.is_server_error():
|
|
||||||
message = '%s Server Error: %s' % (
|
|
||||||
self.response.status_code, self.response.reason)
|
|
||||||
|
|
||||||
if self.explanation:
|
|
||||||
message = '%s ("%s")' % (message, self.explanation)
|
|
||||||
|
|
||||||
return message
|
|
||||||
|
|
||||||
def is_client_error(self):
|
|
||||||
return 400 <= self.response.status_code < 500
|
|
||||||
|
|
||||||
def is_server_error(self):
|
|
||||||
return 500 <= self.response.status_code < 600
|
|
||||||
|
|
||||||
|
|
||||||
class Client(requests.Session):
|
class Client(requests.Session):
|
||||||
def __init__(self, base_url=None, version=DEFAULT_DOCKER_API_VERSION,
|
def __init__(self, base_url=None, version=DEFAULT_DOCKER_API_VERSION,
|
||||||
timeout=DEFAULT_TIMEOUT_SECONDS):
|
timeout=DEFAULT_TIMEOUT_SECONDS):
|
||||||
@ -112,7 +78,7 @@ class Client(requests.Session):
|
|||||||
try:
|
try:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
except requests.exceptions.HTTPError as e:
|
except requests.exceptions.HTTPError as e:
|
||||||
raise APIError(e, response, explanation=explanation)
|
raise errors.APIError(e, response, explanation=explanation)
|
||||||
|
|
||||||
def _result(self, response, json=False, binary=False):
|
def _result(self, response, json=False, binary=False):
|
||||||
assert not (json and binary)
|
assert not (json and binary)
|
||||||
@ -239,9 +205,23 @@ class Client(requests.Session):
|
|||||||
|
|
||||||
def _stream_helper(self, response):
|
def _stream_helper(self, response):
|
||||||
"""Generator for data coming from a chunked-encoded HTTP response."""
|
"""Generator for data coming from a chunked-encoded HTTP response."""
|
||||||
for line in response.iter_lines(chunk_size=32):
|
socket_fp = self._get_raw_response_socket(response)
|
||||||
if line:
|
socket_fp.setblocking(1)
|
||||||
yield line
|
socket = socket_fp.makefile()
|
||||||
|
while True:
|
||||||
|
# Because Docker introduced newlines at the end of chunks in v0.9,
|
||||||
|
# and only on some API endpoints, we have to cater for both cases.
|
||||||
|
size_line = socket.readline()
|
||||||
|
if size_line == '\r\n':
|
||||||
|
size_line = socket.readline()
|
||||||
|
|
||||||
|
size = int(size_line, 16)
|
||||||
|
if size <= 0:
|
||||||
|
break
|
||||||
|
data = socket.readline()
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
yield data
|
||||||
|
|
||||||
def _multiplexed_buffer_helper(self, response):
|
def _multiplexed_buffer_helper(self, response):
|
||||||
"""A generator of multiplexed data blocks read from a buffered
|
"""A generator of multiplexed data blocks read from a buffered
|
||||||
@ -341,7 +321,7 @@ class Client(requests.Session):
|
|||||||
nocache=False, rm=False, stream=False, timeout=None):
|
nocache=False, rm=False, stream=False, timeout=None):
|
||||||
remote = context = headers = None
|
remote = context = headers = None
|
||||||
if path is None and fileobj is None:
|
if path is None and fileobj is None:
|
||||||
raise Exception("Either path or fileobj needs to be provided.")
|
raise TypeError("Either path or fileobj needs to be provided.")
|
||||||
|
|
||||||
if fileobj is not None:
|
if fileobj is not None:
|
||||||
context = utils.mkbuildcontext(fileobj)
|
context = utils.mkbuildcontext(fileobj)
|
||||||
@ -714,8 +694,12 @@ class Client(requests.Session):
|
|||||||
}
|
}
|
||||||
if binds:
|
if binds:
|
||||||
bind_pairs = [
|
bind_pairs = [
|
||||||
'{0}:{1}'.format(host, dest) for host, dest in binds.items()
|
'%s:%s:%s' % (
|
||||||
|
h, d['bind'],
|
||||||
|
'ro' if 'ro' in d and d['ro'] else 'rw'
|
||||||
|
) for h, d in binds.items()
|
||||||
]
|
]
|
||||||
|
|
||||||
start_config['Binds'] = bind_pairs
|
start_config['Binds'] = bind_pairs
|
||||||
|
|
||||||
if volumes_from and not isinstance(volumes_from, six.string_types):
|
if volumes_from and not isinstance(volumes_from, six.string_types):
|
||||||
|
61
fig/packages/docker/errors.py
Normal file
61
fig/packages/docker/errors.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# Copyright 2014 dotCloud inc.
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class APIError(requests.exceptions.HTTPError):
|
||||||
|
def __init__(self, message, response, explanation=None):
|
||||||
|
# requests 1.2 supports response as a keyword argument, but
|
||||||
|
# requests 1.1 doesn't
|
||||||
|
super(APIError, self).__init__(message)
|
||||||
|
self.response = response
|
||||||
|
|
||||||
|
self.explanation = explanation
|
||||||
|
|
||||||
|
if self.explanation is None and response.content:
|
||||||
|
self.explanation = response.content.strip()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
message = super(APIError, self).__str__()
|
||||||
|
|
||||||
|
if self.is_client_error():
|
||||||
|
message = '%s Client Error: %s' % (
|
||||||
|
self.response.status_code, self.response.reason)
|
||||||
|
|
||||||
|
elif self.is_server_error():
|
||||||
|
message = '%s Server Error: %s' % (
|
||||||
|
self.response.status_code, self.response.reason)
|
||||||
|
|
||||||
|
if self.explanation:
|
||||||
|
message = '%s ("%s")' % (message, self.explanation)
|
||||||
|
|
||||||
|
return message
|
||||||
|
|
||||||
|
def is_client_error(self):
|
||||||
|
return 400 <= self.response.status_code < 500
|
||||||
|
|
||||||
|
def is_server_error(self):
|
||||||
|
return 500 <= self.response.status_code < 600
|
||||||
|
|
||||||
|
|
||||||
|
class DockerException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidRepository(DockerException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidConfigFile(DockerException):
|
||||||
|
pass
|
Loading…
x
Reference in New Issue
Block a user