2015-10-14 16:10:05 +00:00
#!/usr/bin/env python
#
2020-06-16 04:29:03 +00:00
# Copyright (C) 2020 GNS3 Technologies Inc.
2015-10-14 16:10:05 +00: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/>.
2024-02-14 08:13:45 +00:00
import asyncio
2015-10-14 16:10:05 +00:00
import pytest
2017-07-06 09:24:55 +00:00
from unittest . mock import MagicMock , patch
2015-10-14 16:10:05 +00:00
2017-03-20 22:50:31 +00:00
from tests . utils import asyncio_patch , AsyncioMagicMock
2017-07-06 09:24:55 +00:00
from gns3server . compute . docker import Docker , DOCKER_PREFERRED_API_VERSION , DOCKER_MINIMUM_API_VERSION
2017-03-27 18:46:25 +00:00
from gns3server . compute . docker . docker_error import DockerError , DockerHttp404Error
2015-10-14 16:10:05 +00:00
2016-05-02 15:06:51 +00:00
@pytest.fixture
2020-06-16 04:29:03 +00:00
async def vm ( loop ) :
2015-10-14 16:10:05 +00:00
vm = Docker ( )
2016-01-11 18:14:45 +00:00
vm . _connected = True
2017-03-20 22:50:31 +00:00
vm . _session = MagicMock ( )
vm . _session . closed = False
2016-05-02 15:06:51 +00:00
return vm
2020-06-16 04:29:03 +00:00
async def test_query_success ( vm ) :
2016-05-02 15:06:51 +00:00
2015-10-14 16:10:05 +00:00
response = MagicMock ( )
response . status = 200
2016-02-11 14:49:28 +00:00
response . headers = { ' CONTENT-TYPE ' : ' application/json ' }
2015-10-14 16:10:05 +00:00
2018-10-15 10:05:49 +00:00
async def read ( ) :
2015-10-14 16:10:05 +00:00
return b ' { " c " : false} '
response . read . side_effect = read
2017-03-20 22:50:31 +00:00
vm . _session . request = AsyncioMagicMock ( return_value = response )
2020-06-16 04:29:03 +00:00
data = await vm . query ( " POST " , " test " , data = { " a " : True } , params = { " b " : 1 } )
2017-03-20 22:50:31 +00:00
vm . _session . request . assert_called_with ( ' POST ' ,
2017-04-24 16:49:47 +00:00
' http://docker/v1.25/test ' ,
2017-03-20 22:50:31 +00:00
data = ' { " a " : true} ' ,
headers = { ' content-type ' : ' application/json ' } ,
params = { ' b ' : 1 } ,
timeout = 300 )
2015-10-14 16:10:05 +00:00
assert data == { " c " : False }
2020-06-16 04:29:03 +00:00
async def test_query_error ( vm ) :
2015-10-14 16:10:05 +00:00
response = MagicMock ( )
response . status = 404
2018-10-15 10:05:49 +00:00
async def read ( ) :
2015-10-14 16:10:05 +00:00
return b " NOT FOUND "
response . read . side_effect = read
2017-03-20 22:50:31 +00:00
vm . _session . request = AsyncioMagicMock ( return_value = response )
with pytest . raises ( DockerError ) :
2020-06-16 04:29:03 +00:00
await vm . query ( " POST " , " test " , data = { " a " : True } , params = { " b " : 1 } )
2017-03-20 22:50:31 +00:00
vm . _session . request . assert_called_with ( ' POST ' ,
2017-04-24 16:49:47 +00:00
' http://docker/v1.25/test ' ,
2017-03-20 22:50:31 +00:00
data = ' { " a " : true} ' ,
headers = { ' content-type ' : ' application/json ' } ,
params = { ' b ' : 1 } ,
timeout = 300 )
2015-10-14 16:10:05 +00:00
2020-06-16 04:29:03 +00:00
async def test_query_error_json ( vm ) :
2015-10-14 16:10:05 +00:00
response = MagicMock ( )
response . status = 404
2018-10-15 10:05:49 +00:00
async def read ( ) :
2015-10-14 16:10:05 +00:00
return b ' { " message " : " Error " } '
response . read . side_effect = read
2017-03-20 22:50:31 +00:00
vm . _session . request = AsyncioMagicMock ( return_value = response )
with pytest . raises ( DockerError ) :
2020-06-16 04:29:03 +00:00
await vm . query ( " POST " , " test " , data = { " a " : True } , params = { " b " : 1 } )
2017-03-20 22:50:31 +00:00
vm . _session . request . assert_called_with ( ' POST ' ,
2017-04-24 16:49:47 +00:00
' http://docker/v1.25/test ' ,
2017-03-20 22:50:31 +00:00
data = ' { " a " : true} ' ,
headers = { ' content-type ' : ' application/json ' } ,
params = { ' b ' : 1 } ,
timeout = 300 )
2015-10-14 16:10:05 +00:00
2020-06-16 04:29:03 +00:00
async def test_list_images ( ) :
2015-10-14 16:10:05 +00: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 15:57:06 +00:00
with asyncio_patch ( " gns3server.compute.docker.Docker.query " , return_value = response ) as mock :
2020-06-16 04:29:03 +00:00
images = await Docker . instance ( ) . list_images ( )
2015-10-14 16:10:05 +00:00
mock . assert_called_with ( " GET " , " images/json " , params = { " all " : 0 } )
2016-01-04 08:28:12 +00: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
2017-03-27 18:46:25 +00:00
2020-06-16 04:29:03 +00:00
async def test_pull_image ( ) :
2017-03-27 18:46:25 +00:00
class Response :
"""
2020-06-16 04:29:03 +00:00
Simulate a response split in multiple packets
2017-03-27 18:46:25 +00:00
"""
def __init__ ( self ) :
self . _read = - 1
2018-10-15 10:05:49 +00:00
async def read ( self , size ) :
2017-03-27 18:46:25 +00:00
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 :
2020-06-16 04:29:03 +00:00
await Docker . instance ( ) . pull_image ( " ubuntu " )
2017-03-27 18:46:25 +00:00
mock . assert_called_with ( " POST " , " images/create " , params = { " fromImage " : " ubuntu " } , timeout = None )
2017-07-06 09:24:55 +00:00
2020-06-16 04:29:03 +00:00
async def test_docker_check_connection_docker_minimum_version ( vm ) :
2017-07-06 09:24:55 +00:00
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 ) :
2020-06-16 04:29:03 +00:00
await vm . _check_connection ( )
2017-07-06 09:24:55 +00:00
2020-06-16 04:29:03 +00:00
async def test_docker_check_connection_docker_preferred_version_against_newer ( vm ) :
2017-07-06 09:24:55 +00:00
response = {
' ApiVersion ' : ' 1.31 '
}
with patch ( " gns3server.compute.docker.Docker.connector " ) , \
asyncio_patch ( " gns3server.compute.docker.Docker.query " , return_value = response ) :
vm . _connected = False
2020-06-16 04:29:03 +00:00
await vm . _check_connection ( )
2017-07-06 09:24:55 +00:00
assert vm . _api_version == DOCKER_PREFERRED_API_VERSION
2020-06-16 04:29:03 +00:00
async def test_docker_check_connection_docker_preferred_version_against_older ( vm ) :
2017-07-06 09:24:55 +00:00
response = {
' ApiVersion ' : ' 1.27 ' ,
}
with patch ( " gns3server.compute.docker.Docker.connector " ) , \
asyncio_patch ( " gns3server.compute.docker.Docker.query " , return_value = response ) :
vm . _connected = False
2020-06-16 04:29:03 +00:00
await vm . _check_connection ( )
assert vm . _api_version == DOCKER_MINIMUM_API_VERSION
2024-02-14 08:13:45 +00:00
@pytest.mark.asyncio
async def test_install_busybox ( ) :
mock_process = MagicMock ( )
mock_process . returncode = 1 # means that busybox is not dynamically linked
mock_process . communicate = AsyncioMagicMock ( return_value = ( b " " , b " not a dynamic executable " ) )
with patch ( " gns3server.compute.docker.os.path.isfile " , return_value = False ) :
with patch ( " gns3server.compute.docker.shutil.which " , return_value = " /usr/bin/busybox " ) :
with asyncio_patch ( " gns3server.compute.docker.asyncio.create_subprocess_exec " , return_value = mock_process ) as create_subprocess_mock :
with patch ( " gns3server.compute.docker.shutil.copy2 " ) as copy2_mock :
dst_dir = Docker . resources_path ( )
await Docker . install_busybox ( dst_dir )
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 ( )
mock_process . returncode = 0 # means that busybox is dynamically linked
mock_process . communicate = AsyncioMagicMock ( return_value = ( b " Dynamically linked library " , b " " ) )
with patch ( " os.path.isfile " , return_value = False ) :
with patch ( " gns3server.compute.docker.shutil.which " , return_value = " /usr/bin/busybox " ) :
with asyncio_patch ( " gns3server.compute.docker.asyncio.create_subprocess_exec " , return_value = mock_process ) :
with pytest . raises ( DockerError ) as e :
dst_dir = Docker . resources_path ( )
await Docker . install_busybox ( dst_dir )
2024-04-23 11:00:37 +00:00
assert str ( e . value ) == " No busybox executable could be found, please install busybox (apt install busybox-static on Debian/Ubuntu) and make sure it is in your PATH "
2024-02-14 08:13:45 +00:00
@pytest.mark.asyncio
async def test_install_busybox_no_executables ( ) :
with patch ( " gns3server.compute.docker.os.path.isfile " , return_value = False ) :
with patch ( " gns3server.compute.docker.shutil.which " , return_value = None ) :
with pytest . raises ( DockerError ) as e :
dst_dir = Docker . resources_path ( )
await Docker . install_busybox ( dst_dir )
2024-04-23 11:00:37 +00:00
assert str ( e . value ) == " No busybox executable could be found, please install busybox (apt install busybox-static on Debian/Ubuntu) and make sure it is in your PATH "