Skip to content

Commit 5166b2c

Browse files
committed
Update docker-py
Using commit: aanand/docker-py@b31bb4d
1 parent 80991f1 commit 5166b2c

4 files changed

Lines changed: 96 additions & 48 deletions

File tree

fig/packages/docker/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
__title__ = 'docker-py'
1616
__version__ = '0.3.0'
1717

18-
from .client import Client, APIError # flake8: noqa
18+
from .client import Client # flake8: noqa

fig/packages/docker/auth/auth.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from fig.packages import six
2121

2222
from ..utils import utils
23+
from .. import errors
2324

2425
INDEX_URL = 'https://index.docker.io/v1/'
2526
DOCKER_CONFIG_FILENAME = '.dockercfg'
@@ -45,18 +46,19 @@ def expand_registry_url(hostname):
4546

4647
def resolve_repository_name(repo_name):
4748
if '://' in repo_name:
48-
raise ValueError('Repository name cannot contain a '
49-
'scheme ({0})'.format(repo_name))
49+
raise errors.InvalidRepository(
50+
'Repository name cannot contain a scheme ({0})'.format(repo_name))
5051
parts = repo_name.split('/', 1)
5152
if '.' not in parts[0] and ':' not in parts[0] and parts[0] != 'localhost':
5253
# This is a docker index repo (ex: foo/bar or ubuntu)
5354
return INDEX_URL, repo_name
5455
if len(parts) < 2:
55-
raise ValueError('Invalid repository name ({0})'.format(repo_name))
56+
raise errors.InvalidRepository(
57+
'Invalid repository name ({0})'.format(repo_name))
5658

5759
if 'index.docker.io' in parts[0]:
58-
raise ValueError('Invalid repository name,'
59-
'try "{0}" instead'.format(parts[1]))
60+
raise errors.InvalidRepository(
61+
'Invalid repository name, try "{0}" instead'.format(parts[1]))
6062

6163
return expand_registry_url(parts[0]), parts[1]
6264

@@ -147,7 +149,8 @@ def load_config(root=None):
147149
data.append(line.strip().split(' = ')[1])
148150
if len(data) < 2:
149151
# Not enough data
150-
raise Exception('Invalid or empty configuration file!')
152+
raise errors.InvalidConfigFile(
153+
'Invalid or empty configuration file!')
151154

152155
username, password = decode_auth(data[0])
153156
conf[INDEX_URL] = {

fig/packages/docker/client.py

Lines changed: 25 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from .auth import auth
2525
from .unixconn import unixconn
2626
from .utils import utils
27+
from . import errors
2728

2829
if not six.PY3:
2930
import websocket
@@ -33,41 +34,6 @@
3334
STREAM_HEADER_SIZE_BYTES = 8
3435

3536

36-
class APIError(requests.exceptions.HTTPError):
37-
def __init__(self, message, response, explanation=None):
38-
# requests 1.2 supports response as a keyword argument, but
39-
# requests 1.1 doesn't
40-
super(APIError, self).__init__(message)
41-
self.response = response
42-
43-
self.explanation = explanation
44-
45-
if self.explanation is None and response.content:
46-
self.explanation = response.content.strip()
47-
48-
def __str__(self):
49-
message = super(APIError, self).__str__()
50-
51-
if self.is_client_error():
52-
message = '%s Client Error: %s' % (
53-
self.response.status_code, self.response.reason)
54-
55-
elif self.is_server_error():
56-
message = '%s Server Error: %s' % (
57-
self.response.status_code, self.response.reason)
58-
59-
if self.explanation:
60-
message = '%s ("%s")' % (message, self.explanation)
61-
62-
return message
63-
64-
def is_client_error(self):
65-
return 400 <= self.response.status_code < 500
66-
67-
def is_server_error(self):
68-
return 500 <= self.response.status_code < 600
69-
70-
7137
class Client(requests.Session):
7238
def __init__(self, base_url=None, version=DEFAULT_DOCKER_API_VERSION,
7339
timeout=DEFAULT_TIMEOUT_SECONDS):
@@ -112,7 +78,7 @@ def _raise_for_status(self, response, explanation=None):
11278
try:
11379
response.raise_for_status()
11480
except requests.exceptions.HTTPError as e:
115-
raise APIError(e, response, explanation=explanation)
81+
raise errors.APIError(e, response, explanation=explanation)
11682

11783
def _result(self, response, json=False, binary=False):
11884
assert not (json and binary)
@@ -239,9 +205,23 @@ def _get_raw_response_socket(self, response):
239205

240206
def _stream_helper(self, response):
241207
"""Generator for data coming from a chunked-encoded HTTP response."""
242-
for line in response.iter_lines(chunk_size=32):
243-
if line:
244-
yield line
208+
socket_fp = self._get_raw_response_socket(response)
209+
socket_fp.setblocking(1)
210+
socket = socket_fp.makefile()
211+
while True:
212+
# Because Docker introduced newlines at the end of chunks in v0.9,
213+
# and only on some API endpoints, we have to cater for both cases.
214+
size_line = socket.readline()
215+
if size_line == '\r\n':
216+
size_line = socket.readline()
217+
218+
size = int(size_line, 16)
219+
if size <= 0:
220+
break
221+
data = socket.readline()
222+
if not data:
223+
break
224+
yield data
245225

246226
def _multiplexed_buffer_helper(self, response):
247227
"""A generator of multiplexed data blocks read from a buffered
@@ -341,7 +321,7 @@ def build(self, path=None, tag=None, quiet=False, fileobj=None,
341321
nocache=False, rm=False, stream=False, timeout=None):
342322
remote = context = headers = None
343323
if path is None and fileobj is None:
344-
raise Exception("Either path or fileobj needs to be provided.")
324+
raise TypeError("Either path or fileobj needs to be provided.")
345325

346326
if fileobj is not None:
347327
context = utils.mkbuildcontext(fileobj)
@@ -714,8 +694,12 @@ def start(self, container, binds=None, volumes_from=None, port_bindings=None,
714694
}
715695
if binds:
716696
bind_pairs = [
717-
'{0}:{1}'.format(host, dest) for host, dest in binds.items()
697+
'%s:%s:%s' % (
698+
h, d['bind'],
699+
'ro' if 'ro' in d and d['ro'] else 'rw'
700+
) for h, d in binds.items()
718701
]
702+
719703
start_config['Binds'] = bind_pairs
720704

721705
if volumes_from and not isinstance(volumes_from, six.string_types):

fig/packages/docker/errors.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Copyright 2014 dotCloud inc.
2+
# Licensed under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS,
10+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
# See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
14+
import requests
15+
16+
17+
class APIError(requests.exceptions.HTTPError):
18+
def __init__(self, message, response, explanation=None):
19+
# requests 1.2 supports response as a keyword argument, but
20+
# requests 1.1 doesn't
21+
super(APIError, self).__init__(message)
22+
self.response = response
23+
24+
self.explanation = explanation
25+
26+
if self.explanation is None and response.content:
27+
self.explanation = response.content.strip()
28+
29+
def __str__(self):
30+
message = super(APIError, self).__str__()
31+
32+
if self.is_client_error():
33+
message = '%s Client Error: %s' % (
34+
self.response.status_code, self.response.reason)
35+
36+
elif self.is_server_error():
37+
message = '%s Server Error: %s' % (
38+
self.response.status_code, self.response.reason)
39+
40+
if self.explanation:
41+
message = '%s ("%s")' % (message, self.explanation)
42+
43+
return message
44+
45+
def is_client_error(self):
46+
return 400 <= self.response.status_code < 500
47+
48+
def is_server_error(self):
49+
return 500 <= self.response.status_code < 600
50+
51+
52+
class DockerException(Exception):
53+
pass
54+
55+
56+
class InvalidRepository(DockerException):
57+
pass
58+
59+
60+
class InvalidConfigFile(DockerException):
61+
pass

0 commit comments

Comments
 (0)