Files
codex-app-server/cloud/tests/test_cloud_hypervisor.py
T
host df8c64bd64 feat(chv): 架构/可维护性/前端只读侧推进——生产实验分离、配置片段化、VM 只读展示
架构:WarmPool 从 cloud/chv/pool.py 移到 experimental_pool.py,
生产残留清理彻底分文件到 cloud/chv/orphans.py(facade 直接读
registry 文件清理,不构造池、不碰 prewarm/acquire/release);
cloud/chv 与 cloud/sandbox 不再导出 WarmPool,旧路径一律
ImportError(CHV 未合并、无生产用户,兼容保留已无保留理由);
worker 改走 orphans facade,不再持有 _warm_pool;#215 对齐从
接口形状推进到 REPLACEMENT_POINTS 替换清单(R1/R2 可机械替换、
R3 永不替换,#215 模块当前不存在由测试钉住)。

可维护性:build_chv_config 拆成 base/ssh/network/quota/disk/
timeout 六片段(组装只做 dict 合并,键冲突即报错;38 字段集
与取值不变,单项非法报错文案逐字一致);ADR-12 模块职责图
与禁 import 清单(AST 钉住);cloud/chv/ 公开 API 全量类型
标注、无 Any 逃逸,并修正三处旧标注错误;死代码扫一遍
(删实验池已无调用的 _quarantine 委托与未用 _LOGGER)。

前端(只读侧,不做控制按钮):chv.ts 新增纯逻辑谓词与文案
(执行器精确匹配、附件守卫、VM 状态文案、列表徽标);列表
行新增 VM 已报告死亡徽标;详情对全部 CHV 任务显示 VM 行
(已报告死亡/未报告死亡,不猜 live);WorkersSection 与成本
卡沿用已有数据,不新开池管理/成本页;服务端未新增第二个
生命周期字段(持久化事实只有三项,chv_vm_dead 已是完整派生),
只把派生抽成 _chv_vm_dead_from_row 并补 goal 回落形态测试。
2026-10-31 06:55:26 +00:00

701 lines
29 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Cloud Hypervisor 执行器 v1 + 生产级唤醒/生命周期测试。
只用标准库与 mock runner/launcher/api_client,绝不启动真实
VM:当前环境没有可跑真 VM 的宿主(生产主机是 CVM、无
/dev/kvm、无嵌套虚拟化;本 VM 容器内同样无 /dev/kvm),
所以本文件验证的是:preflight 分类、API 唤醒顺序、VmConfig、
SSH 就绪、暂停/快照/休眠/关闭/清理、预热池、单任务单 VM
隔离、容量门、磁盘门、worker/server/autoscaler 接线。
真实 VM 启动、快照恢复、压测一律不在本文件声称已验证。
"""
import http.client
import json
import os
import sys
import tempfile
import threading
import unittest
from pathlib import Path
from unittest import mock
try:
from cloud.chv.experimental_pool import WarmPool
from cloud.sandbox import (
ChvFailure,
CloudHypervisorExecutor,
_parse_memory_bytes,
)
except ImportError: # discover -s cloud/tests 时把项目根目录补进 sys.path
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from cloud.chv.experimental_pool import WarmPool
from cloud.sandbox import (
ChvFailure,
CloudHypervisorExecutor,
_parse_memory_bytes,
)
class FakeResult:
def __init__(self, returncode=0, stdout="", stderr=""):
self.returncode = returncode
self.stdout = stdout
self.stderr = stderr
class FakeProc:
"""假 VMM 进程:poll() 返回 None=活着,数字=已死。"""
def __init__(self, alive=True):
self._alive = alive
self.terminated = False
self.killed = False
def poll(self):
return None if self._alive else 1
def terminate(self):
self.terminated = True
self._alive = False
def wait(self, timeout=None):
return 0
def kill(self):
self.killed = True
self._alive = False
def _write(path, content=b"x"):
with open(path, "wb") as fh:
fh.write(content)
os.chmod(path, 0o755)
return path
class ChvCase(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.dir = self.tmp.name
self.binary = _write(os.path.join(self.dir, "cloud-hypervisor"))
self.ch_remote = _write(os.path.join(self.dir, "ch-remote"))
self.kernel = _write(os.path.join(self.dir, "vmlinux"))
self.rootfs = _write(os.path.join(self.dir, "disk.raw"),
b"R" * 100)
self.ssh_key = _write(os.path.join(self.dir, "id_chv"))
self.kvm = _write(os.path.join(self.dir, "kvm"))
self.cpuinfo = os.path.join(self.dir, "cpuinfo")
with open(self.cpuinfo, "w") as fh:
fh.write("flags : fpu vmx sse\n")
self.state_dir = os.path.join(self.dir, "state")
self.calls = [] # runner 调用记录
self.api_calls = [] # api_client 调用记录
self.launches = [] # launcher 调用记录
self.audit = [] # audit 事件记录
def tearDown(self):
self.tmp.cleanup()
def make_exec(self, runner=None, api_client=None, launcher=None,
env_extra=None, **overrides):
env = {"CHV_EGRESS_PROXY": "http://10.0.0.1:8888",
# 实验室宣称:mock runner 假装回环端口已转发到客机
#(feat-chv-fix 起,执行器不再无条件假设回环能通)。
# 实验室显式 opt-in keyscan:生产默认已是
# provisioned;本套件旧 fake 不实现 ssh-keygen/
# genisoimage,测的不是 host key 模式,故显式
# 声明实验室路径,绝不把它写成生产默认。
"CHV_HOST_KEY_MODE": "keyscan",
"CHV_SSH_FORWARD": "1"}
if env_extra:
env.update(env_extra)
if runner is None:
runner = self._default_runner
if api_client is None:
api_client = self._default_api
if launcher is None:
launcher = self._default_launcher
kwargs = dict(
binary=self.binary, ch_remote=self.ch_remote,
kernel=self.kernel, rootfs=self.rootfs,
ssh_key=self.ssh_key, kvm_path=self.kvm,
cpuinfo_path=self.cpuinfo, state_dir=self.state_dir,
runner=runner, launcher=launcher, api_client=api_client,
sleeper=lambda s: None, env=env)
kwargs.update(overrides)
ex = CloudHypervisorExecutor(**kwargs)
ex.audit_callback = lambda e, t, d, k=None: self.audit.append(
(e, t.get("id") if isinstance(t, dict) else t, d, k))
return ex
def _default_runner(self, cmd, timeout=None):
self.calls.append(list(cmd))
if cmd[0] == "ssh":
if cmd[-1] == "true":
return FakeResult(0, "", "")
if "dispatch" in cmd:
return FakeResult(0, json.dumps(
{"status": "completed", "text": "ok-dispatch"}), "")
if "goal" in cmd or "goal-resume" in cmd:
return FakeResult(0, "goal met after rounds\n", "")
if "status" in cmd:
return FakeResult(0, json.dumps(
{"status": "completed", "text": "ok-status"}), "")
return FakeResult(0, "", "")
return FakeResult(0, "", "")
def _default_api(self, socket_path, method, path, payload=None):
self.api_calls.append((method, path, payload))
return (200, {})
def _default_launcher(self, cmd):
self.launches.append(list(cmd))
return FakeProc(alive=True)
def _task(self, task_id="abc123"):
return {"id": task_id, "prompt": "做一个加法函数",
"project_id": "proj1"}
# -- preflight ---------------------------------------------------------
class PreflightTest(ChvCase):
def test_all_present_passes(self):
self.assertTrue(self.make_exec().preflight())
def test_missing_binary_reports_it(self):
ex = self.make_exec(binary="/tmp/no-such-chv-binary")
with self.assertRaises(ChvFailure) as ctx:
ex.preflight()
self.assertEqual(ctx.exception.kind, "binary_missing")
self.assertIn("cloud-hypervisor", str(ctx.exception))
def test_missing_ch_remote_reports_it(self):
ex = self.make_exec(ch_remote="/tmp/no-such-ch-remote")
with self.assertRaises(ChvFailure) as ctx:
ex.preflight()
self.assertEqual(ctx.exception.kind, "binary_missing")
self.assertIn("ch-remote", str(ctx.exception))
def test_missing_kvm_reports_it(self):
ex = self.make_exec(kvm_path="/tmp/no-such-kvm")
with self.assertRaises(ChvFailure) as ctx:
ex.preflight()
self.assertEqual(ctx.exception.kind, "kvm_missing")
self.assertIn("/tmp/no-such-kvm", str(ctx.exception))
def test_missing_cpu_flag_reports_it(self):
with open(self.cpuinfo, "w") as fh:
fh.write("flags : fpu sse\n")
with self.assertRaises(ChvFailure) as ctx:
self.make_exec().preflight()
self.assertEqual(ctx.exception.kind, "cpu_flag_missing")
self.assertIn("vmx", str(ctx.exception))
def test_missing_kernel_reports_it(self):
ex = self.make_exec(kernel="/tmp/no-such-vmlinux")
with self.assertRaises(ChvFailure) as ctx:
ex.preflight()
self.assertEqual(ctx.exception.kind, "kernel_missing")
def test_missing_image_reports_it(self):
ex = self.make_exec(rootfs="/tmp/no-such-disk.raw")
with self.assertRaises(ChvFailure) as ctx:
ex.preflight()
self.assertEqual(ctx.exception.kind, "image_missing")
def test_missing_ssh_key_reports_it(self):
ex = self.make_exec(ssh_key="/tmp/no-such-key")
with self.assertRaises(ChvFailure) as ctx:
ex.preflight()
self.assertEqual(ctx.exception.kind, "ssh_key_missing")
def test_missing_egress_is_fail_closed(self):
ex = self.make_exec(env_extra={"CHV_EGRESS_PROXY": ""})
# env_extra 空值会被 __init__ 的 or 链忽略,故改用显式 env:
ex.egress_proxy = None
ex.allow_direct_egress = False
with self.assertRaises(ChvFailure) as ctx:
ex.preflight()
self.assertEqual(ctx.exception.kind, "egress_not_configured")
def test_multiple_missing_lists_all(self):
ex = self.make_exec(binary="/tmp/no-chv", kernel="/tmp/no-kern")
with self.assertRaises(ChvFailure) as ctx:
ex.preflight()
text = str(ctx.exception)
self.assertIn("cloud-hypervisor", text)
self.assertIn("内核", text)
def test_dispatch_missing_dependency_does_not_call_runner(self):
ex = self.make_exec(binary="/tmp/no-chv")
with self.assertRaises(ChvFailure):
ex.dispatch_task(self._task())
self.assertEqual(self.calls, [])
# -- 配置与命令构造 -----------------------------------------------------
class ConfigCommandTest(ChvCase):
def test_vm_config_shape_and_memory_bytes(self):
ex = self.make_exec()
# feat-chv-secfix(L2):磁盘须在 state_dir 内。
disk = os.path.join(self.state_dir, "x", "disk.raw")
cfg = ex.build_vm_config(disk)
self.assertEqual(cfg["cpus"], {"boot_vcpus": 2,
"max_vcpus": 2})
self.assertEqual(cfg["memory"]["size"], 2 * 1024 ** 3)
self.assertEqual(cfg["kernel"]["path"], self.kernel)
self.assertEqual(cfg["disks"], [{"path": disk}])
self.assertEqual(cfg["cmdline"]["args"],
"console=hvc0 root=/dev/vda1 rw")
def test_memory_parsing(self):
self.assertEqual(_parse_memory_bytes("512M"), 512 * 1024 ** 2)
self.assertEqual(_parse_memory_bytes("1024"), 1024)
self.assertEqual(_parse_memory_bytes("1K"), 1024)
with self.assertRaises(ValueError):
_parse_memory_bytes("abc")
def test_rootfs_image_alias(self):
ex = self.make_exec(rootfs=None, env_extra={})
# rootfs=None 时 __init__ 仍从 env 取不到(env 无 CHV_*),
# 故用 env 含 CHV_IMAGE 的构造验证别名:
env = {"CHV_EGRESS_PROXY": "http://10.0.0.1:8888",
"CHV_IMAGE": self.rootfs}
ex2 = CloudHypervisorExecutor(
binary=self.binary, ch_remote=self.ch_remote,
kernel=self.kernel, ssh_key=self.ssh_key,
kvm_path=self.kvm, cpuinfo_path=self.cpuinfo,
state_dir=self.state_dir, env=env,
sleeper=lambda s: None)
self.assertEqual(ex2.rootfs, self.rootfs)
def test_manual_vm_command_shape(self):
ex = self.make_exec()
self.assertFalse(hasattr(ex, "build_vm_command"))
cmd = ex._build_vm_command(self._task())
self.assertEqual(cmd[0], self.binary)
self.assertIn("--api-socket", cmd)
self.assertIn("--cpus", cmd)
self.assertIn("boot=2", cmd)
self.assertIn("size=2G", cmd)
self.assertIn("--kernel", cmd)
self.assertIn("--disk", cmd)
def test_ssh_command_has_key_path_not_secret_value(self):
ex = self.make_exec()
cmd = ex.build_ssh_command(self._task(), ["true"])
joined = " ".join(cmd)
self.assertIn(self.ssh_key, joined)
self.assertIn("BatchMode=yes", joined)
self.assertNotIn("SECRET", joined)
# egress 代理地址在 env 前缀里(不是密钥),且只在代理已设时出现:
self.assertIn("HTTPS_PROXY=http://10.0.0.1:8888", joined)
# 本轨安全默认变化(#216 的 no + /dev/null 已废除):
# host key 钉死在每 VM 的 known_hosts 文件上:
self.assertIn("StrictHostKeyChecking=yes", joined)
self.assertNotIn("StrictHostKeyChecking=no", joined)
self.assertNotIn("/dev/null", joined)
self.assertIn(os.path.join(self.state_dir,
"chv-abc123", "known_hosts"),
joined)
def test_isolation_names_and_disks_differ(self):
ex = self.make_exec()
a, b = {"id": "task-a"}, {"id": "task-b"}
self.assertNotEqual(ex.vm_name(a), ex.vm_name(b))
self.assertNotEqual(ex.api_socket(a), ex.api_socket(b))
self.assertNotEqual(ex._provision_task_disk(a),
ex._provision_task_disk(b))
# copy 模式:每任务磁盘是基础镜像的真实副本,内容一致、
# 路径不同(两个任务绝不写同一文件):
with open(ex._provision_task_disk(a), "rb") as fh:
self.assertEqual(fh.read(), b"R" * 100)
def test_disk_quota_gate(self):
ex = self.make_exec(env_extra={"CHV_DISK_GB": "0.00000001"})
with self.assertRaises(ChvFailure) as ctx:
ex.wake_vm(self._task())
self.assertEqual(ctx.exception.kind, "disk_quota_exceeded")
def test_capacity_gate_rejects_not_fallback(self):
ex = self.make_exec(env_extra={"CHV_MAX_VMS": "1"})
ex._vms["chv-other"] = FakeProc(alive=True)
with self.assertRaises(ChvFailure) as ctx:
ex.wake_vm(self._task())
self.assertEqual(ctx.exception.kind, "host_capacity_exceeded")
self.assertEqual(self.launches, [])
# -- API 唤醒 -----------------------------------------------------------
class WakeApiTest(ChvCase):
def test_wake_uses_api_mode_not_direct_command(self):
ex = self.make_exec()
out = ex.wake_vm(self._task())
self.assertEqual(out["state"], "running")
self.assertTrue(out["ready"])
self.assertEqual(out["mode"], "cold")
# launcher 只带 --api-socket,不带内核/磁盘(API 模式证明):
self.assertEqual(len(self.launches), 1)
launch = self.launches[0]
self.assertIn("--api-socket", launch)
self.assertNotIn("--kernel", launch)
self.assertNotIn("--disk", launch)
# API 调用顺序:ping → create → boot:
kinds = [(m, p) for m, p, _ in self.api_calls]
self.assertEqual(kinds[0], ("GET", "/vmm.ping"))
self.assertEqual(kinds[1], ("PUT", "/vm.create"))
self.assertEqual(kinds[2], ("PUT", "/vm.boot"))
create_payload = self.api_calls[1][2]
self.assertEqual(create_payload["kernel"]["path"], self.kernel)
self.assertTrue(create_payload["disks"][0]["path"].endswith(
"disk.raw"))
events = [e for e, _, _, _ in self.audit]
self.assertIn("vm_create", events)
self.assertIn("vm_boot", events)
self.assertIn("vm_ready", events)
def test_wake_api_timeout_kills_proc(self):
def never_ok(socket_path, method, path, payload=None):
self.api_calls.append((method, path, payload))
return (500, {})
ex = self.make_exec(api_client=never_ok, api_timeout=3)
proc_holder = []
def launcher(cmd):
proc = FakeProc(alive=True)
proc_holder.append(proc)
self.launches.append(list(cmd))
return proc
ex.launcher = launcher
with self.assertRaises(ChvFailure) as ctx:
ex.wake_vm(self._task())
self.assertEqual(ctx.exception.kind, "api_timeout")
self.assertTrue(proc_holder[0].terminated)
self.assertNotIn("chv-abc123", ex._vms)
def test_guest_ready_timeout_shuts_down(self):
def ssh_never(cmd, timeout=None):
self.calls.append(list(cmd))
if cmd[0] == "ssh":
return FakeResult(1, "", "")
return FakeResult(0, "", "")
ex = self.make_exec(runner=ssh_never, ready_timeout=10,
ready_interval=5)
with self.assertRaises(ChvFailure) as ctx:
ex.wake_vm(self._task())
self.assertEqual(ctx.exception.kind, "guest_not_ready")
# 超时后关 VM:runner 里有 ch-remote shutdown:
self.assertTrue(any("shutdown" in c for c in self.calls))
# -- 暂停 / 快照 / 休眠 / 关闭 / 清理 -----------------------------------
class LifecycleTest(ChvCase):
def test_pause_resume(self):
ex = self.make_exec()
self.assertTrue(ex.pause_vm(self._task()))
self.assertTrue(ex.resume_vm(self._task()))
kinds = [c[-1] for c in self.calls if c[0] == self.ch_remote]
self.assertEqual(kinds, ["pause", "resume"])
def test_restore_then_explicit_resume_order(self):
# feat-chv-secfix(L2/H4):本测试走实验室 keyscan
# 路径 + 声明快照目录(生产默认 provisioned + 白名单
# 目录,旧 file:///snap/1 不再直通)。
ex = self.make_exec(env_extra={"CHV_SNAPSHOT_DIR": "/snap"})
out = ex.restore_vm(self._task(), "file:///snap/1")
self.assertEqual(out["mode"], "restored")
self.assertTrue(out["ready"])
kinds = [c[-1] if c[-1] != "restore" else c[-1]
for c in self.calls if c[0] == self.ch_remote]
# ch-remote 调用里 restore 必须在 resume 之前:
flat = []
for c in self.calls:
if c[0] == self.ch_remote:
flat.append(c[-1] if c[-1].startswith("source_url")
else c[-1])
self.assertIn("resume", flat)
restore_idx = next(i for i, c in enumerate(self.calls)
if c[0] == self.ch_remote
and any(a.startswith("source_url")
for a in c))
resume_idx = next(i for i, c in enumerate(self.calls)
if c[0] == self.ch_remote and c[-1] == "resume")
self.assertLess(restore_idx, resume_idx)
def test_hibernate_is_pause_snapshot_shutdown(self):
ex = self.make_exec()
# feat-chv-fix(P1-7):file:// 快照须真实存在且非空,
# 否则 hibernate 不 shutdown;用临时文件作快照目标。
# feat-chv-secfix(L2):快照须在白名单目录内,放在
# state_dir 下。
snap = os.path.join(self.state_dir, "h1.snap")
os.makedirs(self.state_dir, exist_ok=True)
with open(snap, "wb") as fh:
fh.write(b"S" * 64)
snap_url = "file://" + snap
out = ex.hibernate_vm(self._task(), snap_url)
self.assertEqual(out["state"], "hibernated")
self.assertEqual(out["snapshot"], snap_url)
kinds = [c[-1] for c in self.calls if c[0] == self.ch_remote]
self.assertEqual(kinds[0], "pause")
self.assertTrue(any(a.startswith("source_url")
for c in self.calls for a in c))
self.assertIn("shutdown", kinds)
# 从休眠唤醒 = restore + resume:
ex2 = self.make_exec()
out2 = ex2.wake_from_hibernate(self._task(), snap_url)
self.assertEqual(out2["mode"], "restored")
def test_stop_dead_proc(self):
ex = self.make_exec()
proc = FakeProc(alive=False)
ex._vms["chv-abc123"] = proc
sock = os.path.join(self.state_dir, "chv-abc123.sock")
os.makedirs(self.state_dir, exist_ok=True)
with open(sock, "w") as _fh:
_fh.write("")
self.assertTrue(ex.stop_vm(self._task()))
self.assertFalse(os.path.exists(sock))
self.assertNotIn("chv-abc123", ex._vms)
def test_stop_already_closed(self):
ex = self.make_exec()
self.assertFalse(ex.stop_vm(self._task()))
# 已关仍尽力调过一次 ch-remote shutdown:
self.assertTrue(any("shutdown" in c for c in self.calls))
def test_stop_socket_residual(self):
ex = self.make_exec()
sock = os.path.join(self.state_dir, "chv-abc123.sock")
os.makedirs(self.state_dir, exist_ok=True)
with open(sock, "w") as _fh:
_fh.write("")
self.assertTrue(ex.stop_vm(self._task()))
self.assertFalse(os.path.exists(sock))
def test_cleanup_skips_live_and_non_chv(self):
ex = self.make_exec()
os.makedirs(self.state_dir, exist_ok=True)
live = os.path.join(self.state_dir, "chv-live.sock")
with open(live, "w") as _fh:
_fh.write("")
ex._vms["chv-live"] = FakeProc(alive=True)
dead = os.path.join(self.state_dir, "chv-dead.sock")
with open(dead, "w") as _fh:
_fh.write("")
other = os.path.join(self.state_dir, "other.sock")
with open(other, "w") as _fh:
_fh.write("")
cleaned = ex.cleanup_stale_vms()
self.assertEqual(cleaned, ["chv-dead"])
self.assertTrue(os.path.exists(live))
self.assertTrue(os.path.exists(other))
self.assertFalse(os.path.exists(dead))
def test_cleanup_missing_state_dir(self):
ex = self.make_exec()
self.assertEqual(ex.cleanup_stale_vms(), [])
def test_recycle_preserves_checkpoints_not_temp(self):
ex = self.make_exec()
out = ex.recycle_vm(self._task())
self.assertEqual(out["state"], "recycled")
self.assertIn("task_checkpoints", out["preserved"])
self.assertIn("task_attempts", out["preserved"])
self.assertIn("guest /tmp", out["lost"])
# -- 预热池 --------------------------------------------------------------
class WarmPoolTest(ChvCase):
def test_prewarm_acquire_release(self):
ex = self.make_exec()
pool = WarmPool(ex, size=2, snapshot_url="file:///snap/base")
added = pool.prewarm()
self.assertEqual(len(added), 2)
self.assertEqual(pool.warm_count(), 2)
first = pool.acquire(self._task("t1"))
self.assertEqual(first["mode"], "warm")
second = pool.acquire(self._task("t2"))
self.assertEqual(second["mode"], "warm")
third = pool.acquire(self._task("t3"))
self.assertEqual(third["mode"], "cold")
# release 一律销毁,不放回池:
self.assertTrue(pool.release(self._task("t1"),
pool_vm=added[0]))
self.assertEqual(pool.warm_count(), 0)
def test_prewarm_without_snapshot_is_empty(self):
ex = self.make_exec()
pool = WarmPool(ex, size=2, snapshot_url=None)
self.assertEqual(pool.prewarm(), [])
self.assertEqual(pool.acquire(self._task())["mode"], "cold")
# -- dispatch / goal(wake → 跑 → finally 关) ---------------------------
class DispatchTest(ChvCase):
def test_dispatch_wakes_runs_shuts_down(self):
ex = self.make_exec()
out = ex.dispatch_task(self._task())
self.assertEqual(out["executor"], "cloud-hypervisor")
self.assertEqual(out["status"], "completed")
self.assertIn("ok-dispatch", out["stdout"])
self.assertEqual(out["vm"], "chv-abc123")
# finally 关 VM:ch-remote shutdown 在 dispatch 之后出现:
shutdown_idx = next(i for i, c in enumerate(self.calls)
if c[0] == self.ch_remote
and c[-1] == "shutdown")
ssh_idx = next(i for i, c in enumerate(self.calls)
if c[0] == "ssh" and "dispatch" in c)
self.assertLess(ssh_idx, shutdown_idx)
self.assertNotIn("chv-abc123", ex._vms)
def test_goal_met(self):
ex = self.make_exec()
out = ex.goal_task({"id": "g1", "prompt": "做目标",
"goal_mode": True})
self.assertEqual(out["status"], "completed")
self.assertTrue(out["goal_met"])
def test_goal_resume_breakpoint(self):
ex = self.make_exec()
out = ex.goal_resume_breakpoint_task({"id": "g1",
"prompt": "续跑"})
self.assertEqual(out["status"], "completed")
self.assertTrue(out["goal_met"])
def test_execute_is_ssh_status_only(self):
ex = self.make_exec()
# 本轨 execute 语义门禁:只读已运行 VM。先登记一个活
# 的 VMM 进程表示 VM 已醒(未登记时 execute 报
# vm_not_running,见 test_chv_optimize.py):
ex._vms["chv-abc123"] = FakeProc(alive=True)
out = ex.execute(self._task())
self.assertEqual(out["executor"], "cloud-hypervisor")
self.assertIn("ok-status", out["stdout"])
# execute 不 wake(launcher 未调用):
self.assertEqual(self.launches, [])
# -- worker / autoscaler / server 接线 -----------------------------------
class WiringTest(ChvCase):
def test_worker_executor_name_and_cli_builder(self):
from cloud import worker as worker_mod
from cloud.sandbox import CloudHypervisorExecutor as CHV
ex = self.make_exec()
w = worker_mod.Worker.__new__(worker_mod.Worker)
w.executor = ex
self.assertEqual(w._executor_name(), "cloud-hypervisor")
class Args:
executor = "cloud-hypervisor"
built = worker_mod._build_cli_executor(Args(), None)
self.assertIsInstance(built, CHV)
def test_autoscaler_passthrough(self):
from cloud.autoscaler import Autoscaler
scaler = Autoscaler("/tmp/no.db", [], executor="cloud-hypervisor")
cmd = scaler._worker_command("w1")
self.assertIn("--executor", cmd)
self.assertEqual(cmd[cmd.index("--executor") + 1],
"cloud-hypervisor")
scaler2 = Autoscaler("/tmp/no.db",
["--executor", "docker"],
executor="cloud-hypervisor")
cmd2 = scaler2._worker_command("w1")
self.assertEqual(cmd2.count("--executor"), 1)
self.assertEqual(cmd2[cmd2.index("--executor") + 1], "docker")
def test_server_default_executor(self):
from cloud.server import _default_executor
with mock.patch.dict(os.environ,
{"CLOUD_DEFAULT_EXECUTOR":
"cloud-hypervisor"}):
self.assertEqual(_default_executor(), "cloud-hypervisor")
with mock.patch.dict(os.environ,
{"CLOUD_DEFAULT_EXECUTOR": "bogus"}):
self.assertEqual(_default_executor(), "docker")
class ServerWhitelistTest(unittest.TestCase):
"""server 白名单端到端:真实 HTTP + 临时库 + 临时端口。"""
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.db_path = os.path.join(self.tmp.name, "control.db")
try:
from cloud.auth import AuthStore
from cloud.server import create_server
except ImportError:
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from cloud.auth import AuthStore
from cloud.server import create_server
self.auth = AuthStore(self.db_path)
self.owner_id, self.owner_key = self.auth.create_user("owner")
old = os.environ.get("CODEX_GLOBAL_ADMINS")
self._old_admins = old
os.environ["CODEX_GLOBAL_ADMINS"] = self.owner_id
self.server = create_server(self.db_path, "127.0.0.1", 0)
self.port = self.server.server_address[1]
self.thread = threading.Thread(
target=self.server.serve_forever, daemon=True)
self.thread.start()
status, self.project = self._req(
"POST", "/projects", {"name": "demo"}, self.owner_key)
self.assertEqual(status, 201, self.project)
def tearDown(self):
self.server.shutdown()
self.server.server_close()
self.thread.join(timeout=5)
try:
self.auth.conn.close()
except Exception:
pass
self.tmp.cleanup()
if self._old_admins is None:
os.environ.pop("CODEX_GLOBAL_ADMINS", None)
else:
os.environ["CODEX_GLOBAL_ADMINS"] = self._old_admins
def _req(self, method, path, body=None, key=None):
conn = http.client.HTTPConnection("127.0.0.1", self.port,
timeout=10)
hdrs = {}
payload = None
if body is not None:
payload = json.dumps(body).encode("utf-8")
hdrs["Content-Type"] = "application/json"
if key is not None:
hdrs["X-API-Key"] = key
try:
conn.request(method, path, body=payload, headers=hdrs)
resp = conn.getresponse()
data = resp.read()
finally:
conn.close()
try:
parsed = json.loads(data.decode("utf-8")) if data else None
except ValueError:
parsed = None
return resp.status, parsed
def test_task_executor_cloud_hypervisor_accepted(self):
status, task = self._req(
"POST", "/tasks",
{"project_id": self.project["id"], "prompt": "x",
"executor": "cloud-hypervisor"}, self.owner_key)
self.assertEqual(status, 201, task)
self.assertEqual(task["executor"], "cloud-hypervisor")
def test_task_executor_bogus_rejected(self):
status, body = self._req(
"POST", "/tasks",
{"project_id": self.project["id"], "prompt": "x",
"executor": "bogus"}, self.owner_key)
self.assertEqual(status, 400)
self.assertIn("cloud-hypervisor", body["error"])
if __name__ == "__main__":
unittest.main()