Date: Tuesday, April 19, 2016 @ 15:46:00 Author: fyan Revision: 171400
upgpkg: python-docker-py 1.8.0-1 Added: python-docker-py/trunk/backports.patch Modified: python-docker-py/trunk/PKGBUILD -----------------+ PKGBUILD | 33 +++++--- backports.patch | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+), 13 deletions(-) Modified: PKGBUILD =================================================================== --- PKGBUILD 2016-04-19 13:39:51 UTC (rev 171399) +++ PKGBUILD 2016-04-19 13:46:00 UTC (rev 171400) @@ -5,7 +5,7 @@ # Contributor: Josh VanderLinden <a...@cloudlery.com> pkgname=(python-docker-py python2-docker-py) -pkgver=1.7.2 +pkgver=1.8.0 pkgrel=1 pkgdesc="Python client for Docker." arch=('any') @@ -12,13 +12,20 @@ url="https://github.com/dotcloud/docker-py" license=("Apache") makedepends=('git' 'python-setuptools' 'python2-setuptools' 'python-requests' 'python2-requests' - 'python-six' 'python2-six' 'python-websocket-client' 'python2-websocket-client') + 'python-six' 'python2-six' 'python-websocket-client' 'python2-websocket-client' + 'python2-backports.ssl_match_hostname' 'python2-ipaddress') checkdepends=('python-pytest-cov' 'python2-pytest-cov' 'python-mock' 'python2-mock' 'docker' 'flake8' 'python2-flake8') -source=("git+https://github.com/docker/docker-py.git#tag=$pkgver") -sha256sums=('SKIP') +source=("git+https://github.com/docker/docker-py.git#tag=$pkgver" + backports.patch) +sha256sums=('SKIP' + '0bbf17354fa7e7089180577271f552ea08c6b262fd0ca732626085c0bde7a3e0') prepare() { + # Use match_hostname from backports.ssl_match_hostname, as the required py2-ipaddress module conflicts with python2-ipaddress + # https://github.com/docker/docker-py/pull/1029 + (cd docker-py; patch -p1 -i ../backports.patch) + sed -i 's/==.*$//' docker-py/{,test-}requirements.txt cp -a docker-py{,-py2} @@ -25,10 +32,10 @@ } build() { - cd "$srcdir/docker-py" + cd "$srcdir"/docker-py python setup.py build - cd "$srcdir/docker-py-py2" + cd "$srcdir"/docker-py-py2 python2 setup.py build } @@ -37,10 +44,10 @@ # docker daemon -H fd:// -p "$srcdir/docker.pid" cd "$srcdir/docker-py" - py.test tests/unit + py.test --cov=docker tests/unit cd "$srcdir/docker-py-py2" - py.test2 tests/unit + py.test2 --cov=docker tests/unit # kill $(<"$srcdir/docker.pid") } @@ -48,15 +55,15 @@ package_python-docker-py() { depends=('python-requests' 'python-six' 'python-websocket-client') - cd "$srcdir/docker-py" + cd docker-py python setup.py install -O1 --root="$pkgdir" - install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + install -Dm644 LICENSE "$pkgdir"/usr/share/licenses/$pkgname/LICENSE } package_python2-docker-py() { - depends=('python2-requests' 'python2-six' 'python2-websocket-client') + depends=('python2-requests' 'python2-six' 'python2-websocket-client' 'python2-backports.ssl_match_hostname' 'python2-ipaddress') - cd "$srcdir/docker-py-py2" + cd docker-py-py2 python2 setup.py install -O1 --root="$pkgdir" - install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + install -Dm644 LICENSE "$pkgdir"/usr/share/licenses/$pkgname/LICENSE } Added: backports.patch =================================================================== --- backports.patch (rev 0) +++ backports.patch 2016-04-19 13:46:00 UTC (rev 171400) @@ -0,0 +1,214 @@ +From 9da54931355c5ac7146e1f2b137a12cbbb190cfa Mon Sep 17 00:00:00 2001 +From: Felix Yan <felixonm...@archlinux.org> +Date: Fri, 8 Apr 2016 00:20:42 +0800 +Subject: [PATCH] Use backports.ssl_match_hostname + +The py2-ipaddress module unfortunately conflicts with the pypi:ipaddress module, which is in the dependency tree of widely used pyOpenSSL. I think it would be a good idea to use a well maintained backport of the Python 3.5 implementation of match_hostname() instead of duplicating the effort and maintain another. + +All tests are passing here. + +Signed-off-by: Felix Yan <felixonm...@archlinux.org> +--- + docker/ssladapter/ssl_match_hostname.py | 130 -------------------------------- + docker/ssladapter/ssladapter.py | 2 +- + requirements.txt | 2 +- + setup.py | 2 +- + tests/unit/ssladapter_test.py | 12 ++- + 5 files changed, 12 insertions(+), 136 deletions(-) + delete mode 100644 docker/ssladapter/ssl_match_hostname.py + +diff --git a/docker/ssladapter/ssl_match_hostname.py b/docker/ssladapter/ssl_match_hostname.py +deleted file mode 100644 +index 9de0c5f..0000000 +--- a/docker/ssladapter/ssl_match_hostname.py ++++ /dev/null +@@ -1,130 +0,0 @@ +-# Slightly modified version of match_hostname in python's ssl library +-# https://hg.python.org/cpython/file/tip/Lib/ssl.py +-# Changed to make code python 2.x compatible (unicode strings for ip_address +-# and 3.5-specific var assignment syntax) +- +-import ipaddress +-import re +- +-try: +- from ssl import CertificateError +-except ImportError: +- CertificateError = ValueError +- +-import six +- +- +-def _ipaddress_match(ipname, host_ip): +- """Exact matching of IP addresses. +- +- RFC 6125 explicitly doesn't define an algorithm for this +- (section 1.7.2 - "Out of Scope"). +- """ +- # OpenSSL may add a trailing newline to a subjectAltName's IP address +- ip = ipaddress.ip_address(six.text_type(ipname.rstrip())) +- return ip == host_ip +- +- +-def _dnsname_match(dn, hostname, max_wildcards=1): +- """Matching according to RFC 6125, section 6.4.3 +- +- http://tools.ietf.org/html/rfc6125#section-6.4.3 +- """ +- pats = [] +- if not dn: +- return False +- +- split_dn = dn.split(r'.') +- leftmost, remainder = split_dn[0], split_dn[1:] +- +- wildcards = leftmost.count('*') +- if wildcards > max_wildcards: +- # Issue #17980: avoid denials of service by refusing more +- # than one wildcard per fragment. A survey of established +- # policy among SSL implementations showed it to be a +- # reasonable choice. +- raise CertificateError( +- "too many wildcards in certificate DNS name: " + repr(dn)) +- +- # speed up common case w/o wildcards +- if not wildcards: +- return dn.lower() == hostname.lower() +- +- # RFC 6125, section 6.4.3, subitem 1. +- # The client SHOULD NOT attempt to match a presented identifier in which +- # the wildcard character comprises a label other than the left-most label. +- if leftmost == '*': +- # When '*' is a fragment by itself, it matches a non-empty dotless +- # fragment. +- pats.append('[^.]+') +- elif leftmost.startswith('xn--') or hostname.startswith('xn--'): +- # RFC 6125, section 6.4.3, subitem 3. +- # The client SHOULD NOT attempt to match a presented identifier +- # where the wildcard character is embedded within an A-label or +- # U-label of an internationalized domain name. +- pats.append(re.escape(leftmost)) +- else: +- # Otherwise, '*' matches any dotless string, e.g. www* +- pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) +- +- # add the remaining fragments, ignore any wildcards +- for frag in remainder: +- pats.append(re.escape(frag)) +- +- pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) +- return pat.match(hostname) +- +- +-def match_hostname(cert, hostname): +- """Verify that *cert* (in decoded format as returned by +- SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 +- rules are followed, but IP addresses are not accepted for *hostname*. +- +- CertificateError is raised on failure. On success, the function +- returns nothing. +- """ +- if not cert: +- raise ValueError("empty or no certificate, match_hostname needs a " +- "SSL socket or SSL context with either " +- "CERT_OPTIONAL or CERT_REQUIRED") +- try: +- host_ip = ipaddress.ip_address(six.text_type(hostname)) +- except ValueError: +- # Not an IP address (common case) +- host_ip = None +- dnsnames = [] +- san = cert.get('subjectAltName', ()) +- for key, value in san: +- if key == 'DNS': +- if host_ip is None and _dnsname_match(value, hostname): +- return +- dnsnames.append(value) +- elif key == 'IP Address': +- if host_ip is not None and _ipaddress_match(value, host_ip): +- return +- dnsnames.append(value) +- if not dnsnames: +- # The subject is only checked when there is no dNSName entry +- # in subjectAltName +- for sub in cert.get('subject', ()): +- for key, value in sub: +- # XXX according to RFC 2818, the most specific Common Name +- # must be used. +- if key == 'commonName': +- if _dnsname_match(value, hostname): +- return +- dnsnames.append(value) +- if len(dnsnames) > 1: +- raise CertificateError( +- "hostname %r doesn't match either of %s" +- % (hostname, ', '.join(map(repr, dnsnames)))) +- elif len(dnsnames) == 1: +- raise CertificateError( +- "hostname %r doesn't match %r" +- % (hostname, dnsnames[0]) +- ) +- else: +- raise CertificateError( +- "no appropriate commonName or " +- "subjectAltName fields were found" +- ) +diff --git a/docker/ssladapter/ssladapter.py b/docker/ssladapter/ssladapter.py +index 179510c..e17dfad 100644 +--- a/docker/ssladapter/ssladapter.py ++++ b/docker/ssladapter/ssladapter.py +@@ -18,7 +18,7 @@ + # Monkey-patching match_hostname with a version that supports + # IP-address checking. Not necessary for Python 3.5 and above + if sys.version_info[0] < 3 or sys.version_info[1] < 5: +- from .ssl_match_hostname import match_hostname ++ from backports.ssl_match_hostname import match_hostname + urllib3.connection.match_hostname = match_hostname + + +diff --git a/requirements.txt b/requirements.txt +index c340f17..8161e2b 100644 +--- a/requirements.txt ++++ b/requirements.txt +@@ -1,4 +1,4 @@ + requests==2.5.3 + six>=1.4.0 + websocket-client==0.32.0 +-py2-ipaddress==3.4.1 ; python_version < '3.2' +\ No newline at end of file ++backports.ssl_match_hostname>=3.5 ; python_version < '3.5' +\ No newline at end of file +diff --git a/setup.py b/setup.py +index 0329ba3..2be705d 100644 +--- a/setup.py ++++ b/setup.py +@@ -13,7 +13,7 @@ + ] + + extras_require = { +- ':python_version < "3"': 'py2-ipaddress >= 3.4.1', ++ ':python_version < "3.5"': 'backports.ssl_match_hostname >= 3.5', + } + + exec(open('docker/version.py').read()) +diff --git a/tests/unit/ssladapter_test.py b/tests/unit/ssladapter_test.py +index fa9c77a..2ad1cad 100644 +--- a/tests/unit/ssladapter_test.py ++++ b/tests/unit/ssladapter_test.py +@@ -1,7 +1,13 @@ + from docker.ssladapter import ssladapter +-from docker.ssladapter.ssl_match_hostname import ( +- match_hostname, CertificateError +-) ++ ++try: ++ from backports.ssl_match_hostname import ( ++ match_hostname, CertificateError ++ ) ++except ImportError: ++ from ssl import ( ++ match_hostname, CertificateError ++ ) + + try: + from ssl import OP_NO_SSLv3, OP_NO_SSLv2, OP_NO_TLSv1