AWS Lambda内的AWS S3.waitFor()

时间:2018-08-03 07:14:23

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

从Lambda函数(无服务器的nod​​ejs)内部调用S3.waitFor()函数时出现问题。我正在尝试使用一个休憩API中的S3.putObject()异步地将文件写入S3,并使用S3.waitFor()轮询另一个休憩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和 invalid fileName,当代码在我的本地测试脚本中运行时,它在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值不同。

在lamba上,当文件不存在时,似乎总是获得statusCode 403。在我的本地计算机上,一切正常(每3秒重试5次,并收到statusCode 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函数使其工作? 预先谢谢你!

1 个答案:

答案 0 :(得分:0)

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

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

  

等待对象存在状态是通过每5秒(最多20次)定期调用底层的 S3.headObject()操作来实现的。

与此同时,S3.headObject()本身依赖于HEAD Object API,该API具有以下规则,如AWS Docs here所述:

  

此操作需要 s3:GetObject 权限。有关更多信息,请转到Amazon Simple Storage Service开发人员指南中的在策略中指定权限。 如果您请求的对象不存在,Amazon S3返回的错误取决于您是否还具有s3:ListBucket权限

     
      
  • 如果您对存储桶具有s3:ListBucket 权限,则Amazon S3   将会返回HTTP状态代码 404(“没有这样的密钥”)错误。
  •   
  • 如果您没有s3:ListBucket 权限,Amazon S3将返回   HTTP状态代码 403(“访问被拒绝”)错误。
  •   

这意味着我需要向包含对象的Bucket资源添加s3:ListBucket动作,以便在对象不存在时能够获得响应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调用就可以在存储桶下轮询文件/对象,并且仅在准备就绪时获取结果,或者在资源准备就绪后抛出错误。特定的超时时间。

这样,客户端实现将更加简单。因为它不必自己实施轮询。

希望有人发现它有用。谢谢。