diff options
-rw-r--r-- | Makefile | 20 | ||||
-rw-r--r-- | docker/client.py | 20 | ||||
-rw-r--r-- | pytest.ini | 2 | ||||
-rw-r--r-- | tests/Dockerfile-dind-certs | 20 | ||||
-rw-r--r-- | tests/integration_test.py | 73 |
5 files changed, 123 insertions, 12 deletions
@@ -11,7 +11,10 @@ build: build-py3: docker build -t docker-py3 -f Dockerfile-py3 . -test: flake8 unit-test unit-test-py3 integration-dind +build-dind-certs: + docker build -t dpy-dind-certs -f tests/Dockerfile-dind-certs . + +test: flake8 unit-test unit-test-py3 integration-dind integration-dind-ssl unit-test: build docker run docker-py py.test tests/test.py tests/utils_test.py @@ -26,10 +29,17 @@ integration-test-py3: build-py3 docker run -v /var/run/docker.sock:/var/run/docker.sock docker-py3 py.test tests/integration_test.py integration-dind: build build-py3 - docker run -d --name dpy-dind --privileged dockerswarm/dind:1.8.1 docker -d -H tcp://0.0.0.0:2375 - docker run --env="DOCKER_HOST=tcp://docker:2375" --link=dpy-dind:docker docker-py py.test tests/integration_test.py - docker run --env="DOCKER_HOST=tcp://docker:2375" --link=dpy-dind:docker docker-py3 py.test tests/integration_test.py + docker run -d --name dpy-dind --env="DOCKER_HOST=tcp://localhost:2375" --privileged dockerswarm/dind:1.8.1 docker -d -H tcp://0.0.0.0:2375 + docker run --volumes-from dpy-dind --env="DOCKER_HOST=tcp://docker:2375" --link=dpy-dind:docker docker-py py.test tests/integration_test.py + docker run --volumes-from dpy-dind --env="DOCKER_HOST=tcp://docker:2375" --link=dpy-dind:docker docker-py3 py.test tests/integration_test.py docker rm -vf dpy-dind +integration-dind-ssl: build-dind-certs build build-py3 + docker run -d --name dpy-dind-certs dpy-dind-certs + docker run -d --env="DOCKER_HOST=tcp://localhost:2375" --env="DOCKER_TLS_VERIFY=1" --env="DOCKER_CERT_PATH=/certs" --volumes-from dpy-dind-certs --name dpy-dind-ssl -v /tmp --privileged dockerswarm/dind:1.8.1 docker daemon --tlsverify --tlscacert=/certs/ca.pem --tlscert=/certs/server-cert.pem --tlskey=/certs/server-key.pem -H tcp://0.0.0.0:2375 + docker run --volumes-from dpy-dind-ssl --env="DOCKER_HOST=tcp://docker:2375" --env="DOCKER_TLS_VERIFY=1" --env="DOCKER_CERT_PATH=/certs" --link=dpy-dind-ssl:docker docker-py py.test tests/integration_test.py + docker run --volumes-from dpy-dind-ssl --env="DOCKER_HOST=tcp://docker:2375" --env="DOCKER_TLS_VERIFY=1" --env="DOCKER_CERT_PATH=/certs" --link=dpy-dind-ssl:docker docker-py3 py.test tests/integration_test.py + docker rm -vf dpy-dind-ssl dpy-dind-certs + flake8: build - docker run docker-py flake8 docker tests
\ No newline at end of file + docker run docker-py flake8 docker tests diff --git a/docker/client.py b/docker/client.py index d219472..d339f3b 100644 --- a/docker/client.py +++ b/docker/client.py @@ -188,6 +188,8 @@ class Client( self._raise_for_status(response) if six.PY3: sock = response.raw._fp.fp.raw + if self.base_url.startswith("https://"): + sock = sock._sock else: sock = response.raw._fp.fp._sock try: @@ -244,10 +246,7 @@ class Client( # Disable timeout on the underlying socket to prevent # Read timed out(s) for long running processes socket = self._get_raw_response_socket(response) - if six.PY3: - socket._sock.settimeout(None) - else: - socket.settimeout(None) + self._disable_socket_timeout(socket) while True: header = response.raw.read(constants.STREAM_HEADER_SIZE_BYTES) @@ -276,6 +275,19 @@ class Client( for out in response.iter_content(chunk_size=1, decode_unicode=True): yield out + def _disable_socket_timeout(self, socket): + """ Depending on the combination of python version and whether we're + connecting over http or https, we might need to access _sock, which + may or may not exist; or we may need to just settimeout on socket + itself, which also may or may not have settimeout on it. + + To avoid missing the correct one, we try both. + """ + if hasattr(socket, "settimeout"): + socket.settimeout(None) + if hasattr(socket, "_sock") and hasattr(socket._sock, "settimeout"): + socket._sock.settimeout(None) + def _get_result(self, container, stream, res): cont = self.inspect_container(container) return self._get_result_tty(stream, res, cont['Config']['Tty']) @@ -1,2 +1,2 @@ [pytest] -addopts = --tb=short -rxs +addopts = --tb=short -rxs -s diff --git a/tests/Dockerfile-dind-certs b/tests/Dockerfile-dind-certs new file mode 100644 index 0000000..9e8c042 --- /dev/null +++ b/tests/Dockerfile-dind-certs @@ -0,0 +1,20 @@ +FROM python:2.7 +RUN mkdir /tmp/certs +VOLUME /certs + +WORKDIR /tmp/certs +RUN openssl genrsa -aes256 -passout pass:foobar -out ca-key.pem 4096 +RUN echo "[req]\nprompt=no\ndistinguished_name = req_distinguished_name\n[req_distinguished_name]\ncountryName=AU" > /tmp/config +RUN openssl req -new -x509 -passin pass:foobar -config /tmp/config -days 365 -key ca-key.pem -sha256 -out ca.pem +RUN openssl genrsa -out server-key.pem -passout pass:foobar 4096 +RUN openssl req -subj "/CN=docker" -sha256 -new -key server-key.pem -out server.csr +RUN echo subjectAltName = DNS:docker,DNS:localhost > extfile.cnf +RUN openssl x509 -req -days 365 -passin pass:foobar -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf +RUN openssl genrsa -out key.pem 4096 +RUN openssl req -passin pass:foobar -subj '/CN=client' -new -key key.pem -out client.csr +RUN echo extendedKeyUsage = clientAuth > extfile.cnf +RUN openssl x509 -req -passin pass:foobar -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf +RUN chmod -v 0400 ca-key.pem key.pem server-key.pem +RUN chmod -v 0444 ca.pem server-cert.pem cert.pem + +CMD cp -R /tmp/certs/* /certs && while true; do sleep 1; done diff --git a/tests/integration_test.py b/tests/integration_test.py index 8a92708..c264a40 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -14,6 +14,7 @@ import base64 import contextlib +import errno import json import io import os @@ -21,6 +22,7 @@ import random import shutil import signal import socket +import struct import tarfile import tempfile import threading @@ -76,7 +78,17 @@ def setup_module(): try: c.inspect_image(BUSYBOX) except NotFound: - c.pull(BUSYBOX) + os.write(2, "\npulling busybox\n".encode('utf-8')) + for data in c.pull('busybox', stream=True): + data = json.loads(data.decode('utf-8')) + os.write(2, ("%c[2K\r" % 27).encode('utf-8')) + status = data.get("status") + progress = data.get("progress") + detail = "{0} - {1}".format(status, progress).encode('utf-8') + os.write(2, detail) + os.write(2, "\npulled busybox\n".encode('utf-8')) + + # Double make sure we now have busybox c.inspect_image(BUSYBOX) c.close() @@ -887,7 +899,6 @@ class TestContainerTop(BaseTestCase): self.client.start(container) res = self.client.top(container['Id']) - print(res) self.assertEqual( res['Titles'], ['UID', 'PID', 'PPID', 'C', 'STIME', 'TTY', 'TIME', 'CMD'] @@ -1213,6 +1224,64 @@ class TestRunContainerStreaming(BaseTestCase): self.assertTrue(sock.fileno() > -1) +class TestRunContainerReadingSocket(BaseTestCase): + def runTest(self): + line = 'hi there and stuff and things, words!' + command = "echo '{0}'".format(line) + container = self.client.create_container(BUSYBOX, command, + detach=True, tty=False) + ident = container['Id'] + self.tmp_containers.append(ident) + + opts = {"stdout": 1, "stream": 1, "logs": 1} + pty_stdout = self.client.attach_socket(ident, opts) + self.client.start(ident) + + recoverable_errors = (errno.EINTR, errno.EDEADLK, errno.EWOULDBLOCK) + + def read(n=4096): + """Code stolen from dockerpty to read the socket""" + try: + if hasattr(pty_stdout, 'recv'): + return pty_stdout.recv(n) + return os.read(pty_stdout.fileno(), n) + except EnvironmentError as e: + if e.errno not in recoverable_errors: + raise + + def next_packet_size(): + """Code stolen from dockerpty to get the next packet size""" + data = six.binary_type() + while len(data) < 8: + next_data = read(8 - len(data)) + if not next_data: + return 0 + data = data + next_data + + if data is None: + return 0 + + if len(data) == 8: + _, actual = struct.unpack('>BxxxL', data) + return actual + + next_size = next_packet_size() + self.assertEqual(next_size, len(line)+1) + + data = six.binary_type() + while len(data) < next_size: + next_data = read(next_size - len(data)) + if not next_data: + assert False, "Failed trying to read in the dataz" + data += next_data + self.assertEqual(data.decode('utf-8'), "{0}\n".format(line)) + pty_stdout.close() + + # Prevent segfault at the end of the test run + if hasattr(pty_stdout, "_response"): + del pty_stdout._response + + class TestPauseUnpauseContainer(BaseTestCase): def runTest(self): container = self.client.create_container(BUSYBOX, ['sleep', '9999']) |