如何在 运行 'terraform plan' 时显示 warning/error?

How to show a warning/error when running 'terraform plan'?

我正在构建一个 Terraform plugin/provider (link),它将帮助用户管理他们的云资源,例如云平台上的云实例、Kubernetes 集群等。

目前云平台不支持创建后Kubernetes节点大小变更。如果用户想要更改节点大小,他们需要使用新的节点大小创建一个新的节点池。

所以我在我的插件代码中添加了这个块,特别是在 Kubernetes 集群更新方法中(link):

if d.HasChange("target_nodes_size") {
    errMsg := []string{
        "[ERR] Unable to update 'target_nodes_size' after creation.",
        "Please create a new node pool with the new node size.",
    }
    return fmt.Errorf(strings.Join(errMsg, " "))
}

问题是,只有在我执行运行 terraform apply 命令时才会出现错误。我想要的是,我希望它在用户 运行s terraform plan 命令时显示,以便他们及早知道如果不创建新节点池就无法更改节点大小。

如何使 target_nodes_size 字段不可变并在 terraform plan 输出的早期显示错误?

这里正确的做法是告诉 Terraform 对资源的更改不能就地完成,而是需要重新创建资源(通常先销毁再创建,但您可以使用 lifecycle.create_before_destroy ).

创建提供程序时,您可以使用 ForceNew parameter on a schema's attribute.

例如,aws_launch_configuration 资源在 AWS 的 API 端被认为是不可变的,因此 every non computed attribute in the schema is marked with ForceNew: true.:

func resourceAwsLaunchConfiguration() *schema.Resource {
    return &schema.Resource{
        Create: resourceAwsLaunchConfigurationCreate,
        Read:   resourceAwsLaunchConfigurationRead,
        Delete: resourceAwsLaunchConfigurationDelete,
        Importer: &schema.ResourceImporter{
            State: schema.ImportStatePassthrough,
        },

        Schema: map[string]*schema.Schema{
            "arn": {
                Type:     schema.TypeString,
                Computed: true,
            },

            "name": {
                Type:          schema.TypeString,
                Optional:      true,
                Computed:      true,
                ForceNew:      true,
                ConflictsWith: []string{"name_prefix"},
                ValidateFunc:  validation.StringLenBetween(1, 255),
            },
// ...

如果您随后尝试修改任何 ForceNew: true 字段,那么 Terraform 的计划将显示它需要替换资源,并且在应用时只要用户接受该计划,它就会自动执行此操作。

对于更复杂的示例,aws_elasticsearch_domain 资源允许就地版本更改,但仅限于特定版本升级路径(因此您不能例如从 5.4 转到 7.8直接而不是必须转到 5.4 -> 5.6 -> 6.8 -> 7.8。这是通过在架构上使用 CustomizeDiff attribute 来完成的,它允许您在计划时使用逻辑来给出不同于通常从静态配置中找到的结果。

CustomizeDiff for the aws_elasticsearch_domain elasticsearch_version attribute 看起来像这样:

func resourceAwsElasticSearchDomain() *schema.Resource {
    return &schema.Resource{
        Create: resourceAwsElasticSearchDomainCreate,
        Read:   resourceAwsElasticSearchDomainRead,
        Update: resourceAwsElasticSearchDomainUpdate,
        Delete: resourceAwsElasticSearchDomainDelete,
        Importer: &schema.ResourceImporter{
            State: resourceAwsElasticSearchDomainImport,
        },

        Timeouts: &schema.ResourceTimeout{
            Update: schema.DefaultTimeout(60 * time.Minute),
        },

        CustomizeDiff: customdiff.Sequence(
            customdiff.ForceNewIf("elasticsearch_version", func(_ context.Context, d *schema.ResourceDiff, meta interface{}) bool {
                newVersion := d.Get("elasticsearch_version").(string)
                domainName := d.Get("domain_name").(string)

                conn := meta.(*AWSClient).esconn
                resp, err := conn.GetCompatibleElasticsearchVersions(&elasticsearch.GetCompatibleElasticsearchVersionsInput{
                    DomainName: aws.String(domainName),
                })
                if err != nil {
                    log.Printf("[ERROR] Failed to get compatible ElasticSearch versions %s", domainName)
                    return false
                }
                if len(resp.CompatibleElasticsearchVersions) != 1 {
                    return true
                }
                for _, targetVersion := range resp.CompatibleElasticsearchVersions[0].TargetVersions {
                    if aws.StringValue(targetVersion) == newVersion {
                        return false
                    }
                }
                return true
            }),
            SetTagsDiff,
        ),

尝试在已接受的升级路径(例如 7.4 -> 7.8)上升级 aws_elasticsearch_domainelasticsearch_version 将表明它是计划中的就地升级并在申请时应用时间。另一方面,如果您尝试通过不允许直接升级的路径(例如 5.4 -> 7.8 直接升级),那么 Terraform 的计划将显示它需要销毁现有的 Elasticsearch 域并创建一个新域。