3、CRD版本迁移测试基础:测试目标、测试范围、测试环境搭建、测试数据准备
CRD版本迁移是Operator演进中最容易引发生产故障的环节之一。测试的目标不是“验证迁移工具能用”,而是证明迁移前后CR实例的语义等价性、API兼容性以及Operator的平稳过渡能力。本节从测试目标、范围、环境搭建和数据准备四个维度建立可复用的测试基线。
3.1 测试目标
CRD版本迁移测试的核心目标可归纳为以下四个层次:
| 层次 | 目标描述 | 验证手段 |
|---|---|---|
| L1 数据完整性 | 迁移后CR实例的所有字段值、嵌套结构、默认值、枚举值与原版本完全一致 | 字段级diff、JSON Patch比对 |
| L2 语义等价性 | 同一CR实例在旧版和新版API下,Operator的Reconcile结果(状态、子资源、事件)一致 | Reconcile输出比对、状态快照对比 |
| L3 存储版本切换 | etcd中存储的版本从旧版切换到新版后,读写路径无错误,无数据丢失 | etcd数据导出校验、storage版本标记检查 |
| L4 回滚安全 | 迁移后若发现问题,能通过CRD回滚恢复旧版本,且旧版Operator能正常读取新版存储的数据 | 回滚后Reconcile正常、无panic |
测试优先级建议: L1和L2是必须通过的硬性条件,L3和L4可根据生产环境风险等级选择性执行。对于有状态CRD(如数据库集群、存储卷),L4回滚测试不可跳过。
3.2 测试范围
CRD版本迁移测试的范围应覆盖以下五个维度,避免只测“happy path”:
- API版本组合: 覆盖v1→v2、v1→v1beta1→v2、v1→v2→v1回滚等典型迁移路径。
- 字段变更类型: 新增字段、删除字段、重命名字段、字段类型变更(string→int)、嵌套结构变更、枚举值增减。
- CR实例状态: 正常实例、处于异常/错误状态的实例、包含自定义状态子资源的实例、包含Finalizer的实例。
- 并发场景: 迁移过程中有新的CR创建/更新/删除操作,验证并发安全。
- 集群规模: 单实例、多实例(100+)、跨namespace分布、大规模CRD(字段数超过50)。
常见遗漏范围: 很多团队只测试了“空集群迁移”和“简单CR迁移”,忽略了包含Finalizer、OwnerReference、自定义状态子资源的CR实例,这些场景最容易在迁移后出现Reconcile死循环或数据不一致。
3.3 测试环境搭建
测试环境需要模拟真实集群的版本演进路径,同时支持快速重建和状态快照。推荐使用Kind或K3s搭建轻量级集群,配合etcdctl直接操作底层存储。
3.3.1 环境组件清单
| 组件 | 版本要求 | 用途 |
|---|---|---|
| Kubernetes集群 | v1.22+(支持CRD v1) | 运行CRD和Operator |
| etcdctl | v3.5+ | 直接查看etcd中CRD存储版本 |
| kubectl | v1.24+ | 管理CRD和CR实例 |
| yq / jq | 最新稳定版 | YAML/JSON字段级比对 |
| Operator SDK | v1.25+ | 构建和部署多版本Operator |
3.3.2 环境搭建步骤
- 创建Kind集群并开启CRD v1beta1兼容:
kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane extraMounts: - hostPath: /tmp/etcd-data containerPath: /var/lib/etcd kubeadmConfigPatches: - | apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration metadata: name: config etcd: local: dataDir: /var/lib/etcd - 部署旧版CRD和Operator:
kubectl apply -f config/crd/bases/example.io_myresources_v1.yaml kubectl apply -f config/manager/operator-v1.yaml - 创建测试CR实例并等待Reconcile完成:
kubectl apply -f testdata/cr-sample-v1.yaml kubectl wait --for=condition=Ready myresources/sample --timeout=60s - 保存迁移前状态快照:
kubectl get myresources sample -o yaml > snapshot/pre-migration.yaml etcdctl get /registry/example.io/myresources/default/sample --print-value-only | jq . > snapshot/pre-migration-etcd.json
环境搭建避坑: 如果使用Kind,务必在集群创建时挂载etcd数据目录到宿主机,否则集群删除后无法保留etcd数据用于回滚测试。另外,Kind默认的etcd版本可能不支持某些存储版本标记,建议使用
kindest/node:v1.27.3 及以上镜像。
3.4 测试数据准备
测试数据是版本迁移测试的“弹药”。数据准备需要覆盖CRD版本演进中可能出现的所有字段变化模式。建议按以下分类构造测试数据集:
3.4.1 数据分类与构造策略
| 数据类别 | 构造策略 | 预期覆盖的迁移场景 |
|---|---|---|
| 基础数据 | 每个字段都使用合法值,嵌套结构完整 | 正常迁移路径,验证字段映射正确性 |
| 边界数据 | 字段使用最小值、最大值、空字符串、null、空数组 | 验证默认值填充、空值处理、类型转换边界 |
| 异常数据 | 包含旧版已废弃字段、非法枚举值、类型不匹配的值 | 验证迁移转换器的错误处理与降级逻辑 |
| 状态数据 | CR实例包含自定义status字段、conditions、observedGeneration | 验证状态子资源的版本兼容性 |
| 关联数据 | CR实例包含Finalizer、OwnerReference、跨namespace引用 | 验证垃圾回收和级联删除在迁移后是否正常 |
3.4.2 数据准备脚本示例
以下脚本使用yq批量生成不同类别的测试CR实例:
#!/bin/bash
# generate-test-data.sh
# 生成5类测试CR实例,每类3个,共15个实例
BASE_DIR="testdata/cr-samples"
mkdir -p $BASE_DIR/{basic,boundary,abnormal,status,reference}
# 1. 基础数据
for i in 1 2 3; do
yq eval "
.metadata.name = \"basic-$i\" |
.spec.replicas = $((i * 2)) |
.spec.image = \"nginx:1.2$i\" |
.spec.config.timeout = $((30 + i * 10))
" templates/cr-template.yaml > $BASE_DIR/basic/cr-basic-$i.yaml
done
# 2. 边界数据
yq eval '.spec.replicas = 0 | .spec.image = "" | .spec.config = {}' \
templates/cr-template.yaml > $BASE_DIR/boundary/cr-boundary-empty.yaml
yq eval '.spec.replicas = 9999 | .spec.config.timeout = 0' \
templates/cr-template.yaml > $BASE_DIR/boundary/cr-boundary-max.yaml
# 3. 异常数据
yq eval '.spec.image = "invalid:tag:extra" | .spec.config.timeout = "not-a-number"' \
templates/cr-template.yaml > $BASE_DIR/abnormal/cr-abnormal-type.yaml
# 4. 状态数据
yq eval '.status.phase = "Running" | .status.conditions[0].type = "Ready" | .status.conditions[0].status = "True"' \
templates/cr-template.yaml > $BASE_DIR/status/cr-status-ready.yaml
# 5. 关联数据
yq eval '.metadata.finalizers = ["example.io/cleanup"] | .metadata.ownerReferences[0].apiVersion = "v1" | .metadata.ownerReferences[0].kind = "Namespace" | .metadata.ownerReferences[0].name = "default" | .metadata.ownerReferences[0].uid = "12345678-1234-1234-1234-123456789abc"' \
templates/cr-template.yaml > $BASE_DIR/reference/cr-with-finalizer.yaml
echo "测试数据生成完成,共15个CR实例"
3.4.3 数据验证清单
在迁移测试执行前,需要对测试数据做一次预验证,确保数据本身是有效的:
- 每个CR实例能被旧版CRD正常创建(
kubectl apply无报错) - 每个CR实例能被旧版Operator正常Reconcile(状态字段被填充)
- 每个CR实例的etcd存储版本标记与CRD的storage版本一致
- 异常数据类CR实例在创建时可能被Webhook拒绝,需确认Webhook是否开启
数据准备自动化建议: 将测试数据生成脚本集成到CI流水线中,每次CRD版本变更时自动生成新的测试数据集。同时保留旧版数据集,用于回归测试。推荐使用
gotest + testdata 目录结构管理测试数据。