diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 41bf46b8..a2b17b2c 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -416,16 +416,30 @@ class DockerVM(BaseNode): Because docker run as root we need to fix permission and ownership to allow user to interact with it from their filesystem and do operation like file delete """ + + state = yield from self._get_container_state() + if state == "stopped" or state == "exited": + # We need to restart it to fix permissions + yield from self.manager.query("POST", "containers/{}/start".format(self._cid)) + for volume in self._volumes: log.debug("Docker container '{name}' [{image}] fix ownership on {path}".format( name=self._name, image=self._image, path=volume)) - process = yield from asyncio.subprocess.create_subprocess_exec("docker", - "exec", - self._cid, - "/gns3/bin/busybox", - "sh", - "-c", - "(/gns3/bin/busybox find \"{path}\" -depth -print0 | /gns3/bin/busybox xargs -0 /gns3/bin/busybox stat -c '%a:%u:%g:%n' > \"{path}/.gns3_perms\") && /gns3/bin/busybox chmod -R u+rX \"{path}\" && /gns3/bin/busybox chown {uid}:{gid} -R \"{path}\"".format(uid=os.getuid(), gid=os.getgid(), path=volume)) + process = yield from asyncio.subprocess.create_subprocess_exec( + "docker", + "exec", + self._cid, + "/gns3/bin/busybox", + "sh", + "-c", + "(" + "/gns3/bin/busybox find \"{path}\" -depth -print0" + " | /gns3/bin/busybox xargs -0 /gns3/bin/busybox stat -c '%a:%u:%g:%n' > \"{path}/.gns3_perms\"" + ")" + " && /gns3/bin/busybox chmod -R u+rX \"{path}\"" + " && /gns3/bin/busybox chown {uid}:{gid} -R \"{path}\"" + .format(uid=os.getuid(), gid=os.getgid(), path=volume), + ) yield from process.wait() @asyncio.coroutine @@ -564,13 +578,15 @@ class DockerVM(BaseNode): try: state = yield from self._get_container_state() except DockerHttp404Error: - state = "stopped" + self.status = "stopped" + return if state == "paused": yield from self.unpause() - if state != "stopped": - yield from self._fix_permissions() + yield from self._fix_permissions() + state = yield from self._get_container_state() + if state != "stopped" or state != "exited": # t=5 number of seconds to wait before killing the container try: yield from self.manager.query("POST", "containers/{}/stop".format(self._cid), params={"t": 5}) diff --git a/tests/compute/docker/test_docker_vm.py b/tests/compute/docker/test_docker_vm.py index 6b38f5a6..aeb7dc78 100644 --- a/tests/compute/docker/test_docker_vm.py +++ b/tests/compute/docker/test_docker_vm.py @@ -934,6 +934,7 @@ def test_create_network_interfaces(vm): @pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") def test_fix_permission(vm, loop): vm._volumes = ["/etc"] + vm._get_container_state = AsyncioMagicMock(return_value="running") process = MagicMock() with asyncio_patch("asyncio.subprocess.create_subprocess_exec", return_value=process) as mock_exec: loop.run_until_complete(vm._fix_permissions()) @@ -941,6 +942,19 @@ def test_fix_permission(vm, loop): assert process.wait.called +@pytest.mark.skipif(sys.platform.startswith("win"), reason="Not supported on Windows") +def test_fix_permission_not_running(vm, loop): + vm._volumes = ["/etc"] + vm._get_container_state = AsyncioMagicMock(return_value="stopped") + process = MagicMock() + with asyncio_patch("gns3server.compute.docker.Docker.query") as mock_start: + with asyncio_patch("asyncio.subprocess.create_subprocess_exec", return_value=process) as mock_exec: + loop.run_until_complete(vm._fix_permissions()) + mock_exec.assert_called_with('docker', 'exec', 'e90e34656842', '/gns3/bin/busybox', 'sh', '-c', '(/gns3/bin/busybox find "/etc" -depth -print0 | /gns3/bin/busybox xargs -0 /gns3/bin/busybox stat -c \'%a:%u:%g:%n\' > "/etc/.gns3_perms") && /gns3/bin/busybox chmod -R u+rX "/etc" && /gns3/bin/busybox chown {}:{} -R "/etc"'.format(os.getuid(), os.getgid())) + assert mock_start.called + assert process.wait.called + + def test_read_console_output_with_binary_mode(vm, loop): class InputStreamMock(object): def __init__(self):