* refactor(managed-agent): split server into Maven modules Separate contracts, orchestration, persistence, adapters, assembly, and verification while retaining the existing service delivery model. Move tests and build consumers together and document local evidence and pending CI acceptance. Co-authored-by: Qwen-Coder <[email protected]> * refactor(managed-agent): reduce configuration and SDK boilerplate Co-authored-by: Qwen-Coder <[email protected]> * refactor(managed-agent): narrow contracts and simplify test scaffolding Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): repair verification after module migration Resolve shared fixtures and worker paths from the Maven module root, update CI packaging assertions, and align existing test fixtures with the extracted contracts. Restore restricted Workspace constructors and remove redundant test visibility changes without altering inherited MySQL coverage. Co-authored-by: Qwen-Coder <[email protected]> * test(managed-agent): align remaining Harness and OSS fixtures Preserve SDK refusal messages and register the managed OSS adapter at fixture injection points. Keep the existing retry and client shutdown assertions intact. Co-authored-by: Qwen-Coder <[email protected]> * style(managed-agent): order Hosted cleanup Harness imports Co-authored-by: Qwen-Coder <[email protected]> * test(managed-agent): fix recovery smoke fixture and import order Supply the required transcript coverage in the recovery browser fixture and wait for history before testing a complete handoff. Preserve its existing safety and submission assertions. Order the cleanup coordinator test imports for Checkstyle. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]>
13 KiB
常驻 Workspace 配置
首版配置为每个租户/Workspace 保留一个 Pod/PVC,通过公开 files 配置支持 Read、Write 和 Edit。Session 关闭会结算并撤销原上下文,下一个 Session 保留 同一文件和运行环境。此方案不安装节点 collector,也不自动删除或复用存储。 本目录尚未在 ACK 部署或验收。
运行环境已通过 PR #14340 在主干实现。简化后的 PR #14758 说明本部署边界。 常驻路径独立于已从 #14758 撤出的节点证据候选和已关闭的安装提案 #14761。 可选的显式空闲退役由 #14765 实现,使用方式见下文。本配置不需要 Go 工具链、 定制 containerd/shim、节点 collector 或冷启动安装。
2026-10-24 范围决定明确完整 CSI 审计不再是首版前置条件。Session 交接保留原 Pod/PVC,仅删除 Pod 的空闲退役保留存储归属。下文的显式 S2/S3 回收与保留数据交接实现需要独立原生验收。
可信平台与支持的存储
信任管理员、Kubernetes 控制面、宿主机和已完成资格验证的 CSI 驱动,保留既有
tenant/actor、Workspace generation、原运行环境和 mount 检查。本配置不独立证明
宿主机持续受控。支持的驱动为 diskplugin.csi.alibabacloud.com,要求
ReadWriteOncePod、Filesystem 和 ext4,注册磁盘 serial 必须与实际挂载设备
一致。其他驱动或访问模式需要独立设计和验收。RWOP 不授权节点故障后的存储交接。
配置与安装
将 application.example.yaml 作为额外的 Spring 配置文件,另外提供现有数据库、
Hosted Harness、Session store、Broker token 和凭据加密配置。Kubernetes token
和 CA 配置指向服务端读取的本地文件,凭据不应写入此模板或 worker Workspace。
Session store 的全局 workspace-id 必须与 Broker 的全局 workspace-id 像示例中
一样保持相等,这是现有 Hosted 启动契约的要求;各公开 Workspace 仍解析自己的注册身份。
启用映射前,使用现有注册 operator 登记精确 CSI registration,并在 Workspace
registry 登记匹配的租户/Workspace、代数和存储。Workspace config/policy 分别
使用 managed-runtime-tools/1 和 preapproved-workspace-tools/1,再给可信
actor 授予 OPERATOR 或 OWNER。每个物理卷只能出现在一条常驻映射中。单有
注册信息或调用者提供的 Workspace ID 不会启用 CSI 执行。
使用现有 Java 保护 operator 的 <reviewed-registration-json> <policy-name> <binding-name> 命令生成精确存储及命名空间保护对象。命令只打印
Kubernetes JSON List,不访问集群或数据库。在 reference 发行包中这样调用:
bin/qwen-managed-agent-admin csi-protection \
/absolute/path/registration.json resident-protection resident-binding
使用模块构建产物时,在仓库根目录运行同一个入口:
java -Dloader.main=com.alibaba.qwen.code.managedagent.maintenance.WorkspaceCsiProtectionMain \
-cp packages/managed-agent-server/server/target/qwen-managed-agent-server-<version>-exec.jar \
org.springframework.boot.loader.launch.PropertiesLauncher \
/absolute/path/registration.json resident-protection resident-binding
在另外授权安装后,钉住实际 namespace、policy、binding 的 UID 和代数。使用不可变
镜像摘要,镜像包含 Node.js 22 或更新版本及构建后的 CLI。CSI 文件路径使用 Node
文件系统 API,并读取 Linux /proc/self/mountinfo、/proc/self/fd 和卷的
/sys/dev/block/<major>:<minor>/device/serial;UID 1000 必须能够访问这些位置。
command 只包含执行程序和 CLI 路径;provisioner
会追加 managed-runtime-worker --container-boot 和 boot 文件路径。可选产物
ConfigMap 必须不可变并显式钉住身份。
服务端需要访问 Kubernetes API 和原 Pod 的 worker 端点。worker 以 UID/GID
1000 运行,根文件系统只读,不添加 Linux capabilities,不带 service-account
token,固定 PVC 挂在 /workspace,临时目录使用 emptyDir。Pod 使用
restartPolicy: Never。原 Pod 缺失、重启或被替换均保持准入关闭;本版不会
重建或接管替代 Pod。
使用现有公开 Workspace Session API 创建和关闭 Session,同一 Workspace 只 准入一个未完成 Session。关闭成功后才能启动下一个。释放超时或结果未知会保留 reservation 并阻止切换。每个 Session 的 cwd 固定,可在下一 Session 创建时 选择其他目录。本版不支持 Shell、MCP、Hooks、共享运行环境的子 Session 或 原地 cwd 变更。
显式空闲退役
正常 Session 关闭后 Workspace 仍可复用。退役空闲 Workspace 时,使用私有 Java 维护入口,加载相同部署配置、数据库和 Runtime 凭据密钥。先通过正常服务升级 执行当前 schema 迁移,并封锁旧 Server/Broker 二进制。维护命令只验证目录,不迁移。
创建有界请求文件,只指定原 scope 和当前具有 Workspace OWNER grant 的授权
审计 actor:
{
"tenantId": "tenant-a",
"workspaceId": "workspace-a",
"workspaceGeneration": 1,
"actorId": "owner-a",
"retirementId": "retire-workspace-a-1"
}
操作者须已控制可信维护主机及部署凭据;actor 字段本身不认证身份。在 reference 发行包中:
bin/qwen-managed-agent-admin resident-retirement \
inspect /absolute/path/retirement.json \
--spring.config.additional-location=file:/absolute/path/application.yaml
使用模块构建产物时,在仓库根目录运行,并使用相同的参数:
java -Dloader.main=com.alibaba.qwen.code.managedagent.maintenance.ResidentWorkspaceRetirementMain \
-cp packages/managed-agent-server/server/target/qwen-managed-agent-server-<version>-exec.jar \
org.springframework.boot.loader.launch.PropertiesLauncher \
inspect /absolute/path/retirement.json \
--spring.config.additional-location=file:/absolute/path/application.yaml
将 inspect 替换为 begin,原子拒绝新 Session、获取、激活、派发和恢复。它要求
Session 终结、journal head 封存或永久删除且 writer 已清除、Runtime Session 已释放、
执行已结算。保留原 binding/
active slot,存储保持 RESERVED。回复丢失后复用同一请求。查询只读,不访问 Kubernetes。
删除是独立显式命令,将动作和参数替换为
delete-pod /absolute/path/retirement.json <observed-resourceVersion>。它通过配置的
Kubernetes API 核验原资源,再记录带原 UID/resourceVersion 前置条件、30 秒宽限期
及 Background propagation 的正常 Pod DELETE。凭据留在已有 API/token/CA 配置中。
不删除 PVC/PV、Secret 或保护对象。同版本重试只读原结果,不重发;新版本需要
显式再次调用,总计最多八次。HTTP 回复和 Pod 缺失不会释放存储或重新开放 Workspace。
回收必须使用下文的准备与完成协议。
即使 Kubernetes 已接受删除,回复丢失仍可能留下 UNKNOWN。原 Pod 一旦具有
deletionTimestamp 或已缺失,不同 resourceVersion 也不能授权再次 DELETE。
同版本重放仍只返回保留尝试,不访问 Kubernetes;使用 inspect 读取该回执。
最多八次的上限只允许在原 Pod 仍通过全部核验时显式发起尝试。
完整退役设计见English 与简体中文。 原生 ACK 退役资格验证继续待完成。
显式回收与同租户数据交接
S2/S3 设计 增加健康正常清理路径。原生 ACK 验收仍待完成,不能由命令存在或先前 S1 运行推断通过。保留现有单控制面信任边界,使用专用 runtime namespace/PVC。准入/RBAC 必须保证只有该 Managed provisioner 能创建使用此 PVC 的 Pod;维护和交接期间不得有外部控制器、其他 Pod 创建者、PV 物理别名或管理员存储操作。现有资源保护不约束 Pod 创建的独占性。
维护凭据额外需要 runtime namespace 的 Pod list/patch、kube-node-lease 中 Node Lease 的 get,以及集群 PV/VolumeAttachment list。不修改节点、attachment、PVC 或 PV。不要向 worker 和公开调用者授予这些权限。
沿用上文的 Java 入口、请求与部署配置:
- 关闭全部 Session,然后执行
begin /absolute/path/retirement.json。退役期间与释放后,Workspace 均保持DRAINING。 - 读取精确原 Pod 当前 resourceVersion,在 DELETE 前执行
prepare-reclamation /absolute/path/retirement.json <resourceVersion>。它校验原运行中容器、健康节点/boot、Node Lease、原 attachment 及物理卷独占使用,保存基线,再条件添加操作的自有 finalizer。之后重新读取 resourceVersion。 - 执行
delete-pod /absolute/path/retirement.json <fresh-resourceVersion>,发送原有正常 30 秒优雅 DELETE,保留实际响应或UNKNOWN。没有准备步骤的仅删除操作,不能在 Pod 消失后追认合格。 - 执行
reclaim /absolute/path/retirement.json观察完成。要求保留的原 Pod 和明确终止的原容器、相同节点/boot 上新鲜的 Ready 状态和 Lease、节点不再使用卷且没有 attachment、物理别名或其他 Pod 消费者。任一条件缺失都保留归属。持久化清理回执后,仅条件移除自己的 finalizer,并等待原 Pod 消失,再原子释放操作/绑定/active slot/物理归属。结果未知时执行inspect;命令有界,平台尚未完成时可能需要再次调用。 - 只有达到
RELEASED后,管理员才可将唯一配置映射替换为同租户的新 Workspace ID,准备经审查的 saved Workspace 元数据和 OWNER 授权,并按新配置重启。保留原 PVC/PV、注册、磁盘和文件。新请求的 runtime generation 从 1 开始,取得下一个物理 reservation revision。旧 Workspace/请求保持围栏;配置替换和后续复用后,持有当前 OWNER 授权仍可读取其终态回执。
数据策略是 retain-same-tenant-admin-handoff/1。物理 revision 在占用/释放轮次间递增且不重置;保留的数据所属租户拒绝其他租户的别名。不提供自动分配、数据擦除、强制释放 override 或重新开放已退役 Workspace。节点/API 分区、身份变化、强制删除,或保存清理回执前原 Pod 消失,均保留归属。基础设施修复本身不授权产品释放。回执依赖独占使用边界持续成立,不授权外部重挂载。
故障处理与验收
执行或释放结果未知时,保留其 Session 和 reservation,并拒绝下一个 Session。 原 Pod、容器或 mount 缺失或变化时,Workspace 保持不可用。lease 到期、运维检查 或 Kubernetes 对象缺失都不能清除物理 owner 或授权创建替代 Pod。正常空闲边界的 应用/Broker 重启可使用保留数据库和原 worker;这不代表被中断的 Turn、过期 writer 或丢失 Pod 的恢复已经合格。
声明部署验收前,完成以下验证:
- 在 A 中执行 Write/Edit/Read,关闭 A,再由 B 读取。核对精确文件字节,以及 不变的 Pod/container、binding generation、PVC/PV 和 reservation。确认 A 的 旧上下文拒绝激活和派发,binding 保持 READY,存储保持 RESERVED。
- 在真实 MySQL 上验证不同键并发创建及同键重放,只能准入一个未完成 Session。 使用受控 peer 验证释放响应丢失和 UNKNOWN 执行:占用必须保留,结果不确定的 副作用不能重复。
- 在空闲边界重启实际应用/Broker 进程,保留数据库和 worker。确认接管原运行环境, 不新建 Pod。另行验证缺失或变化身份被拒绝。
- 在独立的空闲退役边界开始维护,确认拒绝新 Session 和 Runtime 工作。仅以 观察到的原 UID/version 删除原 Pod,测试回复丢失和同版本重试,并核验封锁、 active slot 及原存储 reservation 均保留。
每次记录源码提交、环境、原身份及观察结果。组件/H2、MySQL、独立进程与 ACK 证据分别记录。这些部署检查继续标为待完成。既有历史夹具不作为新部署的验收证据。
Session 关闭后常驻资源继续产生正常的 Pod、节点和存储费用。此模板不授权云上 安装、付费资源创建或手工物理销毁;这些动作使用另外批准的部署或维护方案。 失败和验收边界见设计。