跳转到主要内容

编辑声明,保留文档:无损 Pigsty Inventory

为什么 pig inventory 将 YAML 语义与源字节分离,让局部编辑能够保留注释、顺序、锚点与格式。

决策日期: 2026-07-18
状态: 已实现并随 pig v1.6.0 发布。 下述验收修订已实现(Implemented),纳入 v1.10.0 候选版本,尚未发布。
当前参考: pig inventory
范围: 静态 Pigsty Inventory 的查看、局部编辑、校验、比较与安全写入。

决策

PIG 同时把 pigsty.yml 视为配置声明和人工维护的源文档。语义解析器负责判断 Inventory 的含义, 原始字节则决定它的书写方式。局部编辑只替换指定的源范围,并在原子写入前重新解析完整候选文件。

这样可以避免 YAML 工具常见的失败模式:逻辑上正确的一次编辑,却悄悄重写整份文件的注释、键顺序、 引号、锚点、块标量或换行风格。

背景

Pigsty Inventory 是长期存在的运维资产,包含拓扑、调优、凭据、注释、示例、锚点和本地有意义的顺序。 普通的“解析—修改—序列化”流程可能生成有效但无法审阅的差异,并改变运维人员没有选择的结构。

另一方面,没有语义校验的纯文本编辑又可能把无效或自相矛盾的配置写入磁盘。 设计必须同时获得源文件格式与整份配置的正确性。

考虑过的方案

  • 使用一个 YAML 序列化器完整往返。 在真实 Pigsty 语料上,没有选定的序列化器能够逐字节保留完整源契约。
  • 用正则表达式处理 YAML。 引号、注释、别名、块标量和嵌套集合让纯文本语义判断不安全。
  • 只编辑规范化生成副本。 活跃 Inventory 属于运维人员,仍会与生成表示发生漂移。
  • 允许替换所有节点类型。 锚点、别名、标签与块标量需要比普通映射和标量更严格的处理。

契约

  • 拒绝重复键与多文档 YAML;
  • selector 只定位一个无歧义的声明片段;
  • 语义解码与源范围发现是两项独立职责;
  • 编辑从确定的源文件版本开始,文件被并发修改时失败;
  • 编辑片段只进行插入上下文所必需的规范化;
  • 提交前重新解析并校验完整候选文件;
  • 写入使用同目录临时文件、sync、rename 与目录 sync;
  • 拒绝符号链接与不安全路径变化;
  • 成功编辑后移除组和其他用户的权限,保留所有者原有的读写权限;
  • 除非命令明确属于原始文本输出接口,诊断、diff、计划与结构化结果都不输出声明值。

2026-10-16 的验收修订保留 pg_users[].roles 的角色名字符串与结构化授权对象兼容。 编辑、序列化和 CMDB 导出必须保留 YAML 1.1 字符串语义,包括 "1:30";"010" 是回归对照, 不代表此前必然存在损坏。 2026-10-17 的修订进一步要求:作为字符串传入的日期与时间戳,经 PyYAML 加载后仍为字符串。 已有时间戳值作为不透明标量保留原始内容,不增加日期运算,也不归一化整个文档。 验收使用独立 YAML 解析器检查写出后的类型,不能仅依赖 PIG 自身的往返解析。

sty deploy 与 do 使用配置层已经解析的 Inventory 路径。 参数和环境中的相对路径以调用者 cwd 为基准,带引号的 ~/ 使用用户主目录,默认来源在执行时 也保持显式。来源缺失不得回退。静态检查仍不执行动态来源;执行边界继续由 Ansible 负责。

影响

编辑器比普通 YAML marshal 更复杂;当无法证明源文件保真时,一些语法有效的片段也会被有意拒绝。 换来的结果是可审阅差异:未选择部分逐字节稳定,无效 YAML 无法落盘,并发修改不会被静默覆盖。

Inventory 可以包含自定义凭据字段,无法仅凭字段名可靠脱敏。 因此,show 输出所选声明的原始文本;元数据与校验命令则省略声明值。

验证与演进

v1.10.0 已提交修订的固定源码与回归测试证据包括: 来源解析与角色授权、 日期形式字符串保留、 独立 YAML 消费端验收。这些提交不构成版本发布证据。

根级 Inventory 契约与既有 CMDB 边界建立于 ba6e678,实现落地于 ea43858。测试覆盖真实 Pigsty 配置语料、逐字节往返、 局部替换、受保护 YAML 形式、并发变更拒绝、原子写失败、selector、校验和无敏感值诊断。

后续重构移除了旧选项并分离校验阶段,但没有改变源文件保真模型。

当前状态

静态 Inventory 仍是主要配置来源。selector、校验 profile、结构化输出与明确标记为实验性的 CMDB 桥接, 均以当前 pig inventory 参考文档为准。