From ec7c6cb27afe328aa819dd6fa13e23beb7f40032 Mon Sep 17 00:00:00 2001 From: Aionw Date: Mon, 24 Aug 2026 16:42:02 +0800 Subject: [PATCH 1/2] [Wheel] Migrate HTTP metadata service --- .github/CODEOWNERS | 4 +- .github/labeler.yml | 10 +- mooncake-integration/CMakeLists.txt | 2 +- mooncake-store/tests/e2e/readme.md | 4 +- .../tests/e2e/run_nof_heartbeat_tcp_e2e.sh | 2 +- .../tests/e2e/run_oplog_batch_cluster.sh | 2 +- mooncake-wheel/tests/test_meta_server.py | 41 ++++--- .../mooncake/http_metadata_server.py | 111 +++++++++--------- .../tests/packaging/test_installed_wheel.py | 12 ++ python/tests/packaging/test_project.py | 58 +++++++++ .../services}/test_http_metadata_server.py | 4 - scripts/build_wheel.sh | 22 +++- scripts/test_installation.sh | 9 ++ 13 files changed, 192 insertions(+), 89 deletions(-) rename {mooncake-wheel => python}/mooncake/http_metadata_server.py (71%) rename {mooncake-wheel/tests => python/tests/services}/test_http_metadata_server.py (96%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2c9f901b16..ed86d5599a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -12,9 +12,11 @@ /mooncake-integration/transfer_engine @ShangmingCai @alogfans /mooncake-integration/store @ykwd @stmatengss @zxpdemonio /mooncake-pg @UNIDY2002 @ympcMark @yuechen-sys -/mooncake-reshard @ShangmingCai @stmatengss @Bo-Vincent @zxpdemonio +/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/tests/services/test_http_metadata_server.py @ykwd @stmatengss @zxpdemonio /mooncake-transfer-engine @alogfans @doujiang24 @chestnut-Q @staryxchen /mooncake-transfer-engine/tent @alogfans @doujiang24 @chestnut-Q @staryxchen @00fish0 @dtcccc /mooncake-transfer-engine/*/transport/hip_transport/ @alogfans @amd-arozanov diff --git a/.github/labeler.yml b/.github/labeler.yml index 92650848b7..f236933e1f 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -5,7 +5,10 @@ run-ci: store: - changed-files: - - any-glob-to-any-file: 'mooncake-store/**/*' + - any-glob-to-any-file: + - 'mooncake-store/**/*' + - 'python/mooncake/http_metadata_server.py' + - 'python/tests/services/test_http_metadata_server.py' Transfer Engine: - changed-files: @@ -21,7 +24,9 @@ Mooncake EP: Installation: - changed-files: - - any-glob-to-any-file: 'mooncake-wheel/**/*' + - any-glob-to-any-file: + - 'mooncake-wheel/**/*' + - 'python/mooncake/http_metadata_server.py' P2P Store: - changed-files: @@ -57,6 +62,7 @@ Tests: - 'scripts/test_*' - 'mooncake-wheel/tests/**/*' - 'mooncake-reshard/tests/**/*' + - 'python/tests/services/**/*' - 'scripts/tone_tests/**/*' Ascend/NPU: diff --git a/mooncake-integration/CMakeLists.txt b/mooncake-integration/CMakeLists.txt index 0ef9dfde04..99811b01c7 100644 --- a/mooncake-integration/CMakeLists.txt +++ b/mooncake-integration/CMakeLists.txt @@ -197,7 +197,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}/../mooncake-wheel/mooncake/cli_bench.py" "${CMAKE_CURRENT_SOURCE_DIR}/../mooncake-wheel/mooncake/transfer_engine_topology_dump.py" "${CMAKE_CURRENT_SOURCE_DIR}/../mooncake-wheel/mooncake/mooncake_connector_v1.py" diff --git a/mooncake-store/tests/e2e/readme.md b/mooncake-store/tests/e2e/readme.md index e8ad2aedcc..80eb478618 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 4e7488249a..1b7a3e0b02 100644 --- a/mooncake-store/tests/e2e/run_nof_heartbeat_tcp_e2e.sh +++ b/mooncake-store/tests/e2e/run_nof_heartbeat_tcp_e2e.sh @@ -87,7 +87,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 c6ea928bf8..f7067397ad 100755 --- a/mooncake-store/tests/e2e/run_oplog_batch_cluster.sh +++ b/mooncake-store/tests/e2e/run_oplog_batch_cluster.sh @@ -220,7 +220,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 74422d9ebb..0b3036a426 100644 --- a/python/tests/packaging/test_installed_wheel.py +++ b/python/tests/packaging/test_installed_wheel.py @@ -32,6 +32,10 @@ def test_wheel_imports_outside_the_repository(tmp_path: Path) -> None: 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, @@ -45,6 +49,7 @@ def test_wheel_imports_outside_the_repository(tmp_path: Path) -> None: from pathlib import Path import mooncake import mooncake.engine +import mooncake.http_metadata_server import mooncake.reshard import mooncake.store @@ -54,6 +59,7 @@ def test_wheel_imports_outside_the_repository(tmp_path: Path) -> None: assert metadata.version("mooncake-transfer-engine") == {_project_version()!r} assert mooncake.BufferPool is mooncake.store.BufferPool assert mooncake.engine.TransferEngine is not None +assert mooncake.http_metadata_server.KVBootstrapServer is not None """ subprocess.run( [str(python), "-I", "-c", smoke_script], @@ -61,3 +67,9 @@ def test_wheel_imports_outside_the_repository(tmp_path: Path) -> None: env=clean_environment, check=True, ) + subprocess.run( + [str(environment / "bin" / "mooncake_http_metadata_server"), "--help"], + cwd=tmp_path, + env=clean_environment, + check=True, + ) diff --git a/python/tests/packaging/test_project.py b/python/tests/packaging/test_project.py index 4ed4b376b8..e8ed1e1a85 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 @@ -57,6 +58,63 @@ 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() + assert "MIGRATED_PYTHON_MODULES=(http_metadata_server.py)" in legacy_build_script + + environment = os.environ.copy() + environment["PYTHONPATH"] = os.pathsep.join( + [ + str(REPOSITORY_ROOT / "mooncake-wheel"), + str(REPOSITORY_ROOT / "python"), + ] + ) + 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_pg_extension_build_stages_outside_the_source_tree( tmp_path: Path, ) -> None: 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 eed257699d..ac6cf656a9 100755 --- a/scripts/build_wheel.sh +++ b/scripts/build_wheel.sh @@ -180,15 +180,25 @@ if [ "$NPU_BUILD" = "1" ]; then fi echo "Building wheel package..." -# Stage the reshard Python package for the combined Mooncake wheel. The tracked -# source of truth remains in the top-level module. +# Stage migrated root Python modules and the legacy Reshard package for the +# combined-wheel builder. Each tracked source remains in its authoritative tree. +MIGRATED_PYTHON_SOURCE_DIR="python/mooncake" +MIGRATED_PYTHON_STAGING_DIR="$(pwd)/mooncake-wheel/mooncake" +MIGRATED_PYTHON_MODULES=(http_metadata_server.py) RESHARD_SOURCE_DIR="mooncake-reshard/python/mooncake/reshard" RESHARD_STAGING_DIR="$(pwd)/mooncake-wheel/mooncake/reshard" -cleanup_reshard_staging() { +cleanup_migrated_python_staging() { + for module in "${MIGRATED_PYTHON_MODULES[@]}"; do + rm -f "${MIGRATED_PYTHON_STAGING_DIR}/${module}" + done rm -rf "${RESHARD_STAGING_DIR}" } -trap cleanup_reshard_staging EXIT -rm -rf "${RESHARD_STAGING_DIR}" +trap cleanup_migrated_python_staging EXIT +cleanup_migrated_python_staging +for module in "${MIGRATED_PYTHON_MODULES[@]}"; do + cp "${MIGRATED_PYTHON_SOURCE_DIR}/${module}" \ + "${MIGRATED_PYTHON_STAGING_DIR}/${module}" +done cp -R "${RESHARD_SOURCE_DIR}" "${RESHARD_STAGING_DIR}" # Build the wheel package @@ -204,7 +214,7 @@ WHEEL_DIR="$(pwd)" cleanup_wheel_metadata_state() { [[ -f "${WHEEL_DIR}/pyproject.toml.backup" ]] && mv "${WHEEL_DIR}/pyproject.toml.backup" "${WHEEL_DIR}/pyproject.toml" rm -f "${WHEEL_DIR}/README.md" - cleanup_reshard_staging + cleanup_migrated_python_staging } trap cleanup_wheel_metadata_state EXIT diff --git a/scripts/test_installation.sh b/scripts/test_installation.sh index dd8a7bb6b1..57a6a6d9df 100755 --- a/scripts/test_installation.sh +++ b/scripts/test_installation.sh @@ -37,6 +37,7 @@ python -c "import mooncake.reshard.weight" && echo "Success: Reshard import succ 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 mooncake-reshard/tests test_env/reshard_tests cd test_env pip install torch numpy @@ -44,6 +45,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 tests/test_mooncake_config.py @@ -66,6 +70,11 @@ 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" + cd .. echo "Installation test completed successfully!" From f2600002e94cf52fbbc2f41f66df84b8c432dd75 Mon Sep 17 00:00:00 2001 From: Aoi Date: Mon, 21 Sep 2026 20:41:23 +0800 Subject: [PATCH 2/2] [CI/Build] Use setup-python for ROCm wheel builds --- .github/workflows/ci_rocm.yml | 33 +++++++++++++++-------------- .github/workflows/release-rocm.yaml | 31 +++++++++++++++------------ 2 files changed, 34 insertions(+), 30 deletions(-) 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: |