diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/Dockerfile | 28 | ||||
-rw-r--r-- | tests/gpg-keys/ownertrust | 3 | ||||
-rw-r--r-- | tests/gpg-keys/secret | bin | 0 -> 966 bytes | |||
-rw-r--r-- | tests/helpers.py | 19 | ||||
-rw-r--r-- | tests/integration/api_container_test.py | 69 | ||||
-rw-r--r-- | tests/integration/api_exec_test.py | 174 | ||||
-rw-r--r-- | tests/integration/api_service_test.py | 29 | ||||
-rw-r--r-- | tests/integration/api_swarm_test.py | 37 | ||||
-rw-r--r-- | tests/integration/base.py | 5 | ||||
-rw-r--r-- | tests/integration/credentials/__init__.py | 0 | ||||
-rw-r--r-- | tests/integration/credentials/create_gpg_key.sh | 12 | ||||
-rw-r--r-- | tests/integration/credentials/store_test.py | 87 | ||||
-rw-r--r-- | tests/integration/credentials/utils_test.py | 22 | ||||
-rw-r--r-- | tests/integration/models_containers_test.py | 79 | ||||
-rw-r--r-- | tests/integration/models_swarm_test.py | 12 | ||||
-rw-r--r-- | tests/unit/auth_test.py | 7 | ||||
-rw-r--r-- | tests/unit/models_containers_test.py | 2 | ||||
-rw-r--r-- | tests/unit/utils_test.py | 13 |
18 files changed, 460 insertions, 138 deletions
diff --git a/tests/Dockerfile b/tests/Dockerfile new file mode 100644 index 0000000..042fc70 --- /dev/null +++ b/tests/Dockerfile @@ -0,0 +1,28 @@ +ARG PYTHON_VERSION=3.6 +FROM python:$PYTHON_VERSION-jessie +RUN apt-get update && apt-get -y install \ + gnupg2 \ + pass \ + curl + +COPY ./tests/gpg-keys /gpg-keys +RUN gpg2 --import gpg-keys/secret +RUN gpg2 --import-ownertrust gpg-keys/ownertrust +RUN yes | pass init $(gpg2 --no-auto-check-trustdb --list-secret-keys | grep ^sec | cut -d/ -f2 | cut -d" " -f1) +RUN gpg2 --check-trustdb +ARG CREDSTORE_VERSION=v0.6.0 +RUN curl -sSL -o /opt/docker-credential-pass.tar.gz \ + https://github.com/docker/docker-credential-helpers/releases/download/$CREDSTORE_VERSION/docker-credential-pass-$CREDSTORE_VERSION-amd64.tar.gz && \ + tar -xf /opt/docker-credential-pass.tar.gz -O > /usr/local/bin/docker-credential-pass && \ + rm -rf /opt/docker-credential-pass.tar.gz && \ + chmod +x /usr/local/bin/docker-credential-pass + +WORKDIR /src +COPY requirements.txt /src/requirements.txt +RUN pip install -r requirements.txt + +COPY test-requirements.txt /src/test-requirements.txt +RUN pip install -r test-requirements.txt + +COPY . /src +RUN pip install . diff --git a/tests/gpg-keys/ownertrust b/tests/gpg-keys/ownertrust new file mode 100644 index 0000000..141ea57 --- /dev/null +++ b/tests/gpg-keys/ownertrust @@ -0,0 +1,3 @@ +# List of assigned trustvalues, created Wed 25 Apr 2018 01:28:17 PM PDT +# (Use "gpg --import-ownertrust" to restore them) +9781B87DAB042E6FD51388A5464ED987A7B21401:6: diff --git a/tests/gpg-keys/secret b/tests/gpg-keys/secret Binary files differnew file mode 100644 index 0000000..412294d --- /dev/null +++ b/tests/gpg-keys/secret diff --git a/tests/helpers.py b/tests/helpers.py index f912bd8..f344e1c 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -2,16 +2,16 @@ import functools import os import os.path import random +import re +import socket import tarfile import tempfile import time -import re -import six -import socket import docker import paramiko import pytest +import six def make_tree(dirs, files): @@ -119,13 +119,18 @@ def assert_cat_socket_detached_with_keys(sock, inputs): # If we're using a Unix socket, the sock.send call will fail with a # BrokenPipeError ; INET sockets will just stop receiving / sending data # but will not raise an error - if getattr(sock, 'family', -9) == getattr(socket, 'AF_UNIX', -1): - with pytest.raises(socket.error): - sock.sendall(b'make sure the socket is closed\n') - elif isinstance(sock, paramiko.Channel): + if isinstance(sock, paramiko.Channel): with pytest.raises(OSError): sock.sendall(b'make sure the socket is closed\n') else: + if getattr(sock, 'family', -9) == getattr(socket, 'AF_UNIX', -1): + # We do not want to use pytest.raises here because future versions + # of the daemon no longer cause this to raise an error. + try: + sock.sendall(b'make sure the socket is closed\n') + except socket.error: + return + sock.sendall(b"make sure the socket is closed\n") data = sock.recv(128) # New in 18.06: error message is broadcast over the socket when reading diff --git a/tests/integration/api_container_test.py b/tests/integration/api_container_test.py index 558441e..26245c1 100644 --- a/tests/integration/api_container_test.py +++ b/tests/integration/api_container_test.py @@ -5,21 +5,20 @@ import tempfile import threading from datetime import datetime -import docker -from docker.constants import IS_WINDOWS_PLATFORM -from docker.utils.socket import next_frame_header -from docker.utils.socket import read_exactly - import pytest - import requests import six -from .base import BUSYBOX, BaseAPIIntegrationTest +import docker from .. import helpers -from ..helpers import ( - requires_api_version, ctrl_with, assert_cat_socket_detached_with_keys -) +from ..helpers import assert_cat_socket_detached_with_keys +from ..helpers import ctrl_with +from ..helpers import requires_api_version +from .base import BaseAPIIntegrationTest +from .base import BUSYBOX +from docker.constants import IS_WINDOWS_PLATFORM +from docker.utils.socket import next_frame_header +from docker.utils.socket import read_exactly class ListContainersTest(BaseAPIIntegrationTest): @@ -38,7 +37,7 @@ class ListContainersTest(BaseAPIIntegrationTest): assert 'Command' in retrieved assert retrieved['Command'] == six.text_type('true') assert 'Image' in retrieved - assert re.search(r'busybox:.*', retrieved['Image']) + assert re.search(r'alpine:.*', retrieved['Image']) assert 'Status' in retrieved @@ -368,10 +367,9 @@ class CreateContainerTest(BaseAPIIntegrationTest): ) self.tmp_containers.append(container['Id']) config = self.client.inspect_container(container['Id']) - assert ( - sorted(config['Config']['Env']) == - sorted(['Foo', 'Other=one', 'Blank=']) - ) + assert 'Foo' in config['Config']['Env'] + assert 'Other=one' in config['Config']['Env'] + assert 'Blank=' in config['Config']['Env'] @requires_api_version('1.22') def test_create_with_tmpfs(self): @@ -448,19 +446,6 @@ class CreateContainerTest(BaseAPIIntegrationTest): config = self.client.inspect_container(ctnr) assert config['HostConfig']['Init'] is True - @pytest.mark.xfail(True, reason='init-path removed in 17.05.0') - @requires_api_version('1.25') - def test_create_with_init_path(self): - ctnr = self.client.create_container( - BUSYBOX, 'true', - host_config=self.client.create_host_config( - init_path="/usr/libexec/docker-init" - ) - ) - self.tmp_containers.append(ctnr['Id']) - config = self.client.inspect_container(ctnr) - assert config['HostConfig']['InitPath'] == "/usr/libexec/docker-init" - @requires_api_version('1.24') @pytest.mark.xfail(not os.path.exists('/sys/fs/cgroup/cpu.rt_runtime_us'), reason='CONFIG_RT_GROUP_SCHED isn\'t enabled') @@ -1082,11 +1067,17 @@ class PortTest(BaseAPIIntegrationTest): def test_port(self): port_bindings = { '1111': ('127.0.0.1', '4567'), - '2222': ('127.0.0.1', '4568') + '2222': ('127.0.0.1', '4568'), + '3333/udp': ('127.0.0.1', '4569'), } + ports = [ + 1111, + 2222, + (3333, 'udp'), + ] container = self.client.create_container( - BUSYBOX, ['sleep', '60'], ports=list(port_bindings.keys()), + BUSYBOX, ['sleep', '60'], ports=ports, host_config=self.client.create_host_config( port_bindings=port_bindings, network_mode='bridge' ) @@ -1097,13 +1088,15 @@ class PortTest(BaseAPIIntegrationTest): # Call the port function on each biding and compare expected vs actual for port in port_bindings: + port, _, protocol = port.partition('/') actual_bindings = self.client.port(container, port) port_binding = actual_bindings.pop() ip, host_port = port_binding['HostIp'], port_binding['HostPort'] - assert ip == port_bindings[port][0] - assert host_port == port_bindings[port][1] + port_binding = port if not protocol else port + "/" + protocol + assert ip == port_bindings[port_binding][0] + assert host_port == port_bindings[port_binding][1] self.client.kill(id) @@ -1168,10 +1161,10 @@ class RestartContainerTest(BaseAPIIntegrationTest): def test_restart_with_low_timeout(self): container = self.client.create_container(BUSYBOX, ['sleep', '9999']) self.client.start(container) - self.client.timeout = 1 - self.client.restart(container, timeout=3) + self.client.timeout = 3 + self.client.restart(container, timeout=1) self.client.timeout = None - self.client.restart(container, timeout=3) + self.client.restart(container, timeout=1) self.client.kill(container) def test_restart_with_dict_instead_of_id(self): @@ -1256,7 +1249,7 @@ class AttachContainerTest(BaseAPIIntegrationTest): output = self.client.attach(container, stream=False, logs=True) assert output == 'hello\n'.encode(encoding='ascii') - @pytest.mark.timeout(5) + @pytest.mark.timeout(10) @pytest.mark.skipif(os.environ.get('DOCKER_HOST', '').startswith('ssh://'), reason='No cancellable streams over SSH') @pytest.mark.xfail(condition=os.environ.get('DOCKER_TLS_VERIFY') or @@ -1264,14 +1257,14 @@ class AttachContainerTest(BaseAPIIntegrationTest): reason='Flaky test on TLS') def test_attach_stream_and_cancel(self): container = self.client.create_container( - BUSYBOX, 'sh -c "echo hello && sleep 60"', + BUSYBOX, 'sh -c "sleep 2 && echo hello && sleep 60"', tty=True ) self.tmp_containers.append(container) self.client.start(container) output = self.client.attach(container, stream=True, logs=True) - threading.Timer(1, output.close).start() + threading.Timer(3, output.close).start() lines = [] for line in output: diff --git a/tests/integration/api_exec_test.py b/tests/integration/api_exec_test.py index e6079eb..80b63ff 100644 --- a/tests/integration/api_exec_test.py +++ b/tests/integration/api_exec_test.py @@ -1,11 +1,11 @@ +from ..helpers import assert_cat_socket_detached_with_keys +from ..helpers import ctrl_with +from ..helpers import requires_api_version +from .base import BaseAPIIntegrationTest +from .base import BUSYBOX +from docker.utils.proxy import ProxyConfig from docker.utils.socket import next_frame_header from docker.utils.socket import read_exactly -from docker.utils.proxy import ProxyConfig - -from .base import BaseAPIIntegrationTest, BUSYBOX -from ..helpers import ( - requires_api_version, ctrl_with, assert_cat_socket_detached_with_keys -) class ExecTest(BaseAPIIntegrationTest): @@ -81,11 +81,11 @@ class ExecTest(BaseAPIIntegrationTest): self.client.start(id) self.tmp_containers.append(id) - res = self.client.exec_create(id, 'whoami', user='default') + res = self.client.exec_create(id, 'whoami', user='postgres') assert 'Id' in res exec_log = self.client.exec_start(res) - assert exec_log == b'default\n' + assert exec_log == b'postgres\n' def test_exec_command_as_root(self): container = self.client.create_container(BUSYBOX, 'cat', @@ -115,75 +115,6 @@ class ExecTest(BaseAPIIntegrationTest): res += chunk assert res == b'hello\nworld\n' - def test_exec_command_demux(self): - container = self.client.create_container( - BUSYBOX, 'cat', detach=True, stdin_open=True) - id = container['Id'] - self.client.start(id) - self.tmp_containers.append(id) - - script = ' ; '.join([ - # Write something on stdout - 'echo hello out', - # Busybox's sleep does not handle sub-second times. - # This loops takes ~0.3 second to execute on my machine. - 'for i in $(seq 1 50000); do echo $i>/dev/null; done', - # Write something on stderr - 'echo hello err >&2']) - cmd = 'sh -c "{}"'.format(script) - - # tty=False, stream=False, demux=False - res = self.client.exec_create(id, cmd) - exec_log = self.client.exec_start(res) - assert exec_log == b'hello out\nhello err\n' - - # tty=False, stream=True, demux=False - res = self.client.exec_create(id, cmd) - exec_log = self.client.exec_start(res, stream=True) - assert next(exec_log) == b'hello out\n' - assert next(exec_log) == b'hello err\n' - with self.assertRaises(StopIteration): - next(exec_log) - - # tty=False, stream=False, demux=True - res = self.client.exec_create(id, cmd) - exec_log = self.client.exec_start(res, demux=True) - assert exec_log == (b'hello out\n', b'hello err\n') - - # tty=False, stream=True, demux=True - res = self.client.exec_create(id, cmd) - exec_log = self.client.exec_start(res, demux=True, stream=True) - assert next(exec_log) == (b'hello out\n', None) - assert next(exec_log) == (None, b'hello err\n') - with self.assertRaises(StopIteration): - next(exec_log) - - # tty=True, stream=False, demux=False - res = self.client.exec_create(id, cmd, tty=True) - exec_log = self.client.exec_start(res) - assert exec_log == b'hello out\r\nhello err\r\n' - - # tty=True, stream=True, demux=False - res = self.client.exec_create(id, cmd, tty=True) - exec_log = self.client.exec_start(res, stream=True) - assert next(exec_log) == b'hello out\r\n' - assert next(exec_log) == b'hello err\r\n' - with self.assertRaises(StopIteration): - next(exec_log) - - # tty=True, stream=False, demux=True - res = self.client.exec_create(id, cmd, tty=True) - exec_log = self.client.exec_start(res, demux=True) - assert exec_log == (b'hello out\r\nhello err\r\n', None) - - # tty=True, stream=True, demux=True - res = self.client.exec_create(id, cmd, tty=True) - exec_log = self.client.exec_start(res, demux=True, stream=True) - assert next(exec_log) == (b'hello out\r\n', None) - assert next(exec_log) == (b'hello err\r\n', None) - with self.assertRaises(StopIteration): - next(exec_log) - def test_exec_start_socket(self): container = self.client.create_container(BUSYBOX, 'cat', detach=True, stdin_open=True) @@ -257,9 +188,9 @@ class ExecTest(BaseAPIIntegrationTest): self.tmp_containers.append(container) self.client.start(container) - res = self.client.exec_create(container, 'pwd', workdir='/var/www') + res = self.client.exec_create(container, 'pwd', workdir='/var/opt') exec_log = self.client.exec_start(res) - assert exec_log == b'/var/www\n' + assert exec_log == b'/var/opt\n' def test_detach_with_default(self): container = self.client.create_container( @@ -313,3 +244,88 @@ class ExecTest(BaseAPIIntegrationTest): self.addCleanup(sock.close) assert_cat_socket_detached_with_keys(sock, [ctrl_with('x')]) + + +class ExecDemuxTest(BaseAPIIntegrationTest): + cmd = 'sh -c "{}"'.format(' ; '.join([ + # Write something on stdout + 'echo hello out', + # Busybox's sleep does not handle sub-second times. + # This loops takes ~0.3 second to execute on my machine. + 'sleep 0.5', + # Write something on stderr + 'echo hello err >&2']) + ) + + def setUp(self): + super(ExecDemuxTest, self).setUp() + self.container = self.client.create_container( + BUSYBOX, 'cat', detach=True, stdin_open=True + ) + self.client.start(self.container) + self.tmp_containers.append(self.container) + + def test_exec_command_no_stream_no_demux(self): + # tty=False, stream=False, demux=False + res = self.client.exec_create(self.container, self.cmd) + exec_log = self.client.exec_start(res) + assert b'hello out\n' in exec_log + assert b'hello err\n' in exec_log + + def test_exec_command_stream_no_demux(self): + # tty=False, stream=True, demux=False + res = self.client.exec_create(self.container, self.cmd) + exec_log = list(self.client.exec_start(res, stream=True)) + assert len(exec_log) == 2 + assert b'hello out\n' in exec_log + assert b'hello err\n' in exec_log + + def test_exec_command_no_stream_demux(self): + # tty=False, stream=False, demux=True + res = self.client.exec_create(self.container, self.cmd) + exec_log = self.client.exec_start(res, demux=True) + assert exec_log == (b'hello out\n', b'hello err\n') + + def test_exec_command_stream_demux(self): + # tty=False, stream=True, demux=True + res = self.client.exec_create(self.container, self.cmd) + exec_log = list(self.client.exec_start(res, demux=True, stream=True)) + assert len(exec_log) == 2 + assert (b'hello out\n', None) in exec_log + assert (None, b'hello err\n') in exec_log + + def test_exec_command_tty_no_stream_no_demux(self): + # tty=True, stream=False, demux=False + res = self.client.exec_create(self.container, self.cmd, tty=True) + exec_log = self.client.exec_start(res) + assert exec_log == b'hello out\r\nhello err\r\n' + + def test_exec_command_tty_stream_no_demux(self): + # tty=True, stream=True, demux=False + res = self.client.exec_create(self.container, self.cmd, tty=True) + exec_log = list(self.client.exec_start(res, stream=True)) + assert b'hello out\r\n' in exec_log + if len(exec_log) == 2: + assert b'hello err\r\n' in exec_log + else: + assert len(exec_log) == 3 + assert b'hello err' in exec_log + assert b'\r\n' in exec_log + + def test_exec_command_tty_no_stream_demux(self): + # tty=True, stream=False, demux=True + res = self.client.exec_create(self.container, self.cmd, tty=True) + exec_log = self.client.exec_start(res, demux=True) + assert exec_log == (b'hello out\r\nhello err\r\n', None) + + def test_exec_command_tty_stream_demux(self): + # tty=True, stream=True, demux=True + res = self.client.exec_create(self.container, self.cmd, tty=True) + exec_log = list(self.client.exec_start(res, demux=True, stream=True)) + assert (b'hello out\r\n', None) in exec_log + if len(exec_log) == 2: + assert (b'hello err\r\n', None) in exec_log + else: + assert len(exec_log) == 3 + assert (b'hello err', None) in exec_log + assert (b'\r\n', None) in exec_log diff --git a/tests/integration/api_service_test.py b/tests/integration/api_service_test.py index a53ca1c..71e0869 100644 --- a/tests/integration/api_service_test.py +++ b/tests/integration/api_service_test.py @@ -427,6 +427,21 @@ class ServiceTest(BaseAPIIntegrationTest): assert 'Placement' in svc_info['Spec']['TaskTemplate'] assert svc_info['Spec']['TaskTemplate']['Placement'] == placemt + @requires_api_version('1.27') + def test_create_service_with_placement_preferences_tuple(self): + container_spec = docker.types.ContainerSpec(BUSYBOX, ['true']) + placemt = docker.types.Placement(preferences=( + ('spread', 'com.dockerpy.test'), + )) + task_tmpl = docker.types.TaskTemplate( + container_spec, placement=placemt + ) + name = self.get_service_name() + svc_id = self.client.create_service(task_tmpl, name=name) + svc_info = self.client.inspect_service(svc_id) + assert 'Placement' in svc_info['Spec']['TaskTemplate'] + assert svc_info['Spec']['TaskTemplate']['Placement'] == placemt + def test_create_service_with_endpoint_spec(self): container_spec = docker.types.ContainerSpec(BUSYBOX, ['true']) task_tmpl = docker.types.TaskTemplate(container_spec) @@ -835,6 +850,20 @@ class ServiceTest(BaseAPIIntegrationTest): ) assert privileges['SELinuxContext']['Disable'] is True + @requires_api_version('1.38') + def test_create_service_with_init(self): + container_spec = docker.types.ContainerSpec( + 'busybox', ['sleep', '999'], init=True + ) + task_tmpl = docker.types.TaskTemplate(container_spec) + name = self.get_service_name() + svc_id = self.client.create_service(task_tmpl, name=name) + svc_info = self.client.inspect_service(svc_id) + assert 'Init' in svc_info['Spec']['TaskTemplate']['ContainerSpec'] + assert ( + svc_info['Spec']['TaskTemplate']['ContainerSpec']['Init'] is True + ) + @requires_api_version('1.25') def test_update_service_with_defaults_name(self): container_spec = docker.types.ContainerSpec( diff --git a/tests/integration/api_swarm_test.py b/tests/integration/api_swarm_test.py index b58dabc..5d4086a 100644 --- a/tests/integration/api_swarm_test.py +++ b/tests/integration/api_swarm_test.py @@ -35,6 +35,35 @@ class SwarmTest(BaseAPIIntegrationTest): version_2 = self.client.inspect_swarm()['Version']['Index'] assert version_2 != version_1 + @requires_api_version('1.39') + def test_init_swarm_custom_addr_pool_defaults(self): + assert self.init_swarm() + results = self.client.inspect_swarm() + assert set(results['DefaultAddrPool']) == {'10.0.0.0/8'} + assert results['SubnetSize'] == 24 + + @requires_api_version('1.39') + def test_init_swarm_custom_addr_pool_only_pool(self): + assert self.init_swarm(default_addr_pool=['2.0.0.0/16']) + results = self.client.inspect_swarm() + assert set(results['DefaultAddrPool']) == {'2.0.0.0/16'} + assert results['SubnetSize'] == 24 + + @requires_api_version('1.39') + def test_init_swarm_custom_addr_pool_only_subnet_size(self): + assert self.init_swarm(subnet_size=26) + results = self.client.inspect_swarm() + assert set(results['DefaultAddrPool']) == {'10.0.0.0/8'} + assert results['SubnetSize'] == 26 + + @requires_api_version('1.39') + def test_init_swarm_custom_addr_pool_both_args(self): + assert self.init_swarm(default_addr_pool=['2.0.0.0/16', '3.0.0.0/16'], + subnet_size=28) + results = self.client.inspect_swarm() + assert set(results['DefaultAddrPool']) == {'2.0.0.0/16', '3.0.0.0/16'} + assert results['SubnetSize'] == 28 + @requires_api_version('1.24') def test_init_already_in_cluster(self): assert self.init_swarm() @@ -157,12 +186,14 @@ class SwarmTest(BaseAPIIntegrationTest): @requires_api_version('1.24') def test_inspect_node(self): - assert self.init_swarm() + node_id = self.init_swarm() + assert node_id nodes_list = self.client.nodes() assert len(nodes_list) == 1 node = nodes_list[0] node_data = self.client.inspect_node(node['ID']) assert node['ID'] == node_data['ID'] + assert node_id == node['ID'] assert node['Version'] == node_data['Version'] @requires_api_version('1.24') @@ -204,3 +235,7 @@ class SwarmTest(BaseAPIIntegrationTest): self.client.remove_node(node_id, True) assert e.value.response.status_code >= 400 + + @requires_api_version('1.30') + def test_init_swarm_data_path_addr(self): + assert self.init_swarm(data_path_addr='eth0') diff --git a/tests/integration/base.py b/tests/integration/base.py index 262769d..0ebf5b9 100644 --- a/tests/integration/base.py +++ b/tests/integration/base.py @@ -3,11 +3,10 @@ import shutil import unittest import docker -from docker.utils import kwargs_from_env - from .. import helpers +from docker.utils import kwargs_from_env -BUSYBOX = 'busybox:buildroot-2014.02' +BUSYBOX = 'alpine:3.9.3' # FIXME: this should probably be renamed TEST_API_VERSION = os.environ.get('DOCKER_TEST_API_VERSION') diff --git a/tests/integration/credentials/__init__.py b/tests/integration/credentials/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/integration/credentials/__init__.py diff --git a/tests/integration/credentials/create_gpg_key.sh b/tests/integration/credentials/create_gpg_key.sh new file mode 100644 index 0000000..b276c20 --- /dev/null +++ b/tests/integration/credentials/create_gpg_key.sh @@ -0,0 +1,12 @@ +#!/usr/bin/sh +haveged +gpg --batch --gen-key <<-EOF +%echo Generating a standard key +Key-Type: DSA +Key-Length: 1024 +Subkey-Type: ELG-E +Subkey-Length: 1024 +Name-Real: Sakuya Izayoi +Name-Email: sakuya@gensokyo.jp +Expire-Date: 0 +EOF
\ No newline at end of file diff --git a/tests/integration/credentials/store_test.py b/tests/integration/credentials/store_test.py new file mode 100644 index 0000000..dd543e2 --- /dev/null +++ b/tests/integration/credentials/store_test.py @@ -0,0 +1,87 @@ +import os +import random +import sys + +import pytest +import six +from distutils.spawn import find_executable + +from docker.credentials import ( + CredentialsNotFound, Store, StoreError, DEFAULT_LINUX_STORE, + DEFAULT_OSX_STORE +) + + +class TestStore(object): + def teardown_method(self): + for server in self.tmp_keys: + try: + self.store.erase(server) + except StoreError: + pass + + def setup_method(self): + self.tmp_keys = [] + if sys.platform.startswith('linux'): + if find_executable('docker-credential-' + DEFAULT_LINUX_STORE): + self.store = Store(DEFAULT_LINUX_STORE) + elif find_executable('docker-credential-pass'): + self.store = Store('pass') + else: + raise Exception('No supported docker-credential store in PATH') + elif sys.platform.startswith('darwin'): + self.store = Store(DEFAULT_OSX_STORE) + + def get_random_servername(self): + res = 'pycreds_test_{:x}'.format(random.getrandbits(32)) + self.tmp_keys.append(res) + return res + + def test_store_and_get(self): + key = self.get_random_servername() + self.store.store(server=key, username='user', secret='pass') + data = self.store.get(key) + assert data == { + 'ServerURL': key, + 'Username': 'user', + 'Secret': 'pass' + } + + def test_get_nonexistent(self): + key = self.get_random_servername() + with pytest.raises(CredentialsNotFound): + self.store.get(key) + + def test_store_and_erase(self): + key = self.get_random_servername() + self.store.store(server=key, username='user', secret='pass') + self.store.erase(key) + with pytest.raises(CredentialsNotFound): + self.store.get(key) + + def test_unicode_strings(self): + key = self.get_random_servername() + key = six.u(key) + self.store.store(server=key, username='user', secret='pass') + data = self.store.get(key) + assert data + self.store.erase(key) + with pytest.raises(CredentialsNotFound): + self.store.get(key) + + def test_list(self): + names = (self.get_random_servername(), self.get_random_servername()) + self.store.store(names[0], username='sakuya', secret='izayoi') + self.store.store(names[1], username='reimu', secret='hakurei') + data = self.store.list() + assert names[0] in data + assert data[names[0]] == 'sakuya' + assert names[1] in data + assert data[names[1]] == 'reimu' + + def test_execute_with_env_override(self): + self.store.exe = 'env' + self.store.environment = {'FOO': 'bar'} + data = self.store._execute('--null', '') + assert b'\0FOO=bar\0' in data + assert 'FOO' not in os.environ diff --git a/tests/integration/credentials/utils_test.py b/tests/integration/credentials/utils_test.py new file mode 100644 index 0000000..ad55f32 --- /dev/null +++ b/tests/integration/credentials/utils_test.py @@ -0,0 +1,22 @@ +import os + +from docker.credentials.utils import create_environment_dict + +try: + from unittest import mock +except ImportError: + import mock + + +@mock.patch.dict(os.environ) +def test_create_environment_dict(): + base = {'FOO': 'bar', 'BAZ': 'foobar'} + os.environ = base + assert create_environment_dict({'FOO': 'baz'}) == { + 'FOO': 'baz', 'BAZ': 'foobar', + } + assert create_environment_dict({'HELLO': 'world'}) == { + 'FOO': 'bar', 'BAZ': 'foobar', 'HELLO': 'world', + } + + assert os.environ == base diff --git a/tests/integration/models_containers_test.py b/tests/integration/models_containers_test.py index 92eca36..951a08a 100644 --- a/tests/integration/models_containers_test.py +++ b/tests/integration/models_containers_test.py @@ -123,7 +123,9 @@ class ContainerCollectionTest(BaseIntegrationTest): def test_run_with_auto_remove(self): client = docker.from_env(version=TEST_API_VERSION) out = client.containers.run( - 'alpine', 'echo hello', auto_remove=True + # sleep(2) to allow any communication with the container + # before it gets removed by the host. + 'alpine', 'sh -c "echo hello && sleep 2"', auto_remove=True ) assert out == b'hello\n' @@ -132,7 +134,10 @@ class ContainerCollectionTest(BaseIntegrationTest): client = docker.from_env(version=TEST_API_VERSION) with pytest.raises(docker.errors.ContainerError) as e: client.containers.run( - 'alpine', 'sh -c ">&2 echo error && exit 1"', auto_remove=True + # sleep(2) to allow any communication with the container + # before it gets removed by the host. + 'alpine', 'sh -c ">&2 echo error && sleep 2 && exit 1"', + auto_remove=True ) assert e.value.exit_status == 1 assert e.value.stderr is None @@ -341,6 +346,66 @@ class ContainerTest(BaseIntegrationTest): 'memory_stats', 'blkio_stats']: assert key in stats + def test_ports_target_none(self): + client = docker.from_env(version=TEST_API_VERSION) + ports = None + target_ports = {'2222/tcp': ports} + container = client.containers.run( + "alpine", "sleep 100", detach=True, + ports=target_ports + ) + self.tmp_containers.append(container.id) + container.reload() # required to get auto-assigned ports + actual_ports = container.ports + assert sorted(target_ports.keys()) == sorted(actual_ports.keys()) + for target_client, target_host in target_ports.items(): + for actual_port in actual_ports[target_client]: + actual_keys = sorted(actual_port.keys()) + assert sorted(['HostIp', 'HostPort']) == actual_keys + assert target_host is ports + assert int(actual_port['HostPort']) > 0 + client.close() + + def test_ports_target_tuple(self): + client = docker.from_env(version=TEST_API_VERSION) + ports = ('127.0.0.1', 1111) + target_ports = {'2222/tcp': ports} + container = client.containers.run( + "alpine", "sleep 100", detach=True, + ports=target_ports + ) + self.tmp_containers.append(container.id) + container.reload() # required to get auto-assigned ports + actual_ports = container.ports + assert sorted(target_ports.keys()) == sorted(actual_ports.keys()) + for target_client, target_host in target_ports.items(): + for actual_port in actual_ports[target_client]: + actual_keys = sorted(actual_port.keys()) + assert sorted(['HostIp', 'HostPort']) == actual_keys + assert target_host == ports + assert int(actual_port['HostPort']) > 0 + client.close() + + def test_ports_target_list(self): + client = docker.from_env(version=TEST_API_VERSION) + ports = [1234, 4567] + target_ports = {'2222/tcp': ports} + container = client.containers.run( + "alpine", "sleep 100", detach=True, + ports=target_ports + ) + self.tmp_containers.append(container.id) + container.reload() # required to get auto-assigned ports + actual_ports = container.ports + assert sorted(target_ports.keys()) == sorted(actual_ports.keys()) + for target_client, target_host in target_ports.items(): + for actual_port in actual_ports[target_client]: + actual_keys = sorted(actual_port.keys()) + assert sorted(['HostIp', 'HostPort']) == actual_keys + assert target_host == ports + assert int(actual_port['HostPort']) > 0 + client.close() + def test_stop(self): client = docker.from_env(version=TEST_API_VERSION) container = client.containers.run("alpine", "top", detach=True) @@ -378,3 +443,13 @@ class ContainerTest(BaseIntegrationTest): detach=True) self.tmp_containers.append(container.id) assert container.wait()['StatusCode'] == 1 + + def test_create_with_volume_driver(self): + client = docker.from_env(version=TEST_API_VERSION) + container = client.containers.create( + 'alpine', + 'sleep 300', + volume_driver='foo' + ) + self.tmp_containers.append(container.id) + assert container.attrs['HostConfig']['VolumeDriver'] == 'foo' diff --git a/tests/integration/models_swarm_test.py b/tests/integration/models_swarm_test.py index f39f0d3..6c1836d 100644 --- a/tests/integration/models_swarm_test.py +++ b/tests/integration/models_swarm_test.py @@ -31,3 +31,15 @@ class SwarmTest(unittest.TestCase): cm.value.response.status_code == 406 or cm.value.response.status_code == 503 ) + + def test_join_on_already_joined_swarm(self): + client = docker.from_env(version=TEST_API_VERSION) + client.swarm.init() + join_token = client.swarm.attrs['JoinTokens']['Manager'] + with pytest.raises(docker.errors.APIError) as cm: + client.swarm.join( + remote_addrs=['127.0.0.1'], + join_token=join_token, + ) + assert cm.value.response.status_code == 503 + assert 'This node is already part of a swarm.' in cm.value.explanation diff --git a/tests/unit/auth_test.py b/tests/unit/auth_test.py index dc4d6f5..d46da50 100644 --- a/tests/unit/auth_test.py +++ b/tests/unit/auth_test.py @@ -9,8 +9,7 @@ import shutil import tempfile import unittest -from docker import auth, errors -import dockerpycreds +from docker import auth, credentials, errors import pytest try: @@ -661,7 +660,7 @@ class CredstoreTest(unittest.TestCase): } -class InMemoryStore(dockerpycreds.Store): +class InMemoryStore(credentials.Store): def __init__(self, *args, **kwargs): self.__store = {} @@ -669,7 +668,7 @@ class InMemoryStore(dockerpycreds.Store): try: return self.__store[server] except KeyError: - raise dockerpycreds.errors.CredentialsNotFound() + raise credentials.errors.CredentialsNotFound() def store(self, server, username, secret): self.__store[server] = { diff --git a/tests/unit/models_containers_test.py b/tests/unit/models_containers_test.py index f44e365..da5f0ab 100644 --- a/tests/unit/models_containers_test.py +++ b/tests/unit/models_containers_test.py @@ -176,6 +176,7 @@ class ContainerCollectionTest(unittest.TestCase): 'Ulimits': [{"Name": "nofile", "Soft": 1024, "Hard": 2048}], 'UsernsMode': 'host', 'UTSMode': 'host', + 'VolumeDriver': 'some_driver', 'VolumesFrom': ['container'], }, healthcheck={'test': 'true'}, @@ -190,7 +191,6 @@ class ContainerCollectionTest(unittest.TestCase): stop_signal=9, tty=True, user='bob', - volume_driver='some_driver', volumes=[ '/mnt/vol2', '/mnt/vol1', diff --git a/tests/unit/utils_test.py b/tests/unit/utils_test.py index ee660a3..d9cb002 100644 --- a/tests/unit/utils_test.py +++ b/tests/unit/utils_test.py @@ -495,9 +495,12 @@ class PortsTest(unittest.TestCase): assert external_port == [("127.0.0.1", "1000")] def test_split_port_with_protocol(self): - internal_port, external_port = split_port("127.0.0.1:1000:2000/udp") - assert internal_port == ["2000/udp"] - assert external_port == [("127.0.0.1", "1000")] + for protocol in ['tcp', 'udp', 'sctp']: + internal_port, external_port = split_port( + "127.0.0.1:1000:2000/" + protocol + ) + assert internal_port == ["2000/" + protocol] + assert external_port == [("127.0.0.1", "1000")] def test_split_port_with_host_ip_no_port(self): internal_port, external_port = split_port("127.0.0.1::2000") @@ -550,6 +553,10 @@ class PortsTest(unittest.TestCase): with pytest.raises(ValueError): split_port("0.0.0.0:1000:2000:tcp") + def test_split_port_invalid_protocol(self): + with pytest.raises(ValueError): + split_port("0.0.0.0:1000:2000/ftp") + def test_non_matching_length_port_ranges(self): with pytest.raises(ValueError): split_port("0.0.0.0:1000-1010:2000-2002/tcp") |