为了账号安全,请及时绑定邮箱和手机立即绑定

如何在 go 中编写前/后流量钩子函数?

如何在 go 中编写前/后流量钩子函数?

Go
三国纷争 2023-05-22 17:12:37
我开始使用 AWS SAM,现在我只有一些单元测试,但我想尝试在预流量挂钩函数中运行集成测试。不幸的是,似乎没有 Golang 的代码示例,我只能找到 Javascript 的代码示例。我想从测试一个简单查询 DynamoDB 的 lambda 函数开始。
查看完整描述

2 回答

?
慕斯709654

TA贡献1840条经验 获得超5个赞

这样的事情有效:


package main


import (

    "context"

    "encoding/json"


    "github.com/aws/aws-lambda-go/lambda"

    "github.com/aws/aws-sdk-go/aws/session"

    "github.com/aws/aws-sdk-go/service/codedeploy"

)


type CodeDeployEvent struct {

    DeploymentId                  string `json:"deploymentId"`

    LifecycleEventHookExecutionId string `json:"lifecycleEventHookExecutionId"`

}


func HandleRequest(ctx context.Context, event CodeDeployEvent) (string, error) {


    // add some tests here and change status flag as needed . . .


    client := codedeploy.New(session.New())

    params := &codedeploy.PutLifecycleEventHookExecutionStatusInput{

        DeploymentId:                  &event.DeploymentId,

        LifecycleEventHookExecutionId: &event.LifecycleEventHookExecutionId,

        Status:                        "Succeeded",

    }


    req, _ := client.PutLifecycleEventHookExecutionStatusRequest(params)


    _ = req.Send()

}


查看完整回答
反对 回复 2023-05-22
?
12345678_0001

TA贡献1802条经验 获得超5个赞

我开始实施这个并想分享我的完整解决方案。

在弄清楚如何使用它之后,我决定不使用它,因为它有几个缺点。

  • 无法将新版本的金丝雀公开给用户群的专用部分,这意味着有时他们会使用新版本或旧版本

  • 调用发布到 sns 的函数会触发所有下游动作,这可能会获取下游服务的新版本或旧版本,这会在破坏 API 的情况下导致很多问题

  • IAM 更改会立即影响两个版本,可能会破坏旧版本。

相反,我将所有内容部署到预生产帐户,运行我的集成和端到端测试,如果它们成功,我将部署到生产

创建金丝雀部署的 cdk 代码:

const versionAlias = new lambda.Alias(this, 'Alias', {

    aliasName: "alias",

    version: this.lambda.currentVersion,

})


const preHook = new lambda.Function(this, 'LambdaPreHook', {

    description: "pre hook",

    code: lambda.Code.fromAsset('dist/upload/convert-pre-hook'),

    handler: 'main',

    runtime: lambda.Runtime.GO_1_X,

    memorySize: 128,

    timeout: cdk.Duration.minutes(1),

    environment: {

        FUNCTION_NAME: this.lambda.currentVersion.functionName,

    },

    reservedConcurrentExecutions: 5,

    logRetention: RetentionDays.ONE_WEEK,

})

// this.lambda.grantInvoke(preHook) // this doesn't work, I need to grant invoke to all functions :s

preHook.addToRolePolicy(new iam.PolicyStatement({

    actions: [

        "lambda:InvokeFunction",

    ],

    resources: ["*"],

    effect: iam.Effect.ALLOW,

}))


const application = new codedeploy.LambdaApplication(this, 'CodeDeployApplication')

new codedeploy.LambdaDeploymentGroup(this, 'CanaryDeployment', {

    application: application,

    alias: versionAlias,

    deploymentConfig: codedeploy.LambdaDeploymentConfig.ALL_AT_ONCE,

    preHook: preHook,

    autoRollback: {

        failedDeployment: true,

        stoppedDeployment: true,

        deploymentInAlarm: false,

    },

    ignorePollAlarmsFailure: false,

    // alarms:

    // autoRollback: codedeploy.A

    // postHook:

})

我的pre hook函数的go代码。PutLifecycleEventHookExecutionStatus如果预挂钩成功,则告诉代码部署。不幸的是,如果部署消息失败,您在 cdk 部署输出中获得的消息将毫无用处,因此您需要检查挂钩前/后日志。


为了实际运行集成测试,我只需调用 lambda 并检查是否发生错误。


package main


import (

    "encoding/base64"

    "fmt"

    "log"

    "os"


    "github.com/aws/aws-lambda-go/lambda"

    "github.com/aws/aws-sdk-go/aws"

    "github.com/aws/aws-sdk-go/aws/session"

    "github.com/aws/aws-sdk-go/service/codedeploy"

    lambdaService "github.com/aws/aws-sdk-go/service/lambda"

)


var svc *codedeploy.CodeDeploy

var lambdaSvc *lambdaService.Lambda


type codeDeployEvent struct {

    DeploymentId                  string `json:"deploymentId"`

    LifecycleEventHookExecutionId string `json:"lifecycleEventHookExecutionId"`

}


func handler(e codeDeployEvent) error {

    params := &codedeploy.PutLifecycleEventHookExecutionStatusInput{

        DeploymentId:                  &e.DeploymentId,

        LifecycleEventHookExecutionId: &e.LifecycleEventHookExecutionId,

    }

    err := handle()

    if err != nil {

        log.Println(err)

        params.Status = aws.String(codedeploy.LifecycleEventStatusFailed)

    } else {

        params.Status = aws.String(codedeploy.LifecycleEventStatusSucceeded)

    }


    _, err = svc.PutLifecycleEventHookExecutionStatus(params)

    if err != nil {

        return fmt.Errorf("failed putting the lifecycle event hook execution status. the status was %s", *params.Status)

    }


    return nil

}


func handle() error {

    functionName := os.Getenv("FUNCTION_NAME")

    if functionName == "" {

        return fmt.Errorf("FUNCTION_NAME not set")

    }

    log.Printf("function name: %s", functionName)


    // invoke lambda via sdk

    input := &lambdaService.InvokeInput{

        FunctionName:   &functionName,

        Payload:        nil,

        LogType:        aws.String(lambdaService.LogTypeTail),                   // returns the log in the response

        InvocationType: aws.String(lambdaService.InvocationTypeRequestResponse), // synchronous - default

    }

    err := input.Validate()

    if err != nil {

        return fmt.Errorf("validating the input failed: %v", err)

    }


    resp, err := lambdaSvc.Invoke(input)

    if err != nil {

        return fmt.Errorf("failed to invoke lambda: %v", err)

    }


    decodeString, err := base64.StdEncoding.DecodeString(*resp.LogResult)

    if err != nil {

        return fmt.Errorf("failed to decode the log: %v", err)

    }

    log.Printf("log result: %s", decodeString)


    if resp.FunctionError != nil {

        return fmt.Errorf("lambda was invoked but returned error: %s", *resp.FunctionError)

    }

    return nil

}


func main() {

    sess, err := session.NewSession()

    if err != nil {

        return

    }

    svc = codedeploy.New(sess)

    lambdaSvc = lambdaService.New(sess)

    lambda.Start(handler)

}


查看完整回答
反对 回复 2023-05-22
  • 2 回答
  • 0 关注
  • 120 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信