github.com/mweagle/Sparta@v1.15.0/docs_source/content/reference/eventsources/s3.md (about)

     1  ---
     2  date: 2016-03-09T19:56:50+01:00
     3  title: S3
     4  weight: 10
     5  ---
     6  
     7  
     8  In this section we'll walkthrough how to trigger your lambda function in response to S3 events.  This overview is based on the [SpartaImager](https://github.com/mweagle/SpartaImager) sample code if you'd rather jump to the end result.
     9  
    10  # Goal
    11  
    12  Assume we have an S3 bucket that stores images.  You've been asked to write a service that creates a duplicate image that includes a characteristic stamp overlay and store it in the same S3 bucket.
    13  
    14  ## Getting Started
    15  
    16  We'll start with an empty lambda function and build up the needed functionality.
    17  
    18  ```go
    19  import (
    20    awsLambdaEvents "github.com/aws/aws-lambda-go/events"
    21  	awsLambdaContext "github.com/aws/aws-lambda-go/lambdacontext"
    22  )
    23  
    24  type transformedResponse struct {
    25  	Bucket string
    26  	Key    string
    27  }
    28  
    29  func transformImage(ctx context.Context, event awsLambdaEvents.S3Event) ([]transformedResponse, error) {
    30  	logger, _ := ctx.Value(sparta.ContextKeyLogger).(*logrus.Logger)
    31  	lambdaContext, _ := awsLambdaContext.FromContext(ctx)
    32  	logger.WithFields(logrus.Fields{
    33  		"RequestID":   lambdaContext.AwsRequestID,
    34  		"RecordCount": len(event.Records),
    35    }).Info("Request received 👍")
    36  
    37  ```
    38  
    39  
    40  Since the `transformImage` is expected to be triggered by S3 event changes, we can transparently unmarshal
    41  the incoming request into an [S3Event](https://github.com/aws/aws-lambda-go/blob/master/events/s3.go#L9)
    42  defined by the AWS Go Lambda SDK.
    43  
    44  S3 events are delivered in batches, via lists of [EventRecords](https://godoc.org/github.com/mweagle/Sparta/aws/s3#EventRecord), so we'll need to process each record.
    45  
    46  ```go
    47  for _, eachRecord := range event.Records {
    48    // What happened?
    49    switch eachRecord.EventName {
    50    case "ObjectCreated:Put":
    51      {
    52        err = stampImage(eachRecord.S3.Bucket.Name, eachRecord.S3.Object.Key, logger)
    53      }
    54    case "s3:ObjectRemoved:Delete":
    55      {
    56        // Delete stamped image
    57      }
    58    default:
    59      {
    60        logger.Info("Unsupported event: ", eachRecord.EventName)
    61      }
    62    }
    63  
    64    //
    65    if err != nil {
    66      logger.Error("Failed to process event: ", err.Error())
    67      http.Error(w, err.Error(), http.StatusInternalServerError)
    68    }
    69  }
    70  ```
    71  
    72  The [stampImage](https://github.com/mweagle/SpartaImager/blob/master/application.go#L57) function does most of the work, fetching the S3 image to memory,
    73  applying the stamp, and putting the transformed content back to S3 with a new name.  It uses a simple **xformed_** keyname prefix to identify
    74  items which have already been stamped & prevents an "event-storm" from being triggered.  This simple approach is acceptable for an example,
    75  but in production you should use a more durable approach.
    76  
    77  ## Sparta Integration
    78  
    79  With the core of the `transformImage` complete, the next step is to integrate the **go** function with Sparta.  This is performed by the [imagerFunctions](https://github.com/mweagle/SpartaImager/blob/master/application.go#L200) source.
    80  
    81  Our lambda function needs to both *Get* and *Put* items back to an S3 bucket, so we need an IAM Role that grants those privileges under which the function will execute:
    82  
    83  ```go
    84  // Provision an IAM::Role as part of this application
    85  var iamRole = sparta.IAMRoleDefinition{}
    86  
    87  // Setup the ARN that includes all child keys
    88  resourceArn := fmt.Sprintf("%s/*", s3EventBroadcasterBucket)
    89  iamRole.Privileges = append(iamRole.Privileges, sparta.IAMRolePrivilege{
    90    Actions: []string{"s3:GetObject",
    91      "s3:PutObject",
    92    },
    93    Resource: resourceArn,
    94  })
    95  ```
    96  
    97  The `s3EventBroadcasterBucket` param is the ARN of the S3 bucket that will trigger your lambda function (eg: _arn:aws:s3:::MyImagingS3Bucket_).
    98  
    99  With the IAM Role defined, we can create the Sparta lambda function for `transformImage`:
   100  
   101  ```go
   102  // The default timeout is 3 seconds - increase that to 30 seconds s.t. the
   103  // transform lambda doesn't fail early.
   104  transformOptions := &sparta.LambdaFunctionOptions{
   105    Description: "Stamp assets in S3",
   106    MemorySize:  128,
   107    Timeout:     30,
   108  }
   109  lambdaFn, _ := sparta.NewAWSLambda(sparta.LambdaName(transformImage),
   110    transformImage,
   111    iamRole)
   112  lambdaFn.Options = transformOptions
   113  ```
   114  
   115  It typically takes more than 3 seconds to apply the transform, so we increase the execution timeout and provision a new
   116  lambda function using the `iamRole` we defined earlier.
   117  
   118  ## Event Source Registration
   119  
   120  If we were to deploy this Sparta application, the `transformImage` function would have the ability to *Get* and *Put* back
   121  to the `s3EventBroadcasterBucket`, but would not be invoked in response to events triggered by that bucket.  To register
   122  for state change events, we need to configure the lambda's [Permissions](http://docs.aws.amazon.com/lambda/latest/dg/intro-permission-model.html):
   123  
   124  ```go
   125  //////////////////////////////////////////////////////////////////////////////
   126  // S3 configuration
   127  //
   128  lambdaFn.Permissions = append(lambdaFn.Permissions, sparta.S3Permission{
   129    BasePermission: sparta.BasePermission{
   130      SourceArn: s3EventBroadcasterBucket,
   131    },
   132    Events: []string{"s3:ObjectCreated:*", "s3:ObjectRemoved:*"},
   133  })
   134  lambdaFunctions = append(lambdaFunctions, lambdaFn)
   135  ```
   136  
   137  When `Sparta` generates the CloudFormation template, it scans for `Permission` configurations.
   138  For [push based sources](http://docs.aws.amazon.com/lambda/latest/dg/intro-invocation-modes.html) like S3, Sparta uses that
   139  service's APIs to register your lambda function as a publishing target for events.  This remote registration is handled
   140   automatically by CustomResources added to the CloudFormation template.
   141  
   142  # Wrapping Up
   143  
   144  With the `lambdaFn` fully defined, we can provide it to `sparta.Main()` and deploy our service.  The workflow below is shared by all S3-triggered lambda functions:
   145  
   146    * Define the lambda function (`transformImage`).
   147    * Implement the associated business logic  (`stampImage`).
   148    * If needed, create the required [IAMRoleDefinition](https://godoc.org/github.com/mweagle/Sparta*IAMRoleDefinition) with appropriate privileges.
   149    * Provide the lambda function & IAMRoleDefinition to `sparta.NewAWSLambda()`
   150    * Add the necessary [Permissions](https://godoc.org/github.com/mweagle/Sparta#LambdaAWSInfo) to the `LambdaAWSInfo` struct so that the lambda function is triggered.
   151  
   152  The [SpartaImager](https://github.com/mweagle/SpartaImager) repo contains the full code, and includes [API Gateway](/reference/apigateway) support that allows you to publicly fetch the stamped image via an expiring S3 URL.
   153  
   154  ## Other Resources
   155  
   156    * The AWS docs have an excellent [S3 event source](http://docs.aws.amazon.com/lambda/latest/dg/getting-started-amazons3-events.html) walkthrough.