Skip to content

Commit 83abd53

Browse files
authored
Merge branch 'main' into dan/proposechange-
2 parents 8e3388c + 6a0c3a6 commit 83abd53

3 files changed

Lines changed: 90 additions & 1 deletion

File tree

dev/breeze/src/airflow_breeze/global_constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ def get_default_platform_machine() -> str:
502502
REDIS_HOST_PORT = "26379"
503503
RABBITMQ_HOST_PORT = "25672"
504504
SSH_PORT = "12322"
505+
SIMPLE_AUTH_MANAGER_VITE_DEV_PORT = "5174"
505506
VITE_DEV_PORT = "5173"
506507
WEB_HOST_PORT = "28080"
507508
BREEZE_DEBUG_SCHEDULER_PORT = "50231"

dev/breeze/src/airflow_breeze/utils/run_utils.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,18 @@
2525
import shlex
2626
import shutil
2727
import signal
28+
import socket
2829
import stat
2930
import subprocess
3031
import sys
31-
from collections.abc import Mapping
32+
from collections.abc import Iterable, Mapping
3233
from pathlib import Path
3334
from subprocess import CalledProcessError, CompletedProcess
3435
from typing import Any
3536

3637
from rich.markup import escape
3738

39+
from airflow_breeze.global_constants import SIMPLE_AUTH_MANAGER_VITE_DEV_PORT, VITE_DEV_PORT
3840
from airflow_breeze.utils.ci_group import ci_group
3941
from airflow_breeze.utils.console import Output, console_print, get_console
4042
from airflow_breeze.utils.functools_cache import clearable_cache
@@ -532,12 +534,30 @@ def _clean_ui_assets(additional_ui_hooks: list[str]):
532534
console_print("[success]Cleaned ui assets[/]")
533535

534536

537+
def _find_occupied_local_ports(ports: Iterable[str]) -> list[str]:
538+
occupied_ports = []
539+
for port in ports:
540+
with contextlib.suppress(OSError):
541+
with socket.create_connection(("localhost", int(port)), timeout=0.1):
542+
occupied_ports.append(port)
543+
return occupied_ports
544+
545+
535546
def run_compile_ui_assets(
536547
dev: bool,
537548
run_in_background: bool,
538549
force_clean: bool,
539550
additional_ui_hooks: list[str],
540551
):
552+
if dev:
553+
occupied_ports = _find_occupied_local_ports((VITE_DEV_PORT, SIMPLE_AUTH_MANAGER_VITE_DEV_PORT))
554+
if occupied_ports:
555+
console_print(
556+
"[error]Cannot start UI development servers because the following local port(s) "
557+
f"are already in use: {', '.join(occupied_ports)}.[/]\n"
558+
"[info]Stop the processes using these ports and try again.[/]"
559+
)
560+
sys.exit(1)
541561
if force_clean:
542562
_clean_ui_assets(additional_ui_hooks)
543563
if dev:

dev/breeze/tests/test_run_utils.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,20 @@
1616
# under the License.
1717
from __future__ import annotations
1818

19+
import socket
1920
import stat
2021
from unittest import mock
2122

23+
import pytest
24+
25+
from airflow_breeze.global_constants import SIMPLE_AUTH_MANAGER_VITE_DEV_PORT, VITE_DEV_PORT
2226
from airflow_breeze.utils.run_utils import (
27+
_find_occupied_local_ports,
2328
change_directory_permission,
2429
change_file_permission,
2530
check_if_buildx_plugin_installed,
2631
run_command,
32+
run_compile_ui_assets,
2733
)
2834

2935

@@ -57,6 +63,68 @@ def test_run_command_dry_run_quiet_does_not_execute(mock_subprocess_run):
5763
assert result.stderr == ""
5864

5965

66+
def test_find_occupied_local_ports():
67+
with socket.socket() as unused_socket:
68+
unused_socket.bind(("127.0.0.1", 0))
69+
unoccupied_port = str(unused_socket.getsockname()[1])
70+
71+
with socket.create_server(("127.0.0.1", 0)) as server:
72+
occupied_port = str(server.getsockname()[1])
73+
74+
assert _find_occupied_local_ports((unoccupied_port, occupied_port)) == [occupied_port]
75+
76+
77+
@mock.patch("airflow_breeze.utils.run_utils._run_compile_internally")
78+
@mock.patch("airflow_breeze.utils.run_utils._find_occupied_local_ports", return_value=[])
79+
def test_run_compile_ui_assets_checks_both_dev_ports(mock_find_occupied_ports, mock_run_compile):
80+
result = run_compile_ui_assets(
81+
dev=True, run_in_background=False, force_clean=False, additional_ui_hooks=[]
82+
)
83+
84+
assert result == mock_run_compile.return_value
85+
mock_find_occupied_ports.assert_called_once_with((VITE_DEV_PORT, SIMPLE_AUTH_MANAGER_VITE_DEV_PORT))
86+
87+
88+
@pytest.mark.parametrize(
89+
"occupied_ports",
90+
[
91+
pytest.param([VITE_DEV_PORT], id="airflow-ui"),
92+
pytest.param([SIMPLE_AUTH_MANAGER_VITE_DEV_PORT], id="simple-auth-manager-ui"),
93+
],
94+
)
95+
@mock.patch("airflow_breeze.utils.run_utils._clean_ui_assets")
96+
@mock.patch("airflow_breeze.utils.run_utils._find_occupied_local_ports")
97+
@mock.patch("airflow_breeze.utils.run_utils.console_print")
98+
def test_run_compile_ui_assets_exits_before_cleanup_when_dev_port_is_occupied(
99+
mock_console_print, mock_find_occupied_ports, mock_clean_ui_assets, occupied_ports
100+
):
101+
mock_find_occupied_ports.return_value = occupied_ports
102+
103+
with pytest.raises(SystemExit) as ctx:
104+
run_compile_ui_assets(dev=True, run_in_background=False, force_clean=True, additional_ui_hooks=[])
105+
106+
assert ctx.value.code == 1
107+
mock_console_print.assert_called_once_with(
108+
"[error]Cannot start UI development servers because the following local port(s) "
109+
f"are already in use: {', '.join(occupied_ports)}.[/]\n"
110+
"[info]Stop the processes using these ports and try again.[/]"
111+
)
112+
mock_clean_ui_assets.assert_not_called()
113+
114+
115+
@mock.patch("airflow_breeze.utils.run_utils._run_compile_internally")
116+
@mock.patch("airflow_breeze.utils.run_utils._find_occupied_local_ports")
117+
def test_run_compile_ui_assets_does_not_check_dev_ports_for_static_build(
118+
mock_find_occupied_ports, mock_run_compile
119+
):
120+
result = run_compile_ui_assets(
121+
dev=False, run_in_background=False, force_clean=False, additional_ui_hooks=[]
122+
)
123+
124+
assert result == mock_run_compile.return_value
125+
mock_find_occupied_ports.assert_not_called()
126+
127+
60128
@mock.patch("airflow_breeze.utils.run_utils.run_command")
61129
@mock.patch("airflow_breeze.utils.run_utils.console_print")
62130
def test_check_buildah_is_installed(mock_console_print, mock_run_command):

0 commit comments

Comments
 (0)