Skip to content

Commit 2c72cd9

Browse files
authored
Create the go2rtc unix socket inside a temporary folder (home-assistant#157742)
1 parent 3bccb4b commit 2c72cd9

File tree

8 files changed

+73
-25
lines changed

8 files changed

+73
-25
lines changed

homeassistant/components/go2rtc/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import logging
77
from secrets import token_hex
88
import shutil
9+
from tempfile import mkdtemp
910

1011
from aiohttp import BasicAuth, ClientSession, UnixConnector
1112
from aiohttp.client_exceptions import ClientConnectionError, ServerConnectionError
@@ -62,11 +63,11 @@
6263
CONF_DEBUG_UI,
6364
DEBUG_UI_URL_MESSAGE,
6465
DOMAIN,
65-
HA_MANAGED_UNIX_SOCKET,
6666
HA_MANAGED_URL,
6767
RECOMMENDED_VERSION,
6868
)
6969
from .server import Server
70+
from .util import get_go2rtc_unix_socket_path
7071

7172
_LOGGER = logging.getLogger(__name__)
7273

@@ -154,10 +155,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
154155

155156
auth = BasicAuth(username, password)
156157
# HA will manage the binary
158+
temp_dir = mkdtemp(prefix="go2rtc-")
157159
# Manually created session (not using the helper) needs to be closed manually
158160
# See on_stop listener below
159161
session = ClientSession(
160-
connector=UnixConnector(path=HA_MANAGED_UNIX_SOCKET), auth=auth
162+
connector=UnixConnector(path=get_go2rtc_unix_socket_path(temp_dir)),
163+
auth=auth,
161164
)
162165
server = Server(
163166
hass,
@@ -166,6 +169,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
166169
enable_ui=domain_config.get(CONF_DEBUG_UI, False),
167170
username=username,
168171
password=password,
172+
working_dir=temp_dir,
169173
)
170174
try:
171175
await server.start()

homeassistant/components/go2rtc/const.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
DEBUG_UI_URL_MESSAGE = "Url and debug_ui cannot be set at the same time."
77
HA_MANAGED_API_PORT = 11984
88
HA_MANAGED_URL = f"http://localhost:{HA_MANAGED_API_PORT}/"
9-
HA_MANAGED_UNIX_SOCKET = "/run/go2rtc.sock"
109
# When changing this version, also update the corresponding SHA hash (_GO2RTC_SHA)
1110
# in script/hassfest/docker.py.
1211
RECOMMENDED_VERSION = "1.9.12"

homeassistant/components/go2rtc/server.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
from homeassistant.core import HomeAssistant
1313
from homeassistant.exceptions import HomeAssistantError
1414

15-
from .const import HA_MANAGED_API_PORT, HA_MANAGED_UNIX_SOCKET, HA_MANAGED_URL
15+
from .const import HA_MANAGED_API_PORT, HA_MANAGED_URL
16+
from .util import get_go2rtc_unix_socket_path
1617

1718
_LOGGER = logging.getLogger(__name__)
1819
_TERMINATE_TIMEOUT = 5
1920
_SETUP_TIMEOUT = 30
2021
_SUCCESSFUL_BOOT_MESSAGE = "INF [api] listen addr="
21-
_LOCALHOST_IP = "127.0.0.1"
2222
_LOG_BUFFER_SIZE = 512
2323
_RESPAWN_COOLDOWN = 1
2424

@@ -122,7 +122,9 @@ def _format_list_for_yaml(items: tuple[str, ...]) -> str:
122122
return f"[{formatted_items}]"
123123

124124

125-
def _create_temp_file(enable_ui: bool, username: str, password: str) -> str:
125+
def _create_temp_file(
126+
enable_ui: bool, username: str, password: str, working_dir: str
127+
) -> str:
126128
"""Create temporary config file."""
127129
app_modules: tuple[str, ...] = _APP_MODULES
128130
api_paths: tuple[str, ...] = _API_ALLOW_PATHS
@@ -139,11 +141,13 @@ def _create_temp_file(enable_ui: bool, username: str, password: str) -> str:
139141

140142
# Set delete=False to prevent the file from being deleted when the file is closed
141143
# Linux is clearing tmp folder on reboot, so no need to delete it manually
142-
with NamedTemporaryFile(prefix="go2rtc_", suffix=".yaml", delete=False) as file:
144+
with NamedTemporaryFile(
145+
prefix="go2rtc_", suffix=".yaml", dir=working_dir, delete=False
146+
) as file:
143147
file.write(
144148
_GO2RTC_CONFIG_FORMAT.format(
145149
listen_config=listen_config,
146-
unix_socket=HA_MANAGED_UNIX_SOCKET,
150+
unix_socket=get_go2rtc_unix_socket_path(working_dir),
147151
app_modules=_format_list_for_yaml(app_modules),
148152
api_allow_paths=_format_list_for_yaml(api_paths),
149153
username=username,
@@ -165,6 +169,7 @@ def __init__(
165169
enable_ui: bool = False,
166170
username: str,
167171
password: str,
172+
working_dir: str,
168173
) -> None:
169174
"""Initialize the server."""
170175
self._hass = hass
@@ -173,6 +178,7 @@ def __init__(
173178
self._enable_ui = enable_ui
174179
self._username = username
175180
self._password = password
181+
self._working_dir = working_dir
176182
self._log_buffer: deque[str] = deque(maxlen=_LOG_BUFFER_SIZE)
177183
self._process: asyncio.subprocess.Process | None = None
178184
self._startup_complete = asyncio.Event()
@@ -190,7 +196,11 @@ async def _start(self) -> None:
190196
"""Start the server."""
191197
_LOGGER.debug("Starting go2rtc server")
192198
config_file = await self._hass.async_add_executor_job(
193-
_create_temp_file, self._enable_ui, self._username, self._password
199+
_create_temp_file,
200+
self._enable_ui,
201+
self._username,
202+
self._password,
203+
self._working_dir,
194204
)
195205

196206
self._startup_complete.clear()
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"""Go2rtc utility functions."""
2+
3+
from pathlib import Path
4+
5+
_HA_MANAGED_UNIX_SOCKET_FILE = "go2rtc.sock"
6+
7+
8+
def get_go2rtc_unix_socket_path(path: str | Path) -> str:
9+
"""Get the Go2rtc unix socket path."""
10+
if not isinstance(path, Path):
11+
path = Path(path)
12+
return str(path / _HA_MANAGED_UNIX_SOCKET_FILE)

tests/components/go2rtc/conftest.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Go2rtc test configuration."""
22

33
from collections.abc import Generator
4+
from pathlib import Path
45
from unittest.mock import AsyncMock, Mock, patch
56

67
from awesomeversion import AwesomeVersion
@@ -228,3 +229,15 @@ async def async_unload_entry_init(
228229
await hass.async_block_till_done()
229230

230231
return test_camera
232+
233+
234+
@pytest.fixture
235+
def server_dir(tmp_path: Path) -> Generator[Path]:
236+
"""Fixture to provide a temporary directory for the server."""
237+
server_dir = tmp_path / "go2rtc"
238+
server_dir.mkdir()
239+
with patch(
240+
"homeassistant.components.go2rtc.mkdtemp",
241+
return_value=str(server_dir),
242+
):
243+
yield server_dir

tests/components/go2rtc/snapshots/test_server.ambr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
_CallList([
44
_Call(
55
tuple(
6-
b'# This file is managed by Home Assistant\n# Do not edit it manually\n\napp:\n modules: ["api","exec","ffmpeg","http","mjpeg","onvif","rtmp","rtsp","srtp","webrtc","ws"]\n\napi:\n listen: ""\n unix_listen: "/run/go2rtc.sock"\n allow_paths: ["/","/api","/api/frame.jpeg","/api/schemes","/api/streams","/api/webrtc","/api/ws"]\n local_auth: true\n username: d2a0b844f4cdbe773702176c47c9a675eb0c56a0779b8f880cdb3b492ed3b1c1\n password: bc495d266a32e66ba69b9c72546e00101e04fb573f1bd08863fe4ad1aac02949\n\n# ffmpeg needs the exec module\n# Restrict execution to only ffmpeg binary\nexec:\n allow_paths:\n - ffmpeg\n\nrtsp:\n listen: "127.0.0.1:18554"\n\nwebrtc:\n listen: ":18555/tcp"\n ice_servers: []\n',
6+
b'# This file is managed by Home Assistant\n# Do not edit it manually\n\napp:\n modules: ["api","exec","ffmpeg","http","mjpeg","onvif","rtmp","rtsp","srtp","webrtc","ws"]\n\napi:\n listen: ""\n unix_listen: "/test/path/go2rtc.sock"\n allow_paths: ["/","/api","/api/frame.jpeg","/api/schemes","/api/streams","/api/webrtc","/api/ws"]\n local_auth: true\n username: d2a0b844f4cdbe773702176c47c9a675eb0c56a0779b8f880cdb3b492ed3b1c1\n password: bc495d266a32e66ba69b9c72546e00101e04fb573f1bd08863fe4ad1aac02949\n\n# ffmpeg needs the exec module\n# Restrict execution to only ffmpeg binary\nexec:\n allow_paths:\n - ffmpeg\n\nrtsp:\n listen: "127.0.0.1:18554"\n\nwebrtc:\n listen: ":18555/tcp"\n ice_servers: []\n',
77
),
88
dict({
99
}),
@@ -14,7 +14,7 @@
1414
_CallList([
1515
_Call(
1616
tuple(
17-
b'# This file is managed by Home Assistant\n# Do not edit it manually\n\napp:\n modules: ["api","exec","ffmpeg","http","mjpeg","onvif","rtmp","rtsp","srtp","webrtc","ws","debug"]\n\napi:\n listen: ":11984"\n unix_listen: "/run/go2rtc.sock"\n allow_paths: ["/","/api","/api/frame.jpeg","/api/schemes","/api/streams","/api/webrtc","/api/ws","/api/config","/api/log","/api/streams.dot"]\n local_auth: true\n username: user\n password: pass\n\n# ffmpeg needs the exec module\n# Restrict execution to only ffmpeg binary\nexec:\n allow_paths:\n - ffmpeg\n\nrtsp:\n listen: "127.0.0.1:18554"\n\nwebrtc:\n listen: ":18555/tcp"\n ice_servers: []\n',
17+
b'# This file is managed by Home Assistant\n# Do not edit it manually\n\napp:\n modules: ["api","exec","ffmpeg","http","mjpeg","onvif","rtmp","rtsp","srtp","webrtc","ws","debug"]\n\napi:\n listen: ":11984"\n unix_listen: "/test/path/go2rtc.sock"\n allow_paths: ["/","/api","/api/frame.jpeg","/api/schemes","/api/streams","/api/webrtc","/api/ws","/api/config","/api/log","/api/streams.dot"]\n local_auth: true\n username: user\n password: pass\n\n# ffmpeg needs the exec module\n# Restrict execution to only ffmpeg binary\nexec:\n allow_paths:\n - ffmpeg\n\nrtsp:\n listen: "127.0.0.1:18554"\n\nwebrtc:\n listen: ":18555/tcp"\n ice_servers: []\n',
1818
),
1919
dict({
2020
}),

tests/components/go2rtc/test_init.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from collections.abc import Awaitable, Callable
44
import logging
5+
from pathlib import Path
56
from typing import NamedTuple
67
from unittest.mock import ANY, AsyncMock, Mock, patch
78

@@ -39,9 +40,9 @@
3940
CONF_DEBUG_UI,
4041
DEBUG_UI_URL_MESSAGE,
4142
DOMAIN,
42-
HA_MANAGED_UNIX_SOCKET,
4343
RECOMMENDED_VERSION,
4444
)
45+
from homeassistant.components.go2rtc.util import get_go2rtc_unix_socket_path
4546
from homeassistant.components.stream import Orientation
4647
from homeassistant.config_entries import ConfigEntryState
4748
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME
@@ -239,6 +240,7 @@ async def test_setup_go_binary(
239240
server_stop: Mock,
240241
init_test_integration: MockCamera,
241242
has_go2rtc_entry: bool,
243+
server_dir: Path,
242244
config: ConfigType,
243245
ui_enabled: bool,
244246
expected_username: str,
@@ -255,6 +257,7 @@ def after_setup() -> None:
255257
enable_ui=ui_enabled,
256258
username=expected_username,
257259
password=expected_password,
260+
working_dir=str(server_dir),
258261
)
259262
call_kwargs = server.call_args[1]
260263
assert call_kwargs["username"] == expected_username
@@ -1034,7 +1037,7 @@ async def test_stream_orientation_with_generic_camera(
10341037
"rest_client",
10351038
"server",
10361039
)
1037-
async def test_unix_socket_connection(hass: HomeAssistant) -> None:
1040+
async def test_unix_socket_connection(hass: HomeAssistant, server_dir: Path) -> None:
10381041
"""Test Unix socket is used for HA-managed go2rtc instances."""
10391042
config = {DOMAIN: {}}
10401043

@@ -1056,7 +1059,7 @@ async def test_unix_socket_connection(hass: HomeAssistant) -> None:
10561059
assert "connector" in call_kwargs
10571060
connector = call_kwargs["connector"]
10581061
assert isinstance(connector, UnixConnector)
1059-
assert connector.path == HA_MANAGED_UNIX_SOCKET
1062+
assert connector.path == get_go2rtc_unix_socket_path(server_dir)
10601063
# Auth should be auto-generated when credentials are not explicitly configured
10611064
assert "auth" in call_kwargs
10621065
auth = call_kwargs["auth"]
@@ -1120,7 +1123,7 @@ async def test_basic_auth_with_custom_url(hass: HomeAssistant) -> None:
11201123

11211124

11221125
@pytest.mark.usefixtures("rest_client")
1123-
async def test_basic_auth_with_debug_ui(hass: HomeAssistant) -> None:
1126+
async def test_basic_auth_with_debug_ui(hass: HomeAssistant, server_dir: Path) -> None:
11241127
"""Test BasicAuth session is created when username and password are provided with debug_ui."""
11251128
config = {
11261129
DOMAIN: {
@@ -1158,7 +1161,7 @@ async def test_basic_auth_with_debug_ui(hass: HomeAssistant) -> None:
11581161
assert "connector" in call_kwargs
11591162
connector = call_kwargs["connector"]
11601163
assert isinstance(connector, UnixConnector)
1161-
assert connector.path == HA_MANAGED_UNIX_SOCKET
1164+
assert connector.path == get_go2rtc_unix_socket_path(server_dir)
11621165
assert "auth" in call_kwargs
11631166
auth = call_kwargs["auth"]
11641167
assert isinstance(auth, BasicAuth)

tests/components/go2rtc/test_server.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import asyncio
44
from collections.abc import Generator
55
import logging
6+
from pathlib import Path
67
import subprocess
78
from unittest.mock import AsyncMock, MagicMock, Mock, patch
89

@@ -47,16 +48,22 @@ def server(
4748
enable_ui: bool,
4849
username: str,
4950
password: str,
50-
) -> Server:
51+
server_dir: Path,
52+
) -> Generator[Server]:
5153
"""Fixture to initialize the Server."""
52-
return Server(
53-
hass,
54-
binary=TEST_BINARY,
55-
session=mock_session,
56-
enable_ui=enable_ui,
57-
username=username,
58-
password=password,
59-
)
54+
with patch(
55+
"homeassistant.components.go2rtc.server.get_go2rtc_unix_socket_path",
56+
return_value="/test/path/go2rtc.sock",
57+
):
58+
yield Server(
59+
hass,
60+
binary=TEST_BINARY,
61+
session=mock_session,
62+
enable_ui=enable_ui,
63+
username=username,
64+
password=password,
65+
working_dir=str(server_dir),
66+
)
6067

6168

6269
@pytest.fixture

0 commit comments

Comments
 (0)