summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Dockerfile28
-rw-r--r--tests/gpg-keys/ownertrust3
-rw-r--r--tests/gpg-keys/secretbin0 -> 966 bytes
-rw-r--r--tests/helpers.py19
-rw-r--r--tests/integration/api_container_test.py69
-rw-r--r--tests/integration/api_exec_test.py174
-rw-r--r--tests/integration/api_service_test.py29
-rw-r--r--tests/integration/api_swarm_test.py37
-rw-r--r--tests/integration/base.py5
-rw-r--r--tests/integration/credentials/__init__.py0
-rw-r--r--tests/integration/credentials/create_gpg_key.sh12
-rw-r--r--tests/integration/credentials/store_test.py87
-rw-r--r--tests/integration/credentials/utils_test.py22
-rw-r--r--tests/integration/models_containers_test.py79
-rw-r--r--tests/integration/models_swarm_test.py12
-rw-r--r--tests/unit/auth_test.py7
-rw-r--r--tests/unit/models_containers_test.py2
-rw-r--r--tests/unit/utils_test.py13
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
new file mode 100644
index 0000000..412294d
--- /dev/null
+++ b/tests/gpg-keys/secret
Binary files differ
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")