1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-24 17:28:08 +00:00

Fix Session is closed when listing docker images

Fix #955
This commit is contained in:
Julien Duponchelle 2017-03-20 23:50:31 +01:00
parent 78c154e376
commit 34f5a6f82c
No known key found for this signature in database
GPG Key ID: CE8B29639E07F5E8
2 changed files with 52 additions and 39 deletions

View File

@ -46,31 +46,43 @@ class Docker(BaseManager):
self._connected = False self._connected = False
# Allow locking during ubridge operations # Allow locking during ubridge operations
self.ubridge_lock = asyncio.Lock() self.ubridge_lock = asyncio.Lock()
self._version_checked = False
self._session = None self._session = None
self._connector = None
@asyncio.coroutine @asyncio.coroutine
def connector(self): def session(self):
if not self._connected or self._connector.closed: if not self._connected or self._session.closed:
if not sys.platform.startswith("linux"):
raise DockerError("Docker is supported only on Linux")
try: try:
self._connector = aiohttp.connector.UnixConnector(self._server_url, conn_timeout=2)
self._connected = True self._connected = True
connector = self.connector()
self._session = aiohttp.ClientSession(connector=connector)
version = yield from self.query("GET", "version") version = yield from self.query("GET", "version")
except (aiohttp.errors.ClientOSError, FileNotFoundError): except (aiohttp.errors.ClientOSError, FileNotFoundError):
self._connected = False self._connected = False
raise DockerError("Can't connect to docker daemon") raise DockerError("Can't connect to docker daemon")
if parse_version(version["ApiVersion"]) < parse_version(DOCKER_MINIMUM_API_VERSION): if parse_version(version["ApiVersion"]) < parse_version(DOCKER_MINIMUM_API_VERSION):
raise DockerError("Docker API version is {}. GNS3 requires a minimum API version of {}".format(version["ApiVersion"], DOCKER_MINIMUM_API_VERSION)) raise DockerError("Docker API version is {}. GNS3 requires a minimum API version of {}".format(version["ApiVersion"], DOCKER_MINIMUM_API_VERSION))
return self._session
def connector(self):
if self._connector is None or self._connector.closed:
if not sys.platform.startswith("linux"):
raise DockerError("Docker is supported only on Linux")
try:
self._connector = aiohttp.connector.UnixConnector(self._server_url, conn_timeout=2)
except (aiohttp.errors.ClientOSError, FileNotFoundError):
raise DockerError("Can't connect to docker daemon")
return self._connector return self._connector
@asyncio.coroutine @asyncio.coroutine
def unload(self): def unload(self):
yield from super().unload() yield from super().unload()
if self._connected: if self._connected:
self._connector.close() if self._session and not self._session.closed:
yield from self._session.close()
if self._connector and not self._connector.closed:
yield from self._connector.close()
@asyncio.coroutine @asyncio.coroutine
def query(self, method, path, data={}, params={}): def query(self, method, path, data={}, params={}):
@ -108,9 +120,8 @@ class Docker(BaseManager):
data = json.dumps(data) data = json.dumps(data)
url = "http://docker/" + path url = "http://docker/" + path
try: try:
if self._session is None or self._session.closed is True: session = yield from self.session()
self._session = aiohttp.ClientSession(connector=(yield from self.connector())) response = yield from session.request(
response = yield from self._session.request(
method, method,
url, url,
params=params, params=params,
@ -147,7 +158,7 @@ class Docker(BaseManager):
url = "http://docker/" + path url = "http://docker/" + path
connection = yield from aiohttp.ws_connect(url, connection = yield from aiohttp.ws_connect(url,
connector=(yield from self.connector()), connector=self.connector(),
origin="http://docker", origin="http://docker",
autoping=True) autoping=True)
return connection return connection

View File

@ -19,7 +19,7 @@ import pytest
import asyncio import asyncio
from unittest.mock import MagicMock from unittest.mock import MagicMock
from tests.utils import asyncio_patch from tests.utils import asyncio_patch, AsyncioMagicMock
from gns3server.compute.docker import Docker from gns3server.compute.docker import Docker
from gns3server.compute.docker.docker_error import DockerError from gns3server.compute.docker.docker_error import DockerError
@ -28,6 +28,8 @@ from gns3server.compute.docker.docker_error import DockerError
def vm(): def vm():
vm = Docker() vm = Docker()
vm._connected = True vm._connected = True
vm._session = MagicMock()
vm._session.closed = False
return vm return vm
@ -42,14 +44,14 @@ def test_query_success(loop, vm):
return b'{"c": false}' return b'{"c": false}'
response.read.side_effect = read response.read.side_effect = read
with asyncio_patch("aiohttp.client.ClientSession.request", return_value=response) as mock: vm._session.request = AsyncioMagicMock(return_value=response)
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1}))) data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
mock.assert_called_with('POST', vm._session.request.assert_called_with('POST',
'http://docker/test', 'http://docker/test',
data='{"a": true}', data='{"a": true}',
headers={'content-type': 'application/json'}, headers={'content-type': 'application/json'},
params={'b': 1}, params={'b': 1},
timeout=300) timeout=300)
assert data == {"c": False} assert data == {"c": False}
@ -64,15 +66,15 @@ def test_query_error(loop, vm):
return b"NOT FOUND" return b"NOT FOUND"
response.read.side_effect = read response.read.side_effect = read
with asyncio_patch("aiohttp.client.ClientSession.request", return_value=response) as mock: vm._session.request = AsyncioMagicMock(return_value=response)
with pytest.raises(DockerError): with pytest.raises(DockerError):
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1}))) data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
mock.assert_called_with('POST', vm._session.request.assert_called_with('POST',
'http://docker/test', 'http://docker/test',
data='{"a": true}', data='{"a": true}',
headers={'content-type': 'application/json'}, headers={'content-type': 'application/json'},
params={'b': 1}, params={'b': 1},
timeout=300) timeout=300)
def test_query_error_json(loop, vm): def test_query_error_json(loop, vm):
@ -85,15 +87,15 @@ def test_query_error_json(loop, vm):
return b'{"message": "Error"}' return b'{"message": "Error"}'
response.read.side_effect = read response.read.side_effect = read
with asyncio_patch("aiohttp.client.ClientSession.request", return_value=response) as mock: vm._session.request = AsyncioMagicMock(return_value=response)
with pytest.raises(DockerError): with pytest.raises(DockerError):
data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1}))) data = loop.run_until_complete(asyncio.async(vm.query("POST", "test", data={"a": True}, params={"b": 1})))
mock.assert_called_with('POST', vm._session.request.assert_called_with('POST',
'http://docker/test', 'http://docker/test',
data='{"a": true}', data='{"a": true}',
headers={'content-type': 'application/json'}, headers={'content-type': 'application/json'},
params={'b': 1}, params={'b': 1},
timeout=300) timeout=300)
def test_list_images(loop): def test_list_images(loop):