diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8e23479188..00ff48a6c9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -15,6 +15,7 @@ /mooncake-reshard @ShangmingCai @stmatengss @Bo-Vincent @zxpdemonio /mooncake-store @ykwd @stmatengss @XucSh @YiXR /mooncake-store/*/ha/ @Libotry @YiXR @00fish0 @Icedcoco @Aionw +/python/mooncake/http_metadata_server.py @ykwd @stmatengss @zxpdemonio /python/mooncake/async_store.py @ykwd @stmatengss @zxpdemonio /python/tests/store/async_store_integration.py @ykwd @stmatengss @zxpdemonio /python/tests/store/test_async_store.py @ykwd @stmatengss @zxpdemonio @@ -24,6 +25,7 @@ /python/mooncake/mooncake_elastic_buffer.py @UNIDY2002 @ympcMark @yuechen-sys /python/mooncake/mooncake_ep_buffer.py @UNIDY2002 @ympcMark @yuechen-sys /python/tests/ep/ @UNIDY2002 @ympcMark @yuechen-sys +/python/tests/services/test_http_metadata_server.py @ykwd @stmatengss @zxpdemonio /python/mooncake/cli.py @ykwd @stmatengss @zxpdemonio /python/mooncake/cli_client.py @ykwd @stmatengss @zxpdemonio /python/mooncake/cli_bench.py @ShangmingCai @alogfans @stmatengss diff --git a/.github/labeler.yml b/.github/labeler.yml index 4e55945282..e7053b2d76 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -7,6 +7,8 @@ store: - changed-files: - any-glob-to-any-file: - 'mooncake-store/**/*' + - 'python/mooncake/http_metadata_server.py' + - 'python/tests/services/test_http_metadata_server.py' - 'python/mooncake/async_store.py' - 'python/mooncake/cli.py' - 'python/mooncake/cli_client.py' @@ -46,6 +48,7 @@ Installation: - changed-files: - any-glob-to-any-file: - 'mooncake-wheel/**/*' + - 'python/mooncake/http_metadata_server.py' - 'python/mooncake/cli.py' - 'python/mooncake/cli_client.py' - 'python/mooncake/cli_bench.py' diff --git a/.github/workflows/ci_rocm.yml b/.github/workflows/ci_rocm.yml index 559d979082..23a9f54251 100644 --- a/.github/workflows/ci_rocm.yml +++ b/.github/workflows/ci_rocm.yml @@ -77,7 +77,7 @@ jobs: sleep $((attempt * 10)) done - - name: Install toolchain and Python ${{ matrix.python-version }} + - name: Install toolchain shell: bash run: | set -eo pipefail @@ -85,23 +85,24 @@ jobs: apt-get update -y apt-get install -y --no-install-recommends \ curl build-essential sudo pkg-config \ - ninja-build software-properties-common - PYV="${{ matrix.python-version }}" - if ! command -v "python${PYV}" >/dev/null 2>&1; then - add-apt-repository -y ppa:deadsnakes/ppa - apt-get update -y - fi - # Always install -dev + -venv: the image's system python3.10 exists but - # ships without the venv module / dev headers. - apt-get install -y --no-install-recommends \ - "python${PYV}" "python${PYV}-dev" "python${PYV}-venv" - curl -sS https://bootstrap.pypa.io/get-pip.py | "python${PYV}" - PYTHON_BIN="$(command -v python${PYV})" - echo "PYTHON_BIN=${PYTHON_BIN}" >> "$GITHUB_ENV" - # hipify-perl lives in /opt/rocm/bin; CMake's USE_HIP path requires it. - # Use GITHUB_PATH (setting PATH via GITHUB_ENV is not honored reliably). + ninja-build + # hipify-perl is required by the CMake USE_HIP path. echo "/opt/rocm/bin" >> "$GITHUB_PATH" + - name: Setup Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Set Python bin + shell: bash + run: | + set -euo pipefail + PYTHON_BIN="$(command -v python${{ matrix.python-version }})" + "$PYTHON_BIN" --version + "$PYTHON_BIN" -m pip --version + echo "PYTHON_BIN=${PYTHON_BIN}" >> "$GITHUB_ENV" + - name: Run sccache-cache uses: mozilla-actions/sccache-action@v0.0.9 diff --git a/.github/workflows/release-rocm.yaml b/.github/workflows/release-rocm.yaml index 5f989ec0f3..516b027722 100644 --- a/.github/workflows/release-rocm.yaml +++ b/.github/workflows/release-rocm.yaml @@ -105,7 +105,7 @@ jobs: exit 1 fi - - name: Install toolchain and Python ${{ matrix.python-version }} + - name: Install toolchain shell: bash run: | set -eo pipefail @@ -113,21 +113,24 @@ jobs: apt-get update -y apt-get install -y --no-install-recommends \ git curl ca-certificates build-essential sudo pkg-config \ - ninja-build software-properties-common - PYV="${{ matrix.python-version }}" - if ! command -v "python${PYV}" >/dev/null 2>&1; then - add-apt-repository -y ppa:deadsnakes/ppa - apt-get update -y - fi - # Always install -dev + -venv: the image's system python3.10 exists but - # ships without the venv module / dev headers. - apt-get install -y --no-install-recommends \ - "python${PYV}" "python${PYV}-dev" "python${PYV}-venv" - curl -sS https://bootstrap.pypa.io/get-pip.py | "python${PYV}" - PYTHON_BIN="$(command -v python${PYV})" - echo "PYTHON_BIN=${PYTHON_BIN}" >> "$GITHUB_ENV" + ninja-build + # hipify-perl is required by the CMake USE_HIP path. echo "/opt/rocm/bin" >> "$GITHUB_PATH" + - name: Setup Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Set Python bin + shell: bash + run: | + set -euo pipefail + PYTHON_BIN="$(command -v python${{ matrix.python-version }})" + "$PYTHON_BIN" --version + "$PYTHON_BIN" -m pip --version + echo "PYTHON_BIN=${PYTHON_BIN}" >> "$GITHUB_ENV" + - name: Install dependencies shell: bash run: | diff --git a/mooncake-integration/CMakeLists.txt b/mooncake-integration/CMakeLists.txt index caa1d53e55..09a1170409 100644 --- a/mooncake-integration/CMakeLists.txt +++ b/mooncake-integration/CMakeLists.txt @@ -219,7 +219,7 @@ endif() if(NOT SKBUILD) install( FILES - "${CMAKE_CURRENT_SOURCE_DIR}/../mooncake-wheel/mooncake/http_metadata_server.py" + "${CMAKE_CURRENT_SOURCE_DIR}/../python/mooncake/http_metadata_server.py" "${CMAKE_CURRENT_SOURCE_DIR}/../python/mooncake/_launcher.py" "${CMAKE_CURRENT_SOURCE_DIR}/../python/mooncake/cli_bench.py" "${CMAKE_CURRENT_SOURCE_DIR}/../python/mooncake/transfer_engine_topology_dump.py" diff --git a/mooncake-store/tests/e2e/readme.md b/mooncake-store/tests/e2e/readme.md index ddc1d03536..9b081689fb 100644 --- a/mooncake-store/tests/e2e/readme.md +++ b/mooncake-store/tests/e2e/readme.md @@ -93,7 +93,7 @@ The script first verifies steady-state `put/get` success with `memory + nof` rep - `mooncake-store/src/mooncake_master` - `mooncake-integration/store*.so` - SPDK has already been built under `extern/spdk` -- Python environment contains `aiohttp` because the script launches a standalone metadata process with `mooncake-wheel/mooncake/http_metadata_server.py` +- Python environment contains `aiohttp` because the script launches a standalone metadata process with `python/mooncake/http_metadata_server.py` - The script uses `sudo -n` to set hugepages and mount `/dev/hugepages`, so the current user must have passwordless sudo **Usage**: @@ -118,7 +118,7 @@ PRE_FAULT_SUCCESS_TARGET=10 BUILD_DIR=/path/to/build ./run_nof_heartbeat_tcp_e2e **Notes**: - The client payload size defaults to `4096` bytes because the current NoF path requires 4K-aligned I/O. -- The script uses a standalone metadata server process (`mooncake-wheel/mooncake/http_metadata_server.py`) instead of the embedded master metadata server so all four components remain explicit during the test. +- The script uses a standalone metadata server process (`python/mooncake/http_metadata_server.py`) instead of the embedded master metadata server so all four components remain explicit during the test. - In default mode, the script verifies **service continuity** after NoF unmount by checking that post-fault I/O still succeeds. - In `CLIENT_GLOBAL_SEGMENT_SIZE=0` mode, the script verifies **NoF-only failure behavior** by checking that post-fault I/O starts failing after the NoF segment is removed. - Logs are written under `LOG_DIR` (default `/tmp/mooncake_nof_heartbeat_e2e`) and the final pass/fail summary is printed from `summary.log`. diff --git a/mooncake-store/tests/e2e/run_nof_heartbeat_tcp_e2e.sh b/mooncake-store/tests/e2e/run_nof_heartbeat_tcp_e2e.sh index 4bdb294fb4..0a3ebb2de6 100644 --- a/mooncake-store/tests/e2e/run_nof_heartbeat_tcp_e2e.sh +++ b/mooncake-store/tests/e2e/run_nof_heartbeat_tcp_e2e.sh @@ -90,7 +90,7 @@ python3 "$REPO_ROOT/extern/spdk/scripts/rpc.py" nvmf_create_subsystem "$TARGET_N python3 "$REPO_ROOT/extern/spdk/scripts/rpc.py" nvmf_subsystem_add_ns "$TARGET_NQN" Malloc0 >/dev/null || true python3 "$REPO_ROOT/extern/spdk/scripts/rpc.py" nvmf_subsystem_add_listener "$TARGET_NQN" -t tcp -a "$TARGET_HOST" -s "$TARGET_PORT" >/dev/null || true -python3 "$REPO_ROOT/mooncake-wheel/mooncake/http_metadata_server.py" --host "$METADATA_HOST" --port "$METADATA_PORT" >"$LOG_DIR/metadata.log" 2>&1 & +python3 "$REPO_ROOT/python/mooncake/http_metadata_server.py" --host "$METADATA_HOST" --port "$METADATA_PORT" >"$LOG_DIR/metadata.log" 2>&1 & META_PID=$! sleep 2 diff --git a/mooncake-store/tests/e2e/run_oplog_batch_cluster.sh b/mooncake-store/tests/e2e/run_oplog_batch_cluster.sh index 2c77a42f33..e8b77e1016 100755 --- a/mooncake-store/tests/e2e/run_oplog_batch_cluster.sh +++ b/mooncake-store/tests/e2e/run_oplog_batch_cluster.sh @@ -247,7 +247,7 @@ require_executable() { up_cluster() { MASTER_BIN="$BUILD_DIR/mooncake-store/src/mooncake_master" INSPECTOR_BIN="$BUILD_DIR/mooncake-store/tools/oplog_batch_inspector" - METADATA_SCRIPT="$REPO_ROOT/mooncake-wheel/mooncake/http_metadata_server.py" + METADATA_SCRIPT="$REPO_ROOT/python/mooncake/http_metadata_server.py" require_executable "$MASTER_BIN" [[ "$USE_ETCD_OBSERVER" != true ]] || require_executable "$INSPECTOR_BIN" [[ -f "$METADATA_SCRIPT" ]] || die "missing executable: $METADATA_SCRIPT" diff --git a/mooncake-wheel/tests/test_meta_server.py b/mooncake-wheel/tests/test_meta_server.py index 2a2f27b80a..7162ea1ee7 100644 --- a/mooncake-wheel/tests/test_meta_server.py +++ b/mooncake-wheel/tests/test_meta_server.py @@ -1,4 +1,6 @@ -import os, sys, random +import os +import sys +import random import time import threading @@ -7,16 +9,19 @@ # how to test: start mooncake_master and http_metadata_server. # Default is tcp, if you want to use rdma, should set MC_RPC_PROTOCOL=rdma and DEVICE_NAME=rdma_xxx at first. # ./mooncake_master -# python mooncake-wheel/mooncake/http_metadata_server.py --port 8080 +# mooncake_http_metadata_server --port 8080 # python mooncake-wheel/tests/test_meta_server.py 127.0.0.1 8 import mooncake + print(mooncake.__file__) + def test_worker(store, num_req): - for i in range(num_req): - store.is_exist(str(random.randint(0, 1000000))) - + for i in range(num_req): + store.is_exist(str(random.randint(0, 1000000))) + + master_host = "master.mooncake.dc" if len(sys.argv) < 2 else sys.argv[1] num_thread = 1 if len(sys.argv) < 3 else int(sys.argv[2]) # Initialize the store @@ -30,25 +35,27 @@ def test_worker(store, num_req): local_buffer_size = 512 * 1024 * 1024 master_server_address = os.getenv("MASTER_SERVER", f"{master_host}:50051") value_length = 1 * 1024 * 1024 -retcode = store.setup(local_hostname, - metadata_server, - global_segment_size, - local_buffer_size, - protocol, - device_name, - master_server_address) +retcode = store.setup( + local_hostname, + metadata_server, + global_segment_size, + local_buffer_size, + protocol, + device_name, + master_server_address, +) if retcode: exit(1) time.sleep(1) # Give some time for initialization start_time = time.perf_counter() threads = [] for _ in range(num_thread): - thread = threading.Thread(target=test_worker, args=(store, 15000)) - thread.start() - threads.append(thread) + thread = threading.Thread(target=test_worker, args=(store, 15000)) + thread.start() + threads.append(thread) for thread in threads: - thread.join() + thread.join() end_time = time.perf_counter() total_time = end_time - start_time if end_time > start_time else 0 ops_per_second = 15000 * num_thread / total_time if total_time > 0 else 0 -print(f"total time: {total_time} s, QPS: {ops_per_second:.2f}") \ No newline at end of file +print(f"total time: {total_time} s, QPS: {ops_per_second:.2f}") diff --git a/mooncake-wheel/mooncake/http_metadata_server.py b/python/mooncake/http_metadata_server.py similarity index 71% rename from mooncake-wheel/mooncake/http_metadata_server.py rename to python/mooncake/http_metadata_server.py index cc98eba563..056ced78e3 100644 --- a/mooncake-wheel/mooncake/http_metadata_server.py +++ b/python/mooncake/http_metadata_server.py @@ -20,6 +20,7 @@ class KVPoll(Enum): """Status of the KV server.""" + Failed = 0 Bootstrapping = 1 WaitingForInput = 2 @@ -29,10 +30,10 @@ class KVPoll(Enum): class KVBootstrapServer: """HTTP server for storing and retrieving metadata.""" - + def __init__(self, port: int, host: str = "0.0.0.0"): """Initialize the server. - + Args: port: Port to listen on host: Host to bind to (default: 0.0.0.0) @@ -46,7 +47,7 @@ def __init__(self, port: int, host: str = "0.0.0.0"): self._runner = None self.thread = None self._setup_routes() - + def run(self): """Start the server in a background thread.""" self.thread = threading.Thread(target=self._run_server, daemon=True) @@ -56,65 +57,77 @@ def run(self): def _setup_routes(self): """Set up the HTTP routes.""" - self.app.router.add_route('*', '/metadata', self._handle_metadata) + self.app.router.add_route("*", "/metadata", self._handle_metadata) async def _handle_metadata(self, request: web.Request): """Handle metadata requests.""" - key = request.query.get('key', '').strip() + key = request.query.get("key", "").strip() if not key: - return web.Response(text='metadata key is required', status=400, - content_type='application/json') - - if request.method == 'GET': + return web.Response( + text="metadata key is required", + status=400, + content_type="application/json", + ) + + if request.method == "GET": return await self._handle_get(key) - elif request.method == 'PUT': + elif request.method == "PUT": return await self._handle_put(key, request) - elif request.method == 'DELETE': + elif request.method == "DELETE": return await self._handle_delete(key) - return web.Response(text='Method not allowed', status=405, - content_type='application/json') + return web.Response( + text="Method not allowed", status=405, content_type="application/json" + ) async def _handle_get(self, key): """Handle GET requests.""" async with self.lock: value = self.store.get(key) if value is None: - return web.Response(text='metadata not found', status=404, - content_type='application/json') - return web.Response(body=value, status=200, - content_type='application/json') + return web.Response( + text="metadata not found", status=404, content_type="application/json" + ) + return web.Response(body=value, status=200, content_type="application/json") async def _handle_put(self, key, request): """Handle PUT requests.""" data = await request.read() async with self.lock: if key.find("rpc_meta") != -1 and key in self.store: - return web.Response(text='Duplicate rpc_meta key not allowed', status=400, - content_type='application/json') + return web.Response( + text="Duplicate rpc_meta key not allowed", + status=400, + content_type="application/json", + ) self.store[key] = data - return web.Response(text='metadata updated', status=200, - content_type='application/json') + return web.Response( + text="metadata updated", status=200, content_type="application/json" + ) async def _handle_delete(self, key): """Handle DELETE requests.""" async with self.lock: if key not in self.store: - return web.Response(text='metadata not found', status=404, - content_type='application/json') + return web.Response( + text="metadata not found", + status=404, + content_type="application/json", + ) del self.store[key] - return web.Response(text='metadata deleted', status=200, - content_type='application/json') - + return web.Response( + text="metadata deleted", status=200, content_type="application/json" + ) + def _run_server(self): """Run the server in the current thread.""" try: # Event Loop self._loop = asyncio.new_event_loop() asyncio.set_event_loop(self._loop) - + self._runner = web.AppRunner(self.app) self._loop.run_until_complete(self._runner.setup()) - + site = web.TCPSite(self._runner, host=self.host, port=self.port) self._loop.run_until_complete(site.start()) self._loop.run_forever() @@ -132,11 +145,11 @@ def close(self): if self._loop is not None and self._loop.is_running(): self._loop.call_soon_threadsafe(self._loop.stop) logging.info("Stopping server loop...") - + if self.thread is not None and self.thread.is_alive(): self.thread.join(timeout=2) logging.info("Server thread stopped") - + def poll(self) -> KVPoll: """Poll the server status.""" if self.thread is None: @@ -150,26 +163,16 @@ def parse_args(): """Parse command line arguments.""" parser = argparse.ArgumentParser( description="HTTP Metadata Server for Mooncake", - formatter_class=argparse.ArgumentDefaultsHelpFormatter + formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) + parser.add_argument("--port", type=int, default=8080, help="Port to listen on") + parser.add_argument("--host", type=str, default="0.0.0.0", help="Host to bind to") parser.add_argument( - "--port", - type=int, - default=8080, - help="Port to listen on" - ) - parser.add_argument( - "--host", - type=str, - default="0.0.0.0", - help="Host to bind to" - ) - parser.add_argument( - "--log-level", - type=str, + "--log-level", + type=str, choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], - default="INFO", - help="Logging level" + default="INFO", + help="Logging level", ) return parser.parse_args() @@ -177,33 +180,33 @@ def parse_args(): def main(): """Main entry point for the mooncake_http_metadata_server command.""" args = parse_args() - + # Configure logging logging.basicConfig( level=getattr(logging, args.log_level), - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", ) - + # Create and start the server server = KVBootstrapServer(port=args.port, host=args.host) server.run() - + # Handle graceful shutdown def signal_handler(sig, frame): logging.info("Shutting down...") server.close() sys.exit(0) - + signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) - + # Keep the main thread alive try: while True: sleep(1) except KeyboardInterrupt: server.close() - + return 0 diff --git a/python/tests/packaging/test_installed_wheel.py b/python/tests/packaging/test_installed_wheel.py index 01d2970cdd..3baed52b97 100644 --- a/python/tests/packaging/test_installed_wheel.py +++ b/python/tests/packaging/test_installed_wheel.py @@ -43,6 +43,10 @@ def test_wheel_imports_outside_the_repository( environment = tmp_path / "environment" subprocess.run([sys.executable, "-m", "venv", str(environment)], check=True) python = environment / "bin" / "python" + subprocess.run( + [str(python), "-m", "pip", "install", "aiohttp"], + check=True, + ) subprocess.run( [str(python), "-m", "pip", "install", "--no-deps", str(wheel)], check=True, @@ -74,6 +78,7 @@ def test_wheel_imports_outside_the_repository( import mooncake.async_store import mooncake.engine +import mooncake.http_metadata_server import mooncake.mooncake_config import mooncake.reshard import mooncake.store @@ -90,6 +95,7 @@ def test_wheel_imports_outside_the_repository( ) assert Path(mooncake.async_store.__file__).resolve().parent == package_path.parent assert mooncake.engine.TransferEngine is not None +assert mooncake.http_metadata_server.KVBootstrapServer is not None assert mooncake.mooncake_config.MooncakeConfig is not None for ep_module in ( "ep.py", @@ -132,6 +138,12 @@ def test_wheel_imports_outside_the_repository( env=clean_environment, check=True, ) + subprocess.run( + [str(environment / "bin" / "mooncake_http_metadata_server"), "--help"], + cwd=tmp_path, + env=clean_environment, + check=True, + ) package_query = "from pathlib import Path; import mooncake; print(Path(mooncake.__file__).parent)" package_result = subprocess.run( diff --git a/python/tests/packaging/test_project.py b/python/tests/packaging/test_project.py index 2455281b75..001cb1fe90 100644 --- a/python/tests/packaging/test_project.py +++ b/python/tests/packaging/test_project.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os from pathlib import Path import shutil import subprocess @@ -83,6 +84,68 @@ def test_tracked_source_roots_contain_no_generated_native_artifacts() -> None: assert not list((REPOSITORY_ROOT / "mooncake-pg" / "torch").rglob("*.so")) +def test_http_metadata_service_has_one_authoritative_source( + tmp_path: Path, +) -> None: + package_root = REPOSITORY_ROOT / "python" / "mooncake" + legacy_package_root = REPOSITORY_ROOT / "mooncake-wheel" / "mooncake" + test_root = REPOSITORY_ROOT / "python" / "tests" / "services" + legacy_test_root = REPOSITORY_ROOT / "mooncake-wheel" / "tests" + + module = package_root / "http_metadata_server.py" + assert module.is_file() + assert not (legacy_package_root / module.name).exists() + assert (test_root / "test_http_metadata_server.py").is_file() + assert not (legacy_test_root / "test_http_metadata_server.py").exists() + + project = tomllib.loads((REPOSITORY_ROOT / "pyproject.toml").read_text()) + legacy_project = tomllib.loads( + (REPOSITORY_ROOT / "mooncake-wheel" / "pyproject.toml").read_text() + ) + entry_point = "mooncake.http_metadata_server:main" + assert project["project"]["scripts"]["mooncake_http_metadata_server"] == entry_point + assert ( + legacy_project["project"]["scripts"]["mooncake_http_metadata_server"] + == entry_point + ) + + integration_cmake = ( + REPOSITORY_ROOT / "mooncake-integration" / "CMakeLists.txt" + ).read_text() + assert "../python/mooncake/http_metadata_server.py" in integration_cmake + assert "../mooncake-wheel/mooncake/http_metadata_server.py" not in integration_cmake + + legacy_build_script = (REPOSITORY_ROOT / "scripts" / "build_wheel.sh").read_text() + migrated_modules = ( + legacy_build_script.split("MIGRATED_PYTHON_MODULES=(", 1)[1] + .split(")", 1)[0] + .split() + ) + assert "http_metadata_server.py" in migrated_modules + + environment = os.environ.copy() + environment["PYTHONPATH"] = os.pathsep.join( + [ + str(REPOSITORY_ROOT / "python"), + str(REPOSITORY_ROOT / "mooncake-wheel"), + ] + ) + environment["PYTHONNOUSERSITE"] = "1" + import_check = f""" +from pathlib import Path +import mooncake.http_metadata_server as service + +assert Path(service.__file__).resolve() == Path({str(module)!r}).resolve() +assert service.KVBootstrapServer is not None +""" + subprocess.run( + [sys.executable, "-c", import_check], + cwd=tmp_path, + env=environment, + check=True, + ) + + def test_async_store_has_one_authoritative_source() -> None: canonical_module = REPOSITORY_ROOT / "python" / "mooncake" / "async_store.py" legacy_module = ( @@ -179,7 +242,6 @@ def test_scikit_build_consumes_unified_python_sources() -> None: # Modules still supplied from the legacy tree at build time. Phase 2 should # migrate these into python/mooncake and drop the DIRECTORY rule above. for module in ( - "http_metadata_server.py", "mooncake_store_service.py", "mooncake_connector_v1.py", "vllm_v1_proxy_server.py", diff --git a/mooncake-wheel/tests/test_http_metadata_server.py b/python/tests/services/test_http_metadata_server.py similarity index 96% rename from mooncake-wheel/tests/test_http_metadata_server.py rename to python/tests/services/test_http_metadata_server.py index a494d5f203..1668030174 100644 --- a/mooncake-wheel/tests/test_http_metadata_server.py +++ b/python/tests/services/test_http_metadata_server.py @@ -1,9 +1,5 @@ #!/usr/bin/env python3 -import sys import unittest -from pathlib import Path - -sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from mooncake.http_metadata_server import KVBootstrapServer diff --git a/scripts/build_wheel.sh b/scripts/build_wheel.sh index 1f6e46866a..006c9e5716 100755 --- a/scripts/build_wheel.sh +++ b/scripts/build_wheel.sh @@ -185,6 +185,7 @@ echo "Building wheel package..." MIGRATED_PYTHON_SOURCE_DIR="python/mooncake" MIGRATED_PYTHON_STAGING_DIR="$(pwd)/mooncake-wheel/mooncake" MIGRATED_PYTHON_MODULES=( + http_metadata_server.py _launcher.py cli.py cli_bench.py diff --git a/scripts/test_installation.sh b/scripts/test_installation.sh index 04a9798efa..148f4d9971 100755 --- a/scripts/test_installation.sh +++ b/scripts/test_installation.sh @@ -38,6 +38,7 @@ python -c "import mooncake.cli, mooncake.cli_bench, mooncake.cli_client, mooncak echo "Running import structure test..." # Run the import structure test cp -r mooncake-wheel/tests test_env/ +cp -r python/tests/services test_env/service_tests cp -r python/tests/unit test_env/unit_tests cp -r mooncake-reshard/tests test_env/reshard_tests cp -r python/tests/ssd test_env/ssd_tests @@ -47,6 +48,9 @@ python -c "import mooncake._fast_copy" python tests/test_fast_copy.py python tests/test_import_structure.py +echo "Running HTTP metadata server test..." +python -m unittest service_tests/test_http_metadata_server.py + echo "Running mooncake config test..." python unit_tests/test_mooncake_config.py @@ -69,6 +73,10 @@ echo "Verifying transfer_engine_bench entry point..." which transfer_engine_bench || { echo "ERROR: transfer_engine_bench entry point not found!"; exit 1; } echo "Success: transfer_engine_bench entry point found" +echo "Verifying mooncake_http_metadata_server entry point..." +which mooncake_http_metadata_server || { echo "ERROR: mooncake_http_metadata_server entry point not found!"; exit 1; } +mooncake_http_metadata_server --help >/dev/null +echo "Success: mooncake_http_metadata_server entry point found" echo "Verifying transfer_engine_topology_dump entry point..." transfer_engine_topology_dump --help >/dev/null python -m mooncake.transfer_engine_topology_dump --help >/dev/null