ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

KubeVirt API 序列化兼容性测试指南:用 JSON/YAML fixture 守护 `kubevirt.io/v1` 的向后兼容

KubeVirt API 序列化兼容性测试指南:用 JSON/YAML fixture 守护 `kubevirt.io/v1` 的向后兼容 云原生【免费下载链接】kubevirtKubernetes Virtualization API and runtime in order to define and manage virtual machines.项目地址https://gitcode.com/gh_mirrors/ku/kubevirt点击查看免费下载KubeVirt 在staging/src/kubevirt.io/api/apitesting下维护了一套 API 序列化兼容性测试API serialization compatibility tests通过固化每个发布版本的序列化对象快照确保 API 演进过程中旧客户端不会被破坏。本文以 apitesting/testdata/README.md 为主体结合 roundtrip 包 的源码实现完整讲解这套测试的目录结构、运行/重新生成流程、失败输出解读以及 API 修改时的红线清单帮助开发者和评审者在日常迭代中正确维护兼容性数据。这套测试解决什么问题KubeVirt 对外暴露的 API 属于长期契约一个在v1.0.0时代编写并持久化的 VirtualMachine 对象必须在v1.9.0的 API server 上仍然能被解码、被正确往返序列化round-trip。任何字段删除、类型变更、字段改名都会在集群升级后让旧数据读不出来或读出不同的东西。为此staging/src/kubevirt.io/api/apitesting/testdata/目录树存放了大量以 JSON 和 YAML 格式固化的序列化 API 对象。测试运行时会用当前代码对这些文件执行三类校验可解码历史版本的序列化数据必须能被当前版本的 Go 结构体无错解码字节级往返一致解码后再编码必须与原始序列化字节完全一致或与随附的after_roundtrip期望文件一致语义等价同一对象从 JSON 和 YAML 两种格式解码后必须得到语义相同的对象。这套机制直接保护了 KubeVirt 三大核心资源类型的兼容性契约。测试数据目录结构与命名约定当前覆盖的是 group-version 为kubevirt.io/v1的三个 API 类型更多类型可在未来加入VirtualMachineInstance运行中的虚拟机实例VirtualMachine虚拟机定义KubeVirtKubeVirt 部署自身的 CRD目录布局如下apitesting/testdata/ ├── HEAD/ # 当前主干版本main branch │ ├── kubevirt.io.v1.KubeVirt.json │ ├── kubevirt.io.v1.KubeVirt.yaml │ ├── kubevirt.io.v1.VirtualMachine.json │ ├── kubevirt.io.v1.VirtualMachine.yaml │ ├── kubevirt.io.v1.VirtualMachineInstance.json │ └── kubevirt.io.v1.VirtualMachineInstance.yaml ├── release-1.8/ # 历史发布版本 │ ├── kubevirt.io.v1.VirtualMachine.json / .yaml │ ├── kubevirt.io.v1.VirtualMachine.after_roundtrip.json / .yaml │ ├── kubevirt.io.v1.VirtualMachineInstance.json / .yaml │ ├── kubevirt.io.v1.VirtualMachineInstance.after_roundtrip.json / .yaml │ └── kubevirt.io.v1.KubeVirt.json / .yaml └── release-1.9/ # 另一个历史发布版本结构同上文件命名遵循统一格式group.version.kind.[json|yaml]。例如kubevirt.io.v1.VirtualMachineInstance.json表示 groupkubevirt.io、versionv1、kindVirtualMachineInstance的 JSON 快照。该命名规则由源码中的makeName(gvk)函数生成见 compatibility.gogroup 为空时使用core作为前缀否则拼接为group.version.kind。这些 fixture 并不是手工维护的。测试运行时会通过反射机制确定性填充一个类型的全部字段详见下文fixture 是如何生成的一节把填充结果序列化后与磁盘文件逐字节比对——这正是这些文件看起来字段取值都是xxxValue、时间戳都是固定年份的原因例如 HEAD 目录下的 VirtualMachineInstance.json 中name、generateName、namespace等字段均为xxxValue形式的确定性占位值。DEVELOPERS GUIDE开发者的日常工作流每个 release 的数据固化流程每当 KubeVirt 发布新版本时需要把当前版本文件复制到对应的 release 目录作为历史兼容基线。以v1.2.0为例注意示例使用release-1.2目录实际仓库当前包含release-1.8、release-1.9等目录export VERSIONrelease-1.2 git checkout ${VERSION} cp -fr staging/src/kubevirt.io/api/apitesting/testdata/{HEAD,${VERSION}} git checkout -b add-${VERSION}-api-testdata master git add . git commit -m Add ${VERSION} API testdata要点HEAD目录始终存放由当前提交生成的序列化对象历史版本目录则在每次发版时从HEAD快照而来。这样新旧版本之间的差异就沉淀为静态文件供后续回归比对。只跑当前版本HEAD的测试go test kubevirt.io/api/apitesting -run //HEAD其中//HEAD是 Go 子测试路径过滤语法对应Run(t)中为每个 GVK 生成的HEAD子测试见 compatibility.go。该用例会验证磁盘上的 JSON/YAML 与内存对象序列化结果逐字节相等磁盘文件能被无错解码解码结果与填充出的期望对象语义相等使用apiequality.Semantic.DeepEqual比较。同一 group/version/kind 的所有格式都必须能解码为相同对象且往返序列化后字节完全一致。API 变更后重新生成 fixture新增字段、废弃字段或新增 API 类型都会改变序列化结果此时 fixture 文件需要更新。重新生成的方式是带上环境变量重跑测试UPDATE_COMPATIBILITY_FIXTURE_DATAtrue go test kubevirt.io/api/apitesting -run //HEAD实现上runCurrentVersionTest在比对失败时会检查UPDATE_COMPATIBILITY_FIXTURE_DATA环境变量若为true则把当前期望的 JSON/YAML 写回磁盘并提示verify, commit, and rerun tests见 compatibility.go。因此正确的流程是修改 API 结构体如staging/src/kubevirt.io/api/core/v1/types.go用UPDATE_COMPATIBILITY_FIXTURE_DATAtrue重新生成 HEAD fixtures人工审查 diff确认是预期变更提交 fixture再不带环境变量跑一遍确保测试通过。在 Bazel 构建体系中fixture 通过data glob([testdata/**])随测试包一起打包且测试开启race on见 apitesting/BUILD.bazel。跑某个历史版本的测试go test kubevirt.io/api/apitesting -run //release-1.1例如当前仓库中可以这样验证v1.9.0的快照go test kubevirt.io/api/apitesting -run //release-1.9只测某个特定 group/version/kind如果只想关注某个具体类型示例中为apps/v1的Deployment可以按子测试路径过滤go test kubevirt.io/api/apitesting -run /apps.v1.Deployment/对应到 KubeVirt例如go test kubevirt.io/api/apitesting -run /kubevirt.io.v1.VirtualMachineInstance/子测试的层级结构是TestCompatibility/group.version.kind/release 目录名排序是确定性的按 group、version、kind 字典序这保证了失败输出的可复现性见 compatibility.go。失败输出解读一次真实的兼容性回归当历史数据在当前代码下解码/往返后与期望不一致测试会打印详细的 diff。下面是文档给出的VirtualMachineInstance在release-0.50上的失败样例节选--- FAIL: TestCompatibility/kubevirt.io.v1.VirtualMachineInstance (0.01s) --- FAIL: TestCompatibility/kubevirt.io.v1.VirtualMachineInstance/release-0.50 (0.01s) compatibility.go:416: json differs compatibility.go:417: ( ... // 215 identical lines readonly: true }, - floppy: { - readonly: true, - tray: trayValue - }, cdrom: { bus: busValue, ... // 678 identical lines tscFrequency: -12 }, - virtualMachineRevisionName: virtualMachineRevisionNameValue virtualMachineRevisionName: virtualMachineRevisionNameValue, runtimeUser: 0 } } ) compatibility.go:422: yaml differs ...逐行解读-表示旧快照有而新编码没有表示新编码新增API 字段spec.domain.devices.disks.floppy被移除对应 KubeVirt 早期的 floppy 磁盘支持移除议题与相关 PRAPI 字段status.runtimeUser被新增对应为 VMI 暴露运行用户信息的相关 PR。这两类变更都会让历史版本的序列化快照与当前代码的序列化结果产生差异——前者是字段消失后者是字段新增。测试正是靠这些 diff 把API 演进对旧数据的影响显式暴露出来。after_roundtrip 文件新字段引入的专项处理有一种特殊情况需要单独机制给既有 API 类型新增非指针字段。这类字段即使未赋值也会序列化出零值导致用当前代码往返历史数据时多出字段。文档中的示例--- FAIL: TestCompatibility/kubevirt.io.v1.VirtualMachine/release-1.0 (0.09s) compatibility.go:411: json differs compatibility.go:412: ( ... // 1113 identical lines status: {} } - ] ], dummyField: null }, status: { ... // 111 identical lines ) compatibility.go:417: yaml differs ...在dummyField加入之前旧版本的序列化表示中根本没有该字段加入后往返结果中出现了dummyField: null。这种差异会导致字节级往返比对失败输出中包含预期之外的字段。解决方式是在历史版本目录中紧挨着序列化数据文件放置一个after_roundtrip期望文件即group.version.kind_after_roundtrip.[json|yaml]注意命名源码中实际拼接格式为group.version.kind.after_roundtrip.json如 release-1.9 目录 所示。该文件记录了用当前代码把历史数据往返一次后的期望输出把新增字段带来的差异显式固化下来。runPreviousVersionTest的实现逻辑是若存在after_roundtrip文件则以它为准比对否则回退到原始快照字节见 compatibility.go。这些after_roundtrip文件同样可以用环境变量生成UPDATE_COMPATIBILITY_FIXTURE_DATAtrue go test kubevirt.io/api/apitesting -run //release-1.8当runPreviousVersionTest发现 JSON/YAML 往返结果与期望不符时若环境变量为true就会写入对应的after_roundtrip文件见 compatibility.go。源码视角fixture 与测试框架是如何工作的测试入口与 Scheme 注册roundtrip_test.go 是唯一入口把kubevirtv1.SchemeBuilder注册进runtime.Scheme然后调用roundtrip.NewCompatibilityTestOptions(scheme).Complete(t).Run(t)。Complete()负责填充默认值testdata目录、HEAD子目录、release-*目录列表、待测 kinds、JSON/YAML serializerRun()负责遍历 GVK 并生成HEAD与各历史版本的子测试最后还有一个unused_fixtures子测试检查 HEAD 目录里是否有未被任何用例引用的多余 fixture 文件并强制清理见 compatibility.go。fixture 的确定性填充construct.go 中的CompatibilityTestObject通过反射递归填充对象的每个字段保证每次调用结果完全一致字符串字段填入json字段名Value如nameValue布尔字段置为true以确保omitempty字段也会被序列化出来整数字段填入从 protobuf tag 提取的编号无 tag 时退化为字段名长度的负数切片填成单元素切片map填成xxxKey: xxxValue的单键条目指针填充为底层类型的零值实例特殊类型由defaultFillFuncs()定制例如metav1.Time用2000i年份生成固定时间戳对应 fixture 里2008-01-01T01:01:01Z这类日期RawExtension填入固定归一化 JSONIntOrString填入字符串形式。这套实现明确标注参考自 k8s 的apimachinery/pkg/api/apitesting未来可能直接引入上游见 construct.go 注释。被测 kinds 的选择Complete()会枚举 Scheme 中全部已知类型并做筛选跳过 internal version、跳过*List类型、跳过CreateOptions/UpdateOptions等核心类型这些在 k8s 中已覆盖见ignoreCoreKinds并显式跳过VirtualMachineInstanceMigration、VirtualMachineInstancePreset、VirtualMachineInstanceReplicaSet三个类型见 compatibility.go。因此最终落盘的 fixture 就是 README 中列出的三类核心资源。REVIEWERS GUIDE评审者的兼容性红线任何对 API Go 结构体的修改都会同步改变对应的 JSON/YAML fixture变更后需重新生成。评审者应借助上述测试判断当前版本的改动是否会破坏升级后的旧客户端。修改 API 时绝对不允许的行为清单这些正是测试所保护的向后兼容契约删除已有字段——旧数据里序列化出来的字段将无法被新结构体表达新增必填字段——旧数据缺少该字段将导致解码失败改变既有字段的类型——旧数据中的值无法正确反序列化重命名字段——序列化键名变化会让旧数据丢失该字段改变字段行为——例如把必填字段改成可选、或反之。新增非指针字段属于被允许但需要配套处理的场景它会让历史数据往返后多出零值字段此时应通过after_roundtrip期望文件显式接纳这一变化详见上一节。评审时看到这类 diff应确认新增字段有合理的默认语义、且已生成 after_roundtrip 文件。Open Issues测试覆盖的盲区README 中记录了一个已知的开放问题考虑升级恰好卡在创建 CRD 之后、管理员被迫中止升级的场景——这种情况是否是被支持的合法场景它该如何被测试这反映了当前测试主要覆盖序列化/反序列化契约而对升级流程中断等运维层面的场景尚无覆盖属于未来可以完善的方向。总结日常维护 Checklist场景命令只验证当前版本序列化契约go test kubevirt.io/api/apitesting -run //HEAD验证某个历史版本仍可兼容go test kubevirt.io/api/apitesting -run //release-1.9只测某类资源go test kubevirt.io/api/apitesting -run /kubevirt.io.v1.VirtualMachine/API 变更后更新 HEAD fixturesUPDATE_COMPATIBILITY_FIXTURE_DATAtrue go test kubevirt.io/api/apitesting -run //HEAD历史版本往返结果变化后更新期望UPDATE_COMPATIBILITY_FIXTURE_DATAtrue go test kubevirt.io/api/apitesting -run //release-1.8发版时固化历史快照cp -fr staging/src/kubevirt.io/api/apitesting/testdata/{HEAD,release-X.Y}并提交一句话原则凡是改变了staging/src/kubevirt.io/api/core/v1中 API 结构体的 PR都必须同步更新staging/src/kubevirt.io/api/apitesting/testdata下的 fixture并让兼容性测试保持绿色——这是 KubeVirt 保证 API 向后兼容、保护存量集群平滑升级的最后一道自动防线。赞分享云原生【免费下载链接】kubevirtKubernetes Virtualization API and runtime in order to define and manage virtual machines.项目地址https://gitcode.com/gh_mirrors/ku/kubevirt点击查看免费下载相关推荐go-swagger Spec Diff 使用指南用 swagger diff 守护 API 向后兼容性go swagger Spec Diff 使用指南用 swagger diff 守护 API 向后兼容性 导读 本文围绕 go swagger 工具链中的 s代码生成开发工具后端API设计go-swagger 规范差异比对指南用 swagger diff 守护 API 向后兼容性go swagger 规范差异比对指南用 swagger diff 守护 API 向后兼容性 导读 本文讲解 go swagger 工具链中的 swagger代码生成开发工具后端API设计WinFsp 向后兼容测试用旧版文件系统二进制守护 API 演进稳定性WinFsp 向后兼容测试用旧版文件系统二进制守护 API 演进稳定性 本文围绕 tst/compat/README.md https://link.gitc存储驱动开发上一篇rPPG-Toolbox 实战指南从零跑通无接触心率估计的完整方案下一篇docling 实战指南3 条命令把扫描版 PDF 变成 AI 可用的数据创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表