amazon-web-services - 由于类似 catch22 的情况,AWS 自定义资源 ROLLBACK_FAILED

标签 amazon-web-services aws-cloudformation aws-cloudformation-custom-resource

我有一个自定义资源,只要发生回滚,它就会陷入类似 catch22 的情况。

下面的代码是我的代码正在执行的操作的简化示例。如果是创建请求,它会创建一个表,如果是删除,它会删除,如果是更新,它会将旧属性与新属性进行比较,并在其中一列具有新值时返回错误(列更新不尚支持)。

出现问题的 ROLLBACK_FAILED 发生在

  1. [已解决] 每当创建请求类型失败时(例如由于 sql 语法错误)。在这种情况下,它将触发 ROLLBACK 阶段的删除请求,但该请求将失败,因为该表还不存在。
  2. 每当更新请求类型由于更新的列值而失败时。在这种情况下,它将触发 ROLLBACK 阶段的新更新请求,其中 event.ResourcePropertiesevent.OldResourceProperties 发生切换,这仍然会导致错误。<
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "strings"

    "github.com/aws/aws-lambda-go/cfn"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/service/rdsdataservice"
    "github.com/google/uuid"
)

func main() {
    lambda.Start(cfn.LambdaWrap(handler))
}

type properties struct {
    TableName string
    Columns   []struct {
        Name  string
        Value string
    }
}

func handler(ctx context.Context, event cfn.Event) (physicalResourceID string, data map[string]interface{}, err error) {
    prid := event.PhysicalResourceID
    if event.RequestType == cfn.RequestCreate {
        prid = strings.ReplaceAll(uuid.New().String(), "-", "")
    }

    var props properties
    b, _ := json.Marshal(event.ResourceProperties)
    json.Unmarshal(b, &props)

    rds := rdsdataservice.New(nil)

    if event.RequestType == cfn.RequestCreate {
        rds.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{
            Sql: aws.String(fmt.Sprintf("CREATE TABLE %s", props.TableName)),
        })
    }

    if event.RequestType == cfn.RequestDelete {
        rds.ExecuteStatement(&rdsdataservice.ExecuteStatementInput{
            Sql: aws.String(fmt.Sprintf("CREATE TABLE %s", props.TableName)),
        })
    }

    if event.RequestType == cfn.RequestUpdate {
        var oldProps properties
        b, _ := json.Marshal(event.OldResourceProperties)
        json.Unmarshal(b, &oldProps)

        columns := map[string]string{}
        for _, column := range props.Columns {
            columns[column.Name] = column.Value
        }

        for _, column := range oldProps.Columns {
            if val, ok := columns[column.Name]; ok {
                if val != column.Value {
                    return "", nil, fmt.Errorf("cannot change column type")
                }
            }
        }

        // Do some extra stuff here for adding/removing columns
    }
    return prid, nil, nil
}

我想到了两种可能的解决方案。我可以实现其中之一,但存在潜在的问题。但在我看来应该有更好的方法,因为我不能是唯一遇到这个问题的人。或者我做了一些非常错误的事情..

  1. 在某些情况下仅对此特定资源禁用回滚(有时我仍然想要回滚)
  2. 可以访问最后的状态,以便我可以检查要做什么;如果删除的最后状态为 CREATE_FAILED,则不要执行任何操作。如果更新的最后状态为 UPDATE_FAILED,请不要执行任何操作。

我可以使用下面的代码来实现第二个选项。但随着事件数量的增加,这可能会变得非常成问题。

events, err := cloud.DescribeStackEvents(&cloudformation.DescribeStackEventsInput{
    StackName: &event.StackID,
})

最佳答案

对于遇到同样问题的人;我通过查看堆栈事件解决了这个问题,并找到了正在更新的最后一个资源状态。由于当前状态始终在列表中,因此我忽略 IN_PROGRESS 值。如果状态 IN_PROGRESS 之后的第一个值为 FAILED,则表示该资源无法更新,因此可以应用不同的 ROLLBACK 策略。

GO中对应的函数

func isFailedEvent(sess *session.Session, event cfn.Event) (bool, error) {
    cloud := cloudformation.New(sess)
    var isFailedResource bool
    if err := cloud.DescribeStackEventsPages(&cloudformation.DescribeStackEventsInput{
        StackName: aws.String(event.StackID),
    }, func(out *cloudformation.DescribeStackEventsOutput, lastPage bool) bool {
        for _, e := range out.StackEvents {
            if *e.LogicalResourceId == event.LogicalResourceID {
                if strings.HasSuffix(*e.ResourceStatus, "IN_PROGRESS") {
                    continue
                }
                if strings.HasSuffix(*e.ResourceStatus, "FAILED") && !strings.Contains(*e.ResourceStatusReason, "cancelled") {
                    isFailedResource = true
                }
                return false
            }
        }
        return true
    }); err != nil {
        return false, fmt.Errorf("describe stack events: %s", err)
    }
    return isFailedResource, nil
}

关于amazon-web-services - 由于类似 catch22 的情况,AWS 自定义资源 ROLLBACK_FAILED,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74362326/

相关文章:

amazon-web-services - 有没有办法将我的 cloudformation 堆栈中的 ec2 用户数据创建的资源用于堆栈中的另一个资源?

bash - 如何使用 BASH 将 JSON Web key 集 (JWKS) 公钥转换为 PEM 文件?

javascript - 使用 Node.js 将图像上传到 AWS S3 存储桶。 JS

amazon-web-services - 调用另一个(长)Lambda 时 AWS Lambda 超时

amazon-web-services - 使用 Cloudformation 和 Spot 实例执行 RollingUpdate

amazon-web-services - AWS 从现有存储虚拟机 (SVM) 创建 ONTAP 卷

amazon-web-services - npm run build 不会在 elasticbeanstalk 上创建构建目录

amazon-web-services - LaunchConfiguration 和 cfn-hup 可以一起玩吗?

json - Opsworks实例启动失败: Status: start_failed: No such cookbook: apt

amazon-web-services - 参数值在 aws cli Cloudformation 中不起作用