gns3-server/tests/compute/docker/test_docker.py

259 lines
8.9 KiB
Python
Raw Normal View History

2015-10-14 19:10:05 +03:00
#!/usr/bin/env python
#
# Copyright (C) 2020 GNS3 Technologies Inc.
2015-10-14 19:10:05 +03:00
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2023-08-11 10:32:05 +03:00
import asyncio
2015-10-14 19:10:05 +03:00
import pytest
import pytest_asyncio
from unittest.mock import MagicMock, patch
2015-10-14 19:10:05 +03:00
from tests.utils import asyncio_patch, AsyncioMagicMock
from gns3server.compute.docker import Docker, DOCKER_PREFERRED_API_VERSION, DOCKER_MINIMUM_API_VERSION
from gns3server.compute.docker.docker_error import DockerError, DockerHttp404Error
2015-10-14 19:10:05 +03:00
@pytest_asyncio.fixture
2020-10-02 09:37:50 +03:00
async def vm():
2015-10-14 19:10:05 +03:00
vm = Docker()
2016-01-11 20:14:45 +02:00
vm._connected = True
vm._session = MagicMock()
vm._session.closed = False
2016-05-02 18:06:51 +03:00
return vm
2020-10-02 09:37:50 +03:00
@pytest.mark.asyncio
async def test_query_success(vm):
2016-05-02 18:06:51 +03:00
2015-10-14 19:10:05 +03:00
response = MagicMock()
response.status = 200
2016-02-11 16:49:28 +02:00
response.headers = {'CONTENT-TYPE': 'application/json'}
2015-10-14 19:10:05 +03:00
async def read():
2015-10-14 19:10:05 +03:00
return b'{"c": false}'
response.read.side_effect = read
vm._session.request = AsyncioMagicMock(return_value=response)
data = await vm.query("POST", "test", data={"a": True}, params={"b": 1})
vm._session.request.assert_called_with('POST',
'http://docker/v1.25/test',
data='{"a": true}',
headers={'content-type': 'application/json'},
params={'b': 1},
timeout=300)
2015-10-14 19:10:05 +03:00
assert data == {"c": False}
2020-10-02 09:37:50 +03:00
@pytest.mark.asyncio
async def test_query_error(vm):
2015-10-14 19:10:05 +03:00
response = MagicMock()
response.status = 404
async def read():
2015-10-14 19:10:05 +03:00
return b"NOT FOUND"
response.read.side_effect = read
vm._session.request = AsyncioMagicMock(return_value=response)
with pytest.raises(DockerError):
await vm.query("POST", "test", data={"a": True}, params={"b": 1})
vm._session.request.assert_called_with('POST',
'http://docker/v1.25/test',
data='{"a": true}',
headers={'content-type': 'application/json'},
params={'b': 1},
timeout=300)
2015-10-14 19:10:05 +03:00
2020-10-02 09:37:50 +03:00
@pytest.mark.asyncio
async def test_query_error_json(vm):
2015-10-14 19:10:05 +03:00
response = MagicMock()
response.status = 404
async def read():
2015-10-14 19:10:05 +03:00
return b'{"message": "Error"}'
response.read.side_effect = read
vm._session.request = AsyncioMagicMock(return_value=response)
with pytest.raises(DockerError):
await vm.query("POST", "test", data={"a": True}, params={"b": 1})
vm._session.request.assert_called_with('POST',
'http://docker/v1.25/test',
data='{"a": true}',
headers={'content-type': 'application/json'},
params={'b': 1},
timeout=300)
2015-10-14 19:10:05 +03:00
2020-10-02 09:37:50 +03:00
@pytest.mark.asyncio
async def test_list_images():
2015-10-14 19:10:05 +03:00
response = [
{
"RepoTags": [
"ubuntu:12.04",
"ubuntu:precise",
"ubuntu:latest"
],
"Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c",
"Created": 1365714795,
"Size": 131506275,
"VirtualSize": 131506275
},
{
"RepoTags": [
"ubuntu:12.10",
"ubuntu:quantal",
"<none>:<none>"
],
"ParentId": "27cf784147099545",
"Id": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
"Created": 1364102658,
"Size": 24653,
"VirtualSize": 180116135
}
]
2016-04-15 18:57:06 +03:00
with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock:
images = await Docker.instance().list_images()
2015-10-14 19:10:05 +03:00
mock.assert_called_with("GET", "images/json", params={"all": 0})
2016-01-04 10:28:12 +02:00
assert len(images) == 5
assert {"image": "ubuntu:12.04"} in images
assert {"image": "ubuntu:precise"} in images
assert {"image": "ubuntu:latest"} in images
assert {"image": "ubuntu:12.10"} in images
assert {"image": "ubuntu:quantal"} in images
2020-10-02 09:37:50 +03:00
@pytest.mark.asyncio
async def test_pull_image():
class Response:
"""
Simulate a response split in multiple packets
"""
def __init__(self):
self._read = -1
async def read(self, size):
self._read += 1
if self._read == 0:
return b'{"progress": "0/100",'
elif self._read == 1:
return '"id": 42}'
else:
None
mock_query = MagicMock()
mock_query.content.return_value = Response()
with asyncio_patch("gns3server.compute.docker.Docker.query", side_effect=DockerHttp404Error("404")):
with asyncio_patch("gns3server.compute.docker.Docker.http_query", return_value=mock_query) as mock:
await Docker.instance().pull_image("ubuntu")
mock.assert_called_with("POST", "images/create", params={"fromImage": "ubuntu"}, timeout=None)
2020-10-02 09:37:50 +03:00
@pytest.mark.asyncio
async def test_docker_check_connection_docker_minimum_version(vm):
response = {
'ApiVersion': '1.01',
'Version': '1.12'
}
with patch("gns3server.compute.docker.Docker.connector"), \
asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response):
vm._connected = False
with pytest.raises(DockerError):
await vm._check_connection()
2020-10-02 09:37:50 +03:00
@pytest.mark.asyncio
async def test_docker_check_connection_docker_preferred_version_against_newer(vm):
response = {
'ApiVersion': '1.31'
}
with patch("gns3server.compute.docker.Docker.connector"), \
asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response):
vm._connected = False
await vm._check_connection()
assert vm._api_version == DOCKER_PREFERRED_API_VERSION
2020-10-02 09:37:50 +03:00
@pytest.mark.asyncio
async def test_docker_check_connection_docker_preferred_version_against_older(vm):
response = {
'ApiVersion': '1.27',
}
with patch("gns3server.compute.docker.Docker.connector"), \
asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response):
vm._connected = False
await vm._check_connection()
assert vm._api_version == DOCKER_MINIMUM_API_VERSION
2023-08-11 10:32:05 +03:00
@pytest.mark.asyncio
async def test_install_busybox():
mock_process = MagicMock(spec=asyncio.subprocess.Process)
mock_process.communicate.return_value = (b"", b"not a dynamic executable")
mock_process.returncode = 1 # means that busybox is not dynamically linked
with patch("os.path.isfile", return_value=False):
with patch("shutil.which", return_value="/usr/bin/busybox"):
with patch("asyncio.create_subprocess_exec", return_value=mock_process) as create_subprocess_mock:
with patch("shutil.copy2") as copy2_mock:
await Docker.install_busybox()
create_subprocess_mock.assert_called_with(
"ldd",
"/usr/bin/busybox",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.DEVNULL,
)
assert copy2_mock.called
@pytest.mark.asyncio
async def test_install_busybox_dynamic_linked():
mock_process = MagicMock(spec=asyncio.subprocess.Process)
mock_process.communicate.return_value = (b"Dynamically linked library", b"")
mock_process.returncode = 0 # means that busybox is dynamically linked
with patch("os.path.isfile", return_value=False):
with patch("shutil.which", return_value="/usr/bin/busybox"):
with patch("asyncio.create_subprocess_exec", return_value=mock_process):
with pytest.raises(DockerError) as e:
await Docker.install_busybox()
assert str(e.value) == "No busybox executable could be found"
@pytest.mark.asyncio
async def test_install_busybox_no_executables():
with patch("os.path.isfile", return_value=False):
with patch("shutil.which", return_value=None):
with pytest.raises(DockerError) as e:
await Docker.install_busybox()
assert str(e.value) == "No busybox executable could be found"