From 501c2f378153015a4a72f0b1a8e89dcb0bd3762a Mon Sep 17 00:00:00 2001 From: KIDANI Akito Date: Sat, 6 Jan 2024 09:30:01 +0900 Subject: [PATCH] Add S3ObjectLambdaEvent (#536) * Add S3ObjectLambdaEvent * Update S3 Object Lambda sample code * Avoid the generic name of Configuration * Follow stylecheck * Add S3ObjectLambda prefix * fix Url to URL --------- Co-authored-by: Bryan Moffatt --- events/README_S3_Object_Lambda.md | 83 +++++++++++++++++++ events/s3_object_lambda.go | 64 ++++++++++++++ events/s3_object_lambda_test.go | 44 ++++++++++ ...-lambda-event-get-object-assumed-role.json | 42 ++++++++++ ...s3-object-lambda-event-get-object-iam.json | 29 +++++++ ...3-object-lambda-event-head-object-iam.json | 27 ++++++ ...-object-lambda-event-list-objects-iam.json | 27 ++++++ ...ject-lambda-event-list-objects-v2-iam.json | 27 ++++++ 8 files changed, 343 insertions(+) create mode 100644 events/README_S3_Object_Lambda.md create mode 100644 events/s3_object_lambda.go create mode 100644 events/s3_object_lambda_test.go create mode 100644 events/testdata/s3-object-lambda-event-get-object-assumed-role.json create mode 100644 events/testdata/s3-object-lambda-event-get-object-iam.json create mode 100644 events/testdata/s3-object-lambda-event-head-object-iam.json create mode 100644 events/testdata/s3-object-lambda-event-list-objects-iam.json create mode 100644 events/testdata/s3-object-lambda-event-list-objects-v2-iam.json diff --git a/events/README_S3_Object_Lambda.md b/events/README_S3_Object_Lambda.md new file mode 100644 index 00000000..2166cb91 --- /dev/null +++ b/events/README_S3_Object_Lambda.md @@ -0,0 +1,83 @@ +# Sample Function + +The following is a sample class and Lambda function that receives Amazon S3 Object Lambda event record data as an input and returns an object metadata output. + +```go + +// main.go +package main + +import ( + "context" + "crypto/md5" + "encoding/hex" + "encoding/json" + "io/ioutil" + "net/http" + "github.com/aws/aws-lambda-go/lambda" + "github.com/aws/aws-lambda-go/events" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/s3" +) + +func handler(ctx context.Context, event events.S3ObjectLambdaEvent) error { + url := event.GetObjectContext.InputS3Url + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + transformedObject := TransformedObject{ + Metadata: Metadata{ + Length: len(bodyBytes), + Md5: toMd5(bodyBytes), + }, + } + jsonData, err := json.Marshal(transformedObject) + if err != nil { + return err + } + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + return err + } + svc := s3.NewFromConfig(cfg) + input := &s3.WriteGetObjectResponseInput{ + RequestRoute: &event.GetObjectContext.OutputRoute, + RequestToken: &event.GetObjectContext.OutputToken, + Body: strings.NewReader(string(jsonData)), + } + res, err := svc.WriteGetObjectResponse(ctx, input) + if err != nil { + return err + } + fmt.Printf("%v", res) + return nil +} + +func toMd5(data []byte) string { + hasher := md5.New() + hasher.Write(data) + hash := hasher.Sum(nil) + + return hex.EncodeToString(hash) +} + +type TransformedObject struct { + Metadata Metadata `json:"metadata"` +} + +type Metadata struct { + Length int `json:"length"` + Md5 string `json:"md5"` +} + +func main() { + lambda.Start(handler) +} + +``` diff --git a/events/s3_object_lambda.go b/events/s3_object_lambda.go new file mode 100644 index 00000000..47a28b04 --- /dev/null +++ b/events/s3_object_lambda.go @@ -0,0 +1,64 @@ +package events + +type S3ObjectLambdaEvent struct { + XAmzRequestID string `json:"xAmzRequestId"` + GetObjectContext *S3ObjectLambdaGetObjectContext `json:"getObjectContext,omitempty"` + ListObjectsContext *S3ObjectLambdaListObjectsContext `json:"listObjectsContext,omitempty"` + ListObjectsV2Context *S3ObjectLambdaListObjectsV2Context `json:"listObjectsV2Context,omitempty"` + HeadObjectContext *S3ObjectLambdaHeadObjectContext `json:"headObjectContext,omitempty"` + Configuration S3ObjectLambdaConfiguration `json:"configuration"` + UserRequest S3ObjectLambdaUserRequest `json:"userRequest"` + UserIdentity S3ObjectLambdaUserIdentity `json:"userIdentity"` + ProtocolVersion string `json:"protocolVersion"` +} + +type S3ObjectLambdaGetObjectContext struct { + InputS3URL string `json:"inputS3Url"` + OutputRoute string `json:"outputRoute"` + OutputToken string `json:"outputToken"` +} + +type S3ObjectLambdaListObjectsContext struct { + InputS3URL string `json:"inputS3Url"` +} + +type S3ObjectLambdaListObjectsV2Context struct { + InputS3URL string `json:"inputS3Url"` +} + +type S3ObjectLambdaHeadObjectContext struct { + InputS3URL string `json:"inputS3Url"` +} + +type S3ObjectLambdaConfiguration struct { + AccessPointARN string `json:"accessPointArn"` + SupportingAccessPointARN string `json:"supportingAccessPointArn"` + Payload string `json:"payload"` +} + +type S3ObjectLambdaUserRequest struct { + URL string `json:"url"` + Headers map[string]string `json:"headers"` +} + +type S3ObjectLambdaUserIdentity struct { + Type string `json:"type"` + PrincipalID string `json:"principalId"` + ARN string `json:"arn"` + AccountID string `json:"accountId"` + AccessKeyID string `json:"accessKeyId"` + SessionContext *S3ObjectLambdaSessionContext `json:"sessionContext,omitempty"` +} + +type S3ObjectLambdaSessionContext struct { + Attributes map[string]string `json:"attributes"` + SessionIssuer *S3ObjectLambdaSessionIssuer `json:"sessionIssuer,omitempty"` +} + +type S3ObjectLambdaSessionIssuer struct { + Type string `json:"type"` + PrincipalID string `json:"principalId"` + ARN string `json:"arn"` + AccountID string `json:"accountId"` + UserName string `json:"userName"` +} diff --git a/events/s3_object_lambda_test.go b/events/s3_object_lambda_test.go new file mode 100644 index 00000000..66e40ac7 --- /dev/null +++ b/events/s3_object_lambda_test.go @@ -0,0 +1,44 @@ +package events + +import ( + "encoding/json" + "testing" + + "github.com/aws/aws-lambda-go/events/test" + "github.com/stretchr/testify/assert" +) + +func TestS3ObjectLambdaEventMarshaling(t *testing.T) { + tests := []struct { + file string + }{ + {"./testdata/s3-object-lambda-event-get-object-iam.json"}, + {"./testdata/s3-object-lambda-event-get-object-assumed-role.json"}, + {"./testdata/s3-object-lambda-event-head-object-iam.json"}, + {"./testdata/s3-object-lambda-event-list-objects-iam.json"}, + {"./testdata/s3-object-lambda-event-list-objects-v2-iam.json"}, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.file, func(t *testing.T) { + inputJSON := test.ReadJSONFromFile(t, tc.file) + + var inputEvent S3ObjectLambdaEvent + if err := json.Unmarshal(inputJSON, &inputEvent); err != nil { + t.Errorf("could not unmarshal event. details: %v", err) + } + + outputJSON, err := json.Marshal(inputEvent) + if err != nil { + t.Errorf("could not marshal event. details: %v", err) + } + + assert.JSONEq(t, string(inputJSON), string(outputJSON)) + }) + } +} + +func TestS3ObjectLambdaMarshalingMalformedJson(t *testing.T) { + test.TestMalformedJson(t, S3ObjectLambdaEvent{}) +} diff --git a/events/testdata/s3-object-lambda-event-get-object-assumed-role.json b/events/testdata/s3-object-lambda-event-get-object-assumed-role.json new file mode 100644 index 00000000..34aa55b1 --- /dev/null +++ b/events/testdata/s3-object-lambda-event-get-object-assumed-role.json @@ -0,0 +1,42 @@ +{ + "xAmzRequestId": "requestId", + "getObjectContext": { + "inputS3Url": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/example?X-Amz-Security-Token=", + "outputRoute": "io-use1-001", + "outputToken": "OutputToken" + }, + "configuration": { + "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap", + "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap", + "payload": "{}" + }, + "userRequest": { + "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example", + "headers": { + "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com", + "Accept-Encoding": "identity", + "X-Amz-Content-SHA256": "e3b0c44298fc1example" + } + }, + "userIdentity": { + "type": "AssumedRole", + "principalId": "principalId", + "arn": "arn:aws:sts::111122223333:assumed-role/Admin/example", + "accountId": "111122223333", + "accessKeyId": "accessKeyId", + "sessionContext": { + "attributes": { + "mfaAuthenticated": "false", + "creationDate": "Wed Mar 10 23:41:52 UTC 2021" + }, + "sessionIssuer": { + "type": "Role", + "principalId": "principalId", + "arn": "arn:aws:iam::111122223333:role/Admin", + "accountId": "111122223333", + "userName": "Admin" + } + } + }, + "protocolVersion": "1.00" +} \ No newline at end of file diff --git a/events/testdata/s3-object-lambda-event-get-object-iam.json b/events/testdata/s3-object-lambda-event-get-object-iam.json new file mode 100644 index 00000000..5f415a2e --- /dev/null +++ b/events/testdata/s3-object-lambda-event-get-object-iam.json @@ -0,0 +1,29 @@ +{ + "xAmzRequestId": "requestId", + "getObjectContext": { + "inputS3Url": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/example?X-Amz-Security-Token=", + "outputRoute": "io-use1-001", + "outputToken": "OutputToken" + }, + "configuration": { + "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap", + "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap", + "payload": "{}" + }, + "userRequest": { + "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example", + "headers": { + "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com", + "Accept-Encoding": "identity", + "X-Amz-Content-SHA256": "e3b0c44298fc1example" + } + }, + "userIdentity": { + "type": "IAMUser", + "principalId": "principalId", + "arn": "arn:aws:iam::111122223333:user/username", + "accountId": "111122223333", + "accessKeyId": "accessKeyId" + }, + "protocolVersion": "1.00" +} \ No newline at end of file diff --git a/events/testdata/s3-object-lambda-event-head-object-iam.json b/events/testdata/s3-object-lambda-event-head-object-iam.json new file mode 100644 index 00000000..85822dc7 --- /dev/null +++ b/events/testdata/s3-object-lambda-event-head-object-iam.json @@ -0,0 +1,27 @@ +{ + "xAmzRequestId": "requestId", + "headObjectContext": { + "inputS3Url": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/example?X-Amz-Security-Token=" + }, + "configuration": { + "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap", + "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap", + "payload": "{}" + }, + "userRequest": { + "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example", + "headers": { + "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com", + "Accept-Encoding": "identity", + "X-Amz-Content-SHA256": "e3b0c44298fc1example" + } + }, + "userIdentity": { + "type": "IAMUser", + "principalId": "principalId", + "arn": "arn:aws:iam::111122223333:user/username", + "accountId": "111122223333", + "accessKeyId": "accessKeyId" + }, + "protocolVersion": "1.01" +} \ No newline at end of file diff --git a/events/testdata/s3-object-lambda-event-list-objects-iam.json b/events/testdata/s3-object-lambda-event-list-objects-iam.json new file mode 100644 index 00000000..2a08f998 --- /dev/null +++ b/events/testdata/s3-object-lambda-event-list-objects-iam.json @@ -0,0 +1,27 @@ +{ + "xAmzRequestId": "requestId", + "listObjectsContext": { + "inputS3Url": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/example?X-Amz-Security-Token=" + }, + "configuration": { + "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap", + "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap", + "payload": "{}" + }, + "userRequest": { + "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example", + "headers": { + "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com", + "Accept-Encoding": "identity", + "X-Amz-Content-SHA256": "e3b0c44298fc1example" + } + }, + "userIdentity": { + "type": "IAMUser", + "principalId": "principalId", + "arn": "arn:aws:iam::111122223333:user/username", + "accountId": "111122223333", + "accessKeyId": "accessKeyId" + }, + "protocolVersion": "1.01" +} \ No newline at end of file diff --git a/events/testdata/s3-object-lambda-event-list-objects-v2-iam.json b/events/testdata/s3-object-lambda-event-list-objects-v2-iam.json new file mode 100644 index 00000000..41d95d09 --- /dev/null +++ b/events/testdata/s3-object-lambda-event-list-objects-v2-iam.json @@ -0,0 +1,27 @@ +{ + "xAmzRequestId": "requestId", + "listObjectsV2Context": { + "inputS3Url": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/example?X-Amz-Security-Token=" + }, + "configuration": { + "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap", + "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap", + "payload": "{}" + }, + "userRequest": { + "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example", + "headers": { + "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com", + "Accept-Encoding": "identity", + "X-Amz-Content-SHA256": "e3b0c44298fc1example" + } + }, + "userIdentity": { + "type": "IAMUser", + "principalId": "principalId", + "arn": "arn:aws:iam::111122223333:user/username", + "accountId": "111122223333", + "accessKeyId": "accessKeyId" + }, + "protocolVersion": "1.01" +} \ No newline at end of file