node.js - AWS Lambda 中的 Amazon S3 waitFor()

标签 node.js amazon-web-services amazon-s3 aws-lambda serverless-framework

从 Lambda 函数(无服务器 Nodejs)内部调用 S3.waitFor() 函数时遇到问题。我尝试使用 S3.putObject() 从一个 Rest api 将文件异步写入 Amazon S3,并使用 S3.waitFor() 从另一个 Rest api 轮询结果文件,以查看写入是否已准备好/完成。

请参阅以下代码片段:

...
S3.waitFor('objectExists', {
  Bucket: bucketName,
  Key: fileName,
  $waiter: {
    maxAttempts: 5,
    delay: 3
  }
}, (error, data) => {
  if (error) {
    console.log("error:" + JSON.stringify(error))
  } else {
    console.log("Success")
  }
});
...

给定有效bucketName和无效文件名,当代码在我的本地测试脚本中运行时,它会在15秒(3秒x 5次重试)后返回错误,并生成如下结果:

error: {
  "message": "Resource is not in the state objectExists",
  "code": "ResourceNotReady",
  "region": null,
  "time": "2018-08-03T06:08:12.276Z",
  "requestId": "AD621033DCEA7670",
  "extendedRequestId": "JNkxddWX3IZfauJJ63SgVwyv5nShQ+Mworb8pgCmb1f/cQbTu3+52aFuEi8XGro72mJ4ik6ZMGA=",
  "retryable": true,
  "statusCode": 404,
  "retryDelay": 3000
}

同时,当它在AWS lambda函数中运行时,它直接返回结果,如下所示:

error: {
  "message": "Resource is not in the state objectExists",
  "code": "ResourceNotReady",
  "region": null,
  "time": "2018-08-03T05:49:43.178Z",
  "requestId": "E34D731777472663",
  "extendedRequestId": "ONMGnQkd14gvCfE/FWk54uYRG6Uas/hvV6OYeiax5BTOCVwbxGGvmHxMlOHuHPzxL5gZOahPUGM=",
  "retryable": false,
  "statusCode": 403,
  "retryDelay": 3000
}

正如您所看到的,两者之间的 retryable 和 statusCode 值是不同的。

在amba上,当文件不存在时,它似乎总是得到statusCode 403。在我的本地上,一切都按预期运行(每 3 秒重试 5 次并收到状态代码 404)。

我想知道这是否与 IAM 角色有关。这是我的 serverless.yml 中的 IAM 角色语句设置:

iamRoleStatements:
- Effect: "Allow"
 Action:
   - "logs:CreateLogGroup"
   - "logs:CreateLogStream"
   - "logs:PutLogEvents"
   - "ec2:CreateNetworkInterface"
   - "ec2:DescribeNetworkInterfaces"
   - "ec2:DeleteNetworkInterface"
   - "sns:Publish"
   - "sns:Subscribe"
   - "s3:*"
 Resource: "*"

如何让它通过 lambda 函数工作? 预先感谢您!

最佳答案

事实证明,关键在于如何为存储桶及其下的所有对象设置 IAM 角色。

基于 AWS 文档 here ,它指出 S3.waitFor() 依赖于底层 S3.headObject()

Waits for the objectExists state by periodically calling the underlying S3.headObject() operation every 5 seconds (at most 20 times).

同时,S3.headObject() 本身依赖于 HEAD 对象 API,该 API 具有 AWS 文档 here 中所述的以下规则:

You need the s3:GetObject permission for this operation. For more information, go to Specifying Permissions in a Policy in the Amazon Simple Storage Service Developer Guide. If the object you request does not exist, the error Amazon S3 returns depends on whether you also have the s3:ListBucket permission.

  • If you have the s3:ListBucket permission on the bucket, Amazon S3 will return a HTTP status code 404 ("no such key") error.
  • if you don’t have the s3:ListBucket permission, Amazon S3 will return a HTTP status code 403 ("access denied") error.

这意味着我需要将 s3:ListBucket Action 添加到包含对象的 Bucket 资源中,以便在对象不存在时能够获取响应 404。

因此,我配置了 cloudformation AWS::IAM::Policy 资源,如下所示,其中专门针对存储桶本身(即:S3FileStorageBucket)添加了 s3:Get*s3:List* 操作。

    "IamPolicyLambdaExecution": {
        "Type": "AWS::IAM::Policy",
        "DependsOn": [
            "IamRoleLambdaExecution",
            "S3FileStorageBucket"
        ],
        "Properties": {
            "PolicyName": { "Fn::Join": ["-", ["Live-RolePolicy", { "Ref": "environment"}]]},
            "PolicyDocument": {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Effect":"Allow",
                        "Action": [
                            "s3:Get*",
                            "s3:List*"
                        ],
                        "Resource": {
                            "Fn::Join": [
                                "",
                                [
                                    "arn:aws:s3:::",
                                    {
                                        "Ref": "S3FileStorageBucket"
                                    }
                                ]
                            ]
                        }
                    },
                    {
                        "Effect":"Allow",
                        "Action": [
                            "s3:GetObject",
                            "s3:PutObject",
                            "s3:DeleteObject"
                        ],
                        "Resource": {
                            "Fn::Join": [
                                "",
                                [
                                    "arn:aws:s3:::",
                                    {
                                        "Ref": "S3FileStorageBucket"
                                    },
                                    "/*"
                                ]
                            ]
                        }
                    },
                    ...

现在,我已经能够执行 S3.waitFor() 只需一次 API 调用即可轮询存储桶下的文件/对象,并且仅在准备就绪时获取结果,或者在特定超时后资源未准备好时抛出错误。

这样,客户端实现就会简单得多。因为它不必自己实现 poll。

希望有人觉得它有用。谢谢。

关于node.js - AWS Lambda 中的 Amazon S3 waitFor(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51667179/

相关文章:

javascript - Node.js:代码在 Web 请求完成之前继续运行

javascript - 在 JavaScript 中压缩数组

amazon-web-services - 如何将 Cognito 用户池与 Facebook 等外部提供商相结合?

ios - 如何保护 iOS 应用程序中的 awsconfiguration.json 数据详细信息?

python - 使用客户端加密的 AWS S3

node.js - 使用 typeorm 在 postgres 中搜索数组中的项目

javascript - node.js 中的 require(../) 是如何工作的?

java - 为 AWS Spot 实例请求实现幂等性

python - 将 SQL 查询的结果写入 CSV 并避免额外的换行符

php - 使用 Amazon Web Services for EC2 设置数据驱动网站的综合指南