mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-09-16 23:10:47 +03:00
When a Docker node is created on a remote compute whose Docker daemon does not have the image, the compute now raises ImageMissingError instead of blindly pulling from the Docker repository. The controller exports the image from the Docker daemon on its host (docker save stream) and streams it to the compute which loads it, so locally built or docker-loaded images work across computes. When the image is not available on the controller host either, the compute is asked to pull it from the Docker repository as a fallback. - add a POST /docker/images/load compute endpoint that streams a docker save tar into the Docker daemon - let Docker.http_query pass raw (non-dict) request bodies through so the tar can be streamed to the daemon - drop the inline pull from DockerVM.create() and the now unused DockerVM.pull_image wrapper
63 lines
2.5 KiB
Python
63 lines
2.5 KiB
Python
#
|
|
# Copyright (C) 2026 GNS3 Technologies Inc.
|
|
#
|
|
# 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/>.
|
|
|
|
import pytest
|
|
|
|
from fastapi import FastAPI, status
|
|
from httpx import AsyncClient
|
|
from tests.utils import asyncio_patch
|
|
|
|
pytestmark = pytest.mark.asyncio
|
|
|
|
|
|
class TestImagesRoutes:
|
|
|
|
async def test_pull_docker_image(self, app: FastAPI, compute_client: AsyncClient) -> None:
|
|
|
|
with asyncio_patch("gns3server.compute.docker.Docker.pull_image") as mock:
|
|
response = await compute_client.post(
|
|
app.url_path_for("compute:pull_docker_image"),
|
|
json={"image": "nginx:latest"}
|
|
)
|
|
mock.assert_called_once_with("nginx:latest", force=True)
|
|
assert response.status_code == status.HTTP_204_NO_CONTENT
|
|
|
|
@pytest.mark.parametrize("image", ["", " ", "nginx latest"])
|
|
async def test_pull_docker_image_rejects_invalid_name(
|
|
self, app: FastAPI, compute_client: AsyncClient, image: str
|
|
) -> None:
|
|
|
|
with asyncio_patch("gns3server.compute.docker.Docker.pull_image") as mock:
|
|
response = await compute_client.post(
|
|
app.url_path_for("compute:pull_docker_image"),
|
|
json={"image": image}
|
|
)
|
|
mock.assert_not_called()
|
|
assert response.status_code == status.HTTP_422_UNPROCESSABLE_CONTENT
|
|
|
|
async def test_load_docker_image(self, app: FastAPI, compute_client: AsyncClient) -> None:
|
|
|
|
with asyncio_patch("gns3server.compute.docker.Docker.load_image") as mock:
|
|
response = await compute_client.post(
|
|
app.url_path_for("compute:load_docker_image"),
|
|
content=b"docker-save-tar-bytes"
|
|
)
|
|
mock.assert_called_once()
|
|
# the tar is streamed to the Docker daemon as an async iterable
|
|
stream = mock.call_args[0][0]
|
|
assert hasattr(stream, "__aiter__")
|
|
assert response.status_code == status.HTTP_204_NO_CONTENT
|