mirror of
https://github.com/home-assistant/supervisor.git
synced 2026-02-15 07:27:13 +00:00
Fix 'DockerMount is not JSON serializable' in DockerAPI.run_command (#6477)
This commit is contained in:
@@ -27,6 +27,7 @@ from docker.api.client import APIClient
|
|||||||
from docker.client import DockerClient
|
from docker.client import DockerClient
|
||||||
from docker.models.containers import Container, ContainerCollection
|
from docker.models.containers import Container, ContainerCollection
|
||||||
from docker.models.networks import Network
|
from docker.models.networks import Network
|
||||||
|
from docker.types import Mount
|
||||||
from docker.types.daemon import CancellableStream
|
from docker.types.daemon import CancellableStream
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
@@ -621,6 +622,8 @@ class DockerAPI(CoreSysAttributes):
|
|||||||
image: str,
|
image: str,
|
||||||
version: str = "latest",
|
version: str = "latest",
|
||||||
command: str | list[str] | None = None,
|
command: str | list[str] | None = None,
|
||||||
|
*,
|
||||||
|
mounts: list[DockerMount] | None = None,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> CommandReturn:
|
) -> CommandReturn:
|
||||||
"""Create a temporary container and run command.
|
"""Create a temporary container and run command.
|
||||||
@@ -641,6 +644,11 @@ class DockerAPI(CoreSysAttributes):
|
|||||||
detach=True,
|
detach=True,
|
||||||
network=self.network.name,
|
network=self.network.name,
|
||||||
use_config_proxy=False,
|
use_config_proxy=False,
|
||||||
|
mounts=(
|
||||||
|
[cast(Mount, mount.to_dict()) for mount in mounts]
|
||||||
|
if mounts
|
||||||
|
else None
|
||||||
|
),
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import pytest
|
|||||||
from requests import RequestException
|
from requests import RequestException
|
||||||
|
|
||||||
from supervisor.coresys import CoreSys
|
from supervisor.coresys import CoreSys
|
||||||
|
from supervisor.docker.const import DockerMount, MountBindOptions, MountType
|
||||||
from supervisor.docker.manager import CommandReturn, DockerAPI
|
from supervisor.docker.manager import CommandReturn, DockerAPI
|
||||||
from supervisor.exceptions import DockerError
|
from supervisor.exceptions import DockerError
|
||||||
|
|
||||||
@@ -43,6 +44,7 @@ async def test_run_command_success(docker: DockerAPI):
|
|||||||
use_config_proxy=False,
|
use_config_proxy=False,
|
||||||
stdout=True,
|
stdout=True,
|
||||||
stderr=True,
|
stderr=True,
|
||||||
|
mounts=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Verify container cleanup
|
# Verify container cleanup
|
||||||
@@ -74,6 +76,7 @@ async def test_run_command_with_defaults(docker: DockerAPI):
|
|||||||
detach=True,
|
detach=True,
|
||||||
network=docker.network.name,
|
network=docker.network.name,
|
||||||
use_config_proxy=False,
|
use_config_proxy=False,
|
||||||
|
mounts=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Verify container.logs was called with default stdout/stderr
|
# Verify container.logs was called with default stdout/stderr
|
||||||
@@ -140,6 +143,64 @@ async def test_run_command_custom_stdout_stderr(docker: DockerAPI):
|
|||||||
assert result.output == b"output"
|
assert result.output == b"output"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_run_command_with_mounts(docker: DockerAPI):
|
||||||
|
"""Test command execution with mounts are correctly converted."""
|
||||||
|
# Mock container and its methods
|
||||||
|
mock_container = MagicMock()
|
||||||
|
mock_container.wait.return_value = {"StatusCode": 0}
|
||||||
|
mock_container.logs.return_value = b"output"
|
||||||
|
|
||||||
|
# Mock docker containers.run to return our mock container
|
||||||
|
docker.dockerpy.containers.run.return_value = mock_container
|
||||||
|
|
||||||
|
# Create test mounts
|
||||||
|
mounts = [
|
||||||
|
DockerMount(
|
||||||
|
type=MountType.BIND,
|
||||||
|
source="/dev",
|
||||||
|
target="/dev",
|
||||||
|
read_only=True,
|
||||||
|
bind_options=MountBindOptions(read_only_non_recursive=True),
|
||||||
|
),
|
||||||
|
DockerMount(
|
||||||
|
type=MountType.VOLUME,
|
||||||
|
source="my_volume",
|
||||||
|
target="/data",
|
||||||
|
read_only=False,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Execute the command with mounts
|
||||||
|
result = docker.run_command(image="alpine", command="test", mounts=mounts)
|
||||||
|
|
||||||
|
# Verify the result
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
# Check that mounts were converted correctly
|
||||||
|
docker.dockerpy.containers.run.assert_called_once_with(
|
||||||
|
"alpine:latest",
|
||||||
|
command="test",
|
||||||
|
detach=True,
|
||||||
|
network=docker.network.name,
|
||||||
|
use_config_proxy=False,
|
||||||
|
mounts=[
|
||||||
|
{
|
||||||
|
"Type": "bind",
|
||||||
|
"Source": "/dev",
|
||||||
|
"Target": "/dev",
|
||||||
|
"ReadOnly": True,
|
||||||
|
"BindOptions": {"ReadOnlyNonRecursive": True},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "volume",
|
||||||
|
"Source": "my_volume",
|
||||||
|
"Target": "/data",
|
||||||
|
"ReadOnly": False,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("path_extern", "tmp_supervisor_data")
|
@pytest.mark.usefixtures("path_extern", "tmp_supervisor_data")
|
||||||
async def test_run_container_with_cidfile(coresys: CoreSys, docker: DockerAPI):
|
async def test_run_container_with_cidfile(coresys: CoreSys, docker: DockerAPI):
|
||||||
"""Test container creation with cidfile and bind mount."""
|
"""Test container creation with cidfile and bind mount."""
|
||||||
|
|||||||
Reference in New Issue
Block a user