mcpField
P
provider-framework-migration

provider-framework-migration

github.com/hashicorp开发909948 次安装今天更新
代码仓库

关于 provider-framework-migration

介绍将 Terraform Provider 资源从 Plugin SDKv2 逐步迁移到 Plugin Framework 的技能,重点是并行服务两种实现,并保持 Schema、状态和行为兼容。

主要功能

  1. 01SDKv2 与 Framework mux
  2. 02按资源迁移步骤
  3. 03Schema 映射指南
  4. 04处理 null 与零值差异
  5. 05状态兼容性测试

适用场景

  1. 01逐步迁移现有资源
  2. 02保持迁移期间的配置兼容
  3. 03验证旧版本写入的状态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/hashicorp/agent-skills --skill provider-framework-migration

安装后在 Claude Code 中输入/provider-framework-migration确认已加载。

附带文件references/schema-mapping.md

详细介绍

从 Plugin SDKv2 迁移到 Plugin Framework

对于全新资源和数据源,必须使用 Plugin Framework;SDKv2 仅用于维护。迁移按资源逐个进行,并采取渐进方式:通过 mux 的服务可让 SDKv2 和 Framework 实现并行运行,因此无需一次性全面重写。本指南涵盖 mux 设置、逐个资源的迁移流程,以及可能把机械式转换变成静默破坏性变更的行为陷阱。

参考资料(按需加载):

  • references/schema-mapping.md — SDKv2 → Framework 的完整转换表,附带代码示例

官方指南:Framework 迁移。

判断是否需要迁移

迁移存在实际风险,但对用户可见的收益很少,因此应先进行评估:

  • 除非有迫切需求,否则不要迁移复杂或使用广泛的资源(例如,只有 Framework 才支持的功能,或 SDKv2 无法修复的错误)。两个 SDK 在行为上有所不同,尤其是 null 值与零值的处理方式;这些差异会对现有用户造成破坏性变更。这是 terraform-provider-aws 等大型 Provider 一贯遵循的策略。
  • 简单资源可以安全迁移:扁平 Schema,不使用 DiffSuppressFunc、CustomizeDiff、StateFunc,也没有复杂的嵌套块。
  • 新功能从不要求迁移旧代码——可以使用 mux,并在 Framework 中与旧资源并行编写新资源。

要判断 Provider 当前处于哪种模式,请检查 go.mod:存在 terraform-plugin-mux 表示它已同时提供两种实现;只有 terraform-plugin-sdk/v2 表示仅使用 SDKv2(mux 设置是第一步);只有 terraform-plugin-framework 则表示迁移已完成。

第 1 步:为 Provider 设置 mux

在 main.go 中组合两个插件服务器。要提供协议版本 6,必须使用 tf5to6server 升级 SDKv2 服务器(协议 6 要求 Terraform CLI >= 1.0;如果必须支持 0.12+,则改用 tf6to5server/tf5muxserver,通过协议 5 设置 mux——但 Framework Provider 无法使用嵌套属性等仅限协议 6 的功能):

package main

import (
    "context"
    "flag"
    "log"

    "github.com/hashicorp/terraform-plugin-framework/providerserver"
    "github.com/hashicorp/terraform-plugin-go/tfprotov6"
    "github.com/hashicorp/terraform-plugin-go/tfprotov6/tf6server"
    "github.com/hashicorp/terraform-plugin-mux/tf5to6server"
    "github.com/hashicorp/terraform-plugin-mux/tf6muxserver"

    "example.org/terraform-provider-examplecloud/internal/provider"
    sdkprovider "example.org/terraform-provider-examplecloud/internal/sdkprovider"
)

func main() {
    var debug bool
    flag.BoolVar(&debug, "debug", false, "run with support for debuggers")
    flag.Parse()

    ctx := context.Background()

    upgradedSDKServer, err := tf5to6server.UpgradeServer(
        ctx,
        sdkprovider.Provider().GRPCProvider,
    )
    if err != nil {
        log.Fatal(err)
    }

    providers := []func() tfprotov6.ProviderServer{
        providerserver.NewProtocol6(provider.New(version)()),
        func() tfprotov6.ProviderServer { return upgradedSDKServer },
    }

    muxServer, err := tf6muxserver.NewMuxServer(ctx, providers...)
    if err != nil {
        log.Fatal(err)
    }

    var serveOpts []tf6server.ServeOpt
    if debug {
        serveOpts = append(serveOpts, tf6server.WithManagedDebug())
    }

    err = tf6server.Serve("registry.terraform.io/example/examplecloud",
        muxServer.ProviderServer, serveOpts...)
    if err != nil {
        log.Fatal(err)
    }
}

实际操作中容易忽视的 mux 要求:

  • 两个插件的 Provider Schema 必须完全一致——Provider 级别的属性、类型和描述都必须相同。Provider 配置应只有一个真实来源,另一份保持镜像同步。
  • 每个资源和数据源只能存在于两个插件中的一个。迁移的最后一步是删除 SDKv2 注册项。
  • 如果使用协议 6 发布到 Registry,请在 terraform-registry-manifest.json 中设置 "metadata": {"protocol_versions": ["6.0"]}。

第 2 步:修改前建立基准

迁移后的资源对用户而言必须毫无差异。使用迁移前已有的测试来证明这一点:

  1. 确保该资源有通过的验收测试覆盖:_basic 测试带有导入步骤(ImportStateVerify: true)、_disappears 测试,以及针对每个属性的更新测试。如果覆盖不足,请先针对 SDKv2 实现补齐——这些测试是迁移的验收标准,迁移后必须保持原样并通过。
  2. 记录测试未覆盖的行为:属性默认值、省略可选属性时的处理方式(null 与 ""/0/false 的区别即将变得重要),以及任何 DiffSuppressFunc/StateFunc 归一化处理。

第 3 步:迁移资源

使用 references/schema-mapping.md 中的映射表转换架构和 CRUD。以下规则可避免破坏性变更:

  • 块必须保留为块。 在用户配置中以 block { ... } 语法编写的 SDKv2 Elem: &schema.Resource{...},必须转换为 Framework 块(schema.ListNestedBlock/SetNestedBlock)——将其转换为嵌套属性会改变用户必须编写的 HCL 语法,属于破坏性变更。嵌套属性仅适用于新架构。
  • 空值不等于零值。 SDKv2 的 d.Get("name") 在未设置时会返回 "";Framework 模型提供的 types.String 能区分 null、unknown 和 ""。对于旧代码中所有检查 == "" 或依赖 GetOk 的地方,都要明确决定 null 的含义,并确保发送给 API 的内容与 SDKv2 相同(通常是:null 时省略该字段)。
  • 保留 id 属性。 新增的 Framework 资源可以省略冗余的 id,但迁移后的资源必须保留完全相同的架构——删除或重命名属性会破坏现有状态和配置。
  • 状态必须能够往返。 Framework 会读取 SDKv2 写入的状态。如果每个属性都保留其名称和类型,就不需要状态升级。如果旧架构存储的值被新类型包以不同方式规范化,就需要 StateUpgrader——这表明该资源可能属于不应迁移的类别。

第 4 步:迁移注册

在 Framework provider 的 Resources() 中注册该资源,并在同一提交中将其从 SDKv2 provider 的 ResourcesMap 中删除——重复注册会导致 mux 报错。

第 5 步:验证

  1. 现有验收测试应当无需修改即可通过——尤其是 ImportStateVerify,它会对比导入的状态与存储的状态,能够发现大多数 null 与零值之间的回归问题。
  2. 添加状态兼容性步骤:使用最后一个已发布的(SDKv2)provider 版本应用配置,然后使用迁移后的构建版本运行 plan——计划必须为空。在 terraform-plugin-testing 中,这是一个两步测试:先通过 ExternalProviders 使用旧版本,然后通过 ProtoV6ProviderFactories 和 ConfigPlanChecks 断言计划为空。如果有 provider-test-patterns 技能,可参考其中的模式。
  3. 针对真实的迁移前状态文件运行 terraform plan,确认没有差异。

检查清单

  • 资源足够简单,可以迁移(没有复杂的差异自定义),或确有迁移需求
  • Mux 同时提供两个插件;两者的 provider 级架构相同
  • 验收测试在迁移前已存在,且迁移后无需修改即可通过
  • 块仍为块;属性名称和类型未变;保留了 id
  • 保留了 null/省略语义(API 收到的内容与 SDKv2 相同)
  • 在同一变更中移除了 SDKv2 注册
  • 已针对上一个版本写入的状态验证计划为空
  • 如果仓库会跟踪发行说明,已添加变更日志条目

相关技能

迁移代码中的 Framework CRUD、查找器和等待器模式,可使用 provider-resources 技能(如果有);回归测试和版本升级测试模式,可使用 provider-test-patterns 技能。

评论

支持 Markdown