5.9 KiB
专用测试控制台实施方案
2026-10-13 目录展示已按用户后续确认改为“一项测试行为一个项目”,内部文件不平铺;清单与接口补充见 ADR 0003。本文其余存储和执行边界继续适用。
日期:2026-10-13。本文是当前维护入口,覆盖此前 Rundeck 方案的产品范围;历史验收记录继续保留。
已确认的行为
- 公司内网免登录;只读查看脚本代码,上传创建新脚本或不可变的新版本。
- 登记三个已知测试目录内的 Python/Shell 脚本,不批量复制源文件。原脚本保持原位置、参数与工作目录。危险的服务管理脚本仅登记待配置。
- 允许已登记测试按原逻辑写入原日志/结果目录;不修改源文件,不授权停止他人的模型服务。
- 每次运行有独立记录、实时标准输出/错误、退出状态、源文件摘要和结果文件。文件型日志需登记路径才会采集。历史结果有独立归档,不覆盖原文件。
- 数据库存元数据与状态,源文件/完整日志/报告存在文件系统,不把文件正文重复塞进数据库。
- 上传目录 runtime/library/<脚本ID>/versions/<版本>/;运行目录 runtime/runs/<运行ID>/。
- Git 管理控制台源码和部署配置,不在每次运行时拉取。没有提交、标签或推送授权。
架构与信任边界
FastAPI 提供网页/API,SQLite WAL 保存目录与队列;网页容器没有 Docker socket 或 GPU。独立宿主执行器处理单并发队列,启动有固定标签、限定资源、无 Docker socket 的临时容器。可配置的工作目录和输出路径必须位于已登记的来源目录,上传脚本只能写本次结果目录。源文件挂载只读,只有明确登记的输出目录可写。
所有内网用户共享权限,可上传和运行自己的代码,因此信任边界是允许访问的两个公司网段。执行容器限制宿主文件与设备,不能作为抵御恶意内网用户的完整安全平台。API 校验 Host、Origin、来源网段和专用请求头,拒绝跨站修改;不接收任意 shell 命令、环境变量、镜像或宿主挂载。
运行记录固定版本、配置快照与源码摘要;排队后原文件变动则拒绝执行。超时/取消只终止本次容器,不宣称远端模型推理已取消。执行器重启时只恢复/终止本项目标签容器,不自动补跑。日志、上传、结果大小与执行时长有上限。业务判定只在脚本提供明确报告时呈现;退出零只表示进程正常结束。
旧 smoke.sh 缺少当前服务要求的认证。为保留原代码,在其运行容器中放置仅服务本次进程的回环代理:凭据经 stdin 传递、只保留内存,固定上游地址、模型、1 次 models 与 3 次 completion,不重试。真实验收最多一轮,保留失败和截断输出。
实施和验收
- 固定 API,先测试版本不可覆盖、目录边界、排队幂等与单并发、日志游标、归档和取消。
- 实现目录/上传/代码只读、历史、实时日志、文件下载;快速样例验证成功、失败、取消和结果。
- 只读扫描已知来源,配置一个轻量既有脚本。新端口 14442 并行部署,旧入口 14440 暂保留。
- 浏览器贯通上传、新版本、运行、日志、结果和历史;在模型服务仍正常的条件下执行一次有界 smoke。
- 验收后切换 14440 的项目入口,保留旧容器/数据和明确回退路径。未通过则保留新预览并如实报告。
前后端接口契约 v1
JSON 错误 {detail: string}。修改请求必须含 X-XAS-Console: 1。所有时间为 UTC ISO。
- GET
/api/summary:{scripts, ready, runs, active, worker:{online,last_seen}, storage:{run_bytes}, limits:{...}} - GET
/api/scripts:{items:[Script]};GET/api/scripts/{id}: Script(含 versions) - Script:
{id,name,description,kind:'managed'|'external',language:'python'|'shell',source_path,source_group,status:'ready'|'needs_configuration'|'missing',enabled,reason,version,sha256,updated_at,config:{args:[],cwd:'',output_paths:[],timeout_seconds:300,profile:'standard'|'legacy_smoke'},versions:[{version,sha256,created_at,filename}]} - GET
/api/scripts/{id}/code?version=:{code,filename,sha256,truncated,redacted};GET/api/scripts/{id}/download?version=返回只读源文件(含秘密的文件拒绝下载) - POST
/api/scripts/upload: multipartfile,name,description(新建);POST/api/scripts/{id}/versions: multipartfile(仅 managed);返回 Script。 - PATCH
/api/scripts/{id}:{name?,description?,enabled?,config?:{args?,cwd?,output_paths?,timeout_seconds?}};不允许更新代码、profile、路径来源或版本。 - POST
/api/discover: 返回{registered,missing,total},仅扫描预设来源。 - POST
/api/scripts/{id}/runs:{request_id:UUID},返回 Run;重复请求返回同一记录。运行采用当前登记配置和版本。 - GET
/api/runs?script_id=&limit=100:{items:[Run]};GET/api/runs/{id}: Run。 - Run:
{id,script_id,script_name,version,sha256,state:'queued'|'running'|'succeeded'|'failed'|'cancelled'|'timed_out'|'interrupted',created_at,started_at,finished_at,exit_code,message,config,artifacts:[{id,name,size,kind:'text'|'json'|'file'}],synthetic,source_ip}。 - POST
/api/runs/{id}/cancel: 返回 Run;GET/api/runs/{id}/log?offset=0:{text,next_offset,done,truncated}。 - GET
/api/runs/{id}/log/download;GET/api/runs/{id}/artifacts/{artifact_id}(下载);GET 同路径加/preview:{text,truncated}。
网页无需外部 CDN。中文、清晰加载/空/错误状态,脚本列表筛选,详情页源码只读、配置编辑、版本上传、运行记录。运行页轮询日志与状态,取消只对进行中的任务显示。结果区展示文件列表、文本预览/下载。默认突出真实状态,不把模拟验收算作模型测评。