Files

53 lines
5.9 KiB
Markdown
Raw Permalink 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.
# 专用测试控制台实施方案
> 2026-10-13 目录展示已按用户后续确认改为“一项测试行为一个项目”,内部文件不平铺;清单与接口补充见 [ADR 0003](adr/0003-test-behavior-catalog.md)。本文其余存储和执行边界继续适用。
日期: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,不重试。真实验收最多一轮,保留失败和截断输出。
## 实施和验收
1. 固定 API,先测试版本不可覆盖、目录边界、排队幂等与单并发、日志游标、归档和取消。
2. 实现目录/上传/代码只读、历史、实时日志、文件下载;快速样例验证成功、失败、取消和结果。
3. 只读扫描已知来源,配置一个轻量既有脚本。新端口 14442 并行部署,旧入口 14440 暂保留。
4. 浏览器贯通上传、新版本、运行、日志、结果和历史;在模型服务仍正常的条件下执行一次有界 smoke。
5. 验收后切换 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`: multipart `file`, `name`, `description`(新建);POST `/api/scripts/{id}/versions`: multipart `file`(仅 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。中文、清晰加载/空/错误状态,脚本列表筛选,详情页源码只读、配置编辑、版本上传、运行记录。运行页轮询日志与状态,取消只对进行中的任务显示。结果区展示文件列表、文本预览/下载。默认突出真实状态,不把模拟验收算作模型测评。