Bundle firecracker runtime and switch ollama demo to live logs
This commit is contained in:
parent
ef0ddeaa11
commit
65f7c0d262
26 changed files with 1896 additions and 408 deletions
171
src/pyro_mcp/runtime.py
Normal file
171
src/pyro_mcp/runtime.py
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
"""Bundled runtime resolver and diagnostics."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import importlib.resources as resources
|
||||
import json
|
||||
import os
|
||||
import stat
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
DEFAULT_PLATFORM = "linux-x86_64"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RuntimePaths:
|
||||
"""Resolved paths for bundled runtime components."""
|
||||
|
||||
bundle_root: Path
|
||||
manifest_path: Path
|
||||
firecracker_bin: Path
|
||||
jailer_bin: Path
|
||||
artifacts_dir: Path
|
||||
notice_path: Path
|
||||
manifest: dict[str, Any]
|
||||
|
||||
|
||||
def _sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as fp:
|
||||
for block in iter(lambda: fp.read(1024 * 1024), b""):
|
||||
digest.update(block)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def _ensure_executable(path: Path) -> None:
|
||||
mode = path.stat().st_mode
|
||||
if mode & stat.S_IXUSR:
|
||||
return
|
||||
path.chmod(mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
||||
|
||||
|
||||
def _default_bundle_parent() -> Path:
|
||||
return Path(str(resources.files("pyro_mcp.runtime_bundle")))
|
||||
|
||||
|
||||
def resolve_runtime_paths(
|
||||
*,
|
||||
platform: str = DEFAULT_PLATFORM,
|
||||
verify_checksums: bool = True,
|
||||
) -> RuntimePaths:
|
||||
"""Resolve and validate bundled runtime assets."""
|
||||
bundle_parent = Path(os.environ.get("PYRO_RUNTIME_BUNDLE_DIR", _default_bundle_parent()))
|
||||
bundle_root = bundle_parent / platform
|
||||
manifest_path = bundle_root / "manifest.json"
|
||||
notice_path = bundle_parent / "NOTICE"
|
||||
|
||||
if not manifest_path.exists():
|
||||
raise RuntimeError(
|
||||
f"bundled runtime manifest not found at {manifest_path}; reinstall package or "
|
||||
"use a wheel that includes bundled runtime assets"
|
||||
)
|
||||
if not notice_path.exists():
|
||||
raise RuntimeError(f"runtime NOTICE file missing at {notice_path}")
|
||||
|
||||
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||
if not isinstance(manifest, dict):
|
||||
raise RuntimeError("invalid runtime manifest format")
|
||||
|
||||
binaries = manifest.get("binaries")
|
||||
if not isinstance(binaries, dict):
|
||||
raise RuntimeError("runtime manifest is missing `binaries`")
|
||||
firecracker_entry = binaries.get("firecracker")
|
||||
jailer_entry = binaries.get("jailer")
|
||||
if not isinstance(firecracker_entry, dict) or not isinstance(jailer_entry, dict):
|
||||
raise RuntimeError("runtime manifest does not define firecracker/jailer binaries")
|
||||
|
||||
firecracker_bin = bundle_root / str(firecracker_entry.get("path", ""))
|
||||
jailer_bin = bundle_root / str(jailer_entry.get("path", ""))
|
||||
artifacts_dir = bundle_root / "profiles"
|
||||
|
||||
for path in (firecracker_bin, jailer_bin, artifacts_dir):
|
||||
if not path.exists():
|
||||
raise RuntimeError(f"runtime asset missing: {path}")
|
||||
|
||||
_ensure_executable(firecracker_bin)
|
||||
_ensure_executable(jailer_bin)
|
||||
|
||||
if verify_checksums:
|
||||
for entry in (firecracker_entry, jailer_entry):
|
||||
raw_path = entry.get("path")
|
||||
raw_hash = entry.get("sha256")
|
||||
if not isinstance(raw_path, str) or not isinstance(raw_hash, str):
|
||||
raise RuntimeError("runtime binary manifest entry is malformed")
|
||||
full_path = bundle_root / raw_path
|
||||
actual = _sha256(full_path)
|
||||
if actual != raw_hash:
|
||||
raise RuntimeError(
|
||||
f"runtime checksum mismatch for {full_path}; expected {raw_hash}, got {actual}"
|
||||
)
|
||||
profiles = manifest.get("profiles")
|
||||
if not isinstance(profiles, dict):
|
||||
raise RuntimeError("runtime manifest is missing `profiles`")
|
||||
for profile_name, profile_spec in profiles.items():
|
||||
if not isinstance(profile_spec, dict):
|
||||
raise RuntimeError(f"profile manifest entry for {profile_name!r} is malformed")
|
||||
for kind in ("kernel", "rootfs"):
|
||||
spec = profile_spec.get(kind)
|
||||
if not isinstance(spec, dict):
|
||||
raise RuntimeError(f"profile {profile_name!r} is missing {kind} spec")
|
||||
raw_path = spec.get("path")
|
||||
raw_hash = spec.get("sha256")
|
||||
if not isinstance(raw_path, str) or not isinstance(raw_hash, str):
|
||||
raise RuntimeError(f"profile {profile_name!r} {kind} spec is malformed")
|
||||
full_path = bundle_root / raw_path
|
||||
if not full_path.exists():
|
||||
raise RuntimeError(f"profile asset missing: {full_path}")
|
||||
actual = _sha256(full_path)
|
||||
if actual != raw_hash:
|
||||
raise RuntimeError(
|
||||
f"profile checksum mismatch for {full_path}; "
|
||||
f"expected {raw_hash}, got {actual}"
|
||||
)
|
||||
|
||||
return RuntimePaths(
|
||||
bundle_root=bundle_root,
|
||||
manifest_path=manifest_path,
|
||||
firecracker_bin=firecracker_bin,
|
||||
jailer_bin=jailer_bin,
|
||||
artifacts_dir=artifacts_dir,
|
||||
notice_path=notice_path,
|
||||
manifest=manifest,
|
||||
)
|
||||
|
||||
|
||||
def doctor_report(*, platform: str = DEFAULT_PLATFORM) -> dict[str, Any]:
|
||||
"""Build a runtime diagnostics report."""
|
||||
report: dict[str, Any] = {
|
||||
"platform": platform,
|
||||
"runtime_ok": False,
|
||||
"issues": [],
|
||||
"kvm": {
|
||||
"exists": Path("/dev/kvm").exists(),
|
||||
"readable": os.access("/dev/kvm", os.R_OK),
|
||||
"writable": os.access("/dev/kvm", os.W_OK),
|
||||
},
|
||||
}
|
||||
try:
|
||||
paths = resolve_runtime_paths(platform=platform, verify_checksums=True)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
report["issues"] = [str(exc)]
|
||||
return report
|
||||
|
||||
profiles = paths.manifest.get("profiles", {})
|
||||
profile_names = sorted(profiles.keys()) if isinstance(profiles, dict) else []
|
||||
report["runtime_ok"] = True
|
||||
report["runtime"] = {
|
||||
"bundle_root": str(paths.bundle_root),
|
||||
"manifest_path": str(paths.manifest_path),
|
||||
"firecracker_bin": str(paths.firecracker_bin),
|
||||
"jailer_bin": str(paths.jailer_bin),
|
||||
"artifacts_dir": str(paths.artifacts_dir),
|
||||
"notice_path": str(paths.notice_path),
|
||||
"bundle_version": paths.manifest.get("bundle_version"),
|
||||
"profiles": profile_names,
|
||||
}
|
||||
if not report["kvm"]["exists"]:
|
||||
report["issues"] = ["/dev/kvm is not available on this host"]
|
||||
return report
|
||||
Loading…
Add table
Add a link
Reference in a new issue