Files
qinglong/docs/operations/ql3-local-data-directory-adoption.md
T

8.5 KiB
Raw Blame History

QingLong 2.x Data Directory 接管

本流程先为完整 QingLong 2.x data 目录生成一个只读、确定性、按 Profile 有界的 3.0 接管计划,再把审核过的资产 no-replace 暂存并稳定校验。它不会删除或修改源文件,也不替代 SQLite 接管流程

1. 前置条件

  • 使用最终运行 QingLong 的同一个 POSIX 用户执行;
  • dataRoot 必须是绝对、canonical、非根、非 symlink 的目录;
  • dataRoot 必须由当前 UID 拥有,且 group/world 不可写;
  • command file 继续遵守 ql3-adoption 的当前 UID、canonical、单链接、0600 私有文件要求;
  • 生产盘点建议先停止 2.x writer。若文件或目录在盘点中变化,命令会失败关闭,不会给出部分成功计划。
  • stage 前必须已经完成 SQLite inspect、stage、verify 与 activation,并保留五个绝对路径和 activationDigest
  • deploymentRootstagingRoot 的父目录必须是当前 UID 拥有的 canonical 0700 目录;stagingRoot 必须尚不存在且位于 deploymentRoot 内、dataRoot 外。

2. 执行 inspect

Edge/低配路由设备:

{
  "schemaVersion": 1,
  "operation": "local-data-directory.adoption.inspect",
  "options": {
    "dataRoot": "/opt/qinglong/data",
    "profile": "edge"
  }
}

Standalone/NAS

{
  "schemaVersion": 1,
  "operation": "local-data-directory.adoption.inspect",
  "options": {
    "dataRoot": "/opt/qinglong/data",
    "profile": "standalone"
  }
}
chmod 0600 /secure/operator/ql3-data-directory-inspect.json
ql3-adoption run --command-file /secure/operator/ql3-data-directory-inspect.json

命令成功时返回 status=inspectedqinglong3-legacy-data-directory-adoption-plan。保存完整 JSON 和 planDigest,但不要把它 当作已经授权复制的 manifest。

3. 审核处置矩阵

类别 默认处置 说明
config transform 迁移到 3.0 配置模型,不原样覆盖
scripts copy_reviewed 审核兼容性和安全后复制
db transform 主 SQLite 走独立流程;审核 Keyv 和 sidecar
upload copy_reviewed 审核文件类型与容量后复制
ssh.d transform 进入后续私有凭据交付,不进入普通归档
logsyslogbak retain_external 作为历史/恢复资产另行保留
reporawdep_cachedeps regenerate 在目标架构重新 checkout/安装

recursive_content 类别会稳定读取普通单链接文件并生成摘要;root_only 类别只检查类别根,不扫描内部内容。

4. 解释 assessment

  • reviewable:没有未知顶层条目,也没有 unsafe 条目;仍需 operator 审核分类、数量、字节和数据库识别计数;
  • manual_review:出现未知顶层条目或 unsafe 条目;不得继续自动 staging;
  • broadReadableEntries > 0:存在 group/world 可读条目,是敏感性审核信号,但不等同于可被外部修改;
  • activeSqliteSidecars > 0:发现 database.sqlite-*keyv.sqlite-* 活跃 sidecar,先停止 writer 并完成 SQLite checkpoint/一致性处置后重新盘点;
  • primaryDatabaseFiles 应按生产布局识别 db/database.sqlitelegacyKeyValueDatabaseFiles 识别 db/keyv.sqlite

输出不会包含 dataRoot 原文、任意文件名或文件内容。未知名称只进入摘要。不要尝试从摘要反推或把摘要当作内容备份。

5. Profile 预算

Profile 最大条目 最大哈希字节 单文件上限 最大深度
Edge 8,192 512 MiB 64 MiB 32
Standalone 65,536 4 GiB 512 MiB 64

超过任一限制都会以 LOCAL_DATA_DIRECTORY_ADOPTION_CONFIGURATION_INVALID 失败。不要为了通过门禁临时改名、删除或排除资产;先保留 现场并决定它应拆分为外部恢复资产、在目标重新生成,还是进入后续人工迁移协议。

6. 执行私有 stage

审核 assessment=reviewableprimaryDatabaseFiles=1、所有 activeSqliteSidecars=0 后,提交完整 plan 与 SQLite activation 双围栏:

{
  "schemaVersion": 1,
  "operation": "local-data-directory.adoption.stage",
  "options": {
    "deploymentRoot": "/opt/qinglong3/adoption",
    "dataRoot": "/opt/qinglong/data",
    "stagingRoot": "/opt/qinglong3/adoption/staging/reviewed-data",
    "profile": "edge",
    "expectedPlanDigest": "<64-hex-directory-plan-digest>",
    "sqlite": {
      "sourcePath": "/opt/qinglong/data/db/database.sqlite",
      "targetPath": "/opt/qinglong3/adoption/sqlite/qinglong3.sqlite",
      "recoveryPath": "/opt/qinglong3/adoption/sqlite/database.pre-ql3.sqlite",
      "manifestPath": "/opt/qinglong3/adoption/sqlite/adoption.json",
      "activationPath": "/opt/qinglong3/adoption/sqlite/activation.json",
      "expectedActivationDigest": "<64-hex-sqlite-activation-digest>"
    }
  }
}

成功结果为 status=staged。暂存区固定包含 payload/copy-reviewed/{scripts,upload}payload/transform-input/{config,db,ssh.d} 与私有 manifest.json;不存在的类别不会被制造。db/database.sqlite 及其 sidecar 不复制, 主库恢复继续以 SQLite adoption 的 recovery/target/activation 为权威。日志、备份和缓存也不会进入 payload。

所有目录归一化为 0700,所有文件归一化为 0600。复制使用 64 KiB 缓冲,并再次执行当前 Profile 的条目、字节、单文件和深度预算。

7. 执行稳定 verify

保存 stage 返回的 manifestDigest,使用同一组路径和 SQLite activation 执行:

{
  "schemaVersion": 1,
  "operation": "local-data-directory.adoption.verify",
  "options": {
    "deploymentRoot": "/opt/qinglong3/adoption",
    "dataRoot": "/opt/qinglong/data",
    "stagingRoot": "/opt/qinglong3/adoption/staging/reviewed-data",
    "profile": "edge",
    "expectedManifestDigest": "<64-hex-directory-manifest-digest>",
    "sqlite": {
      "sourcePath": "/opt/qinglong/data/db/database.sqlite",
      "targetPath": "/opt/qinglong3/adoption/sqlite/qinglong3.sqlite",
      "recoveryPath": "/opt/qinglong3/adoption/sqlite/database.pre-ql3.sqlite",
      "manifestPath": "/opt/qinglong3/adoption/sqlite/adoption.json",
      "activationPath": "/opt/qinglong3/adoption/sqlite/activation.json",
      "expectedActivationDigest": "<64-hex-sqlite-activation-digest>"
    }
  }
}

verify 会重新验证目录计划、SQLite activation/source/target、清单 exact shape、私有权限和完整 payload 语义摘要。成功结果为 status=verified,且 evidence 应与 stage 的低敏 evidence 一致。

8. 崩溃残留

stage 创建暂存根后立即写入 .incomplete。只有 payload 和 manifest.json 都持久化后才删除它。命令失败或进程崩溃后:

  • 不要直接把残留目录当作恢复资产;
  • 不要在原路径重试,stage 会 no-replace 拒绝;
  • 先保存现场用于诊断,再由 operator 显式移走残留目录,并使用一个新的空路径重试;
  • verify 遇到 .incomplete、额外文件或缺失文件一律失败关闭。

9. 常见失败

  • 根目录或条目 group/world 可写:修正 ownership/permission 后重新盘点;
  • symlink、硬链接或特殊文件:保留现场,确认来源和目标后人工处置;盘点不会跟随或读取;
  • 目录在盘点中变化:停止 2.x writer、同步器、下载器和仓库更新后重试;
  • 单文件或总内容超过 Profile 预算:不要改用 tar 绕过;为该资产设计独立流式迁移/外部保留流程;
  • 未知顶层条目:根据插件或用户资产来源登记明确处置,再进入后续 staging 设计。
  • activation 不匹配:重新执行 SQLite verify/activation,不能只替换 digest
  • stagingRoot 已存在:检查是否为崩溃残留,禁止覆盖或合并;
  • stage/verify 后源或目标 drift:停止所有 writer,回到 inspect,生成并重新审核新的 plan。

10. 当前边界

本流程已经提供 inspect、私有 stage 和稳定 verify。它仍不:

  • 删除或修改任何源资产;
  • config、Keyv 或 ssh.d 自动转换为 3.0 目标模型;
  • 把历史日志/备份复制到默认目标,或复用跨架构 repo/dependency cache
  • 授权 service-manager/Compose cutover 或 Legacy rollback
  • 证明固定物理路由器/NAS 上的耗时、RSS、I/O、磁盘峰值和断电恢复。

只有 status=verified 仍不是 cutover 授权。继续保留原始 2.x data directory、SQLite recovery 和两份 manifest,等待后续转换与部署 lineage 完成。