研发协作
在 Terraform Provider 中把资源从…
当需要将 Terraform provider 的 SDKv2 资源/数据源迁移到 Plugin Framework,或搭建 mux 双插件服务器、排查迁移后 plan diff 与 state 错误时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
在 Terraform Provider通用
解决思路
- 先做迁移决策:不要在没有明确驱动需求(Framework 独有特性、SDKv2 无法修复的 bug)时迁移复杂或高频使用的资源;简单资源(扁平 schema、无 DiffSuppressFunc、无 CustomizeDiff、无 StateFunc、无复杂嵌套块)可安全迁移;新能力无需迁移旧代码,直接在 Framework 中与旧资源并存编写。
- 通过 go.mod 判断 provider 当前模式:含 terraform-plugin-mux 表示已同时服务两种插件;仅含 terraform-plugin-sdk/v2 表示纯 SDKv2(mux 搭建是第一步);仅含 terraform-plugin-framework 表示迁移已完成。
- 在 main.go 中 mux 两个插件服务器:用 tf5to6server.UpgradeServer 将 SDKv2 服务器升级到协议 6,再用 tf6muxserver.NewMuxServer 组合 providerserver.NewProtocol6 的 Framework 服务器与升级后的 SDKv2 服务器,最后用 tf6server.Serve 提供服务;协议 6 需要 Terraform CLI >= 1.0,若需支持 0.12+ 则改用 tf6to5server/tf5muxserver 在协议 5 上 mux(但 Framework provider 将无法使用嵌套属性等协议 6 独有特性)。
- 满足 mux 的硬性要求:两个插件的 provider schema 必须完全一致(相同的 provider 级属性、类型、描述),保持 provider 配置的单一事实来源并镜像;每个资源/数据源只能存在于两个插件之一,迁移的最后一步是删除 SDKv2 注册;若以协议 6 发布到 Registry,在 terraform-registry-manifest.json 中设置 "metadata": {"protocol_versions": ["6.0"]}。
- 迁移前先建立基线:确保资源有通过的验收测试覆盖(带 ImportStateVerify: true 导入步骤的 _basic、_disappears、逐属性更新测试),若缺失则先针对 SDKv2 实现补齐,这些测试是迁移的验收标准且迁移后必须原样通过;同时记录测试未覆盖的行为,如属性默认值、可选属性省略时的行为(null 与 ""/0/false 的区别即将变得重要)、以及任何 DiffSuppressFunc/StateFunc 的归一化。
- 按 references/schema-mapping.md 的映射表翻译 schema 与 CRUD,并遵守防破坏规则:块保持为块——SDKv2 中 Elem: &schema.Resource{...} 且用户配置写 block { ... } 语法的必须转为 Framework 的 Block(schema.ListNestedBlock/SetNestedBlock),改成嵌套 attribute 会改变用户 HCL 语法属破坏性变更,嵌套 attribute 仅用于新 schema。
- 显式处理 null 与零值差异:SDKv2 的 d.Get("name") 对未设置返回 "",Framework 模型的 types.String 区分 null、unknown 与 "";所有旧代码检查 == "" 或依赖 GetOk 的地方都要明确 null 的含义,并确保发给 API 的内容与 SDKv2 一致(通常是 null 时省略该字段)。
- 保留 id 属性:全新的 Framework 资源可以省略冗余的 id,但迁移的资源必须保持完全相同的 schema,删除或重命名属性会破坏现有 state 与配置。
- 确保 state 可往返:Framework 读取 SDKv2 写入的 state,若每个属性保持名称与类型则无需 state 升级;若旧 schema 存储的值被新类型包以不同方式归一化,则需要 StateUpgrader——这应视为该资源可能属于不宜迁移类别的信号。
- 移动注册:在同一次提交中把资源注册到 Framework provider 的 Resources() 并从 SDKv2 provider 的 ResourcesMap 中删除,因为 mux 遇到重复会报错。
- 验证:迁移前已有的验收测试必须不做修改地通过,尤其是 ImportStateVerify(它对比导入 state 与存储 state,能捕获大多数 null-vs-zero 回归);增加 state 兼容性步骤——用最后发布的 SDKv2 provider 版本 apply 配置,再用迁移后的构建 plan,plan 必须为空(在 terraform-plugin-testing 中用 ExternalProviders 跑旧版本、再用 ProtoV6ProviderFactories 配合 ConfigPlanChecks 断言空 plan 的两步测试);对真实的迁移前 state 文件运行 terraform plan 应无 diff。
- 完成检查清单:资源足够简单或有明确驱动需求;mux 同时服务两个插件且 provider 级 schema 一致;验收测试迁移前已存在且迁移后原样通过;块仍是块、属性名与类型未变、id 保留;null/省略语义保持一致(API 收到与 SDKv2 相同的内容);SDKv2 注册在同一次变更中删除;对上一版本写入的 state 验证空 plan;若仓库跟踪发布说明则添加 changelog 条目。
适用边界 · 注意事项
- 不要在没有明确驱动需求时迁移复杂或高频使用的资源——两个 SDK 在行为上(尤其是 null 与零值)存在差异,会以破坏性变更的形式暴露给现有用户,大型 provider(如 terraform-provider-aws)的既定策略就是如此。
- 不要将用户以 block { ... } 语法编写的 SDKv2 块改为嵌套 attribute,这会改变用户必须编写的 HCL 语法,属于破坏性变更。
- 不要删除或重命名迁移资源的属性(包括冗余的 id),这会破坏现有 state 与配置;若需要 StateUpgrader 才能保持 state 往返,应重新考虑是否迁移该资源。