Reformat code with black and isort.

This commit is contained in:
Felix Fontein
2025-10-06 18:34:59 +02:00
parent f45232635c
commit d65d37e9e9
132 changed files with 17581 additions and 14729 deletions
@@ -7,7 +7,9 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import datetime
@@ -27,14 +29,21 @@ from socketserver import ThreadingTCPServer
import pytest
import requests
from ansible_collections.community.docker.plugins.module_utils._api import constants, errors
from ansible_collections.community.docker.plugins.module_utils._api.api.client import APIClient
from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import DEFAULT_DOCKER_API_VERSION
from ansible_collections.community.docker.plugins.module_utils._api import (
constants,
errors,
)
from ansible_collections.community.docker.plugins.module_utils._api.api.client import (
APIClient,
)
from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import (
DEFAULT_DOCKER_API_VERSION,
)
from requests.packages import urllib3
from .. import fake_api
try:
from unittest import mock
except ImportError:
@@ -44,12 +53,19 @@ except ImportError:
DEFAULT_TIMEOUT_SECONDS = constants.DEFAULT_TIMEOUT_SECONDS
def response(status_code=200, content='', headers=None, reason=None, elapsed=0,
request=None, raw=None):
def response(
status_code=200,
content="",
headers=None,
reason=None,
elapsed=0,
request=None,
raw=None,
):
res = requests.Response()
res.status_code = status_code
if not isinstance(content, bytes):
content = json.dumps(content).encode('ascii')
content = json.dumps(content).encode("ascii")
res._content = content
res.headers = requests.structures.CaseInsensitiveDict(headers or {})
res.reason = reason
@@ -74,7 +90,7 @@ def fake_resp(method, url, *args, **kwargs):
elif (url, method) in fake_api.fake_responses:
key = (url, method)
if not key:
raise Exception(f'{method} {url}')
raise Exception(f"{method} {url}")
status_code, content = fake_api.fake_responses[key]()
return response(status_code=status_code, content=content)
@@ -83,38 +99,38 @@ fake_request = mock.Mock(side_effect=fake_resp)
def fake_get(self, url, *args, **kwargs):
return fake_request('GET', url, *args, **kwargs)
return fake_request("GET", url, *args, **kwargs)
def fake_post(self, url, *args, **kwargs):
return fake_request('POST', url, *args, **kwargs)
return fake_request("POST", url, *args, **kwargs)
def fake_put(self, url, *args, **kwargs):
return fake_request('PUT', url, *args, **kwargs)
return fake_request("PUT", url, *args, **kwargs)
def fake_delete(self, url, *args, **kwargs):
return fake_request('DELETE', url, *args, **kwargs)
return fake_request("DELETE", url, *args, **kwargs)
def fake_read_from_socket(self, response, stream, tty=False, demux=False):
return b""
url_base = f'{fake_api.prefix}/'
url_prefix = f'{url_base}v{DEFAULT_DOCKER_API_VERSION}/'
url_base = f"{fake_api.prefix}/"
url_prefix = f"{url_base}v{DEFAULT_DOCKER_API_VERSION}/"
class BaseAPIClientTest(unittest.TestCase):
def setUp(self):
self.patcher = mock.patch.multiple(
'ansible_collections.community.docker.plugins.module_utils._api.api.client.APIClient',
"ansible_collections.community.docker.plugins.module_utils._api.api.client.APIClient",
get=fake_get,
post=fake_post,
put=fake_put,
delete=fake_delete,
_read_from_socket=fake_read_from_socket
_read_from_socket=fake_read_from_socket,
)
self.patcher.start()
self.client = APIClient(version=DEFAULT_DOCKER_API_VERSION)
@@ -123,15 +139,20 @@ class BaseAPIClientTest(unittest.TestCase):
self.client.close()
self.patcher.stop()
def base_create_payload(self, img='busybox', cmd=None):
def base_create_payload(self, img="busybox", cmd=None):
if not cmd:
cmd = ['true']
return {"Tty": False, "Image": img, "Cmd": cmd,
"AttachStdin": False,
"AttachStderr": True, "AttachStdout": True,
"StdinOnce": False,
"OpenStdin": False, "NetworkDisabled": False,
}
cmd = ["true"]
return {
"Tty": False,
"Image": img,
"Cmd": cmd,
"AttachStdin": False,
"AttachStderr": True,
"AttachStdout": True,
"StdinOnce": False,
"OpenStdin": False,
"NetworkDisabled": False,
}
class DockerApiTest(BaseAPIClientTest):
@@ -139,55 +160,48 @@ class DockerApiTest(BaseAPIClientTest):
with pytest.raises(errors.DockerException) as excinfo:
APIClient(version=1.12)
assert str(
excinfo.value
) == 'Version parameter must be a string or None. Found float'
assert (
str(excinfo.value)
== "Version parameter must be a string or None. Found float"
)
def test_url_valid_resource(self):
url = self.client._url('/hello/{0}/world', 'somename')
assert url == f'{url_prefix}hello/somename/world'
url = self.client._url("/hello/{0}/world", "somename")
assert url == f"{url_prefix}hello/somename/world"
url = self.client._url(
'/hello/{0}/world/{1}', 'somename', 'someothername'
)
assert url == f'{url_prefix}hello/somename/world/someothername'
url = self.client._url("/hello/{0}/world/{1}", "somename", "someothername")
assert url == f"{url_prefix}hello/somename/world/someothername"
url = self.client._url('/hello/{0}/world', 'some?name')
assert url == f'{url_prefix}hello/some%3Fname/world'
url = self.client._url("/hello/{0}/world", "some?name")
assert url == f"{url_prefix}hello/some%3Fname/world"
url = self.client._url("/images/{0}/push", "localhost:5000/image")
assert url == f'{url_prefix}images/localhost:5000/image/push'
assert url == f"{url_prefix}images/localhost:5000/image/push"
def test_url_invalid_resource(self):
with pytest.raises(ValueError):
self.client._url('/hello/{0}/world', ['sakuya', 'izayoi'])
self.client._url("/hello/{0}/world", ["sakuya", "izayoi"])
def test_url_no_resource(self):
url = self.client._url('/simple')
assert url == f'{url_prefix}simple'
url = self.client._url("/simple")
assert url == f"{url_prefix}simple"
def test_url_unversioned_api(self):
url = self.client._url(
'/hello/{0}/world', 'somename', versioned_api=False
)
assert url == f'{url_base}hello/somename/world'
url = self.client._url("/hello/{0}/world", "somename", versioned_api=False)
assert url == f"{url_base}hello/somename/world"
def test_version(self):
self.client.version()
fake_request.assert_called_with(
'GET',
url_prefix + 'version',
timeout=DEFAULT_TIMEOUT_SECONDS
"GET", url_prefix + "version", timeout=DEFAULT_TIMEOUT_SECONDS
)
def test_version_no_api_version(self):
self.client.version(False)
fake_request.assert_called_with(
'GET',
url_base + 'version',
timeout=DEFAULT_TIMEOUT_SECONDS
"GET", url_base + "version", timeout=DEFAULT_TIMEOUT_SECONDS
)
def test_retrieve_server_version(self):
@@ -204,95 +218,94 @@ class DockerApiTest(BaseAPIClientTest):
self.client.info()
fake_request.assert_called_with(
'GET',
url_prefix + 'info',
timeout=DEFAULT_TIMEOUT_SECONDS
"GET", url_prefix + "info", timeout=DEFAULT_TIMEOUT_SECONDS
)
def test_search(self):
self.client.get_json('/images/search', params={'term': 'busybox'})
self.client.get_json("/images/search", params={"term": "busybox"})
fake_request.assert_called_with(
'GET',
url_prefix + 'images/search',
params={'term': 'busybox'},
timeout=DEFAULT_TIMEOUT_SECONDS
"GET",
url_prefix + "images/search",
params={"term": "busybox"},
timeout=DEFAULT_TIMEOUT_SECONDS,
)
def test_login(self):
self.client.login('sakuya', 'izayoi')
self.client.login("sakuya", "izayoi")
args = fake_request.call_args
assert args[0][0] == 'POST'
assert args[0][1] == url_prefix + 'auth'
assert json.loads(args[1]['data']) == {
'username': 'sakuya', 'password': 'izayoi'
assert args[0][0] == "POST"
assert args[0][1] == url_prefix + "auth"
assert json.loads(args[1]["data"]) == {
"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 args[1]["headers"] == {"Content-Type": "application/json"}
assert self.client._auth_configs.auths["docker.io"] == {
"email": None,
"password": "izayoi",
"username": "sakuya",
"serveraddress": None,
}
def _socket_path_for_client_session(self, client):
socket_adapter = client.get_adapter('http+docker://')
socket_adapter = client.get_adapter("http+docker://")
return socket_adapter.socket_path
def test_url_compatibility_unix(self):
c = APIClient(
base_url="unix://socket",
version=DEFAULT_DOCKER_API_VERSION)
c = APIClient(base_url="unix://socket", version=DEFAULT_DOCKER_API_VERSION)
assert self._socket_path_for_client_session(c) == '/socket'
assert self._socket_path_for_client_session(c) == "/socket"
def test_url_compatibility_unix_triple_slash(self):
c = APIClient(
base_url="unix:///socket",
version=DEFAULT_DOCKER_API_VERSION)
c = APIClient(base_url="unix:///socket", version=DEFAULT_DOCKER_API_VERSION)
assert self._socket_path_for_client_session(c) == '/socket'
assert self._socket_path_for_client_session(c) == "/socket"
def test_url_compatibility_http_unix_triple_slash(self):
c = APIClient(
base_url="http+unix:///socket",
version=DEFAULT_DOCKER_API_VERSION)
base_url="http+unix:///socket", version=DEFAULT_DOCKER_API_VERSION
)
assert self._socket_path_for_client_session(c) == '/socket'
assert self._socket_path_for_client_session(c) == "/socket"
def test_url_compatibility_http(self):
c = APIClient(
base_url="http://hostname:1234",
version=DEFAULT_DOCKER_API_VERSION)
base_url="http://hostname:1234", version=DEFAULT_DOCKER_API_VERSION
)
assert c.base_url == "http://hostname:1234"
def test_url_compatibility_tcp(self):
c = APIClient(
base_url="tcp://hostname:1234",
version=DEFAULT_DOCKER_API_VERSION)
base_url="tcp://hostname:1234", version=DEFAULT_DOCKER_API_VERSION
)
assert c.base_url == "http://hostname:1234"
def test_remove_link(self):
self.client.delete_call('/containers/{0}', '3cc2351ab11b', params={'v': False, 'link': True, 'force': False})
self.client.delete_call(
"/containers/{0}",
"3cc2351ab11b",
params={"v": False, "link": True, "force": False},
)
fake_request.assert_called_with(
'DELETE',
url_prefix + 'containers/3cc2351ab11b',
params={'v': False, 'link': True, 'force': False},
timeout=DEFAULT_TIMEOUT_SECONDS
"DELETE",
url_prefix + "containers/3cc2351ab11b",
params={"v": False, "link": True, "force": False},
timeout=DEFAULT_TIMEOUT_SECONDS,
)
def test_stream_helper_decoding(self):
status_code, content = fake_api.fake_responses[url_prefix + 'events']()
content_str = json.dumps(content).encode('utf-8')
status_code, content = fake_api.fake_responses[url_prefix + "events"]()
content_str = json.dumps(content).encode("utf-8")
body = io.BytesIO(content_str)
# mock a stream interface
raw_resp = urllib3.HTTPResponse(body=body)
setattr(raw_resp._fp, 'chunked', True)
setattr(raw_resp._fp, 'chunk_left', len(body.getvalue()) - 1)
setattr(raw_resp._fp, "chunked", True)
setattr(raw_resp._fp, "chunk_left", len(body.getvalue()) - 1)
# pass `decode=False` to the helper
raw_resp._fp.seek(0)
@@ -307,11 +320,11 @@ class DockerApiTest(BaseAPIClientTest):
assert result == content
# non-chunked response, pass `decode=False` to the helper
setattr(raw_resp._fp, 'chunked', False)
setattr(raw_resp._fp, "chunked", False)
raw_resp._fp.seek(0)
resp = response(status_code=status_code, content=content, raw=raw_resp)
result = next(self.client._stream_helper(resp))
assert result == content_str.decode('utf-8')
assert result == content_str.decode("utf-8")
# non-chunked response, pass `decode=True` to the helper
raw_resp._fp.seek(0)
@@ -326,7 +339,7 @@ class UnixSocketStreamTest(unittest.TestCase):
self.build_context = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, socket_dir)
self.addCleanup(shutil.rmtree, self.build_context)
self.socket_file = os.path.join(socket_dir, 'test_sock.sock')
self.socket_file = os.path.join(socket_dir, "test_sock.sock")
self.server_socket = self._setup_socket()
self.stop_server = False
server_thread = threading.Thread(target=self.run_server)
@@ -367,17 +380,17 @@ class UnixSocketStreamTest(unittest.TestCase):
self.server_socket.close()
def early_response_sending_handler(self, connection):
data = b''
data = b""
headers = None
connection.sendall(self.response)
while not headers:
data += connection.recv(2048)
parts = data.split(b'\r\n\r\n', 1)
parts = data.split(b"\r\n\r\n", 1)
if len(parts) == 2:
headers, data = parts
mo = re.search(r'Content-Length: ([0-9]+)', headers.decode())
mo = re.search(r"Content-Length: ([0-9]+)", headers.decode())
assert mo
content_length = int(mo.group(1))
@@ -387,77 +400,78 @@ class UnixSocketStreamTest(unittest.TestCase):
data += connection.recv(2048)
@pytest.mark.skipif(
constants.IS_WINDOWS_PLATFORM, reason='Unix only'
)
@pytest.mark.skipif(constants.IS_WINDOWS_PLATFORM, reason="Unix only")
def test_early_stream_response(self):
self.request_handler = self.early_response_sending_handler
lines = []
for i in range(0, 50):
line = str(i).encode()
lines += [f'{len(line):x}'.encode(), line]
lines.append(b'0')
lines.append(b'')
lines += [f"{len(line):x}".encode(), line]
lines.append(b"0")
lines.append(b"")
self.response = (
b'HTTP/1.1 200 OK\r\n'
b'Transfer-Encoding: chunked\r\n'
b'\r\n'
) + b'\r\n'.join(lines)
b"HTTP/1.1 200 OK\r\n" b"Transfer-Encoding: chunked\r\n" b"\r\n"
) + b"\r\n".join(lines)
with APIClient(
base_url="http+unix://" + self.socket_file,
version=DEFAULT_DOCKER_API_VERSION) as client:
base_url="http+unix://" + self.socket_file,
version=DEFAULT_DOCKER_API_VERSION,
) as client:
for i in range(5):
try:
params = {
't': None,
'remote': None,
'q': False,
'nocache': False,
'rm': False,
'forcerm': False,
'pull': False,
'dockerfile': 'Dockerfile',
"t": None,
"remote": None,
"q": False,
"nocache": False,
"rm": False,
"forcerm": False,
"pull": False,
"dockerfile": "Dockerfile",
}
headers = {'Content-Type': 'application/tar'}
data = b'...'
response = client._post(client._url('/build'), params=params, headers=headers, data=data, stream=True)
headers = {"Content-Type": "application/tar"}
data = b"..."
response = client._post(
client._url("/build"),
params=params,
headers=headers,
data=data,
stream=True,
)
stream = client._stream_helper(response, decode=False)
break
except requests.ConnectionError as e:
if i == 4:
raise e
assert list(stream) == [
str(i).encode() for i in range(50)
]
assert list(stream) == [str(i).encode() for i in range(50)]
@pytest.mark.skip(
'This test requires starting a networking server and tries to access it. '
'This does not work with network separation with Docker-based unit tests, '
'but it does work with podman-based unit tests.'
"This test requires starting a networking server and tries to access it. "
"This does not work with network separation with Docker-based unit tests, "
"but it does work with podman-based unit tests."
)
class TCPSocketStreamTest(unittest.TestCase):
stdout_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'''
"""
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?
'''
"""
@classmethod
def setup_class(cls):
cls.server = ThreadingTCPServer(('', 0), cls.get_handler_class())
cls.server = ThreadingTCPServer(("", 0), cls.get_handler_class())
cls.thread = threading.Thread(target=cls.server.serve_forever)
cls.thread.daemon = True
cls.thread.start()
cls.address = f'http://{socket.gethostname()}:{cls.server.server_address[1]}'
cls.address = f"http://{socket.gethostname()}:{cls.server.server_address[1]}"
@classmethod
def teardown_class(cls):
@@ -474,10 +488,9 @@ class TCPSocketStreamTest(unittest.TestCase):
def do_POST(self):
resp_data = self.get_resp_data()
self.send_response(101)
self.send_header(
'Content-Type', 'application/vnd.docker.raw-stream')
self.send_header('Connection', 'Upgrade')
self.send_header('Upgrade', 'tcp')
self.send_header("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)
@@ -485,22 +498,22 @@ class TCPSocketStreamTest(unittest.TestCase):
self.wfile.flush()
def get_resp_data(self):
path = self.path.split('/')[-1]
if path == 'tty':
path = self.path.split("/")[-1]
if path == "tty":
return stdout_data + stderr_data
elif path == 'no-tty':
data = b''
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(f'Unknown path {path}')
raise Exception(f"Unknown path {path}")
@staticmethod
def frame_header(stream, data):
return struct.pack('>BxxxL', stream, len(data))
return struct.pack(">BxxxL", stream, len(data))
return Handler
@@ -511,12 +524,11 @@ class TCPSocketStreamTest(unittest.TestCase):
version=DEFAULT_DOCKER_API_VERSION,
) as client:
if tty:
url = client._url('/tty')
url = client._url("/tty")
else:
url = client._url('/no-tty')
url = client._url("/no-tty")
resp = client._post(url, stream=True)
return client._read_from_socket(
resp, stream=stream, tty=tty, demux=demux)
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)
@@ -565,8 +577,8 @@ class UserAgentTest(unittest.TestCase):
def setUp(self):
self.patcher = mock.patch.object(
APIClient,
'send',
return_value=fake_resp("GET", f"{fake_api.prefix}/version")
"send",
return_value=fake_resp("GET", f"{fake_api.prefix}/version"),
)
self.mock_send = self.patcher.start()
@@ -579,18 +591,16 @@ class UserAgentTest(unittest.TestCase):
assert self.mock_send.call_count == 1
headers = self.mock_send.call_args[0][0].headers
expected = 'ansible-community.docker'
assert headers['User-Agent'] == expected
expected = "ansible-community.docker"
assert headers["User-Agent"] == expected
def test_custom_user_agent(self):
client = APIClient(
user_agent='foo/bar',
version=DEFAULT_DOCKER_API_VERSION)
client = APIClient(user_agent="foo/bar", version=DEFAULT_DOCKER_API_VERSION)
client.version()
assert self.mock_send.call_count == 1
headers = self.mock_send.call_args[0][0].headers
assert headers['User-Agent'] == 'foo/bar'
assert headers["User-Agent"] == "foo/bar"
class DisableSocketTest(unittest.TestCase):
@@ -7,7 +7,9 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DEFAULT_DOCKER_API_VERSION = '1.45'
DEFAULT_DOCKER_API_VERSION = "1.45"
+286 -321
View File
@@ -7,31 +7,36 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible_collections.community.docker.plugins.module_utils._api import constants
from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import DEFAULT_DOCKER_API_VERSION
from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import (
DEFAULT_DOCKER_API_VERSION,
)
from . import fake_stat
CURRENT_VERSION = f'v{DEFAULT_DOCKER_API_VERSION}'
FAKE_CONTAINER_ID = '3cc2351ab11b'
FAKE_IMAGE_ID = 'e9aa60c60128'
FAKE_EXEC_ID = 'd5d177f121dc'
FAKE_NETWORK_ID = '33fb6a3462b8'
FAKE_IMAGE_NAME = 'test_image'
FAKE_TARBALL_PATH = '/path/to/tarball'
FAKE_REPO_NAME = 'repo'
FAKE_TAG_NAME = 'tag'
FAKE_FILE_NAME = 'file'
FAKE_URL = 'myurl'
FAKE_PATH = '/path'
FAKE_VOLUME_NAME = 'perfectcherryblossom'
FAKE_NODE_ID = '24ifsmvkjbyhk'
FAKE_SECRET_ID = 'epdyrw4tsi03xy3deu8g8ly6o'
FAKE_SECRET_NAME = 'super_secret'
CURRENT_VERSION = f"v{DEFAULT_DOCKER_API_VERSION}"
FAKE_CONTAINER_ID = "3cc2351ab11b"
FAKE_IMAGE_ID = "e9aa60c60128"
FAKE_EXEC_ID = "d5d177f121dc"
FAKE_NETWORK_ID = "33fb6a3462b8"
FAKE_IMAGE_NAME = "test_image"
FAKE_TARBALL_PATH = "/path/to/tarball"
FAKE_REPO_NAME = "repo"
FAKE_TAG_NAME = "tag"
FAKE_FILE_NAME = "file"
FAKE_URL = "myurl"
FAKE_PATH = "/path"
FAKE_VOLUME_NAME = "perfectcherryblossom"
FAKE_NODE_ID = "24ifsmvkjbyhk"
FAKE_SECRET_ID = "epdyrw4tsi03xy3deu8g8ly6o"
FAKE_SECRET_NAME = "super_secret"
# Each method is prefixed with HTTP method (get, post...)
# for clarity and readability
@@ -40,31 +45,33 @@ FAKE_SECRET_NAME = 'super_secret'
def get_fake_version():
status_code = 200
response = {
'ApiVersion': '1.35',
'Arch': 'amd64',
'BuildTime': '2018-01-10T20:09:37.000000000+00:00',
'Components': [{
'Details': {
'ApiVersion': '1.35',
'Arch': 'amd64',
'BuildTime': '2018-01-10T20:09:37.000000000+00:00',
'Experimental': 'false',
'GitCommit': '03596f5',
'GoVersion': 'go1.9.2',
'KernelVersion': '4.4.0-112-generic',
'MinAPIVersion': '1.12',
'Os': 'linux'
},
'Name': 'Engine',
'Version': '18.01.0-ce'
}],
'GitCommit': '03596f5',
'GoVersion': 'go1.9.2',
'KernelVersion': '4.4.0-112-generic',
'MinAPIVersion': '1.12',
'Os': 'linux',
'Platform': {'Name': ''},
'Version': '18.01.0-ce'
"ApiVersion": "1.35",
"Arch": "amd64",
"BuildTime": "2018-01-10T20:09:37.000000000+00:00",
"Components": [
{
"Details": {
"ApiVersion": "1.35",
"Arch": "amd64",
"BuildTime": "2018-01-10T20:09:37.000000000+00:00",
"Experimental": "false",
"GitCommit": "03596f5",
"GoVersion": "go1.9.2",
"KernelVersion": "4.4.0-112-generic",
"MinAPIVersion": "1.12",
"Os": "linux",
},
"Name": "Engine",
"Version": "18.01.0-ce",
}
],
"GitCommit": "03596f5",
"GoVersion": "go1.9.2",
"KernelVersion": "4.4.0-112-generic",
"MinAPIVersion": "1.12",
"Os": "linux",
"Platform": {"Name": ""},
"Version": "18.01.0-ce",
}
return status_code, response
@@ -72,16 +79,20 @@ def get_fake_version():
def get_fake_info():
status_code = 200
response = {'Containers': 1, 'Images': 1, 'Debug': False,
'MemoryLimit': False, 'SwapLimit': False,
'IPv4Forwarding': True}
response = {
"Containers": 1,
"Images": 1,
"Debug": False,
"MemoryLimit": False,
"SwapLimit": False,
"IPv4Forwarding": True,
}
return status_code, response
def post_fake_auth():
status_code = 200
response = {'Status': 'Login Succeeded',
'IdentityToken': '9cbaf023786cd7'}
response = {"Status": "Login Succeeded", "IdentityToken": "9cbaf023786cd7"}
return status_code, response
@@ -91,34 +102,28 @@ def get_fake_ping():
def get_fake_search():
status_code = 200
response = [{'Name': 'busybox', 'Description': 'Fake Description'}]
response = [{"Name": "busybox", "Description": "Fake Description"}]
return status_code, response
def get_fake_images():
status_code = 200
response = [{
'Id': FAKE_IMAGE_ID,
'Created': '2 days ago',
'Repository': 'busybox',
'RepoTags': ['busybox:latest', 'busybox:1.0'],
}]
response = [
{
"Id": FAKE_IMAGE_ID,
"Created": "2 days ago",
"Repository": "busybox",
"RepoTags": ["busybox:latest", "busybox:1.0"],
}
]
return status_code, response
def get_fake_image_history():
status_code = 200
response = [
{
"Id": "b750fe79269d",
"Created": 1364102658,
"CreatedBy": "/bin/bash"
},
{
"Id": "27cf78414709",
"Created": 1364068391,
"CreatedBy": ""
}
{"Id": "b750fe79269d", "Created": 1364102658, "CreatedBy": "/bin/bash"},
{"Id": "27cf78414709", "Created": 1364068391, "CreatedBy": ""},
]
return status_code, response
@@ -126,64 +131,63 @@ def get_fake_image_history():
def post_fake_import_image():
status_code = 200
response = 'Import messages...'
response = "Import messages..."
return status_code, response
def get_fake_containers():
status_code = 200
response = [{
'Id': FAKE_CONTAINER_ID,
'Image': 'busybox:latest',
'Created': '2 days ago',
'Command': 'true',
'Status': 'fake status'
}]
response = [
{
"Id": FAKE_CONTAINER_ID,
"Image": "busybox:latest",
"Created": "2 days ago",
"Command": "true",
"Status": "fake status",
}
]
return status_code, response
def post_fake_start_container():
status_code = 200
response = {'Id': FAKE_CONTAINER_ID}
response = {"Id": FAKE_CONTAINER_ID}
return status_code, response
def post_fake_resize_container():
status_code = 200
response = {'Id': FAKE_CONTAINER_ID}
response = {"Id": FAKE_CONTAINER_ID}
return status_code, response
def post_fake_create_container():
status_code = 200
response = {'Id': FAKE_CONTAINER_ID}
response = {"Id": FAKE_CONTAINER_ID}
return status_code, response
def get_fake_inspect_container(tty=False):
status_code = 200
response = {
'Id': FAKE_CONTAINER_ID,
'Config': {'Labels': {'foo': 'bar'}, 'Privileged': True, 'Tty': tty},
'ID': FAKE_CONTAINER_ID,
'Image': 'busybox:latest',
'Name': 'foobar',
"Id": FAKE_CONTAINER_ID,
"Config": {"Labels": {"foo": "bar"}, "Privileged": True, "Tty": tty},
"ID": FAKE_CONTAINER_ID,
"Image": "busybox:latest",
"Name": "foobar",
"State": {
"Status": "running",
"Running": True,
"Pid": 0,
"ExitCode": 0,
"StartedAt": "2013-09-25T14:01:18.869545111+02:00",
"Ghost": False
"Ghost": False,
},
"HostConfig": {
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"LogConfig": {"Type": "json-file", "Config": {}},
},
"MacAddress": "02:42:ac:11:00:0a"
"MacAddress": "02:42:ac:11:00:0a",
}
return status_code, response
@@ -191,13 +195,12 @@ def get_fake_inspect_container(tty=False):
def get_fake_inspect_image():
status_code = 200
response = {
'Id': FAKE_IMAGE_ID,
'Parent': "27cf784147099545",
'Created': "2013-03-23T22:24:18.818426-07:00",
'Container': FAKE_CONTAINER_ID,
'Config': {'Labels': {'bar': 'foo'}},
'ContainerConfig':
{
"Id": FAKE_IMAGE_ID,
"Parent": "27cf784147099545",
"Created": "2013-03-23T22:24:18.818426-07:00",
"Container": FAKE_CONTAINER_ID,
"Config": {"Labels": {"bar": "foo"}},
"ContainerConfig": {
"Hostname": "",
"User": "",
"Memory": 0,
@@ -215,118 +218,128 @@ def get_fake_inspect_image():
"Image": "base",
"Volumes": "",
"VolumesFrom": "",
"WorkingDir": ""
"WorkingDir": "",
},
'Size': 6823592
"Size": 6823592,
}
return status_code, response
def get_fake_insert_image():
status_code = 200
response = {'StatusCode': 0}
response = {"StatusCode": 0}
return status_code, response
def get_fake_wait():
status_code = 200
response = {'StatusCode': 0}
response = {"StatusCode": 0}
return status_code, response
def get_fake_logs():
status_code = 200
response = (b'\x01\x00\x00\x00\x00\x00\x00\x00'
b'\x02\x00\x00\x00\x00\x00\x00\x00'
b'\x01\x00\x00\x00\x00\x00\x00\x11Flowering Nights\n'
b'\x01\x00\x00\x00\x00\x00\x00\x10(Sakuya Iyazoi)\n')
response = (
b"\x01\x00\x00\x00\x00\x00\x00\x00"
b"\x02\x00\x00\x00\x00\x00\x00\x00"
b"\x01\x00\x00\x00\x00\x00\x00\x11Flowering Nights\n"
b"\x01\x00\x00\x00\x00\x00\x00\x10(Sakuya Iyazoi)\n"
)
return status_code, response
def get_fake_diff():
status_code = 200
response = [{'Path': '/test', 'Kind': 1}]
response = [{"Path": "/test", "Kind": 1}]
return status_code, response
def get_fake_events():
status_code = 200
response = [{'status': 'stop', 'id': FAKE_CONTAINER_ID,
'from': FAKE_IMAGE_ID, 'time': 1423247867}]
response = [
{
"status": "stop",
"id": FAKE_CONTAINER_ID,
"from": FAKE_IMAGE_ID,
"time": 1423247867,
}
]
return status_code, response
def get_fake_export():
status_code = 200
response = 'Byte Stream....'
response = "Byte Stream...."
return status_code, response
def post_fake_exec_create():
status_code = 200
response = {'Id': FAKE_EXEC_ID}
response = {"Id": FAKE_EXEC_ID}
return status_code, response
def post_fake_exec_start():
status_code = 200
response = (b'\x01\x00\x00\x00\x00\x00\x00\x11bin\nboot\ndev\netc\n'
b'\x01\x00\x00\x00\x00\x00\x00\x12lib\nmnt\nproc\nroot\n'
b'\x01\x00\x00\x00\x00\x00\x00\x0csbin\nusr\nvar\n')
response = (
b"\x01\x00\x00\x00\x00\x00\x00\x11bin\nboot\ndev\netc\n"
b"\x01\x00\x00\x00\x00\x00\x00\x12lib\nmnt\nproc\nroot\n"
b"\x01\x00\x00\x00\x00\x00\x00\x0csbin\nusr\nvar\n"
)
return status_code, response
def post_fake_exec_resize():
status_code = 201
return status_code, ''
return status_code, ""
def get_fake_exec_inspect():
return 200, {
'OpenStderr': True,
'OpenStdout': True,
'Container': get_fake_inspect_container()[1],
'Running': False,
'ProcessConfig': {
'arguments': ['hello world'],
'tty': False,
'entrypoint': 'echo',
'privileged': False,
'user': ''
"OpenStderr": True,
"OpenStdout": True,
"Container": get_fake_inspect_container()[1],
"Running": False,
"ProcessConfig": {
"arguments": ["hello world"],
"tty": False,
"entrypoint": "echo",
"privileged": False,
"user": "",
},
'ExitCode': 0,
'ID': FAKE_EXEC_ID,
'OpenStdin': False
"ExitCode": 0,
"ID": FAKE_EXEC_ID,
"OpenStdin": False,
}
def post_fake_stop_container():
status_code = 200
response = {'Id': FAKE_CONTAINER_ID}
response = {"Id": FAKE_CONTAINER_ID}
return status_code, response
def post_fake_kill_container():
status_code = 200
response = {'Id': FAKE_CONTAINER_ID}
response = {"Id": FAKE_CONTAINER_ID}
return status_code, response
def post_fake_pause_container():
status_code = 200
response = {'Id': FAKE_CONTAINER_ID}
response = {"Id": FAKE_CONTAINER_ID}
return status_code, response
def post_fake_unpause_container():
status_code = 200
response = {'Id': FAKE_CONTAINER_ID}
response = {"Id": FAKE_CONTAINER_ID}
return status_code, response
def post_fake_restart_container():
status_code = 200
response = {'Id': FAKE_CONTAINER_ID}
response = {"Id": FAKE_CONTAINER_ID}
return status_code, response
@@ -337,55 +350,55 @@ def post_fake_rename_container():
def delete_fake_remove_container():
status_code = 200
response = {'Id': FAKE_CONTAINER_ID}
response = {"Id": FAKE_CONTAINER_ID}
return status_code, response
def post_fake_image_create():
status_code = 200
response = {'Id': FAKE_IMAGE_ID}
response = {"Id": FAKE_IMAGE_ID}
return status_code, response
def delete_fake_remove_image():
status_code = 200
response = {'Id': FAKE_IMAGE_ID}
response = {"Id": FAKE_IMAGE_ID}
return status_code, response
def get_fake_get_image():
status_code = 200
response = 'Byte Stream....'
response = "Byte Stream...."
return status_code, response
def post_fake_load_image():
status_code = 200
response = {'Id': FAKE_IMAGE_ID}
response = {"Id": FAKE_IMAGE_ID}
return status_code, response
def post_fake_commit():
status_code = 200
response = {'Id': FAKE_CONTAINER_ID}
response = {"Id": FAKE_CONTAINER_ID}
return status_code, response
def post_fake_push():
status_code = 200
response = {'Id': FAKE_IMAGE_ID}
response = {"Id": FAKE_IMAGE_ID}
return status_code, response
def post_fake_build_container():
status_code = 200
response = {'Id': FAKE_CONTAINER_ID}
response = {"Id": FAKE_CONTAINER_ID}
return status_code, response
def post_fake_tag_image():
status_code = 200
response = {'Id': FAKE_IMAGE_ID}
response = {"Id": FAKE_IMAGE_ID}
return status_code, response
@@ -397,27 +410,27 @@ def get_fake_stats():
def get_fake_top():
return 200, {
'Processes': [
"Processes": [
[
'root',
'26501',
'6907',
'0',
'10:32',
'pts/55',
'00:00:00',
'sleep 60',
"root",
"26501",
"6907",
"0",
"10:32",
"pts/55",
"00:00:00",
"sleep 60",
],
],
'Titles': [
'UID',
'PID',
'PPID',
'C',
'STIME',
'TTY',
'TIME',
'CMD',
"Titles": [
"UID",
"PID",
"PPID",
"C",
"STIME",
"TTY",
"TIME",
"CMD",
],
}
@@ -425,18 +438,19 @@ def get_fake_top():
def get_fake_volume_list():
status_code = 200
response = {
'Volumes': [
"Volumes": [
{
'Name': 'perfectcherryblossom',
'Driver': 'local',
'Mountpoint': '/var/lib/docker/volumes/perfectcherryblossom',
'Scope': 'local'
}, {
'Name': 'subterraneananimism',
'Driver': 'local',
'Mountpoint': '/var/lib/docker/volumes/subterraneananimism',
'Scope': 'local'
}
"Name": "perfectcherryblossom",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/perfectcherryblossom",
"Scope": "local",
},
{
"Name": "subterraneananimism",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/subterraneananimism",
"Scope": "local",
},
]
}
return status_code, response
@@ -445,13 +459,11 @@ def get_fake_volume_list():
def get_fake_volume():
status_code = 200
response = {
'Name': 'perfectcherryblossom',
'Driver': 'local',
'Mountpoint': '/var/lib/docker/volumes/perfectcherryblossom',
'Labels': {
'com.example.some-label': 'some-value'
},
'Scope': 'local'
"Name": "perfectcherryblossom",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/perfectcherryblossom",
"Labels": {"com.example.some-label": "some-value"},
"Scope": "local",
}
return status_code, response
@@ -461,7 +473,7 @@ def fake_remove_volume():
def post_fake_update_container():
return 200, {'Warnings': []}
return 200, {"Warnings": []}
def post_fake_update_node():
@@ -473,38 +485,33 @@ def post_fake_join_swarm():
def get_fake_network_list():
return 200, [{
"Name": "bridge",
"Id": FAKE_NETWORK_ID,
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": False,
"Internal": False,
"IPAM": {
"Driver": "default",
"Config": [
{
"Subnet": "172.17.0.0/16"
return 200, [
{
"Name": "bridge",
"Id": FAKE_NETWORK_ID,
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": False,
"Internal": False,
"IPAM": {"Driver": "default", "Config": [{"Subnet": "172.17.0.0/16"}]},
"Containers": {
FAKE_CONTAINER_ID: {
"EndpointID": "ed2419a97c1d99",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": "",
}
]
},
"Containers": {
FAKE_CONTAINER_ID: {
"EndpointID": "ed2419a97c1d99",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500",
},
}
}]
]
def get_fake_network():
@@ -529,127 +536,85 @@ def post_fake_network_disconnect():
def post_fake_secret():
status_code = 200
response = {'ID': FAKE_SECRET_ID}
response = {"ID": FAKE_SECRET_ID}
return status_code, response
# Maps real api url to fake response callback
prefix = 'http+docker://localhost'
prefix = "http+docker://localhost"
if constants.IS_WINDOWS_PLATFORM:
prefix = 'http+docker://localnpipe'
prefix = "http+docker://localnpipe"
fake_responses = {
f'{prefix}/version':
get_fake_version,
f'{prefix}/{CURRENT_VERSION}/version':
get_fake_version,
f'{prefix}/{CURRENT_VERSION}/info':
get_fake_info,
f'{prefix}/{CURRENT_VERSION}/auth':
post_fake_auth,
f'{prefix}/{CURRENT_VERSION}/_ping':
get_fake_ping,
f'{prefix}/{CURRENT_VERSION}/images/search':
get_fake_search,
f'{prefix}/{CURRENT_VERSION}/images/json':
get_fake_images,
f'{prefix}/{CURRENT_VERSION}/images/test_image/history':
get_fake_image_history,
f'{prefix}/{CURRENT_VERSION}/images/create':
post_fake_import_image,
f'{prefix}/{CURRENT_VERSION}/containers/json':
get_fake_containers,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/start':
post_fake_start_container,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/resize':
post_fake_resize_container,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/json':
get_fake_inspect_container,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/rename':
post_fake_rename_container,
f'{prefix}/{CURRENT_VERSION}/images/e9aa60c60128/tag':
post_fake_tag_image,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/wait':
get_fake_wait,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/logs':
get_fake_logs,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/changes':
get_fake_diff,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/export':
get_fake_export,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/update':
post_fake_update_container,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/exec':
post_fake_exec_create,
f'{prefix}/{CURRENT_VERSION}/exec/d5d177f121dc/start':
post_fake_exec_start,
f'{prefix}/{CURRENT_VERSION}/exec/d5d177f121dc/json':
get_fake_exec_inspect,
f'{prefix}/{CURRENT_VERSION}/exec/d5d177f121dc/resize':
post_fake_exec_resize,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/stats':
get_fake_stats,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/top':
get_fake_top,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/stop':
post_fake_stop_container,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/kill':
post_fake_kill_container,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/pause':
post_fake_pause_container,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/unpause':
post_fake_unpause_container,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/restart':
post_fake_restart_container,
f'{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b':
delete_fake_remove_container,
f'{prefix}/{CURRENT_VERSION}/images/create':
post_fake_image_create,
f'{prefix}/{CURRENT_VERSION}/images/e9aa60c60128':
delete_fake_remove_image,
f'{prefix}/{CURRENT_VERSION}/images/e9aa60c60128/get':
get_fake_get_image,
f'{prefix}/{CURRENT_VERSION}/images/load':
post_fake_load_image,
f'{prefix}/{CURRENT_VERSION}/images/test_image/json':
get_fake_inspect_image,
f'{prefix}/{CURRENT_VERSION}/images/test_image/insert':
get_fake_insert_image,
f'{prefix}/{CURRENT_VERSION}/images/test_image/push':
post_fake_push,
f'{prefix}/{CURRENT_VERSION}/commit':
post_fake_commit,
f'{prefix}/{CURRENT_VERSION}/containers/create':
post_fake_create_container,
f'{prefix}/{CURRENT_VERSION}/build':
post_fake_build_container,
f'{prefix}/{CURRENT_VERSION}/events':
get_fake_events,
(f'{prefix}/{CURRENT_VERSION}/volumes', 'GET'):
get_fake_volume_list,
(f'{prefix}/{CURRENT_VERSION}/volumes/create', 'POST'):
get_fake_volume,
(f'{prefix}/{CURRENT_VERSION}/volumes/{FAKE_VOLUME_NAME}', 'GET'):
get_fake_volume,
(f'{prefix}/{CURRENT_VERSION}/volumes/{FAKE_VOLUME_NAME}', 'DELETE'):
fake_remove_volume,
(f'{prefix}/{CURRENT_VERSION}/nodes/{FAKE_NODE_ID}/update?version=1', 'POST'):
post_fake_update_node,
(f'{prefix}/{CURRENT_VERSION}/swarm/join', 'POST'):
post_fake_join_swarm,
(f'{prefix}/{CURRENT_VERSION}/networks', 'GET'):
get_fake_network_list,
(f'{prefix}/{CURRENT_VERSION}/networks/create', 'POST'):
post_fake_network,
(f'{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}', 'GET'):
get_fake_network,
(f'{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}', 'DELETE'):
delete_fake_network,
(f'{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}/connect', 'POST'):
post_fake_network_connect,
(f'{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}/disconnect', 'POST'):
post_fake_network_disconnect,
f'{prefix}/{CURRENT_VERSION}/secrets/create':
post_fake_secret,
f"{prefix}/version": get_fake_version,
f"{prefix}/{CURRENT_VERSION}/version": get_fake_version,
f"{prefix}/{CURRENT_VERSION}/info": get_fake_info,
f"{prefix}/{CURRENT_VERSION}/auth": post_fake_auth,
f"{prefix}/{CURRENT_VERSION}/_ping": get_fake_ping,
f"{prefix}/{CURRENT_VERSION}/images/search": get_fake_search,
f"{prefix}/{CURRENT_VERSION}/images/json": get_fake_images,
f"{prefix}/{CURRENT_VERSION}/images/test_image/history": get_fake_image_history,
f"{prefix}/{CURRENT_VERSION}/images/create": post_fake_import_image,
f"{prefix}/{CURRENT_VERSION}/containers/json": get_fake_containers,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/start": post_fake_start_container,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/resize": post_fake_resize_container,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/json": get_fake_inspect_container,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/rename": post_fake_rename_container,
f"{prefix}/{CURRENT_VERSION}/images/e9aa60c60128/tag": post_fake_tag_image,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/wait": get_fake_wait,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/logs": get_fake_logs,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/changes": get_fake_diff,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/export": get_fake_export,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/update": post_fake_update_container,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/exec": post_fake_exec_create,
f"{prefix}/{CURRENT_VERSION}/exec/d5d177f121dc/start": post_fake_exec_start,
f"{prefix}/{CURRENT_VERSION}/exec/d5d177f121dc/json": get_fake_exec_inspect,
f"{prefix}/{CURRENT_VERSION}/exec/d5d177f121dc/resize": post_fake_exec_resize,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/stats": get_fake_stats,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/top": get_fake_top,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/stop": post_fake_stop_container,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/kill": post_fake_kill_container,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/pause": post_fake_pause_container,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/unpause": post_fake_unpause_container,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b/restart": post_fake_restart_container,
f"{prefix}/{CURRENT_VERSION}/containers/3cc2351ab11b": delete_fake_remove_container,
f"{prefix}/{CURRENT_VERSION}/images/create": post_fake_image_create,
f"{prefix}/{CURRENT_VERSION}/images/e9aa60c60128": delete_fake_remove_image,
f"{prefix}/{CURRENT_VERSION}/images/e9aa60c60128/get": get_fake_get_image,
f"{prefix}/{CURRENT_VERSION}/images/load": post_fake_load_image,
f"{prefix}/{CURRENT_VERSION}/images/test_image/json": get_fake_inspect_image,
f"{prefix}/{CURRENT_VERSION}/images/test_image/insert": get_fake_insert_image,
f"{prefix}/{CURRENT_VERSION}/images/test_image/push": post_fake_push,
f"{prefix}/{CURRENT_VERSION}/commit": post_fake_commit,
f"{prefix}/{CURRENT_VERSION}/containers/create": post_fake_create_container,
f"{prefix}/{CURRENT_VERSION}/build": post_fake_build_container,
f"{prefix}/{CURRENT_VERSION}/events": get_fake_events,
(f"{prefix}/{CURRENT_VERSION}/volumes", "GET"): get_fake_volume_list,
(f"{prefix}/{CURRENT_VERSION}/volumes/create", "POST"): get_fake_volume,
(f"{prefix}/{CURRENT_VERSION}/volumes/{FAKE_VOLUME_NAME}", "GET"): get_fake_volume,
(
f"{prefix}/{CURRENT_VERSION}/volumes/{FAKE_VOLUME_NAME}",
"DELETE",
): fake_remove_volume,
(
f"{prefix}/{CURRENT_VERSION}/nodes/{FAKE_NODE_ID}/update?version=1",
"POST",
): post_fake_update_node,
(f"{prefix}/{CURRENT_VERSION}/swarm/join", "POST"): post_fake_join_swarm,
(f"{prefix}/{CURRENT_VERSION}/networks", "GET"): get_fake_network_list,
(f"{prefix}/{CURRENT_VERSION}/networks/create", "POST"): post_fake_network,
(f"{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}", "GET"): get_fake_network,
(
f"{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}",
"DELETE",
): delete_fake_network,
(
f"{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}/connect",
"POST",
): post_fake_network_connect,
(
f"{prefix}/{CURRENT_VERSION}/networks/{FAKE_NETWORK_ID}/disconnect",
"POST",
): post_fake_network_disconnect,
f"{prefix}/{CURRENT_VERSION}/secrets/create": post_fake_secret,
}
@@ -7,7 +7,9 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
OBJ = {
@@ -20,26 +22,17 @@ OBJ = {
"tx_bytes": 1176,
"tx_packets": 13,
"tx_errors": 0,
"tx_dropped": 0
"tx_dropped": 0,
},
"cpu_stats": {
"cpu_usage": {
"total_usage": 157260874053,
"percpu_usage": [
52196306950,
24118413549,
53292684398,
27653469156
],
"percpu_usage": [52196306950, 24118413549, 53292684398, 27653469156],
"usage_in_kernelmode": 37140000000,
"usage_in_usermode": 62140000000
"usage_in_usermode": 62140000000,
},
"system_cpu_usage": 3.0881377e+14,
"throttling_data": {
"periods": 0,
"throttled_periods": 0,
"throttled_time": 0
}
"system_cpu_usage": 3.0881377e14,
"throttling_data": {"periods": 0, "throttled_periods": 0, "throttled_time": 0},
},
"memory_stats": {
"usage": 179314688,
@@ -48,7 +41,7 @@ OBJ = {
"active_anon": 90804224,
"active_file": 2195456,
"cache": 3096576,
"hierarchical_memory_limit": 1.844674407371e+19,
"hierarchical_memory_limit": 1.844674407371e19,
"inactive_anon": 85516288,
"inactive_file": 798720,
"mapped_file": 2646016,
@@ -73,73 +66,31 @@ OBJ = {
"total_unevictable": 0,
"total_writeback": 0,
"unevictable": 0,
"writeback": 0
"writeback": 0,
},
"failcnt": 0,
"limit": 8039038976
"limit": 8039038976,
},
"blkio_stats": {
"io_service_bytes_recursive": [
{
"major": 8,
"minor": 0,
"op": "Read",
"value": 72843264
}, {
"major": 8,
"minor": 0,
"op": "Write",
"value": 4096
}, {
"major": 8,
"minor": 0,
"op": "Sync",
"value": 4096
}, {
"major": 8,
"minor": 0,
"op": "Async",
"value": 72843264
}, {
"major": 8,
"minor": 0,
"op": "Total",
"value": 72847360
}
{"major": 8, "minor": 0, "op": "Read", "value": 72843264},
{"major": 8, "minor": 0, "op": "Write", "value": 4096},
{"major": 8, "minor": 0, "op": "Sync", "value": 4096},
{"major": 8, "minor": 0, "op": "Async", "value": 72843264},
{"major": 8, "minor": 0, "op": "Total", "value": 72847360},
],
"io_serviced_recursive": [
{
"major": 8,
"minor": 0,
"op": "Read",
"value": 10581
}, {
"major": 8,
"minor": 0,
"op": "Write",
"value": 1
}, {
"major": 8,
"minor": 0,
"op": "Sync",
"value": 1
}, {
"major": 8,
"minor": 0,
"op": "Async",
"value": 10581
}, {
"major": 8,
"minor": 0,
"op": "Total",
"value": 10582
}
{"major": 8, "minor": 0, "op": "Read", "value": 10581},
{"major": 8, "minor": 0, "op": "Write", "value": 1},
{"major": 8, "minor": 0, "op": "Sync", "value": 1},
{"major": 8, "minor": 0, "op": "Async", "value": 10581},
{"major": 8, "minor": 0, "op": "Total", "value": 10582},
],
"io_queue_recursive": [],
"io_service_time_recursive": [],
"io_wait_time_recursive": [],
"io_merged_recursive": [],
"io_time_recursive": [],
"sectors_recursive": []
}
"sectors_recursive": [],
},
}
File diff suppressed because it is too large Load Diff
@@ -7,34 +7,35 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import unittest
import pytest
from ansible_collections.community.docker.plugins.module_utils._api import errors
from ansible_collections.community.docker.plugins.module_utils._api.constants import (
DEFAULT_NPIPE,
DEFAULT_UNIX_SOCKET,
IS_WINDOWS_PLATFORM,
)
from ansible_collections.community.docker.plugins.module_utils._api.context.api import ContextAPI
from ansible_collections.community.docker.plugins.module_utils._api.context.context import Context
from ansible_collections.community.docker.plugins.module_utils._api.context.api import (
ContextAPI,
)
from ansible_collections.community.docker.plugins.module_utils._api.context.context import (
Context,
)
class BaseContextTest(unittest.TestCase):
@pytest.mark.skipif(
IS_WINDOWS_PLATFORM, reason='Linux specific path check'
)
@pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason="Linux specific path check")
def test_url_compatibility_on_linux(self):
c = Context("test")
assert c.Host == DEFAULT_UNIX_SOCKET[5:]
@pytest.mark.skipif(
not IS_WINDOWS_PLATFORM, reason='Windows specific path check'
)
@pytest.mark.skipif(not IS_WINDOWS_PLATFORM, reason="Windows specific path check")
def test_url_compatibility_on_windows(self):
c = Context("test")
assert c.Host == DEFAULT_NPIPE
@@ -7,17 +7,19 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import unittest
import requests
from ansible_collections.community.docker.plugins.module_utils._api.errors import (
APIError, DockerException,
create_unexpected_kwargs_error,
APIError,
DockerException,
create_api_error_from_http_exception,
create_unexpected_kwargs_error,
)
@@ -32,90 +34,90 @@ class APIErrorTest(unittest.TestCase):
"""The status_code property is present with 200 response."""
resp = requests.Response()
resp.status_code = 200
err = APIError('', response=resp)
err = APIError("", response=resp)
assert err.status_code == 200
def test_status_code_400(self):
"""The status_code property is present with 400 response."""
resp = requests.Response()
resp.status_code = 400
err = APIError('', response=resp)
err = APIError("", response=resp)
assert err.status_code == 400
def test_status_code_500(self):
"""The status_code property is present with 500 response."""
resp = requests.Response()
resp.status_code = 500
err = APIError('', response=resp)
err = APIError("", response=resp)
assert err.status_code == 500
def test_is_server_error_200(self):
"""Report not server error on 200 response."""
resp = requests.Response()
resp.status_code = 200
err = APIError('', response=resp)
err = APIError("", response=resp)
assert err.is_server_error() is False
def test_is_server_error_300(self):
"""Report not server error on 300 response."""
resp = requests.Response()
resp.status_code = 300
err = APIError('', response=resp)
err = APIError("", response=resp)
assert err.is_server_error() is False
def test_is_server_error_400(self):
"""Report not server error on 400 response."""
resp = requests.Response()
resp.status_code = 400
err = APIError('', response=resp)
err = APIError("", response=resp)
assert err.is_server_error() is False
def test_is_server_error_500(self):
"""Report server error on 500 response."""
resp = requests.Response()
resp.status_code = 500
err = APIError('', response=resp)
err = APIError("", response=resp)
assert err.is_server_error() is True
def test_is_client_error_500(self):
"""Report not client error on 500 response."""
resp = requests.Response()
resp.status_code = 500
err = APIError('', response=resp)
err = APIError("", response=resp)
assert err.is_client_error() is False
def test_is_client_error_400(self):
"""Report client error on 400 response."""
resp = requests.Response()
resp.status_code = 400
err = APIError('', response=resp)
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)
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)
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)
err = APIError("", response=resp)
assert err.is_error() is True
def test_create_error_from_exception(self):
resp = requests.Response()
resp.status_code = 500
err = APIError('')
err = APIError("")
try:
resp.raise_for_status()
except requests.exceptions.HTTPError as e:
@@ -128,9 +130,9 @@ class APIErrorTest(unittest.TestCase):
class CreateUnexpectedKwargsErrorTest(unittest.TestCase):
def test_create_unexpected_kwargs_error_single(self):
e = create_unexpected_kwargs_error('f', {'foo': 'bar'})
e = create_unexpected_kwargs_error("f", {"foo": "bar"})
assert str(e) == "f() got an unexpected keyword argument 'foo'"
def test_create_unexpected_kwargs_error_multiple(self):
e = create_unexpected_kwargs_error('f', {'foo': 'bar', 'baz': 'bosh'})
e = create_unexpected_kwargs_error("f", {"foo": "bar", "baz": "bosh"})
assert str(e) == "f() got unexpected keyword arguments 'baz', 'foo'"
@@ -7,19 +7,23 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import unittest
from ansible_collections.community.docker.plugins.module_utils._api.transport.sshconn import SSHSocket, SSHHTTPAdapter
from ansible_collections.community.docker.plugins.module_utils._api.transport.sshconn import (
SSHHTTPAdapter,
SSHSocket,
)
class SSHAdapterTest(unittest.TestCase):
@staticmethod
def test_ssh_hostname_prefix_trim():
conn = SSHHTTPAdapter(
base_url="ssh://user@hostname:1234", shell_out=True)
conn = SSHHTTPAdapter(base_url="ssh://user@hostname:1234", shell_out=True)
assert conn.ssh_host == "user@hostname:1234"
@staticmethod
@@ -7,30 +7,30 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import unittest
import pytest
from ansible_collections.community.docker.plugins.module_utils._api.transport import (
ssladapter,
)
from ansible_collections.community.docker.plugins.module_utils._api.transport import ssladapter
HAS_MATCH_HOSTNAME = True
try:
from backports.ssl_match_hostname import (
match_hostname, CertificateError
)
from backports.ssl_match_hostname import CertificateError, match_hostname
except ImportError:
try:
from ssl import (
match_hostname, CertificateError
)
from ssl import CertificateError, match_hostname
except ImportError:
HAS_MATCH_HOSTNAME = False
try:
from ssl import OP_NO_SSLv3, OP_NO_SSLv2, OP_NO_TLSv1
from ssl import OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_TLSv1
except ImportError:
OP_NO_SSLv2 = 0x1000000
OP_NO_SSLv3 = 0x2000000
@@ -47,51 +47,51 @@ class SSLAdapterTest(unittest.TestCase):
assert not ssl_context.options & OP_NO_TLSv1
@pytest.mark.skipif(not HAS_MATCH_HOSTNAME, reason='match_hostname is not available')
@pytest.mark.skipif(not HAS_MATCH_HOSTNAME, reason="match_hostname is not available")
class MatchHostnameTest(unittest.TestCase):
cert = {
'issuer': (
(('countryName', 'US'),),
(('stateOrProvinceName', 'California'),),
(('localityName', 'San Francisco'),),
(('organizationName', 'Docker Inc'),),
(('organizationalUnitName', 'Docker-Python'),),
(('commonName', 'localhost'),),
(('emailAddress', 'info@docker.com'),)
"issuer": (
(("countryName", "US"),),
(("stateOrProvinceName", "California"),),
(("localityName", "San Francisco"),),
(("organizationName", "Docker Inc"),),
(("organizationalUnitName", "Docker-Python"),),
(("commonName", "localhost"),),
(("emailAddress", "info@docker.com"),),
),
'notAfter': 'Mar 25 23:08:23 2030 GMT',
'notBefore': 'Mar 25 23:08:23 2016 GMT',
'serialNumber': 'BD5F894C839C548F',
'subject': (
(('countryName', 'US'),),
(('stateOrProvinceName', 'California'),),
(('localityName', 'San Francisco'),),
(('organizationName', 'Docker Inc'),),
(('organizationalUnitName', 'Docker-Python'),),
(('commonName', 'localhost'),),
(('emailAddress', 'info@docker.com'),)
"notAfter": "Mar 25 23:08:23 2030 GMT",
"notBefore": "Mar 25 23:08:23 2016 GMT",
"serialNumber": "BD5F894C839C548F",
"subject": (
(("countryName", "US"),),
(("stateOrProvinceName", "California"),),
(("localityName", "San Francisco"),),
(("organizationName", "Docker Inc"),),
(("organizationalUnitName", "Docker-Python"),),
(("commonName", "localhost"),),
(("emailAddress", "info@docker.com"),),
),
'subjectAltName': (
('DNS', 'localhost'),
('DNS', '*.gensokyo.jp'),
('IP Address', '127.0.0.1'),
"subjectAltName": (
("DNS", "localhost"),
("DNS", "*.gensokyo.jp"),
("IP Address", "127.0.0.1"),
),
'version': 3
"version": 3,
}
def test_match_ip_address_success(self):
assert match_hostname(self.cert, '127.0.0.1') is None
assert match_hostname(self.cert, "127.0.0.1") is None
def test_match_localhost_success(self):
assert match_hostname(self.cert, 'localhost') is None
assert match_hostname(self.cert, "localhost") is None
def test_match_dns_success(self):
assert match_hostname(self.cert, 'touhou.gensokyo.jp') is None
assert match_hostname(self.cert, "touhou.gensokyo.jp") is None
def test_match_ip_address_failure(self):
with pytest.raises(CertificateError):
match_hostname(self.cert, '192.168.0.25')
match_hostname(self.cert, "192.168.0.25")
def test_match_dns_failure(self):
with pytest.raises(CertificateError):
match_hostname(self.cert, 'foobar.co.uk')
match_hostname(self.cert, "foobar.co.uk")
@@ -7,7 +7,9 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os
@@ -19,9 +21,13 @@ import tempfile
import unittest
import pytest
from ansible_collections.community.docker.plugins.module_utils._api.constants import IS_WINDOWS_PLATFORM
from ansible_collections.community.docker.plugins.module_utils._api.utils.build import exclude_paths, tar
from ansible_collections.community.docker.plugins.module_utils._api.constants import (
IS_WINDOWS_PLATFORM,
)
from ansible_collections.community.docker.plugins.module_utils._api.utils.build import (
exclude_paths,
tar,
)
def make_tree(dirs, files):
@@ -31,7 +37,7 @@ def make_tree(dirs, files):
os.makedirs(os.path.join(base, path))
for path in files:
with open(os.path.join(base, path), 'w') as f:
with open(os.path.join(base, path), "w") as f:
f.write("content")
return base
@@ -42,45 +48,45 @@ def convert_paths(collection):
def convert_path(path):
return path.replace('/', os.path.sep)
return path.replace("/", os.path.sep)
class ExcludePathsTest(unittest.TestCase):
dirs = [
'foo',
'foo/bar',
'bar',
'target',
'target/subdir',
'subdir',
'subdir/target',
'subdir/target/subdir',
'subdir/subdir2',
'subdir/subdir2/target',
'subdir/subdir2/target/subdir'
"foo",
"foo/bar",
"bar",
"target",
"target/subdir",
"subdir",
"subdir/target",
"subdir/target/subdir",
"subdir/subdir2",
"subdir/subdir2/target",
"subdir/subdir2/target/subdir",
]
files = [
'Dockerfile',
'Dockerfile.alt',
'.dockerignore',
'a.py',
'a.go',
'b.py',
'cde.py',
'foo/a.py',
'foo/b.py',
'foo/bar/a.py',
'bar/a.py',
'foo/Dockerfile3',
'target/file.txt',
'target/subdir/file.txt',
'subdir/file.txt',
'subdir/target/file.txt',
'subdir/target/subdir/file.txt',
'subdir/subdir2/file.txt',
'subdir/subdir2/target/file.txt',
'subdir/subdir2/target/subdir/file.txt',
"Dockerfile",
"Dockerfile.alt",
".dockerignore",
"a.py",
"a.go",
"b.py",
"cde.py",
"foo/a.py",
"foo/b.py",
"foo/bar/a.py",
"bar/a.py",
"foo/Dockerfile3",
"target/file.txt",
"target/subdir/file.txt",
"subdir/file.txt",
"subdir/target/file.txt",
"subdir/target/subdir/file.txt",
"subdir/subdir2/file.txt",
"subdir/subdir2/target/file.txt",
"subdir/subdir2/target/subdir/file.txt",
]
all_paths = set(dirs + files)
@@ -95,14 +101,14 @@ class ExcludePathsTest(unittest.TestCase):
return set(exclude_paths(self.base, patterns, dockerfile=dockerfile))
def test_no_excludes(self):
assert self.exclude(['']) == convert_paths(self.all_paths)
assert self.exclude([""]) == convert_paths(self.all_paths)
def test_no_dupes(self):
paths = exclude_paths(self.base, ['!a.py'])
paths = exclude_paths(self.base, ["!a.py"])
assert sorted(paths) == sorted(set(paths))
def test_wildcard_exclude(self):
assert self.exclude(['*']) == set(['Dockerfile', '.dockerignore'])
assert self.exclude(["*"]) == set(["Dockerfile", ".dockerignore"])
def test_exclude_dockerfile_dockerignore(self):
"""
@@ -110,7 +116,7 @@ class ExcludePathsTest(unittest.TestCase):
Dockerfile and/or .dockerignore, don't exclude them from
the actual tar file.
"""
assert self.exclude(['Dockerfile', '.dockerignore']) == convert_paths(
assert self.exclude(["Dockerfile", ".dockerignore"]) == convert_paths(
self.all_paths
)
@@ -119,287 +125,296 @@ class ExcludePathsTest(unittest.TestCase):
If we're using a custom Dockerfile, make sure that's not
excluded.
"""
assert self.exclude(['*'], dockerfile='Dockerfile.alt') == set(['Dockerfile.alt', '.dockerignore'])
assert self.exclude(["*"], dockerfile="Dockerfile.alt") == set(
["Dockerfile.alt", ".dockerignore"]
)
assert self.exclude(
['*'], dockerfile='foo/Dockerfile3'
) == convert_paths(set(['foo/Dockerfile3', '.dockerignore']))
assert self.exclude(["*"], dockerfile="foo/Dockerfile3") == convert_paths(
set(["foo/Dockerfile3", ".dockerignore"])
)
# https://github.com/docker/docker-py/issues/1956
assert self.exclude(
['*'], dockerfile='./foo/Dockerfile3'
) == convert_paths(set(['foo/Dockerfile3', '.dockerignore']))
assert self.exclude(["*"], dockerfile="./foo/Dockerfile3") == convert_paths(
set(["foo/Dockerfile3", ".dockerignore"])
)
def test_exclude_dockerfile_child(self):
includes = self.exclude(['foo/'], dockerfile='foo/Dockerfile3')
assert convert_path('foo/Dockerfile3') in includes
assert convert_path('foo/a.py') not in includes
includes = self.exclude(["foo/"], dockerfile="foo/Dockerfile3")
assert convert_path("foo/Dockerfile3") in includes
assert convert_path("foo/a.py") not in includes
def test_single_filename(self):
assert self.exclude(['a.py']) == convert_paths(
self.all_paths - set(['a.py'])
)
assert self.exclude(["a.py"]) == convert_paths(self.all_paths - set(["a.py"]))
def test_single_filename_leading_dot_slash(self):
assert self.exclude(['./a.py']) == convert_paths(
self.all_paths - set(['a.py'])
)
assert self.exclude(["./a.py"]) == convert_paths(self.all_paths - set(["a.py"]))
# As odd as it sounds, a filename pattern with a trailing slash on the
# end *will* result in that file being excluded.
def test_single_filename_trailing_slash(self):
assert self.exclude(['a.py/']) == convert_paths(
self.all_paths - set(['a.py'])
)
assert self.exclude(["a.py/"]) == convert_paths(self.all_paths - set(["a.py"]))
def test_wildcard_filename_start(self):
assert self.exclude(['*.py']) == convert_paths(
self.all_paths - set(['a.py', 'b.py', 'cde.py'])
assert self.exclude(["*.py"]) == convert_paths(
self.all_paths - set(["a.py", "b.py", "cde.py"])
)
def test_wildcard_with_exception(self):
assert self.exclude(['*.py', '!b.py']) == convert_paths(
self.all_paths - set(['a.py', 'cde.py'])
assert self.exclude(["*.py", "!b.py"]) == convert_paths(
self.all_paths - set(["a.py", "cde.py"])
)
def test_wildcard_with_wildcard_exception(self):
assert self.exclude(['*.*', '!*.go']) == convert_paths(
self.all_paths - set([
'a.py', 'b.py', 'cde.py', 'Dockerfile.alt',
])
assert self.exclude(["*.*", "!*.go"]) == convert_paths(
self.all_paths
- set(
[
"a.py",
"b.py",
"cde.py",
"Dockerfile.alt",
]
)
)
def test_wildcard_filename_end(self):
assert self.exclude(['a.*']) == convert_paths(
self.all_paths - set(['a.py', 'a.go'])
assert self.exclude(["a.*"]) == convert_paths(
self.all_paths - set(["a.py", "a.go"])
)
def test_question_mark(self):
assert self.exclude(['?.py']) == convert_paths(
self.all_paths - set(['a.py', 'b.py'])
assert self.exclude(["?.py"]) == convert_paths(
self.all_paths - set(["a.py", "b.py"])
)
def test_single_subdir_single_filename(self):
assert self.exclude(['foo/a.py']) == convert_paths(
self.all_paths - set(['foo/a.py'])
assert self.exclude(["foo/a.py"]) == convert_paths(
self.all_paths - set(["foo/a.py"])
)
def test_single_subdir_single_filename_leading_slash(self):
assert self.exclude(['/foo/a.py']) == convert_paths(
self.all_paths - set(['foo/a.py'])
assert self.exclude(["/foo/a.py"]) == convert_paths(
self.all_paths - set(["foo/a.py"])
)
def test_exclude_include_absolute_path(self):
base = make_tree([], ['a.py', 'b.py'])
assert exclude_paths(
base,
['/*', '!/*.py']
) == set(['a.py', 'b.py'])
base = make_tree([], ["a.py", "b.py"])
assert exclude_paths(base, ["/*", "!/*.py"]) == set(["a.py", "b.py"])
def test_single_subdir_with_path_traversal(self):
assert self.exclude(['foo/whoops/../a.py']) == convert_paths(
self.all_paths - set(['foo/a.py'])
assert self.exclude(["foo/whoops/../a.py"]) == convert_paths(
self.all_paths - set(["foo/a.py"])
)
def test_single_subdir_wildcard_filename(self):
assert self.exclude(['foo/*.py']) == convert_paths(
self.all_paths - set(['foo/a.py', 'foo/b.py'])
assert self.exclude(["foo/*.py"]) == convert_paths(
self.all_paths - set(["foo/a.py", "foo/b.py"])
)
def test_wildcard_subdir_single_filename(self):
assert self.exclude(['*/a.py']) == convert_paths(
self.all_paths - set(['foo/a.py', 'bar/a.py'])
assert self.exclude(["*/a.py"]) == convert_paths(
self.all_paths - set(["foo/a.py", "bar/a.py"])
)
def test_wildcard_subdir_wildcard_filename(self):
assert self.exclude(['*/*.py']) == convert_paths(
self.all_paths - set(['foo/a.py', 'foo/b.py', 'bar/a.py'])
assert self.exclude(["*/*.py"]) == convert_paths(
self.all_paths - set(["foo/a.py", "foo/b.py", "bar/a.py"])
)
def test_directory(self):
assert self.exclude(['foo']) == convert_paths(
self.all_paths - set([
'foo', 'foo/a.py', 'foo/b.py', 'foo/bar', 'foo/bar/a.py',
'foo/Dockerfile3'
])
assert self.exclude(["foo"]) == convert_paths(
self.all_paths
- set(
[
"foo",
"foo/a.py",
"foo/b.py",
"foo/bar",
"foo/bar/a.py",
"foo/Dockerfile3",
]
)
)
def test_directory_with_trailing_slash(self):
assert self.exclude(['foo']) == convert_paths(
self.all_paths - set([
'foo', 'foo/a.py', 'foo/b.py',
'foo/bar', 'foo/bar/a.py', 'foo/Dockerfile3'
])
assert self.exclude(["foo"]) == convert_paths(
self.all_paths
- set(
[
"foo",
"foo/a.py",
"foo/b.py",
"foo/bar",
"foo/bar/a.py",
"foo/Dockerfile3",
]
)
)
def test_directory_with_single_exception(self):
assert self.exclude(['foo', '!foo/bar/a.py']) == convert_paths(
self.all_paths - set([
'foo/a.py', 'foo/b.py', 'foo', 'foo/bar',
'foo/Dockerfile3'
])
assert self.exclude(["foo", "!foo/bar/a.py"]) == convert_paths(
self.all_paths
- set(["foo/a.py", "foo/b.py", "foo", "foo/bar", "foo/Dockerfile3"])
)
def test_directory_with_subdir_exception(self):
assert self.exclude(['foo', '!foo/bar']) == convert_paths(
self.all_paths - set([
'foo/a.py', 'foo/b.py', 'foo', 'foo/Dockerfile3'
])
assert self.exclude(["foo", "!foo/bar"]) == convert_paths(
self.all_paths - set(["foo/a.py", "foo/b.py", "foo", "foo/Dockerfile3"])
)
@pytest.mark.skipif(
not IS_WINDOWS_PLATFORM, reason='Backslash patterns only on Windows'
not IS_WINDOWS_PLATFORM, reason="Backslash patterns only on Windows"
)
def test_directory_with_subdir_exception_win32_pathsep(self):
assert self.exclude(['foo', '!foo\\bar']) == convert_paths(
self.all_paths - set([
'foo/a.py', 'foo/b.py', 'foo', 'foo/Dockerfile3'
])
assert self.exclude(["foo", "!foo\\bar"]) == convert_paths(
self.all_paths - set(["foo/a.py", "foo/b.py", "foo", "foo/Dockerfile3"])
)
def test_directory_with_wildcard_exception(self):
assert self.exclude(['foo', '!foo/*.py']) == convert_paths(
self.all_paths - set([
'foo/bar', 'foo/bar/a.py', 'foo', 'foo/Dockerfile3'
])
assert self.exclude(["foo", "!foo/*.py"]) == convert_paths(
self.all_paths - set(["foo/bar", "foo/bar/a.py", "foo", "foo/Dockerfile3"])
)
def test_subdirectory(self):
assert self.exclude(['foo/bar']) == convert_paths(
self.all_paths - set(['foo/bar', 'foo/bar/a.py'])
assert self.exclude(["foo/bar"]) == convert_paths(
self.all_paths - set(["foo/bar", "foo/bar/a.py"])
)
@pytest.mark.skipif(
not IS_WINDOWS_PLATFORM, reason='Backslash patterns only on Windows'
not IS_WINDOWS_PLATFORM, reason="Backslash patterns only on Windows"
)
def test_subdirectory_win32_pathsep(self):
assert self.exclude(['foo\\bar']) == convert_paths(
self.all_paths - set(['foo/bar', 'foo/bar/a.py'])
assert self.exclude(["foo\\bar"]) == convert_paths(
self.all_paths - set(["foo/bar", "foo/bar/a.py"])
)
def test_double_wildcard(self):
assert self.exclude(['**/a.py']) == convert_paths(
self.all_paths - set([
'a.py', 'foo/a.py', 'foo/bar/a.py', 'bar/a.py'
])
assert self.exclude(["**/a.py"]) == convert_paths(
self.all_paths - set(["a.py", "foo/a.py", "foo/bar/a.py", "bar/a.py"])
)
assert self.exclude(['foo/**/bar']) == convert_paths(
self.all_paths - set(['foo/bar', 'foo/bar/a.py'])
assert self.exclude(["foo/**/bar"]) == convert_paths(
self.all_paths - set(["foo/bar", "foo/bar/a.py"])
)
def test_single_and_double_wildcard(self):
assert self.exclude(['**/target/*/*']) == convert_paths(
self.all_paths - set([
'target/subdir/file.txt',
'subdir/target/subdir/file.txt',
'subdir/subdir2/target/subdir/file.txt',
])
assert self.exclude(["**/target/*/*"]) == convert_paths(
self.all_paths
- set(
[
"target/subdir/file.txt",
"subdir/target/subdir/file.txt",
"subdir/subdir2/target/subdir/file.txt",
]
)
)
def test_trailing_double_wildcard(self):
assert self.exclude(['subdir/**']) == convert_paths(
self.all_paths - set([
'subdir/file.txt',
'subdir/target/file.txt',
'subdir/target/subdir/file.txt',
'subdir/subdir2/file.txt',
'subdir/subdir2/target/file.txt',
'subdir/subdir2/target/subdir/file.txt',
'subdir/target',
'subdir/target/subdir',
'subdir/subdir2',
'subdir/subdir2/target',
'subdir/subdir2/target/subdir',
])
assert self.exclude(["subdir/**"]) == convert_paths(
self.all_paths
- set(
[
"subdir/file.txt",
"subdir/target/file.txt",
"subdir/target/subdir/file.txt",
"subdir/subdir2/file.txt",
"subdir/subdir2/target/file.txt",
"subdir/subdir2/target/subdir/file.txt",
"subdir/target",
"subdir/target/subdir",
"subdir/subdir2",
"subdir/subdir2/target",
"subdir/subdir2/target/subdir",
]
)
)
def test_double_wildcard_with_exception(self):
assert self.exclude(['**', '!bar', '!foo/bar']) == convert_paths(
set([
'foo/bar', 'foo/bar/a.py', 'bar', 'bar/a.py', 'Dockerfile',
'.dockerignore',
])
assert self.exclude(["**", "!bar", "!foo/bar"]) == convert_paths(
set(
[
"foo/bar",
"foo/bar/a.py",
"bar",
"bar/a.py",
"Dockerfile",
".dockerignore",
]
)
)
def test_include_wildcard(self):
# This may be surprising but it matches the CLI's behavior
# (tested with 18.05.0-ce on linux)
base = make_tree(['a'], ['a/b.py'])
assert exclude_paths(
base,
['*', '!*/b.py']
) == set()
base = make_tree(["a"], ["a/b.py"])
assert exclude_paths(base, ["*", "!*/b.py"]) == set()
def test_last_line_precedence(self):
base = make_tree(
[],
['garbage.md',
'trash.md',
'README.md',
'README-bis.md',
'README-secret.md'])
assert exclude_paths(
base,
['*.md', '!README*.md', 'README-secret.md']
) == set(['README.md', 'README-bis.md'])
[
"garbage.md",
"trash.md",
"README.md",
"README-bis.md",
"README-secret.md",
],
)
assert exclude_paths(base, ["*.md", "!README*.md", "README-secret.md"]) == set(
["README.md", "README-bis.md"]
)
def test_parent_directory(self):
base = make_tree(
[],
['a.py',
'b.py',
'c.py'])
base = make_tree([], ["a.py", "b.py", "c.py"])
# Dockerignore reference stipulates that absolute paths are
# equivalent to relative paths, hence /../foo should be
# equivalent to ../foo. It also stipulates that paths are run
# through Go's filepath.Clean, which explicitly "replace
# "/.." by "/" at the beginning of a path".
assert exclude_paths(
base,
['../a.py', '/../b.py']
) == set(['c.py'])
assert exclude_paths(base, ["../a.py", "/../b.py"]) == set(["c.py"])
class TarTest(unittest.TestCase):
def test_tar_with_excludes(self):
dirs = [
'foo',
'foo/bar',
'bar',
"foo",
"foo/bar",
"bar",
]
files = [
'Dockerfile',
'Dockerfile.alt',
'.dockerignore',
'a.py',
'a.go',
'b.py',
'cde.py',
'foo/a.py',
'foo/b.py',
'foo/bar/a.py',
'bar/a.py',
"Dockerfile",
"Dockerfile.alt",
".dockerignore",
"a.py",
"a.go",
"b.py",
"cde.py",
"foo/a.py",
"foo/b.py",
"foo/bar/a.py",
"bar/a.py",
]
exclude = [
'*.py',
'!b.py',
'!a.go',
'foo',
'Dockerfile*',
'.dockerignore',
"*.py",
"!b.py",
"!a.go",
"foo",
"Dockerfile*",
".dockerignore",
]
expected_names = set([
'Dockerfile',
'.dockerignore',
'a.go',
'b.py',
'bar',
'bar/a.py',
])
expected_names = set(
[
"Dockerfile",
".dockerignore",
"a.go",
"b.py",
"bar",
"bar/a.py",
]
)
base = make_tree(dirs, files)
self.addCleanup(shutil.rmtree, base)
@@ -411,99 +426,99 @@ class TarTest(unittest.TestCase):
def test_tar_with_empty_directory(self):
base = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, base)
for d in ['foo', 'bar']:
for d in ["foo", "bar"]:
os.makedirs(os.path.join(base, d))
with tar(base) as archive:
tar_data = tarfile.open(fileobj=archive)
assert sorted(tar_data.getnames()) == ['bar', 'foo']
assert sorted(tar_data.getnames()) == ["bar", "foo"]
@pytest.mark.skipif(
IS_WINDOWS_PLATFORM or os.geteuid() == 0,
reason='root user always has access ; no chmod on Windows'
reason="root user always has access ; no chmod on Windows",
)
def test_tar_with_inaccessible_file(self):
base = tempfile.mkdtemp()
full_path = os.path.join(base, 'foo')
full_path = os.path.join(base, "foo")
self.addCleanup(shutil.rmtree, base)
with open(full_path, 'w') as f:
f.write('content')
with open(full_path, "w") as f:
f.write("content")
os.chmod(full_path, 0o222)
with pytest.raises(IOError) as ei:
tar(base)
assert f'Can not read file in context: {full_path}' in ei.exconly()
assert f"Can not read file in context: {full_path}" in ei.exconly()
@pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason='No symlinks on Windows')
@pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason="No symlinks on Windows")
def test_tar_with_file_symlinks(self):
base = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, base)
with open(os.path.join(base, 'foo'), 'w') as f:
with open(os.path.join(base, "foo"), "w") as f:
f.write("content")
os.makedirs(os.path.join(base, 'bar'))
os.symlink('../foo', os.path.join(base, 'bar/foo'))
os.makedirs(os.path.join(base, "bar"))
os.symlink("../foo", os.path.join(base, "bar/foo"))
with tar(base) as archive:
tar_data = tarfile.open(fileobj=archive)
assert sorted(tar_data.getnames()) == ['bar', 'bar/foo', 'foo']
assert sorted(tar_data.getnames()) == ["bar", "bar/foo", "foo"]
@pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason='No symlinks on Windows')
@pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason="No symlinks on Windows")
def test_tar_with_directory_symlinks(self):
base = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, base)
for d in ['foo', 'bar']:
for d in ["foo", "bar"]:
os.makedirs(os.path.join(base, d))
os.symlink('../foo', os.path.join(base, 'bar/foo'))
os.symlink("../foo", os.path.join(base, "bar/foo"))
with tar(base) as archive:
tar_data = tarfile.open(fileobj=archive)
assert sorted(tar_data.getnames()) == ['bar', 'bar/foo', 'foo']
assert sorted(tar_data.getnames()) == ["bar", "bar/foo", "foo"]
@pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason='No symlinks on Windows')
@pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason="No symlinks on Windows")
def test_tar_with_broken_symlinks(self):
base = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, base)
for d in ['foo', 'bar']:
for d in ["foo", "bar"]:
os.makedirs(os.path.join(base, d))
os.symlink('../baz', os.path.join(base, 'bar/foo'))
os.symlink("../baz", os.path.join(base, "bar/foo"))
with tar(base) as archive:
tar_data = tarfile.open(fileobj=archive)
assert sorted(tar_data.getnames()) == ['bar', 'bar/foo', 'foo']
assert sorted(tar_data.getnames()) == ["bar", "bar/foo", "foo"]
@pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason='No UNIX sockets on Win32')
@pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason="No UNIX sockets on Win32")
def test_tar_socket_file(self):
base = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, base)
for d in ['foo', 'bar']:
for d in ["foo", "bar"]:
os.makedirs(os.path.join(base, d))
sock = socket.socket(socket.AF_UNIX)
self.addCleanup(sock.close)
sock.bind(os.path.join(base, 'test.sock'))
sock.bind(os.path.join(base, "test.sock"))
with tar(base) as archive:
tar_data = tarfile.open(fileobj=archive)
assert sorted(tar_data.getnames()) == ['bar', 'foo']
assert sorted(tar_data.getnames()) == ["bar", "foo"]
def tar_test_negative_mtime_bug(self):
base = tempfile.mkdtemp()
filename = os.path.join(base, 'th.txt')
filename = os.path.join(base, "th.txt")
self.addCleanup(shutil.rmtree, base)
with open(filename, 'w') as f:
f.write('Invisible Full Moon')
with open(filename, "w") as f:
f.write("Invisible Full Moon")
os.utime(filename, (12345, -3600.0))
with tar(base) as archive:
tar_data = tarfile.open(fileobj=archive)
assert tar_data.getnames() == ['th.txt']
assert tar_data.getmember('th.txt').mtime == -3600
assert tar_data.getnames() == ["th.txt"]
assert tar_data.getmember("th.txt").mtime == -3600
@pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason='No symlinks on Windows')
@pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason="No symlinks on Windows")
def test_tar_directory_link(self):
dirs = ['a', 'b', 'a/c']
files = ['a/hello.py', 'b/utils.py', 'a/c/descend.py']
dirs = ["a", "b", "a/c"]
files = ["a/hello.py", "b/utils.py", "a/c/descend.py"]
base = make_tree(dirs, files)
self.addCleanup(shutil.rmtree, base)
os.symlink(os.path.join(base, 'b'), os.path.join(base, 'a/c/b'))
os.symlink(os.path.join(base, "b"), os.path.join(base, "a/c/b"))
with tar(base) as archive:
tar_data = tarfile.open(fileobj=archive)
names = tar_data.getnames()
for member in dirs + files:
assert member in names
assert 'a/c/b' in names
assert 'a/c/b/utils.py' not in names
assert "a/c/b" in names
assert "a/c/b/utils.py" not in names
@@ -7,18 +7,20 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import json
import os
import unittest
import shutil
import tempfile
import json
from pytest import mark, fixture
import unittest
from ansible_collections.community.docker.plugins.module_utils._api.utils import config
from pytest import fixture, mark
try:
from unittest import mock
@@ -33,46 +35,46 @@ class FindConfigFileTest(unittest.TestCase):
self.mkdir = tmpdir.mkdir
def test_find_config_fallback(self):
tmpdir = self.mkdir('test_find_config_fallback')
tmpdir = self.mkdir("test_find_config_fallback")
with mock.patch.dict(os.environ, {'HOME': str(tmpdir)}):
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.mkdir('test_find_config_from_explicit_path')
config_path = tmpdir.ensure('my-config-file.json')
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.mkdir('test_find_config_from_environment')
config_path = tmpdir.ensure('config.json')
tmpdir = self.mkdir("test_find_config_from_environment")
config_path = tmpdir.ensure("config.json")
with mock.patch.dict(os.environ, {'DOCKER_CONFIG': str(tmpdir)}):
with mock.patch.dict(os.environ, {"DOCKER_CONFIG": str(tmpdir)}):
assert config.find_config_file() == str(config_path)
@mark.skipif("sys.platform == 'win32'")
def test_find_config_from_home_posix(self):
tmpdir = self.mkdir('test_find_config_from_home_posix')
config_path = tmpdir.ensure('.docker', 'config.json')
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)}):
with mock.patch.dict(os.environ, {"HOME": str(tmpdir)}):
assert config.find_config_file() == str(config_path)
@mark.skipif("sys.platform == 'win32'")
def test_find_config_from_home_legacy_name(self):
tmpdir = self.mkdir('test_find_config_from_home_legacy_name')
config_path = tmpdir.ensure('.dockercfg')
tmpdir = self.mkdir("test_find_config_from_home_legacy_name")
config_path = tmpdir.ensure(".dockercfg")
with mock.patch.dict(os.environ, {'HOME': str(tmpdir)}):
with mock.patch.dict(os.environ, {"HOME": str(tmpdir)}):
assert config.find_config_file() == str(config_path)
@mark.skipif("sys.platform != 'win32'")
def test_find_config_from_home_windows(self):
tmpdir = self.mkdir('test_find_config_from_home_windows')
config_path = tmpdir.ensure('.docker', 'config.json')
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)}):
with mock.patch.dict(os.environ, {"USERPROFILE": str(tmpdir)}):
assert config.find_config_file() == str(config_path)
@@ -89,32 +91,24 @@ class LoadConfigTest(unittest.TestCase):
folder = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, folder)
dockercfg_path = os.path.join(folder, 'config.json')
dockercfg_path = os.path.join(folder, "config.json")
config_data = {
'HttpHeaders': {
'Name': 'Spike',
'Surname': 'Spiegel'
},
"HttpHeaders": {"Name": "Spike", "Surname": "Spiegel"},
}
with open(dockercfg_path, 'w') as f:
with open(dockercfg_path, "w") as f:
json.dump(config_data, f)
cfg = config.load_general_config(dockercfg_path)
assert 'HttpHeaders' in cfg
assert cfg['HttpHeaders'] == {
'Name': 'Spike',
'Surname': 'Spiegel'
}
assert "HttpHeaders" in cfg
assert cfg["HttpHeaders"] == {"Name": "Spike", "Surname": "Spiegel"}
def test_load_config_detach_keys(self):
folder = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, folder)
dockercfg_path = os.path.join(folder, 'config.json')
config_data = {
'detachKeys': 'ctrl-q, ctrl-u, ctrl-i'
}
with open(dockercfg_path, 'w') as f:
dockercfg_path = os.path.join(folder, "config.json")
config_data = {"detachKeys": "ctrl-q, ctrl-u, ctrl-i"}
with open(dockercfg_path, "w") as f:
json.dump(config_data, f)
cfg = config.load_general_config(dockercfg_path)
@@ -123,13 +117,11 @@ class LoadConfigTest(unittest.TestCase):
def test_load_config_from_env(self):
folder = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, folder)
dockercfg_path = os.path.join(folder, 'config.json')
config_data = {
'detachKeys': 'ctrl-q, ctrl-u, ctrl-i'
}
with open(dockercfg_path, 'w') as f:
dockercfg_path = os.path.join(folder, "config.json")
config_data = {"detachKeys": "ctrl-q, ctrl-u, ctrl-i"}
with open(dockercfg_path, "w") as f:
json.dump(config_data, f)
with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}):
with mock.patch.dict(os.environ, {"DOCKER_CONFIG": folder}):
cfg = config.load_general_config(None)
assert cfg == config_data
@@ -7,20 +7,28 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import unittest
from ansible_collections.community.docker.plugins.module_utils._api.api.client import APIClient
from ansible_collections.community.docker.plugins.module_utils._api.utils.decorators import update_headers
from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import DEFAULT_DOCKER_API_VERSION
from ansible_collections.community.docker.plugins.module_utils._api.api.client import (
APIClient,
)
from ansible_collections.community.docker.plugins.module_utils._api.utils.decorators import (
update_headers,
)
from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import (
DEFAULT_DOCKER_API_VERSION,
)
class DecoratorsTest(unittest.TestCase):
def test_update_headers(self):
sample_headers = {
'X-Docker-Locale': 'en-US',
"X-Docker-Locale": "en-US",
}
def f(self, headers=None):
@@ -32,17 +40,15 @@ class DecoratorsTest(unittest.TestCase):
g = update_headers(f)
assert g(client, headers=None) is None
assert g(client, headers={}) == {}
assert g(client, headers={'Content-type': 'application/json'}) == {
'Content-type': 'application/json',
assert g(client, headers={"Content-type": "application/json"}) == {
"Content-type": "application/json",
}
client._general_configs = {
'HttpHeaders': sample_headers
}
client._general_configs = {"HttpHeaders": sample_headers}
assert g(client, headers=None) == sample_headers
assert g(client, headers={}) == sample_headers
assert g(client, headers={'Content-type': 'application/json'}) == {
'Content-type': 'application/json',
'X-Docker-Locale': 'en-US',
assert g(client, headers={"Content-type": "application/json"}) == {
"Content-type": "application/json",
"X-Docker-Locale": "en-US",
}
@@ -7,10 +7,16 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible_collections.community.docker.plugins.module_utils._api.utils.json_stream import json_splitter, stream_as_text, json_stream
from ansible_collections.community.docker.plugins.module_utils._api.utils.json_stream import (
json_splitter,
json_stream,
stream_as_text,
)
class TestJsonSplitter:
@@ -21,24 +27,24 @@ class TestJsonSplitter:
def test_json_splitter_with_object(self):
data = '{"foo": "bar"}\n \n{"next": "obj"}'
assert json_splitter(data) == ({'foo': 'bar'}, '{"next": "obj"}')
assert json_splitter(data) == ({"foo": "bar"}, '{"next": "obj"}')
def test_json_splitter_leading_whitespace(self):
data = '\n \r{"foo": "bar"}\n\n {"next": "obj"}'
assert json_splitter(data) == ({'foo': 'bar'}, '{"next": "obj"}')
assert json_splitter(data) == ({"foo": "bar"}, '{"next": "obj"}')
class TestStreamAsText:
def test_stream_with_non_utf_unicode_character(self):
stream = [b'\xed\xf3\xf3']
output, = stream_as_text(stream)
assert output == ''
stream = [b"\xed\xf3\xf3"]
(output,) = stream_as_text(stream)
assert output == ""
def test_stream_with_utf_character(self):
stream = ['ěĝ'.encode('utf-8')]
output, = stream_as_text(stream)
assert output == 'ěĝ'
stream = ["ěĝ".encode("utf-8")]
(output,) = stream_as_text(stream)
assert output == "ěĝ"
class TestJsonStream:
@@ -50,21 +56,13 @@ class TestJsonStream:
]
output = list(json_stream(stream))
assert output == [
{'one': 'two'},
{"one": "two"},
{},
[1, 2, 3],
[],
]
def test_with_leading_whitespace(self):
stream = [
'\n \r\n {"one": "two"}{"x": 1}',
' {"three": "four"}\t\t{"x": 2}'
]
stream = ['\n \r\n {"one": "two"}{"x": 1}', ' {"three": "four"}\t\t{"x": 2}']
output = list(json_stream(stream))
assert output == [
{'one': 'two'},
{'x': 1},
{'three': 'four'},
{'x': 2}
]
assert output == [{"one": "two"}, {"x": 1}, {"three": "four"}, {"x": 2}]
@@ -7,14 +7,18 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import unittest
import pytest
from ansible_collections.community.docker.plugins.module_utils._api.utils.ports import build_port_bindings, split_port
from ansible_collections.community.docker.plugins.module_utils._api.utils.ports import (
build_port_bindings,
split_port,
)
class PortsTest(unittest.TestCase):
@@ -24,10 +28,8 @@ class PortsTest(unittest.TestCase):
assert external_port == [("127.0.0.1", "1000")]
def test_split_port_with_protocol(self):
for protocol in ['tcp', 'udp', 'sctp']:
internal_port, external_port = split_port(
"127.0.0.1:1000:2000/" + protocol
)
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")]
@@ -67,20 +69,17 @@ class PortsTest(unittest.TestCase):
assert external_port is None
def test_split_port_range_with_protocol(self):
internal_port, external_port = split_port(
"127.0.0.1:1000-1001:2000-2001/udp")
internal_port, external_port = split_port("127.0.0.1:1000-1001:2000-2001/udp")
assert internal_port == ["2000/udp", "2001/udp"]
assert external_port == [("127.0.0.1", "1000"), ("127.0.0.1", "1001")]
def test_split_port_with_ipv6_address(self):
internal_port, external_port = split_port(
"2001:abcd:ef00::2:1000:2000")
internal_port, external_port = split_port("2001:abcd:ef00::2:1000:2000")
assert internal_port == ["2000"]
assert external_port == [("2001:abcd:ef00::2", "1000")]
def test_split_port_with_ipv6_square_brackets_address(self):
internal_port, external_port = split_port(
"[2001:abcd:ef00::2]:1000:2000")
internal_port, external_port = split_port("[2001:abcd:ef00::2]:1000:2000")
assert internal_port == ["2000"]
assert external_port == [("2001:abcd:ef00::2", "1000")]
@@ -117,7 +116,7 @@ class PortsTest(unittest.TestCase):
split_port("")
def test_split_port_non_string(self):
assert split_port(1243) == (['1243'], None)
assert split_port(1243) == (["1243"], None)
def test_build_port_bindings_with_one_port(self):
port_bindings = build_port_bindings(["127.0.0.1:1000:1000"])
@@ -125,14 +124,14 @@ class PortsTest(unittest.TestCase):
def test_build_port_bindings_with_matching_internal_ports(self):
port_bindings = build_port_bindings(
["127.0.0.1:1000:1000", "127.0.0.1:2000:1000"])
assert port_bindings["1000"] == [
("127.0.0.1", "1000"), ("127.0.0.1", "2000")
]
["127.0.0.1:1000:1000", "127.0.0.1:2000:1000"]
)
assert port_bindings["1000"] == [("127.0.0.1", "1000"), ("127.0.0.1", "2000")]
def test_build_port_bindings_with_nonmatching_internal_ports(self):
port_bindings = build_port_bindings(
["127.0.0.1:1000:1000", "127.0.0.1:2000:2000"])
["127.0.0.1:1000:1000", "127.0.0.1:2000:2000"]
)
assert port_bindings["1000"] == [("127.0.0.1", "1000")]
assert port_bindings["2000"] == [("127.0.0.1", "2000")]
@@ -143,16 +142,14 @@ class PortsTest(unittest.TestCase):
def test_build_port_bindings_with_matching_internal_port_ranges(self):
port_bindings = build_port_bindings(
["127.0.0.1:1000-1001:1000-1001", "127.0.0.1:2000-2001:1000-1001"])
assert port_bindings["1000"] == [
("127.0.0.1", "1000"), ("127.0.0.1", "2000")
]
assert port_bindings["1001"] == [
("127.0.0.1", "1001"), ("127.0.0.1", "2001")
]
["127.0.0.1:1000-1001:1000-1001", "127.0.0.1:2000-2001:1000-1001"]
)
assert port_bindings["1000"] == [("127.0.0.1", "1000"), ("127.0.0.1", "2000")]
assert port_bindings["1001"] == [("127.0.0.1", "1001"), ("127.0.0.1", "2001")]
def test_build_port_bindings_with_nonmatching_internal_port_ranges(self):
port_bindings = build_port_bindings(
["127.0.0.1:1000:1000", "127.0.0.1:2000:2000"])
["127.0.0.1:1000:1000", "127.0.0.1:2000:2000"]
)
assert port_bindings["1000"] == [("127.0.0.1", "1000")]
assert port_bindings["2000"] == [("127.0.0.1", "2000")]
@@ -7,40 +7,46 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import unittest
from ansible_collections.community.docker.plugins.module_utils._api.utils.proxy import ProxyConfig
from ansible_collections.community.docker.plugins.module_utils._api.utils.proxy import (
ProxyConfig,
)
HTTP = 'http://test:80'
HTTPS = 'https://test:443'
FTP = 'ftp://user:password@host:23'
NO_PROXY = 'localhost,.localdomain'
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,
"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
})
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)
@@ -53,18 +59,18 @@ class ProxyConfigTest(unittest.TestCase):
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')
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')
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)
@@ -75,16 +81,17 @@ class ProxyConfigTest(unittest.TestCase):
# Proxy config is non null, env is None.
self.assertSetEqual(
set(CONFIG.inject_proxy_environment(None)),
set(f'{k}={v}' for k, v in ENV.items()))
set(f"{k}={v}" for k, v in ENV.items()),
)
# Proxy config is null, env is None.
self.assertIsNone(ProxyConfig().inject_proxy_environment(None), None)
env = ['FOO=BAR', 'BAR=BAZ']
env = ["FOO=BAR", "BAR=BAZ"]
# Proxy config is non null, env is non null
actual = CONFIG.inject_proxy_environment(env)
expected = [f'{k}={v}' for k, v in ENV.items()] + env
expected = [f"{k}={v}" for k, v in ENV.items()] + 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]))
@@ -7,7 +7,9 @@
# It is licensed under the Apache 2.0 license (see LICENSES/Apache-2.0.txt in this collection)
# SPDX-License-Identifier: Apache-2.0
from __future__ import (absolute_import, division, print_function)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import base64
@@ -19,22 +21,36 @@ import tempfile
import unittest
import pytest
from ansible_collections.community.docker.plugins.module_utils._api.api.client import APIClient
from ansible_collections.community.docker.plugins.module_utils._api.constants import IS_WINDOWS_PLATFORM
from ansible_collections.community.docker.plugins.module_utils._api.errors import DockerException
from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import (
convert_filters, convert_volume_binds,
decode_json_header, kwargs_from_env, parse_bytes,
parse_devices, parse_env_file, parse_host,
parse_repository_tag, split_command, format_environment,
from ansible_collections.community.docker.plugins.module_utils._api.api.client import (
APIClient,
)
from ansible_collections.community.docker.plugins.module_utils._api.constants import (
IS_WINDOWS_PLATFORM,
)
from ansible_collections.community.docker.plugins.module_utils._api.errors import (
DockerException,
)
from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import (
convert_filters,
convert_volume_binds,
decode_json_header,
format_environment,
kwargs_from_env,
parse_bytes,
parse_devices,
parse_env_file,
parse_host,
parse_repository_tag,
split_command,
)
from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import (
DEFAULT_DOCKER_API_VERSION,
)
from ansible_collections.community.docker.tests.unit.plugins.module_utils._api.constants import DEFAULT_DOCKER_API_VERSION
TEST_CERT_DIR = os.path.join(
os.path.dirname(__file__),
'testdata/certs',
"testdata/certs",
)
@@ -46,84 +62,85 @@ class KwargsFromEnvTest(unittest.TestCase):
os.environ = self.os_environ
def test_kwargs_from_env_empty(self):
os.environ.update(DOCKER_HOST='',
DOCKER_CERT_PATH='')
os.environ.pop('DOCKER_TLS_VERIFY', None)
os.environ.update(DOCKER_HOST="", DOCKER_CERT_PATH="")
os.environ.pop("DOCKER_TLS_VERIFY", None)
kwargs = kwargs_from_env()
assert kwargs.get('base_url') is None
assert kwargs.get('tls') is None
assert kwargs.get("base_url") is None
assert kwargs.get("tls") is None
def test_kwargs_from_env_tls(self):
os.environ.update(DOCKER_HOST='tcp://192.168.59.103:2376',
DOCKER_CERT_PATH=TEST_CERT_DIR,
DOCKER_TLS_VERIFY='1')
os.environ.update(
DOCKER_HOST="tcp://192.168.59.103:2376",
DOCKER_CERT_PATH=TEST_CERT_DIR,
DOCKER_TLS_VERIFY="1",
)
kwargs = kwargs_from_env(assert_hostname=False)
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
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)
kwargs['version'] = DEFAULT_DOCKER_API_VERSION
parsed_host = parse_host(kwargs["base_url"], IS_WINDOWS_PLATFORM, True)
kwargs["version"] = DEFAULT_DOCKER_API_VERSION
try:
client = APIClient(**kwargs)
assert parsed_host == client.base_url
assert kwargs['tls'].ca_cert == client.verify
assert kwargs['tls'].cert == client.cert
assert kwargs["tls"].ca_cert == client.verify
assert kwargs["tls"].cert == client.cert
except TypeError as e:
self.fail(e)
def test_kwargs_from_env_tls_verify_false(self):
os.environ.update(DOCKER_HOST='tcp://192.168.59.103:2376',
DOCKER_CERT_PATH=TEST_CERT_DIR,
DOCKER_TLS_VERIFY='')
os.environ.update(
DOCKER_HOST="tcp://192.168.59.103:2376",
DOCKER_CERT_PATH=TEST_CERT_DIR,
DOCKER_TLS_VERIFY="",
)
kwargs = kwargs_from_env(assert_hostname=True)
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)
kwargs['version'] = DEFAULT_DOCKER_API_VERSION
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)
kwargs["version"] = DEFAULT_DOCKER_API_VERSION
try:
client = APIClient(**kwargs)
assert parsed_host == client.base_url
assert kwargs['tls'].cert == client.cert
assert not kwargs['tls'].verify
assert kwargs["tls"].cert == client.cert
assert not kwargs["tls"].verify
except TypeError as e:
self.fail(e)
def test_kwargs_from_env_tls_verify_false_no_cert(self):
temp_dir = tempfile.mkdtemp()
cert_dir = os.path.join(temp_dir, '.docker')
cert_dir = os.path.join(temp_dir, ".docker")
shutil.copytree(TEST_CERT_DIR, cert_dir)
os.environ.update(DOCKER_HOST='tcp://192.168.59.103:2376',
HOME=temp_dir,
DOCKER_TLS_VERIFY='')
os.environ.pop('DOCKER_CERT_PATH', None)
os.environ.update(
DOCKER_HOST="tcp://192.168.59.103:2376", HOME=temp_dir, DOCKER_TLS_VERIFY=""
)
os.environ.pop("DOCKER_CERT_PATH", None)
kwargs = kwargs_from_env(assert_hostname=True)
assert 'tcp://192.168.59.103:2376' == kwargs['base_url']
assert "tcp://192.168.59.103:2376" == kwargs["base_url"]
def test_kwargs_from_env_no_cert_path(self):
try:
temp_dir = tempfile.mkdtemp()
cert_dir = os.path.join(temp_dir, '.docker')
cert_dir = os.path.join(temp_dir, ".docker")
shutil.copytree(TEST_CERT_DIR, cert_dir)
os.environ.update(HOME=temp_dir,
DOCKER_CERT_PATH='',
DOCKER_TLS_VERIFY='1')
os.environ.update(HOME=temp_dir, DOCKER_CERT_PATH="", DOCKER_TLS_VERIFY="1")
kwargs = kwargs_from_env()
assert kwargs['tls'].verify
assert cert_dir in kwargs['tls'].ca_cert
assert cert_dir in kwargs['tls'].cert[0]
assert cert_dir in kwargs['tls'].cert[1]
assert kwargs["tls"].verify
assert cert_dir in kwargs["tls"].ca_cert
assert cert_dir in kwargs["tls"].cert[0]
assert cert_dir in kwargs["tls"].cert[1]
finally:
if temp_dir:
shutil.rmtree(temp_dir)
@@ -132,15 +149,17 @@ class KwargsFromEnvTest(unittest.TestCase):
# Values in os.environ are entirely ignored if an alternate is
# provided
os.environ.update(
DOCKER_HOST='tcp://192.168.59.103:2376',
DOCKER_HOST="tcp://192.168.59.103:2376",
DOCKER_CERT_PATH=TEST_CERT_DIR,
DOCKER_TLS_VERIFY=''
DOCKER_TLS_VERIFY="",
)
kwargs = kwargs_from_env(environment={
'DOCKER_HOST': 'http://docker.gensokyo.jp:2581',
})
assert 'http://docker.gensokyo.jp:2581' == kwargs['base_url']
assert 'tls' not in kwargs
kwargs = kwargs_from_env(
environment={
"DOCKER_HOST": "http://docker.gensokyo.jp:2581",
}
)
assert "http://docker.gensokyo.jp:2581" == kwargs["base_url"]
assert "tls" not in kwargs
class ConverVolumeBindsTest(unittest.TestCase):
@@ -149,52 +168,36 @@ class ConverVolumeBindsTest(unittest.TestCase):
assert convert_volume_binds([]) == []
def test_convert_volume_binds_list(self):
data = ['/a:/a:ro', '/b:/c:z']
data = ["/a:/a:ro", "/b:/c:z"]
assert convert_volume_binds(data) == data
def test_convert_volume_binds_complete(self):
data = {
'/mnt/vol1': {
'bind': '/data',
'mode': 'ro'
}
}
assert convert_volume_binds(data) == ['/mnt/vol1:/data:ro']
data = {"/mnt/vol1": {"bind": "/data", "mode": "ro"}}
assert convert_volume_binds(data) == ["/mnt/vol1:/data:ro"]
def test_convert_volume_binds_compact(self):
data = {
'/mnt/vol1': '/data'
}
assert convert_volume_binds(data) == ['/mnt/vol1:/data:rw']
data = {"/mnt/vol1": "/data"}
assert convert_volume_binds(data) == ["/mnt/vol1:/data:rw"]
def test_convert_volume_binds_no_mode(self):
data = {
'/mnt/vol1': {
'bind': '/data'
}
}
assert convert_volume_binds(data) == ['/mnt/vol1:/data:rw']
data = {"/mnt/vol1": {"bind": "/data"}}
assert convert_volume_binds(data) == ["/mnt/vol1:/data:rw"]
def test_convert_volume_binds_unicode_bytes_input(self):
expected = ['/mnt/지연:/unicode/박:rw']
expected = ["/mnt/지연:/unicode/박:rw"]
data = {
'/mnt/지연'.encode('utf-8'): {
'bind': '/unicode/박'.encode('utf-8'),
'mode': 'rw'
"/mnt/지연".encode("utf-8"): {
"bind": "/unicode/박".encode("utf-8"),
"mode": "rw",
}
}
assert convert_volume_binds(data) == expected
def test_convert_volume_binds_unicode_unicode_input(self):
expected = ['/mnt/지연:/unicode/박:rw']
expected = ["/mnt/지연:/unicode/박:rw"]
data = {
'/mnt/지연': {
'bind': '/unicode/박',
'mode': 'rw'
}
}
data = {"/mnt/지연": {"bind": "/unicode/박", "mode": "rw"}}
assert convert_volume_binds(data) == expected
@@ -206,41 +209,36 @@ class ParseEnvFileTest(unittest.TestCase):
Don't forget to unlink the file with os.unlink() after.
"""
local_tempfile = tempfile.NamedTemporaryFile(delete=False)
local_tempfile.write(file_content.encode('UTF-8'))
local_tempfile.write(file_content.encode("UTF-8"))
local_tempfile.close()
return local_tempfile.name
def test_parse_env_file_proper(self):
env_file = self.generate_tempfile(
file_content='USER=jdoe\nPASS=secret')
env_file = self.generate_tempfile(file_content="USER=jdoe\nPASS=secret")
get_parse_env_file = parse_env_file(env_file)
assert get_parse_env_file == {'USER': 'jdoe', 'PASS': 'secret'}
assert get_parse_env_file == {"USER": "jdoe", "PASS": "secret"}
os.unlink(env_file)
def test_parse_env_file_with_equals_character(self):
env_file = self.generate_tempfile(
file_content='USER=jdoe\nPASS=sec==ret')
env_file = self.generate_tempfile(file_content="USER=jdoe\nPASS=sec==ret")
get_parse_env_file = parse_env_file(env_file)
assert get_parse_env_file == {'USER': 'jdoe', 'PASS': 'sec==ret'}
assert get_parse_env_file == {"USER": "jdoe", "PASS": "sec==ret"}
os.unlink(env_file)
def test_parse_env_file_commented_line(self):
env_file = self.generate_tempfile(
file_content='USER=jdoe\n#PASS=secret')
env_file = self.generate_tempfile(file_content="USER=jdoe\n#PASS=secret")
get_parse_env_file = parse_env_file(env_file)
assert get_parse_env_file == {'USER': 'jdoe'}
assert get_parse_env_file == {"USER": "jdoe"}
os.unlink(env_file)
def test_parse_env_file_newline(self):
env_file = self.generate_tempfile(
file_content='\nUSER=jdoe\n\n\nPASS=secret')
env_file = self.generate_tempfile(file_content="\nUSER=jdoe\n\n\nPASS=secret")
get_parse_env_file = parse_env_file(env_file)
assert get_parse_env_file == {'USER': 'jdoe', 'PASS': 'secret'}
assert get_parse_env_file == {"USER": "jdoe", "PASS": "secret"}
os.unlink(env_file)
def test_parse_env_file_invalid_line(self):
env_file = self.generate_tempfile(
file_content='USER jdoe')
env_file = self.generate_tempfile(file_content="USER jdoe")
with pytest.raises(DockerException):
parse_env_file(env_file)
os.unlink(env_file)
@@ -249,46 +247,44 @@ class ParseEnvFileTest(unittest.TestCase):
class ParseHostTest(unittest.TestCase):
def test_parse_host(self):
invalid_hosts = [
'foo://0.0.0.0',
'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',
"foo://0.0.0.0",
"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 = {
'0.0.0.1:5555': 'http://0.0.0.1:5555',
':6666': 'http://127.0.0.1:6666',
'tcp://:7777': 'http://127.0.0.1:7777',
'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',
'12.234.45.127:2375/docker/engine': (
'http://12.234.45.127:2375/docker/engine'
"0.0.0.1:5555": "http://0.0.0.1:5555",
":6666": "http://127.0.0.1:6666",
"tcp://:7777": "http://127.0.0.1:7777",
"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",
"12.234.45.127:2375/docker/engine": (
"http://12.234.45.127:2375/docker/engine"
),
'somehost.net:80/service/swarm': (
'http://somehost.net:80/service/swarm'
"somehost.net:80/service/swarm": ("http://somehost.net:80/service/swarm"),
"npipe:////./pipe/docker_engine": "npipe:////./pipe/docker_engine",
"[fd12::82d1]:2375": "http://[fd12::82d1]:2375",
"https://[fd12:5672::12aa]:1090": "https://[fd12:5672::12aa]:1090",
"[fd12::82d1]:2375/docker/engine": (
"http://[fd12::82d1]:2375/docker/engine"
),
'npipe:////./pipe/docker_engine': 'npipe:////./pipe/docker_engine',
'[fd12::82d1]:2375': 'http://[fd12::82d1]:2375',
'https://[fd12:5672::12aa]:1090': 'https://[fd12:5672::12aa]:1090',
'[fd12::82d1]:2375/docker/engine': (
'http://[fd12::82d1]:2375/docker/engine'
),
'ssh://[fd12::82d1]': 'ssh://[fd12::82d1]:22',
'ssh://user@[fd12::82d1]:8765': 'ssh://user@[fd12::82d1]:8765',
'ssh://': 'ssh://127.0.0.1:22',
'ssh://user@localhost:22': 'ssh://user@localhost:22',
'ssh://user@remote': 'ssh://user@remote:22',
"ssh://[fd12::82d1]": "ssh://[fd12::82d1]:22",
"ssh://user@[fd12::82d1]:8765": "ssh://user@[fd12::82d1]:8765",
"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:
msg = f'Should have failed to parse invalid host: {host}'
msg = f"Should have failed to parse invalid host: {host}"
with self.assertRaises(DockerException, msg=msg):
parse_host(host, None)
@@ -296,35 +292,35 @@ class ParseHostTest(unittest.TestCase):
self.assertEqual(
parse_host(host, None),
expected,
msg=f'Failed to parse valid host: {host}',
msg=f"Failed to parse valid host: {host}",
)
def test_parse_host_empty_value(self):
unix_socket = 'http+unix:///var/run/docker.sock'
npipe = 'npipe:////./pipe/docker_engine'
unix_socket = "http+unix:///var/run/docker.sock"
npipe = "npipe:////./pipe/docker_engine"
for val in [None, '']:
for val in [None, ""]:
assert parse_host(val, is_win32=False) == unix_socket
assert parse_host(val, is_win32=True) == npipe
def test_parse_host_tls(self):
host_value = 'myhost.docker.net:3348'
expected_result = 'https://myhost.docker.net:3348'
host_value = "myhost.docker.net:3348"
expected_result = "https://myhost.docker.net:3348"
assert parse_host(host_value, tls=True) == expected_result
def test_parse_host_tls_tcp_proto(self):
host_value = 'tcp://myhost.docker.net:3348'
expected_result = 'https://myhost.docker.net:3348'
host_value = "tcp://myhost.docker.net:3348"
expected_result = "https://myhost.docker.net:3348"
assert parse_host(host_value, tls=True) == expected_result
def test_parse_host_trailing_slash(self):
host_value = 'tcp://myhost.docker.net:2376/'
expected_result = 'http://myhost.docker.net:2376'
host_value = "tcp://myhost.docker.net:2376/"
expected_result = "http://myhost.docker.net:2376"
assert parse_host(host_value) == expected_result
class ParseRepositoryTagTest(unittest.TestCase):
sha = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
sha = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
def test_index_image_no_tag(self):
assert parse_repository_tag("root") == ("root", None)
@@ -342,77 +338,83 @@ class ParseRepositoryTagTest(unittest.TestCase):
assert parse_repository_tag("url:5000/repo") == ("url:5000/repo", None)
def test_private_reg_image_tag(self):
assert parse_repository_tag("url:5000/repo:tag") == (
"url:5000/repo", "tag"
)
assert parse_repository_tag("url:5000/repo:tag") == ("url:5000/repo", "tag")
def test_index_image_sha(self):
assert parse_repository_tag(f"root@sha256:{self.sha}") == (
"root", f"sha256:{self.sha}"
"root",
f"sha256:{self.sha}",
)
def test_private_reg_image_sha(self):
assert parse_repository_tag(
f"url:5000/repo@sha256:{self.sha}"
) == ("url:5000/repo", f"sha256:{self.sha}")
assert parse_repository_tag(f"url:5000/repo@sha256:{self.sha}") == (
"url:5000/repo",
f"sha256:{self.sha}",
)
class ParseDeviceTest(unittest.TestCase):
def test_dict(self):
devices = parse_devices([{
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'r'
}])
devices = parse_devices(
[
{
"PathOnHost": "/dev/sda1",
"PathInContainer": "/dev/mnt1",
"CgroupPermissions": "r",
}
]
)
assert devices[0] == {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'r'
"PathOnHost": "/dev/sda1",
"PathInContainer": "/dev/mnt1",
"CgroupPermissions": "r",
}
def test_partial_string_definition(self):
devices = parse_devices(['/dev/sda1'])
devices = parse_devices(["/dev/sda1"])
assert devices[0] == {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/sda1',
'CgroupPermissions': 'rwm'
"PathOnHost": "/dev/sda1",
"PathInContainer": "/dev/sda1",
"CgroupPermissions": "rwm",
}
def test_permissionless_string_definition(self):
devices = parse_devices(['/dev/sda1:/dev/mnt1'])
devices = parse_devices(["/dev/sda1:/dev/mnt1"])
assert devices[0] == {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'rwm'
"PathOnHost": "/dev/sda1",
"PathInContainer": "/dev/mnt1",
"CgroupPermissions": "rwm",
}
def test_full_string_definition(self):
devices = parse_devices(['/dev/sda1:/dev/mnt1:r'])
devices = parse_devices(["/dev/sda1:/dev/mnt1:r"])
assert devices[0] == {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'r'
"PathOnHost": "/dev/sda1",
"PathInContainer": "/dev/mnt1",
"CgroupPermissions": "r",
}
def test_hybrid_list(self):
devices = parse_devices([
'/dev/sda1:/dev/mnt1:rw',
{
'PathOnHost': '/dev/sda2',
'PathInContainer': '/dev/mnt2',
'CgroupPermissions': 'r'
}
])
devices = parse_devices(
[
"/dev/sda1:/dev/mnt1:rw",
{
"PathOnHost": "/dev/sda2",
"PathInContainer": "/dev/mnt2",
"CgroupPermissions": "r",
},
]
)
assert devices[0] == {
'PathOnHost': '/dev/sda1',
'PathInContainer': '/dev/mnt1',
'CgroupPermissions': 'rw'
"PathOnHost": "/dev/sda1",
"PathInContainer": "/dev/mnt1",
"CgroupPermissions": "rw",
}
assert devices[1] == {
'PathOnHost': '/dev/sda2',
'PathInContainer': '/dev/mnt2',
'CgroupPermissions': 'r'
"PathOnHost": "/dev/sda2",
"PathInContainer": "/dev/mnt2",
"CgroupPermissions": "r",
}
@@ -439,37 +441,35 @@ class UtilsTest(unittest.TestCase):
def test_convert_filters(self):
tests = [
({'dangling': True}, '{"dangling": ["true"]}'),
({'dangling': "true"}, '{"dangling": ["true"]}'),
({'exited': 0}, '{"exited": ["0"]}'),
({'exited': [0, 1]}, '{"exited": ["0", "1"]}'),
({"dangling": True}, '{"dangling": ["true"]}'),
({"dangling": "true"}, '{"dangling": ["true"]}'),
({"exited": 0}, '{"exited": ["0"]}'),
({"exited": [0, 1]}, '{"exited": ["0", "1"]}'),
]
for filters, expected in tests:
assert convert_filters(filters) == expected
def test_decode_json_header(self):
obj = {'a': 'b', 'c': 1}
data = base64.urlsafe_b64encode(bytes(json.dumps(obj), 'utf-8'))
obj = {"a": "b", "c": 1}
data = base64.urlsafe_b64encode(bytes(json.dumps(obj), "utf-8"))
decoded_data = decode_json_header(data)
assert obj == decoded_data
class SplitCommandTest(unittest.TestCase):
def test_split_command_with_unicode(self):
assert split_command('echo μμ') == ['echo', 'μμ']
assert split_command("echo μμ") == ["echo", "μμ"]
class FormatEnvironmentTest(unittest.TestCase):
def test_format_env_binary_unicode_value(self):
env_dict = {
'ARTIST_NAME': b'\xec\x86\xa1\xec\xa7\x80\xec\x9d\x80'
}
assert format_environment(env_dict) == ['ARTIST_NAME=송지은']
env_dict = {"ARTIST_NAME": b"\xec\x86\xa1\xec\xa7\x80\xec\x9d\x80"}
assert format_environment(env_dict) == ["ARTIST_NAME=송지은"]
def test_format_env_no_value(self):
env_dict = {
'FOO': None,
'BAR': '',
"FOO": None,
"BAR": "",
}
assert sorted(format_environment(env_dict)) == ['BAR=', 'FOO']
assert sorted(format_environment(env_dict)) == ["BAR=", "FOO"]