Skip to content

Commit

Permalink
Merge branch 'main' into lambdaurl-detect-content-type
Browse files Browse the repository at this point in the history
  • Loading branch information
bmoffatt authored Nov 30, 2023
2 parents 525974e + 1dca084 commit 1da03de
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 8 deletions.
38 changes: 38 additions & 0 deletions events/README_SecretsManager_SecretRotationEvent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Sample Function

The following is a sample Lambda function that handles a SecretsManager secret rotation event.

```go
package main

import (
"fmt"
"context"

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

func handler(ctx context.Context, event SecretsManagerSecretRotationEvent) error {
fmt.Printf("rotating secret %s with token %s\n",
event.SecretID, event.ClientRequestToken)

switch event.Step {
case "createSecret":
// create
case "setSecret":
// set
case "finishSecret":
// finish
case "testSecret":
// test
}

return nil
}


func main() {
lambda.Start(handler)
}
```
3 changes: 2 additions & 1 deletion events/code_commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,12 @@ type CodeCommitReference struct {
Commit string `json:"commit"`
Ref string `json:"ref"`
Created bool `json:"created,omitempty"`
Deleted bool `json:"deleted,omitempty"`
}

// String returns a string representation of this object.
// Useful for testing and debugging.
func (r CodeCommitReference) String() string {
return fmt.Sprintf(
"{commit: %v, ref: %v, created: %v}", r.Commit, r.Ref, r.Created)
"{commit: %v, ref: %v, created: %v, deleted: %v}", r.Commit, r.Ref, r.Created, r.Deleted)
}
15 changes: 15 additions & 0 deletions events/code_commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ func TestCodeCommitReference(t *testing.T) {
"ref": "refs/heads/master",
"created": true
}
`),
},
{
Name: "Deleted CodeCommitReference",
Input: []byte(`
{
"commit": "5c4ef1049f1d27deadbeeff313e0730018be182b",
"ref": "refs/heads/master",
"deleted": true
}
`),
},
}
Expand Down Expand Up @@ -62,6 +72,11 @@ func TestCodeCommitCodeCommit(t *testing.T) {
"commit": "5c4ef1049f1d27deadbeeff313e0730018be182b",
"ref": "refs/heads/master",
"created": true
},
{
"commit": "5c4ef1049f1d27deadbeeff313e0730018be182b",
"ref": "refs/heads/master",
"deleted": true
}
]
}
Expand Down
11 changes: 11 additions & 0 deletions events/secretsmanager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package events

// SecretsManagerSecretRotationEvent is the event passed to a Lambda function to handle
// automatic secret rotation.
//
// https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets.html#rotate-secrets_how
type SecretsManagerSecretRotationEvent struct {
Step string `json:"Step"`
SecretID string `json:"SecretId"`
ClientRequestToken string `json:"ClientRequestToken"`
}
30 changes: 30 additions & 0 deletions events/secretsmanager_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package events

import (
"encoding/json"
"testing"

"github.com/aws/aws-lambda-go/events/test"
"github.com/stretchr/testify/assert"
)

func TestSecretsManagerSecretRotationEventMarshaling(t *testing.T) {

// 1. read JSON from file
inputJSON := test.ReadJSONFromFile(t, "./testdata/secretsmanager-secret-rotation-event.json")

// 2. de-serialize into Go object
var inputEvent SecretsManagerSecretRotationEvent
if err := json.Unmarshal(inputJSON, &inputEvent); err != nil {
t.Errorf("could not unmarshal event. details: %v", err)
}

// 3. serialize to JSON
outputJSON, err := json.Marshal(inputEvent)
if err != nil {
t.Errorf("could not marshal event. details: %v", err)
}

// 4. check result
assert.JSONEq(t, string(inputJSON), string(outputJSON))
}
5 changes: 5 additions & 0 deletions events/testdata/secretsmanager-secret-rotation-event.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"Step": "createSecret",
"SecretId": "arn:aws:secretsmanager:us-east-1:111122223333:secret:id-ABCD1E",
"ClientRequestToken": "1ab23456-cde7-8912-34fg-h56i78j9k12l"
}
54 changes: 47 additions & 7 deletions lambda/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ type Handler interface {

type handlerOptions struct {
handlerFunc
baseContext context.Context
contextValues map[interface{}]interface{}
jsonResponseEscapeHTML bool
jsonResponseIndentPrefix string
jsonResponseIndentValue string
enableSIGTERM bool
sigtermCallbacks []func()
baseContext context.Context
contextValues map[interface{}]interface{}
jsonRequestUseNumber bool
jsonRequestDisallowUnknownFields bool
jsonResponseEscapeHTML bool
jsonResponseIndentPrefix string
jsonResponseIndentValue string
enableSIGTERM bool
sigtermCallbacks []func()
}

type Option func(*handlerOptions)
Expand Down Expand Up @@ -99,6 +101,38 @@ func WithSetIndent(prefix, indent string) Option {
})
}

// WithUseNumber sets the UseNumber option on the underlying json decoder
//
// Usage:
//
// lambda.StartWithOptions(
// func (event any) (any, error) {
// return event, nil
// },
// lambda.WithUseNumber(true)
// )
func WithUseNumber(useNumber bool) Option {
return Option(func(h *handlerOptions) {
h.jsonRequestUseNumber = useNumber
})
}

// WithUseNumber sets the DisallowUnknownFields option on the underlying json decoder
//
// Usage:
//
// lambda.StartWithOptions(
// func (event any) (any, error) {
// return event, nil
// },
// lambda.WithDisallowUnknownFields(true)
// )
func WithDisallowUnknownFields(disallowUnknownFields bool) Option {
return Option(func(h *handlerOptions) {
h.jsonRequestDisallowUnknownFields = disallowUnknownFields
})
}

// WithEnableSIGTERM enables SIGTERM behavior within the Lambda platform on container spindown.
// SIGKILL will occur ~500ms after SIGTERM.
// Optionally, an array of callback functions to run on SIGTERM may be provided.
Expand Down Expand Up @@ -289,6 +323,12 @@ func reflectHandler(f interface{}, h *handlerOptions) handlerFunc {
out.Reset()
in := bytes.NewBuffer(payload)
decoder := json.NewDecoder(in)
if h.jsonRequestUseNumber {
decoder.UseNumber()
}
if h.jsonRequestDisallowUnknownFields {
decoder.DisallowUnknownFields()
}
encoder := json.NewEncoder(out)
encoder.SetEscapeHTML(h.jsonResponseEscapeHTML)
encoder.SetIndent(h.jsonResponseIndentPrefix, h.jsonResponseIndentValue)
Expand Down
49 changes: 49 additions & 0 deletions lambda/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"io"
"io/ioutil" //nolint: staticcheck
"reflect"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -309,6 +310,54 @@ func TestInvokes(t *testing.T) {
},
options: []Option{WithSetIndent(">>", " ")},
},
{
name: "WithUseNumber(true) results in json.Number instead of float64 when decoding to an interface{}",
input: `19.99`,
expected: expected{`"Number"`, nil},
handler: func(event interface{}) (string, error) {
return reflect.TypeOf(event).Name(), nil
},
options: []Option{WithUseNumber(true)},
},
{
name: "WithUseNumber(false)",
input: `19.99`,
expected: expected{`"float64"`, nil},
handler: func(event interface{}) (string, error) {
return reflect.TypeOf(event).Name(), nil
},
options: []Option{WithUseNumber(false)},
},
{
name: "No decoder options provided is the same as WithUseNumber(false)",
input: `19.99`,
expected: expected{`"float64"`, nil},
handler: func(event interface{}) (string, error) {
return reflect.TypeOf(event).Name(), nil
},
options: []Option{},
},
{
name: "WithDisallowUnknownFields(true)",
input: `{"Hello": "World"}`,
expected: expected{"", errors.New(`json: unknown field "Hello"`)},
handler: func(_ struct{}) {},
options: []Option{WithDisallowUnknownFields(true)},
},
{
name: "WithDisallowUnknownFields(false)",
input: `{"Hello": "World"}`,
expected: expected{`null`, nil},
handler: func(_ struct{}) {},
options: []Option{WithDisallowUnknownFields(false)},
},
{
name: "No decoder options provided is the same as WithDisallowUnknownFields(false)",
input: `{"Hello": "World"}`,
expected: expected{`null`, nil},
handler: func(_ struct{}) {},
options: []Option{},
},
{
name: "bytes are base64 encoded strings",
input: `"aGVsbG8="`,
Expand Down

0 comments on commit 1da03de

Please sign in to comment.