diff options
author | Jason Pleau <jason@jpleau.ca> | 2016-12-25 20:07:49 -0500 |
---|---|---|
committer | Jason Pleau <jason@jpleau.ca> | 2016-12-25 20:07:49 -0500 |
commit | 7c25f0634f5ff158a492426fa6238e4e6351008b (patch) | |
tree | a04d43d9d45261489645477bb25b1bbe0ccdcfbe /docker | |
parent | 5d8d4569fb7b4e869d9c88fa0f7f703c38f8131e (diff) |
New upstream version 1.10.6
Diffstat (limited to 'docker')
-rw-r--r-- | docker/client.py | 6 | ||||
-rw-r--r-- | docker/transport/__init__.py | 1 | ||||
-rw-r--r-- | docker/transport/npipeconn.py | 23 | ||||
-rw-r--r-- | docker/transport/npipesocket.py | 55 | ||||
-rw-r--r-- | docker/types/services.py | 2 | ||||
-rw-r--r-- | docker/utils/socket.py | 9 | ||||
-rw-r--r-- | docker/utils/utils.py | 11 | ||||
-rw-r--r-- | docker/version.py | 2 |
8 files changed, 85 insertions, 24 deletions
diff --git a/docker/client.py b/docker/client.py index 47ad09e..3fa19e0 100644 --- a/docker/client.py +++ b/docker/client.py @@ -86,7 +86,7 @@ class Client( tls.configure_client(self) elif tls: self._custom_adapter = ssladapter.SSLAdapter( - num_pools=num_pools + pool_connections=num_pools ) self.mount('https://', self._custom_adapter) self.base_url = base_url @@ -218,7 +218,9 @@ class Client( def _get_raw_response_socket(self, response): self._raise_for_status(response) - if six.PY3: + if self.base_url == "http+docker://localnpipe": + sock = response.raw._fp.fp.raw.sock + elif six.PY3: sock = response.raw._fp.fp.raw if self.base_url.startswith("https://"): sock = sock._sock diff --git a/docker/transport/__init__.py b/docker/transport/__init__.py index d647483..46dfdf8 100644 --- a/docker/transport/__init__.py +++ b/docker/transport/__init__.py @@ -2,5 +2,6 @@ from .unixconn import UnixAdapter try: from .npipeconn import NpipeAdapter + from .npipesocket import NpipeSocket except ImportError: pass
\ No newline at end of file diff --git a/docker/transport/npipeconn.py b/docker/transport/npipeconn.py index 917fa8b..017738e 100644 --- a/docker/transport/npipeconn.py +++ b/docker/transport/npipeconn.py @@ -14,7 +14,6 @@ try: except ImportError: import urllib3 - RecentlyUsedContainer = urllib3._collections.RecentlyUsedContainer @@ -46,6 +45,28 @@ class NpipeHTTPConnectionPool(urllib3.connectionpool.HTTPConnectionPool): self.npipe_path, self.timeout ) + # When re-using connections, urllib3 tries to call select() on our + # NpipeSocket instance, causing a crash. To circumvent this, we override + # _get_conn, where that check happens. + def _get_conn(self, timeout): + conn = None + try: + conn = self.pool.get(block=self.block, timeout=timeout) + + except AttributeError: # self.pool is None + raise urllib3.exceptions.ClosedPoolError(self, "Pool is closed.") + + except six.moves.queue.Empty: + if self.block: + raise urllib3.exceptions.EmptyPoolError( + self, + "Pool reached maximum size and no more " + "connections are allowed." + ) + pass # Oh well, we'll create a new connection then + + return conn or self._new_conn() + class NpipeAdapter(requests.adapters.HTTPAdapter): def __init__(self, base_url, timeout=60, diff --git a/docker/transport/npipesocket.py b/docker/transport/npipesocket.py index 9010ceb..3b1b644 100644 --- a/docker/transport/npipesocket.py +++ b/docker/transport/npipesocket.py @@ -1,12 +1,15 @@ import functools import io +import six import win32file import win32pipe +cERROR_PIPE_BUSY = 0xe7 cSECURITY_SQOS_PRESENT = 0x100000 cSECURITY_ANONYMOUS = 0 -cPIPE_READMODE_MESSAGE = 2 + +RETRY_WAIT_TIMEOUT = 10000 def check_closed(f): @@ -44,15 +47,27 @@ class NpipeSocket(object): @check_closed def connect(self, address): win32pipe.WaitNamedPipe(address, self._timeout) - handle = win32file.CreateFile( - address, - win32file.GENERIC_READ | win32file.GENERIC_WRITE, - 0, - None, - win32file.OPEN_EXISTING, - cSECURITY_ANONYMOUS | cSECURITY_SQOS_PRESENT, - 0 - ) + try: + handle = win32file.CreateFile( + address, + win32file.GENERIC_READ | win32file.GENERIC_WRITE, + 0, + None, + win32file.OPEN_EXISTING, + cSECURITY_ANONYMOUS | cSECURITY_SQOS_PRESENT, + 0 + ) + except win32pipe.error as e: + # See Remarks: + # https://msdn.microsoft.com/en-us/library/aa365800.aspx + if e.winerror == cERROR_PIPE_BUSY: + # Another program or thread has grabbed our pipe instance + # before we got to it. Wait for availability and attempt to + # connect again. + win32pipe.WaitNamedPipe(address, RETRY_WAIT_TIMEOUT) + return self.connect(address) + raise e + self.flags = win32pipe.GetNamedPipeInfo(handle)[0] self._handle = handle @@ -94,7 +109,7 @@ class NpipeSocket(object): if mode.strip('b') != 'r': raise NotImplementedError() rawio = NpipeFileIOBase(self) - if bufsize is None or bufsize < 0: + if bufsize is None or bufsize <= 0: bufsize = io.DEFAULT_BUFFER_SIZE return io.BufferedReader(rawio, buffer_size=bufsize) @@ -114,6 +129,9 @@ class NpipeSocket(object): @check_closed def recv_into(self, buf, nbytes=0): + if six.PY2: + return self._recv_into_py2(buf, nbytes) + readbuf = buf if not isinstance(buf, memoryview): readbuf = memoryview(buf) @@ -124,6 +142,12 @@ class NpipeSocket(object): ) return len(data) + def _recv_into_py2(self, buf, nbytes): + err, data = win32file.ReadFile(self._handle, nbytes or len(buf)) + n = len(data) + buf[:n] = data + return n + @check_closed def send(self, string, flags=0): err, nbytes = win32file.WriteFile(self._handle, string) @@ -145,13 +169,16 @@ class NpipeSocket(object): def settimeout(self, value): if value is None: - self._timeout = win32pipe.NMPWAIT_NOWAIT + # Blocking mode + self._timeout = win32pipe.NMPWAIT_WAIT_FOREVER elif not isinstance(value, (float, int)) or value < 0: raise ValueError('Timeout value out of range') elif value == 0: - self._timeout = win32pipe.NMPWAIT_USE_DEFAULT_WAIT + # Non-blocking mode + self._timeout = win32pipe.NMPWAIT_NO_WAIT else: - self._timeout = value + # Timeout mode - Value converted to milliseconds + self._timeout = value * 1000 def gettimeout(self): return self._timeout diff --git a/docker/types/services.py b/docker/types/services.py index 8488d6e..063779c 100644 --- a/docker/types/services.py +++ b/docker/types/services.py @@ -152,7 +152,7 @@ class UpdateConfig(dict): class RestartConditionTypesEnum(object): _values = ( 'none', - 'on_failure', + 'on-failure', 'any', ) NONE, ON_FAILURE, ANY = _values diff --git a/docker/utils/socket.py b/docker/utils/socket.py index ed34350..164b845 100644 --- a/docker/utils/socket.py +++ b/docker/utils/socket.py @@ -5,6 +5,11 @@ import struct import six +try: + from ..transport import NpipeSocket +except ImportError: + NpipeSocket = type(None) + class SocketError(Exception): pass @@ -14,10 +19,12 @@ def read(socket, n=4096): """ Reads at most n bytes from socket """ + recoverable_errors = (errno.EINTR, errno.EDEADLK, errno.EWOULDBLOCK) # wait for data to become available - select.select([socket], [], []) + if not isinstance(socket, NpipeSocket): + select.select([socket], [], []) try: if hasattr(socket, 'recv'): diff --git a/docker/utils/utils.py b/docker/utils/utils.py index d46f8fc..8d55b57 100644 --- a/docker/utils/utils.py +++ b/docker/utils/utils.py @@ -194,8 +194,8 @@ def match_path(path, pattern): if pattern: pattern = os.path.relpath(pattern) - pattern_components = pattern.split('/') - path_components = path.split('/')[:len(pattern_components)] + pattern_components = pattern.split(os.path.sep) + path_components = path.split(os.path.sep)[:len(pattern_components)] return fnmatch('/'.join(path_components), pattern) @@ -438,8 +438,8 @@ def parse_host(addr, is_win32=False, tls=False): "Bind address needs a port: {0}".format(addr)) if proto == "http+unix" or proto == 'npipe': - return "{0}://{1}".format(proto, host) - return "{0}://{1}:{2}{3}".format(proto, host, port, path) + return "{0}://{1}".format(proto, host).rstrip('/') + return "{0}://{1}:{2}{3}".format(proto, host, port, path).rstrip('/') def parse_devices(devices): @@ -986,6 +986,9 @@ def format_environment(environment): def format_env(key, value): if value is None: return key + if isinstance(value, six.binary_type): + value = value.decode('utf-8') + return u'{key}={value}'.format(key=key, value=value) return [format_env(*var) for var in six.iteritems(environment)] diff --git a/docker/version.py b/docker/version.py index 2bf8436..27d014c 100644 --- a/docker/version.py +++ b/docker/version.py @@ -1,2 +1,2 @@ -version = "1.10.3" +version = "1.10.6" version_info = tuple([int(d) for d in version.split("-")[0].split(".")]) |