summaryrefslogtreecommitdiff
path: root/tests/unit
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit')
-rw-r--r--tests/unit/api_build_test.py82
-rw-r--r--tests/unit/api_test.py143
-rw-r--r--tests/unit/auth_test.py442
-rw-r--r--tests/unit/dockertypes_test.py8
-rw-r--r--tests/unit/errors_test.py21
-rw-r--r--tests/unit/models_containers_test.py18
-rw-r--r--tests/unit/models_images_test.py22
-rw-r--r--tests/unit/models_services_test.py8
-rw-r--r--tests/unit/types_containers_test.py6
-rw-r--r--tests/unit/utils_config_test.py24
-rw-r--r--tests/unit/utils_proxy_test.py84
-rw-r--r--tests/unit/utils_test.py41
12 files changed, 743 insertions, 156 deletions
diff --git a/tests/unit/api_build_test.py b/tests/unit/api_build_test.py
index a7f34fd..7e07a26 100644
--- a/tests/unit/api_build_test.py
+++ b/tests/unit/api_build_test.py
@@ -1,12 +1,16 @@
import gzip
import io
+import shutil
import docker
from docker import auth
+from docker.api.build import process_dockerfile
-from .api_test import BaseAPIClientTest, fake_request, url_prefix
import pytest
+from ..helpers import make_tree
+from .api_test import BaseAPIClientTest, fake_request, url_prefix
+
class BuildTest(BaseAPIClientTest):
def test_build_container(self):
@@ -61,7 +65,7 @@ class BuildTest(BaseAPIClientTest):
)
def test_build_remote_with_registry_auth(self):
- self.client._auth_configs = {
+ self.client._auth_configs = auth.AuthConfig({
'auths': {
'https://example.com': {
'user': 'example',
@@ -69,7 +73,7 @@ class BuildTest(BaseAPIClientTest):
'email': 'example@example.com'
}
}
- }
+ })
expected_params = {'t': None, 'q': False, 'dockerfile': None,
'rm': False, 'nocache': False, 'pull': False,
@@ -77,7 +81,7 @@ class BuildTest(BaseAPIClientTest):
'remote': 'https://github.com/docker-library/mongo'}
expected_headers = {
'X-Registry-Config': auth.encode_header(
- self.client._auth_configs['auths']
+ self.client._auth_configs.auths
)
}
@@ -111,7 +115,7 @@ class BuildTest(BaseAPIClientTest):
})
def test_set_auth_headers_with_empty_dict_and_auth_configs(self):
- self.client._auth_configs = {
+ self.client._auth_configs = auth.AuthConfig({
'auths': {
'https://example.com': {
'user': 'example',
@@ -119,12 +123,12 @@ class BuildTest(BaseAPIClientTest):
'email': 'example@example.com'
}
}
- }
+ })
headers = {}
expected_headers = {
'X-Registry-Config': auth.encode_header(
- self.client._auth_configs['auths']
+ self.client._auth_configs.auths
)
}
@@ -132,7 +136,7 @@ class BuildTest(BaseAPIClientTest):
assert headers == expected_headers
def test_set_auth_headers_with_dict_and_auth_configs(self):
- self.client._auth_configs = {
+ self.client._auth_configs = auth.AuthConfig({
'auths': {
'https://example.com': {
'user': 'example',
@@ -140,12 +144,12 @@ class BuildTest(BaseAPIClientTest):
'email': 'example@example.com'
}
}
- }
+ })
headers = {'foo': 'bar'}
expected_headers = {
'X-Registry-Config': auth.encode_header(
- self.client._auth_configs['auths']
+ self.client._auth_configs.auths
),
'foo': 'bar'
}
@@ -161,3 +165,61 @@ class BuildTest(BaseAPIClientTest):
self.client._set_auth_headers(headers)
assert headers == expected_headers
+
+ @pytest.mark.skipif(
+ not docker.constants.IS_WINDOWS_PLATFORM,
+ reason='Windows-specific syntax')
+ def test_process_dockerfile_win_longpath_prefix(self):
+ dirs = [
+ 'foo', 'foo/bar', 'baz',
+ ]
+
+ files = [
+ 'Dockerfile', 'foo/Dockerfile.foo', 'foo/bar/Dockerfile.bar',
+ 'baz/Dockerfile.baz',
+ ]
+
+ base = make_tree(dirs, files)
+ self.addCleanup(shutil.rmtree, base)
+
+ def pre(path):
+ return docker.constants.WINDOWS_LONGPATH_PREFIX + path
+
+ assert process_dockerfile(None, pre(base)) == (None, None)
+ assert process_dockerfile('Dockerfile', pre(base)) == (
+ 'Dockerfile', None
+ )
+ assert process_dockerfile('foo/Dockerfile.foo', pre(base)) == (
+ 'foo/Dockerfile.foo', None
+ )
+ assert process_dockerfile(
+ '../Dockerfile', pre(base + '\\foo')
+ )[1] is not None
+ assert process_dockerfile(
+ '../baz/Dockerfile.baz', pre(base + '/baz')
+ ) == ('../baz/Dockerfile.baz', None)
+
+ def test_process_dockerfile(self):
+ dirs = [
+ 'foo', 'foo/bar', 'baz',
+ ]
+
+ files = [
+ 'Dockerfile', 'foo/Dockerfile.foo', 'foo/bar/Dockerfile.bar',
+ 'baz/Dockerfile.baz',
+ ]
+
+ base = make_tree(dirs, files)
+ self.addCleanup(shutil.rmtree, base)
+
+ assert process_dockerfile(None, base) == (None, None)
+ assert process_dockerfile('Dockerfile', base) == ('Dockerfile', None)
+ assert process_dockerfile('foo/Dockerfile.foo', base) == (
+ 'foo/Dockerfile.foo', None
+ )
+ assert process_dockerfile(
+ '../Dockerfile', base + '/foo'
+ )[1] is not None
+ assert process_dockerfile('../baz/Dockerfile.baz', base + '/baz') == (
+ '../baz/Dockerfile.baz', None
+ )
diff --git a/tests/unit/api_test.py b/tests/unit/api_test.py
index af2bb1c..f4d220a 100644
--- a/tests/unit/api_test.py
+++ b/tests/unit/api_test.py
@@ -15,6 +15,7 @@ from docker.api import APIClient
import requests
from requests.packages import urllib3
import six
+import struct
from . import fake_api
@@ -83,7 +84,7 @@ def fake_delete(self, url, *args, **kwargs):
return fake_request('DELETE', url, *args, **kwargs)
-def fake_read_from_socket(self, response, stream, tty=False):
+def fake_read_from_socket(self, response, stream, tty=False, demux=False):
return six.binary_type()
@@ -105,8 +106,6 @@ class BaseAPIClientTest(unittest.TestCase):
)
self.patcher.start()
self.client = APIClient()
- # Force-clear authconfig to avoid tampering with the tests
- self.client._cfg = {'Configs': {}}
def tearDown(self):
self.client.close()
@@ -221,13 +220,11 @@ class DockerApiTest(BaseAPIClientTest):
'username': 'sakuya', 'password': 'izayoi'
}
assert args[1]['headers'] == {'Content-Type': 'application/json'}
- assert self.client._auth_configs['auths'] == {
- 'docker.io': {
- 'email': None,
- 'password': 'izayoi',
- 'username': 'sakuya',
- 'serveraddress': None,
- }
+ assert self.client._auth_configs.auths['docker.io'] == {
+ 'email': None,
+ 'password': 'izayoi',
+ 'username': 'sakuya',
+ 'serveraddress': None,
}
def test_events(self):
@@ -467,56 +464,124 @@ class UnixSocketStreamTest(unittest.TestCase):
class TCPSocketStreamTest(unittest.TestCase):
- text_data = b'''
+ stdout_data = b'''
Now, those children out there, they're jumping through the
flames in the hope that the god of the fire will make them fruitful.
Really, you can't blame them. After all, what girl would not prefer the
child of a god to that of some acne-scarred artisan?
'''
+ stderr_data = b'''
+ And what of the true God? To whose glory churches and monasteries have been
+ built on these islands for generations past? Now shall what of Him?
+ '''
- def setUp(self):
-
- self.server = six.moves.socketserver.ThreadingTCPServer(
- ('', 0), self.get_handler_class()
- )
- self.thread = threading.Thread(target=self.server.serve_forever)
- self.thread.setDaemon(True)
- self.thread.start()
- self.address = 'http://{}:{}'.format(
- socket.gethostname(), self.server.server_address[1]
- )
-
- def tearDown(self):
- self.server.shutdown()
- self.server.server_close()
- self.thread.join()
-
- def get_handler_class(self):
- text_data = self.text_data
+ @classmethod
+ def setup_class(cls):
+ cls.server = six.moves.socketserver.ThreadingTCPServer(
+ ('', 0), cls.get_handler_class())
+ cls.thread = threading.Thread(target=cls.server.serve_forever)
+ cls.thread.setDaemon(True)
+ cls.thread.start()
+ cls.address = 'http://{}:{}'.format(
+ socket.gethostname(), cls.server.server_address[1])
+
+ @classmethod
+ def teardown_class(cls):
+ cls.server.shutdown()
+ cls.server.server_close()
+ cls.thread.join()
+
+ @classmethod
+ def get_handler_class(cls):
+ stdout_data = cls.stdout_data
+ stderr_data = cls.stderr_data
class Handler(six.moves.BaseHTTPServer.BaseHTTPRequestHandler, object):
def do_POST(self):
+ resp_data = self.get_resp_data()
self.send_response(101)
self.send_header(
- 'Content-Type', 'application/vnd.docker.raw-stream'
- )
+ 'Content-Type', 'application/vnd.docker.raw-stream')
self.send_header('Connection', 'Upgrade')
self.send_header('Upgrade', 'tcp')
self.end_headers()
self.wfile.flush()
time.sleep(0.2)
- self.wfile.write(text_data)
+ self.wfile.write(resp_data)
self.wfile.flush()
+ def get_resp_data(self):
+ path = self.path.split('/')[-1]
+ if path == 'tty':
+ return stdout_data + stderr_data
+ elif path == 'no-tty':
+ data = b''
+ data += self.frame_header(1, stdout_data)
+ data += stdout_data
+ data += self.frame_header(2, stderr_data)
+ data += stderr_data
+ return data
+ else:
+ raise Exception('Unknown path {0}'.format(path))
+
+ @staticmethod
+ def frame_header(stream, data):
+ return struct.pack('>BxxxL', stream, len(data))
+
return Handler
- def test_read_from_socket(self):
+ def request(self, stream=None, tty=None, demux=None):
+ assert stream is not None and tty is not None and demux is not None
with APIClient(base_url=self.address) as client:
- resp = client._post(client._url('/dummy'), stream=True)
- data = client._read_from_socket(resp, stream=True, tty=True)
- results = b''.join(data)
-
- assert results == self.text_data
+ if tty:
+ url = client._url('/tty')
+ else:
+ url = client._url('/no-tty')
+ resp = client._post(url, stream=True)
+ return client._read_from_socket(
+ resp, stream=stream, tty=tty, demux=demux)
+
+ def test_read_from_socket_tty(self):
+ res = self.request(stream=True, tty=True, demux=False)
+ assert next(res) == self.stdout_data + self.stderr_data
+ with self.assertRaises(StopIteration):
+ next(res)
+
+ def test_read_from_socket_tty_demux(self):
+ res = self.request(stream=True, tty=True, demux=True)
+ assert next(res) == (self.stdout_data + self.stderr_data, None)
+ with self.assertRaises(StopIteration):
+ next(res)
+
+ def test_read_from_socket_no_tty(self):
+ res = self.request(stream=True, tty=False, demux=False)
+ assert next(res) == self.stdout_data
+ assert next(res) == self.stderr_data
+ with self.assertRaises(StopIteration):
+ next(res)
+
+ def test_read_from_socket_no_tty_demux(self):
+ res = self.request(stream=True, tty=False, demux=True)
+ assert (self.stdout_data, None) == next(res)
+ assert (None, self.stderr_data) == next(res)
+ with self.assertRaises(StopIteration):
+ next(res)
+
+ def test_read_from_socket_no_stream_tty(self):
+ res = self.request(stream=False, tty=True, demux=False)
+ assert res == self.stdout_data + self.stderr_data
+
+ def test_read_from_socket_no_stream_tty_demux(self):
+ res = self.request(stream=False, tty=True, demux=True)
+ assert res == (self.stdout_data + self.stderr_data, None)
+
+ def test_read_from_socket_no_stream_no_tty(self):
+ res = self.request(stream=False, tty=False, demux=False)
+ res == self.stdout_data + self.stderr_data
+
+ def test_read_from_socket_no_stream_no_tty_demux(self):
+ res = self.request(stream=False, tty=False, demux=True)
+ assert res == (self.stdout_data, self.stderr_data)
class UserAgentTest(unittest.TestCase):
diff --git a/tests/unit/auth_test.py b/tests/unit/auth_test.py
index 947d680..aac8910 100644
--- a/tests/unit/auth_test.py
+++ b/tests/unit/auth_test.py
@@ -9,7 +9,7 @@ import shutil
import tempfile
import unittest
-from docker import auth, errors
+from docker import auth, credentials, errors
import pytest
try:
@@ -106,13 +106,13 @@ class ResolveAuthTest(unittest.TestCase):
private_config = {'auth': encode_auth({'username': 'privateuser'})}
legacy_config = {'auth': encode_auth({'username': 'legacyauth'})}
- auth_config = {
+ auth_config = auth.AuthConfig({
'auths': auth.parse_auth({
'https://index.docker.io/v1/': index_config,
'my.registry.net': private_config,
'http://legacy.registry.url/v1/': legacy_config,
})
- }
+ })
def test_resolve_authconfig_hostname_only(self):
assert auth.resolve_authconfig(
@@ -211,70 +211,21 @@ class ResolveAuthTest(unittest.TestCase):
) is None
def test_resolve_auth_with_empty_credstore_and_auth_dict(self):
- auth_config = {
+ auth_config = auth.AuthConfig({
'auths': auth.parse_auth({
'https://index.docker.io/v1/': self.index_config,
}),
'credsStore': 'blackbox'
- }
- with mock.patch('docker.auth._resolve_authconfig_credstore') as m:
+ })
+ with mock.patch(
+ 'docker.auth.AuthConfig._resolve_authconfig_credstore'
+ ) as m:
m.return_value = None
assert 'indexuser' == auth.resolve_authconfig(
auth_config, None
)['username']
-class CredStoreTest(unittest.TestCase):
- def test_get_credential_store(self):
- auth_config = {
- 'credHelpers': {
- 'registry1.io': 'truesecret',
- 'registry2.io': 'powerlock'
- },
- 'credsStore': 'blackbox',
- }
-
- assert auth.get_credential_store(
- auth_config, 'registry1.io'
- ) == 'truesecret'
- assert auth.get_credential_store(
- auth_config, 'registry2.io'
- ) == 'powerlock'
- assert auth.get_credential_store(
- auth_config, 'registry3.io'
- ) == 'blackbox'
-
- def test_get_credential_store_no_default(self):
- auth_config = {
- 'credHelpers': {
- 'registry1.io': 'truesecret',
- 'registry2.io': 'powerlock'
- },
- }
- assert auth.get_credential_store(
- auth_config, 'registry2.io'
- ) == 'powerlock'
- assert auth.get_credential_store(
- auth_config, 'registry3.io'
- ) is None
-
- def test_get_credential_store_default_index(self):
- auth_config = {
- 'credHelpers': {
- 'https://index.docker.io/v1/': 'powerlock'
- },
- 'credsStore': 'truesecret'
- }
-
- assert auth.get_credential_store(auth_config, None) == 'powerlock'
- assert auth.get_credential_store(
- auth_config, 'docker.io'
- ) == 'powerlock'
- assert auth.get_credential_store(
- auth_config, 'images.io'
- ) == 'truesecret'
-
-
class LoadConfigTest(unittest.TestCase):
def test_load_config_no_file(self):
folder = tempfile.mkdtemp()
@@ -293,8 +244,8 @@ class LoadConfigTest(unittest.TestCase):
cfg = auth.load_config(cfg_path)
assert auth.resolve_authconfig(cfg) is not None
- assert cfg['auths'][auth.INDEX_NAME] is not None
- cfg = cfg['auths'][auth.INDEX_NAME]
+ assert cfg.auths[auth.INDEX_NAME] is not None
+ cfg = cfg.auths[auth.INDEX_NAME]
assert cfg['username'] == 'sakuya'
assert cfg['password'] == 'izayoi'
assert cfg['email'] == 'sakuya@scarlet.net'
@@ -312,8 +263,8 @@ class LoadConfigTest(unittest.TestCase):
)
cfg = auth.load_config(cfg_path)
assert auth.resolve_authconfig(cfg) is not None
- assert cfg['auths'][auth.INDEX_URL] is not None
- cfg = cfg['auths'][auth.INDEX_URL]
+ assert cfg.auths[auth.INDEX_URL] is not None
+ cfg = cfg.auths[auth.INDEX_URL]
assert cfg['username'] == 'sakuya'
assert cfg['password'] == 'izayoi'
assert cfg['email'] == email
@@ -335,8 +286,8 @@ class LoadConfigTest(unittest.TestCase):
}, f)
cfg = auth.load_config(cfg_path)
assert auth.resolve_authconfig(cfg) is not None
- assert cfg['auths'][auth.INDEX_URL] is not None
- cfg = cfg['auths'][auth.INDEX_URL]
+ assert cfg.auths[auth.INDEX_URL] is not None
+ cfg = cfg.auths[auth.INDEX_URL]
assert cfg['username'] == 'sakuya'
assert cfg['password'] == 'izayoi'
assert cfg['email'] == email
@@ -360,7 +311,7 @@ class LoadConfigTest(unittest.TestCase):
with open(dockercfg_path, 'w') as f:
json.dump(config, f)
- cfg = auth.load_config(dockercfg_path)['auths']
+ cfg = auth.load_config(dockercfg_path).auths
assert registry in cfg
assert cfg[registry] is not None
cfg = cfg[registry]
@@ -387,7 +338,7 @@ class LoadConfigTest(unittest.TestCase):
json.dump(config, f)
with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}):
- cfg = auth.load_config(None)['auths']
+ cfg = auth.load_config(None).auths
assert registry in cfg
assert cfg[registry] is not None
cfg = cfg[registry]
@@ -417,8 +368,8 @@ class LoadConfigTest(unittest.TestCase):
with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}):
cfg = auth.load_config(None)
- assert registry in cfg['auths']
- cfg = cfg['auths'][registry]
+ assert registry in cfg.auths
+ cfg = cfg.auths[registry]
assert cfg['username'] == 'sakuya'
assert cfg['password'] == 'izayoi'
assert cfg['email'] == 'sakuya@scarlet.net'
@@ -446,8 +397,8 @@ class LoadConfigTest(unittest.TestCase):
with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}):
cfg = auth.load_config(None)
- assert registry in cfg['auths']
- cfg = cfg['auths'][registry]
+ assert registry in cfg.auths
+ cfg = cfg.auths[registry]
assert cfg['username'] == b'sakuya\xc3\xa6'.decode('utf8')
assert cfg['password'] == b'izayoi\xc3\xa6'.decode('utf8')
assert cfg['email'] == 'sakuya@scarlet.net'
@@ -464,7 +415,7 @@ class LoadConfigTest(unittest.TestCase):
json.dump(config, f)
cfg = auth.load_config(dockercfg_path)
- assert cfg == {'auths': {}}
+ assert dict(cfg) == {'auths': {}}
def test_load_config_invalid_auth_dict(self):
folder = tempfile.mkdtemp()
@@ -479,7 +430,7 @@ class LoadConfigTest(unittest.TestCase):
json.dump(config, f)
cfg = auth.load_config(dockercfg_path)
- assert cfg == {'auths': {'scarlet.net': {}}}
+ assert dict(cfg) == {'auths': {'scarlet.net': {}}}
def test_load_config_identity_token(self):
folder = tempfile.mkdtemp()
@@ -500,7 +451,352 @@ class LoadConfigTest(unittest.TestCase):
json.dump(config, f)
cfg = auth.load_config(dockercfg_path)
- assert registry in cfg['auths']
- cfg = cfg['auths'][registry]
+ assert registry in cfg.auths
+ cfg = cfg.auths[registry]
assert 'IdentityToken' in cfg
assert cfg['IdentityToken'] == token
+
+
+class CredstoreTest(unittest.TestCase):
+ def setUp(self):
+ self.authconfig = auth.AuthConfig({'credsStore': 'default'})
+ self.default_store = InMemoryStore('default')
+ self.authconfig._stores['default'] = self.default_store
+ self.default_store.store(
+ 'https://gensokyo.jp/v2', 'sakuya', 'izayoi',
+ )
+ self.default_store.store(
+ 'https://default.com/v2', 'user', 'hunter2',
+ )
+
+ def test_get_credential_store(self):
+ auth_config = auth.AuthConfig({
+ 'credHelpers': {
+ 'registry1.io': 'truesecret',
+ 'registry2.io': 'powerlock'
+ },
+ 'credsStore': 'blackbox',
+ })
+
+ assert auth_config.get_credential_store('registry1.io') == 'truesecret'
+ assert auth_config.get_credential_store('registry2.io') == 'powerlock'
+ assert auth_config.get_credential_store('registry3.io') == 'blackbox'
+
+ def test_get_credential_store_no_default(self):
+ auth_config = auth.AuthConfig({
+ 'credHelpers': {
+ 'registry1.io': 'truesecret',
+ 'registry2.io': 'powerlock'
+ },
+ })
+ assert auth_config.get_credential_store('registry2.io') == 'powerlock'
+ assert auth_config.get_credential_store('registry3.io') is None
+
+ def test_get_credential_store_default_index(self):
+ auth_config = auth.AuthConfig({
+ 'credHelpers': {
+ 'https://index.docker.io/v1/': 'powerlock'
+ },
+ 'credsStore': 'truesecret'
+ })
+
+ assert auth_config.get_credential_store(None) == 'powerlock'
+ assert auth_config.get_credential_store('docker.io') == 'powerlock'
+ assert auth_config.get_credential_store('images.io') == 'truesecret'
+
+ def test_get_credential_store_with_plain_dict(self):
+ auth_config = {
+ 'credHelpers': {
+ 'registry1.io': 'truesecret',
+ 'registry2.io': 'powerlock'
+ },
+ 'credsStore': 'blackbox',
+ }
+
+ assert auth.get_credential_store(
+ auth_config, 'registry1.io'
+ ) == 'truesecret'
+ assert auth.get_credential_store(
+ auth_config, 'registry2.io'
+ ) == 'powerlock'
+ assert auth.get_credential_store(
+ auth_config, 'registry3.io'
+ ) == 'blackbox'
+
+ def test_get_all_credentials_credstore_only(self):
+ assert self.authconfig.get_all_credentials() == {
+ 'https://gensokyo.jp/v2': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'gensokyo.jp': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'https://default.com/v2': {
+ 'Username': 'user',
+ 'Password': 'hunter2',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ 'default.com': {
+ 'Username': 'user',
+ 'Password': 'hunter2',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ }
+
+ def test_get_all_credentials_with_empty_credhelper(self):
+ self.authconfig['credHelpers'] = {
+ 'registry1.io': 'truesecret',
+ }
+ self.authconfig._stores['truesecret'] = InMemoryStore()
+ assert self.authconfig.get_all_credentials() == {
+ 'https://gensokyo.jp/v2': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'gensokyo.jp': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'https://default.com/v2': {
+ 'Username': 'user',
+ 'Password': 'hunter2',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ 'default.com': {
+ 'Username': 'user',
+ 'Password': 'hunter2',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ 'registry1.io': None,
+ }
+
+ def test_get_all_credentials_with_credhelpers_only(self):
+ del self.authconfig['credsStore']
+ assert self.authconfig.get_all_credentials() == {}
+
+ self.authconfig['credHelpers'] = {
+ 'https://gensokyo.jp/v2': 'default',
+ 'https://default.com/v2': 'default',
+ }
+
+ assert self.authconfig.get_all_credentials() == {
+ 'https://gensokyo.jp/v2': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'gensokyo.jp': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'https://default.com/v2': {
+ 'Username': 'user',
+ 'Password': 'hunter2',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ 'default.com': {
+ 'Username': 'user',
+ 'Password': 'hunter2',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ }
+
+ def test_get_all_credentials_with_auths_entries(self):
+ self.authconfig.add_auth('registry1.io', {
+ 'ServerAddress': 'registry1.io',
+ 'Username': 'reimu',
+ 'Password': 'hakurei',
+ })
+
+ assert self.authconfig.get_all_credentials() == {
+ 'https://gensokyo.jp/v2': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'gensokyo.jp': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'https://default.com/v2': {
+ 'Username': 'user',
+ 'Password': 'hunter2',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ 'default.com': {
+ 'Username': 'user',
+ 'Password': 'hunter2',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ 'registry1.io': {
+ 'ServerAddress': 'registry1.io',
+ 'Username': 'reimu',
+ 'Password': 'hakurei',
+ },
+ }
+
+ def test_get_all_credentials_with_empty_auths_entry(self):
+ self.authconfig.add_auth('default.com', {})
+
+ assert self.authconfig.get_all_credentials() == {
+ 'https://gensokyo.jp/v2': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'gensokyo.jp': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'https://default.com/v2': {
+ 'Username': 'user',
+ 'Password': 'hunter2',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ 'default.com': {
+ 'Username': 'user',
+ 'Password': 'hunter2',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ }
+
+ def test_get_all_credentials_credstore_overrides_auth_entry(self):
+ self.authconfig.add_auth('default.com', {
+ 'Username': 'shouldnotsee',
+ 'Password': 'thisentry',
+ 'ServerAddress': 'https://default.com/v2',
+ })
+
+ assert self.authconfig.get_all_credentials() == {
+ 'https://gensokyo.jp/v2': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'gensokyo.jp': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'https://default.com/v2': {
+ 'Username': 'user',
+ 'Password': 'hunter2',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ 'default.com': {
+ 'Username': 'user',
+ 'Password': 'hunter2',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ }
+
+ def test_get_all_credentials_helpers_override_default(self):
+ self.authconfig['credHelpers'] = {
+ 'https://default.com/v2': 'truesecret',
+ }
+ truesecret = InMemoryStore('truesecret')
+ truesecret.store('https://default.com/v2', 'reimu', 'hakurei')
+ self.authconfig._stores['truesecret'] = truesecret
+ assert self.authconfig.get_all_credentials() == {
+ 'https://gensokyo.jp/v2': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'gensokyo.jp': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'https://default.com/v2': {
+ 'Username': 'reimu',
+ 'Password': 'hakurei',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ 'default.com': {
+ 'Username': 'reimu',
+ 'Password': 'hakurei',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ }
+
+ def test_get_all_credentials_3_sources(self):
+ self.authconfig['credHelpers'] = {
+ 'registry1.io': 'truesecret',
+ }
+ truesecret = InMemoryStore('truesecret')
+ truesecret.store('registry1.io', 'reimu', 'hakurei')
+ self.authconfig._stores['truesecret'] = truesecret
+ self.authconfig.add_auth('registry2.io', {
+ 'ServerAddress': 'registry2.io',
+ 'Username': 'reimu',
+ 'Password': 'hakurei',
+ })
+
+ assert self.authconfig.get_all_credentials() == {
+ 'https://gensokyo.jp/v2': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'gensokyo.jp': {
+ 'Username': 'sakuya',
+ 'Password': 'izayoi',
+ 'ServerAddress': 'https://gensokyo.jp/v2',
+ },
+ 'https://default.com/v2': {
+ 'Username': 'user',
+ 'Password': 'hunter2',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ 'default.com': {
+ 'Username': 'user',
+ 'Password': 'hunter2',
+ 'ServerAddress': 'https://default.com/v2',
+ },
+ 'registry1.io': {
+ 'ServerAddress': 'registry1.io',
+ 'Username': 'reimu',
+ 'Password': 'hakurei',
+ },
+ 'registry2.io': {
+ 'ServerAddress': 'registry2.io',
+ 'Username': 'reimu',
+ 'Password': 'hakurei',
+ }
+ }
+
+
+class InMemoryStore(credentials.Store):
+ def __init__(self, *args, **kwargs):
+ self.__store = {}
+
+ def get(self, server):
+ try:
+ return self.__store[server]
+ except KeyError:
+ raise credentials.errors.CredentialsNotFound()
+
+ def store(self, server, username, secret):
+ self.__store[server] = {
+ 'ServerURL': server,
+ 'Username': username,
+ 'Secret': secret,
+ }
+
+ def list(self):
+ return dict(
+ [(k, v['Username']) for k, v in self.__store.items()]
+ )
+
+ def erase(self, server):
+ del self.__store[server]
diff --git a/tests/unit/dockertypes_test.py b/tests/unit/dockertypes_test.py
index 2be0578..0689d07 100644
--- a/tests/unit/dockertypes_test.py
+++ b/tests/unit/dockertypes_test.py
@@ -14,7 +14,7 @@ from docker.types.services import convert_service_ports
try:
from unittest import mock
-except:
+except: # noqa: E722
import mock
@@ -85,6 +85,12 @@ class HostConfigTest(unittest.TestCase):
with pytest.raises(ValueError):
create_host_config(version='1.23', userns_mode='host12')
+ def test_create_host_config_with_uts(self):
+ config = create_host_config(version='1.15', uts_mode='host')
+ assert config.get('UTSMode') == 'host'
+ with pytest.raises(ValueError):
+ create_host_config(version='1.15', uts_mode='host12')
+
def test_create_host_config_with_oom_score_adj(self):
config = create_host_config(version='1.22', oom_score_adj=100)
assert config.get('OomScoreAdj') == 100
diff --git a/tests/unit/errors_test.py b/tests/unit/errors_test.py
index e27a9b1..2134f86 100644
--- a/tests/unit/errors_test.py
+++ b/tests/unit/errors_test.py
@@ -79,6 +79,27 @@ class APIErrorTest(unittest.TestCase):
err = APIError('', response=resp)
assert err.is_client_error() is True
+ def test_is_error_300(self):
+ """Report no error on 300 response."""
+ resp = requests.Response()
+ resp.status_code = 300
+ err = APIError('', response=resp)
+ assert err.is_error() is False
+
+ def test_is_error_400(self):
+ """Report error on 400 response."""
+ resp = requests.Response()
+ resp.status_code = 400
+ err = APIError('', response=resp)
+ assert err.is_error() is True
+
+ def test_is_error_500(self):
+ """Report error on 500 response."""
+ resp = requests.Response()
+ resp.status_code = 500
+ err = APIError('', response=resp)
+ assert err.is_error() is True
+
def test_create_error_from_exception(self):
resp = requests.Response()
resp.status_code = 500
diff --git a/tests/unit/models_containers_test.py b/tests/unit/models_containers_test.py
index 48a5288..da5f0ab 100644
--- a/tests/unit/models_containers_test.py
+++ b/tests/unit/models_containers_test.py
@@ -95,6 +95,7 @@ class ContainerCollectionTest(unittest.TestCase):
ulimits=[{"Name": "nofile", "Soft": 1024, "Hard": 2048}],
user='bob',
userns_mode='host',
+ uts_mode='host',
version='1.23',
volume_driver='some_driver',
volumes=[
@@ -174,6 +175,8 @@ class ContainerCollectionTest(unittest.TestCase):
'Tmpfs': {'/blah': ''},
'Ulimits': [{"Name": "nofile", "Soft": 1024, "Hard": 2048}],
'UsernsMode': 'host',
+ 'UTSMode': 'host',
+ 'VolumeDriver': 'some_driver',
'VolumesFrom': ['container'],
},
healthcheck={'test': 'true'},
@@ -188,7 +191,6 @@ class ContainerCollectionTest(unittest.TestCase):
stop_signal=9,
tty=True,
user='bob',
- volume_driver='some_driver',
volumes=[
'/mnt/vol2',
'/mnt/vol1',
@@ -230,7 +232,9 @@ class ContainerCollectionTest(unittest.TestCase):
container = client.containers.run('alpine', 'sleep 300', detach=True)
assert container.id == FAKE_CONTAINER_ID
- client.api.pull.assert_called_with('alpine', platform=None, tag=None)
+ client.api.pull.assert_called_with(
+ 'alpine', platform=None, tag=None, stream=True
+ )
def test_run_with_error(self):
client = make_fake_client()
@@ -412,10 +416,11 @@ class ContainerTest(unittest.TestCase):
client.api.exec_create.assert_called_with(
FAKE_CONTAINER_ID, "echo hello world", stdout=True, stderr=True,
stdin=False, tty=False, privileged=True, user='', environment=None,
- workdir=None
+ workdir=None,
)
client.api.exec_start.assert_called_with(
- FAKE_EXEC_ID, detach=False, tty=False, stream=True, socket=False
+ FAKE_EXEC_ID, detach=False, tty=False, stream=True, socket=False,
+ demux=False,
)
def test_exec_run_failure(self):
@@ -425,10 +430,11 @@ class ContainerTest(unittest.TestCase):
client.api.exec_create.assert_called_with(
FAKE_CONTAINER_ID, "docker ps", stdout=True, stderr=True,
stdin=False, tty=False, privileged=True, user='', environment=None,
- workdir=None
+ workdir=None,
)
client.api.exec_start.assert_called_with(
- FAKE_EXEC_ID, detach=False, tty=False, stream=False, socket=False
+ FAKE_EXEC_ID, detach=False, tty=False, stream=False, socket=False,
+ demux=False,
)
def test_export(self):
diff --git a/tests/unit/models_images_test.py b/tests/unit/models_images_test.py
index 6783279..fd894ab 100644
--- a/tests/unit/models_images_test.py
+++ b/tests/unit/models_images_test.py
@@ -1,6 +1,8 @@
+import unittest
+import warnings
+
from docker.constants import DEFAULT_DATA_CHUNK_SIZE
from docker.models.images import Image
-import unittest
from .fake_api import FAKE_IMAGE_ID
from .fake_api_client import make_fake_client
@@ -43,7 +45,9 @@ class ImageCollectionTest(unittest.TestCase):
def test_pull(self):
client = make_fake_client()
image = client.images.pull('test_image:latest')
- client.api.pull.assert_called_with('test_image', tag='latest')
+ client.api.pull.assert_called_with(
+ 'test_image', tag='latest', stream=True
+ )
client.api.inspect_image.assert_called_with('test_image:latest')
assert isinstance(image, Image)
assert image.id == FAKE_IMAGE_ID
@@ -51,7 +55,9 @@ class ImageCollectionTest(unittest.TestCase):
def test_pull_multiple(self):
client = make_fake_client()
images = client.images.pull('test_image')
- client.api.pull.assert_called_with('test_image', tag=None)
+ client.api.pull.assert_called_with(
+ 'test_image', tag=None, stream=True
+ )
client.api.images.assert_called_with(
all=False, name='test_image', filters=None
)
@@ -61,6 +67,16 @@ class ImageCollectionTest(unittest.TestCase):
assert isinstance(image, Image)
assert image.id == FAKE_IMAGE_ID
+ def test_pull_with_stream_param(self):
+ client = make_fake_client()
+ with warnings.catch_warnings(record=True) as w:
+ client.images.pull('test_image', stream=True)
+
+ assert len(w) == 1
+ assert str(w[0].message).startswith(
+ '`stream` is not a valid parameter'
+ )
+
def test_push(self):
client = make_fake_client()
client.images.push('foobar', insecure_registry=True)
diff --git a/tests/unit/models_services_test.py b/tests/unit/models_services_test.py
index 247bb4a..a4ac50c 100644
--- a/tests/unit/models_services_test.py
+++ b/tests/unit/models_services_test.py
@@ -26,6 +26,8 @@ class CreateServiceKwargsTest(unittest.TestCase):
'mounts': [{'some': 'mounts'}],
'stop_grace_period': 5,
'constraints': ['foo=bar'],
+ 'preferences': ['bar=baz'],
+ 'platforms': [('x86_64', 'linux')],
})
task_template = kwargs.pop('task_template')
@@ -41,7 +43,11 @@ class CreateServiceKwargsTest(unittest.TestCase):
'ContainerSpec', 'Resources', 'RestartPolicy', 'Placement',
'LogDriver', 'Networks'
])
- assert task_template['Placement'] == {'Constraints': ['foo=bar']}
+ assert task_template['Placement'] == {
+ 'Constraints': ['foo=bar'],
+ 'Preferences': ['bar=baz'],
+ 'Platforms': [{'Architecture': 'x86_64', 'OS': 'linux'}],
+ }
assert task_template['LogDriver'] == {
'Name': 'logdriver',
'Options': {'foo': 'bar'}
diff --git a/tests/unit/types_containers_test.py b/tests/unit/types_containers_test.py
new file mode 100644
index 0000000..b0ad0a7
--- /dev/null
+++ b/tests/unit/types_containers_test.py
@@ -0,0 +1,6 @@
+from docker.types.containers import ContainerConfig
+
+
+def test_uid_0_is_not_elided():
+ x = ContainerConfig(image='i', version='v', command='true', user=0)
+ assert x['User'] == '0'
diff --git a/tests/unit/utils_config_test.py b/tests/unit/utils_config_test.py
index 50ba383..b0934f9 100644
--- a/tests/unit/utils_config_test.py
+++ b/tests/unit/utils_config_test.py
@@ -4,8 +4,8 @@ import shutil
import tempfile
import json
-from py.test import ensuretemp
-from pytest import mark
+from pytest import mark, fixture
+
from docker.utils import config
try:
@@ -15,25 +15,25 @@ except ImportError:
class FindConfigFileTest(unittest.TestCase):
- def tmpdir(self, name):
- tmpdir = ensuretemp(name)
- self.addCleanup(tmpdir.remove)
- return tmpdir
+
+ @fixture(autouse=True)
+ def tmpdir(self, tmpdir):
+ self.mkdir = tmpdir.mkdir
def test_find_config_fallback(self):
- tmpdir = self.tmpdir('test_find_config_fallback')
+ tmpdir = self.mkdir('test_find_config_fallback')
with mock.patch.dict(os.environ, {'HOME': str(tmpdir)}):
assert config.find_config_file() is None
def test_find_config_from_explicit_path(self):
- tmpdir = self.tmpdir('test_find_config_from_explicit_path')
+ tmpdir = self.mkdir('test_find_config_from_explicit_path')
config_path = tmpdir.ensure('my-config-file.json')
assert config.find_config_file(str(config_path)) == str(config_path)
def test_find_config_from_environment(self):
- tmpdir = self.tmpdir('test_find_config_from_environment')
+ tmpdir = self.mkdir('test_find_config_from_environment')
config_path = tmpdir.ensure('config.json')
with mock.patch.dict(os.environ, {'DOCKER_CONFIG': str(tmpdir)}):
@@ -41,7 +41,7 @@ class FindConfigFileTest(unittest.TestCase):
@mark.skipif("sys.platform == 'win32'")
def test_find_config_from_home_posix(self):
- tmpdir = self.tmpdir('test_find_config_from_home_posix')
+ tmpdir = self.mkdir('test_find_config_from_home_posix')
config_path = tmpdir.ensure('.docker', 'config.json')
with mock.patch.dict(os.environ, {'HOME': str(tmpdir)}):
@@ -49,7 +49,7 @@ class FindConfigFileTest(unittest.TestCase):
@mark.skipif("sys.platform == 'win32'")
def test_find_config_from_home_legacy_name(self):
- tmpdir = self.tmpdir('test_find_config_from_home_legacy_name')
+ tmpdir = self.mkdir('test_find_config_from_home_legacy_name')
config_path = tmpdir.ensure('.dockercfg')
with mock.patch.dict(os.environ, {'HOME': str(tmpdir)}):
@@ -57,7 +57,7 @@ class FindConfigFileTest(unittest.TestCase):
@mark.skipif("sys.platform != 'win32'")
def test_find_config_from_home_windows(self):
- tmpdir = self.tmpdir('test_find_config_from_home_windows')
+ tmpdir = self.mkdir('test_find_config_from_home_windows')
config_path = tmpdir.ensure('.docker', 'config.json')
with mock.patch.dict(os.environ, {'USERPROFILE': str(tmpdir)}):
diff --git a/tests/unit/utils_proxy_test.py b/tests/unit/utils_proxy_test.py
new file mode 100644
index 0000000..ff0e14b
--- /dev/null
+++ b/tests/unit/utils_proxy_test.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+
+import unittest
+import six
+
+from docker.utils.proxy import ProxyConfig
+
+HTTP = 'http://test:80'
+HTTPS = 'https://test:443'
+FTP = 'ftp://user:password@host:23'
+NO_PROXY = 'localhost,.localdomain'
+CONFIG = ProxyConfig(http=HTTP, https=HTTPS, ftp=FTP, no_proxy=NO_PROXY)
+ENV = {
+ 'http_proxy': HTTP,
+ 'HTTP_PROXY': HTTP,
+ 'https_proxy': HTTPS,
+ 'HTTPS_PROXY': HTTPS,
+ 'ftp_proxy': FTP,
+ 'FTP_PROXY': FTP,
+ 'no_proxy': NO_PROXY,
+ 'NO_PROXY': NO_PROXY,
+}
+
+
+class ProxyConfigTest(unittest.TestCase):
+
+ def test_from_dict(self):
+ config = ProxyConfig.from_dict({
+ 'httpProxy': HTTP,
+ 'httpsProxy': HTTPS,
+ 'ftpProxy': FTP,
+ 'noProxy': NO_PROXY
+ })
+ self.assertEqual(CONFIG.http, config.http)
+ self.assertEqual(CONFIG.https, config.https)
+ self.assertEqual(CONFIG.ftp, config.ftp)
+ self.assertEqual(CONFIG.no_proxy, config.no_proxy)
+
+ def test_new(self):
+ config = ProxyConfig()
+ self.assertIsNone(config.http)
+ self.assertIsNone(config.https)
+ self.assertIsNone(config.ftp)
+ self.assertIsNone(config.no_proxy)
+
+ config = ProxyConfig(http='a', https='b', ftp='c', no_proxy='d')
+ self.assertEqual(config.http, 'a')
+ self.assertEqual(config.https, 'b')
+ self.assertEqual(config.ftp, 'c')
+ self.assertEqual(config.no_proxy, 'd')
+
+ def test_truthiness(self):
+ assert not ProxyConfig()
+ assert ProxyConfig(http='non-zero')
+ assert ProxyConfig(https='non-zero')
+ assert ProxyConfig(ftp='non-zero')
+ assert ProxyConfig(no_proxy='non-zero')
+
+ def test_environment(self):
+ self.assertDictEqual(CONFIG.get_environment(), ENV)
+ empty = ProxyConfig()
+ self.assertDictEqual(empty.get_environment(), {})
+
+ def test_inject_proxy_environment(self):
+ # Proxy config is non null, env is None.
+ self.assertSetEqual(
+ set(CONFIG.inject_proxy_environment(None)),
+ set(['{}={}'.format(k, v) for k, v in six.iteritems(ENV)]))
+
+ # Proxy config is null, env is None.
+ self.assertIsNone(ProxyConfig().inject_proxy_environment(None), None)
+
+ env = ['FOO=BAR', 'BAR=BAZ']
+
+ # Proxy config is non null, env is non null
+ actual = CONFIG.inject_proxy_environment(env)
+ expected = ['{}={}'.format(k, v) for k, v in six.iteritems(ENV)] + env
+ # It's important that the first 8 variables are the ones from the proxy
+ # config, and the last 2 are the ones from the input environment
+ self.assertSetEqual(set(actual[:8]), set(expected[:8]))
+ self.assertSetEqual(set(actual[-2:]), set(expected[-2:]))
+
+ # Proxy is null, and is non null
+ self.assertListEqual(ProxyConfig().inject_proxy_environment(env), env)
diff --git a/tests/unit/utils_test.py b/tests/unit/utils_test.py
index 8880cfe..d9cb002 100644
--- a/tests/unit/utils_test.py
+++ b/tests/unit/utils_test.py
@@ -11,6 +11,7 @@ import unittest
from docker.api.client import APIClient
+from docker.constants import IS_WINDOWS_PLATFORM
from docker.errors import DockerException
from docker.utils import (
convert_filters, convert_volume_binds, decode_json_header, kwargs_from_env,
@@ -83,15 +84,17 @@ class KwargsFromEnvTest(unittest.TestCase):
DOCKER_CERT_PATH=TEST_CERT_DIR,
DOCKER_TLS_VERIFY='1')
kwargs = kwargs_from_env(assert_hostname=False)
- assert 'https://192.168.59.103:2376' == kwargs['base_url']
+ assert 'tcp://192.168.59.103:2376' == kwargs['base_url']
assert 'ca.pem' in kwargs['tls'].ca_cert
assert 'cert.pem' in kwargs['tls'].cert[0]
assert 'key.pem' in kwargs['tls'].cert[1]
assert kwargs['tls'].assert_hostname is False
assert kwargs['tls'].verify
+
+ parsed_host = parse_host(kwargs['base_url'], IS_WINDOWS_PLATFORM, True)
try:
client = APIClient(**kwargs)
- assert kwargs['base_url'] == client.base_url
+ assert parsed_host == client.base_url
assert kwargs['tls'].ca_cert == client.verify
assert kwargs['tls'].cert == client.cert
except TypeError as e:
@@ -102,15 +105,16 @@ class KwargsFromEnvTest(unittest.TestCase):
DOCKER_CERT_PATH=TEST_CERT_DIR,
DOCKER_TLS_VERIFY='')
kwargs = kwargs_from_env(assert_hostname=True)
- assert 'https://192.168.59.103:2376' == kwargs['base_url']
+ assert 'tcp://192.168.59.103:2376' == kwargs['base_url']
assert 'ca.pem' in kwargs['tls'].ca_cert
assert 'cert.pem' in kwargs['tls'].cert[0]
assert 'key.pem' in kwargs['tls'].cert[1]
assert kwargs['tls'].assert_hostname is True
assert kwargs['tls'].verify is False
+ parsed_host = parse_host(kwargs['base_url'], IS_WINDOWS_PLATFORM, True)
try:
client = APIClient(**kwargs)
- assert kwargs['base_url'] == client.base_url
+ assert parsed_host == client.base_url
assert kwargs['tls'].cert == client.cert
assert not kwargs['tls'].verify
except TypeError as e:
@@ -272,6 +276,11 @@ class ParseHostTest(unittest.TestCase):
'tcp://',
'udp://127.0.0.1',
'udp://127.0.0.1:2375',
+ 'ssh://:22/path',
+ 'tcp://netloc:3333/path?q=1',
+ 'unix:///sock/path#fragment',
+ 'https://netloc:3333/path;params',
+ 'ssh://:clearpassword@host:22',
]
valid_hosts = {
@@ -281,7 +290,7 @@ class ParseHostTest(unittest.TestCase):
'http://:7777': 'http://127.0.0.1:7777',
'https://kokia.jp:2375': 'https://kokia.jp:2375',
'unix:///var/run/docker.sock': 'http+unix:///var/run/docker.sock',
- 'unix://': 'http+unix://var/run/docker.sock',
+ 'unix://': 'http+unix:///var/run/docker.sock',
'12.234.45.127:2375/docker/engine': (
'http://12.234.45.127:2375/docker/engine'
),
@@ -294,6 +303,9 @@ class ParseHostTest(unittest.TestCase):
'[fd12::82d1]:2375/docker/engine': (
'http://[fd12::82d1]:2375/docker/engine'
),
+ 'ssh://': 'ssh://127.0.0.1:22',
+ 'ssh://user@localhost:22': 'ssh://user@localhost:22',
+ 'ssh://user@remote': 'ssh://user@remote:22',
}
for host in invalid_hosts:
@@ -304,7 +316,7 @@ class ParseHostTest(unittest.TestCase):
assert parse_host(host, None) == expected
def test_parse_host_empty_value(self):
- unix_socket = 'http+unix://var/run/docker.sock'
+ unix_socket = 'http+unix:///var/run/docker.sock'
npipe = 'npipe:////./pipe/docker_engine'
for val in [None, '']:
@@ -449,8 +461,8 @@ class UtilsTest(unittest.TestCase):
tests = [
({'dangling': True}, '{"dangling": ["true"]}'),
({'dangling': "true"}, '{"dangling": ["true"]}'),
- ({'exited': 0}, '{"exited": [0]}'),
- ({'exited': [0, 1]}, '{"exited": [0, 1]}'),
+ ({'exited': 0}, '{"exited": ["0"]}'),
+ ({'exited': [0, 1]}, '{"exited": ["0", "1"]}'),
]
for filters, expected in tests:
@@ -483,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")
@@ -538,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")