-
Notifications
You must be signed in to change notification settings - Fork 547
/
wrap.go
105 lines (86 loc) · 3.22 KB
/
wrap.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
package cfn
import (
"context"
"encoding/json"
"errors"
"log"
"net/http"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambdacontext"
)
// CustomResourceLambdaFunction is a standard form Lambda for a Custom Resource.
type CustomResourceLambdaFunction func(context.Context, Event) (reason string, err error)
// SNSCustomResourceLambdaFunction is a standard form Lambda for a Custom Resource
// that is triggered via a SNS topic.
type SNSCustomResourceLambdaFunction func(context.Context, events.SNSEvent) (reason string, err error)
// CustomResourceFunction is a representation of the customer's Custom Resource function.
// LambdaWrap will take the returned values and turn them into a response to be sent
// to CloudFormation.
type CustomResourceFunction func(context.Context, Event) (physicalResourceID string, data map[string]interface{}, err error)
func lambdaWrapWithClient(lambdaFunction CustomResourceFunction, client httpClient) (fn CustomResourceLambdaFunction) {
fn = func(ctx context.Context, event Event) (reason string, err error) {
r := NewResponse(&event)
funcDidPanic := true
defer func() {
if funcDidPanic {
r.Status = StatusFailed
r.Reason = "Function panicked, see log stream for details"
// FIXME: something should be done if an error is returned here
_ = r.sendWith(client)
}
}()
r.PhysicalResourceID, r.Data, err = lambdaFunction(ctx, event)
funcDidPanic = false
if err != nil {
r.Status = StatusFailed
r.Reason = err.Error()
log.Printf("sending status failed: %s", r.Reason)
} else {
r.Status = StatusSuccess
if r.PhysicalResourceID == "" {
log.Println("PhysicalResourceID must exist on creation, copying Log Stream name")
r.PhysicalResourceID = lambdacontext.LogStreamName
}
}
err = r.sendWith(client)
if err != nil {
reason = err.Error()
}
return
}
return
}
// LambdaWrap returns a CustomResourceLambdaFunction which is something lambda.Start()
// will understand. The purpose of doing this is so that Response Handling boiler
// plate is taken away from the customer and it makes writing a Custom Resource
// simpler.
//
// func myLambda(ctx context.Context, event cfn.Event) (physicalResourceID string, data map[string]interface{}, err error) {
// physicalResourceID = "arn:...."
// return
// }
//
// func main() {
// lambda.Start(cfn.LambdaWrap(myLambda))
// }
func LambdaWrap(lambdaFunction CustomResourceFunction) (fn CustomResourceLambdaFunction) {
return lambdaWrapWithClient(lambdaFunction, http.DefaultClient)
}
// LambdaWrapSNS wraps a Lambda handler with support for SNS-based custom
// resources. Usage and purpose otherwise same as LambdaWrap().
func LambdaWrapSNS(lambdaFunction CustomResourceFunction) SNSCustomResourceLambdaFunction {
inner := LambdaWrap(lambdaFunction)
return func(ctx context.Context, event events.SNSEvent) (reason string, err error) {
if len(event.Records) != 1 {
err = errors.New("expected exactly 1 incoming record")
return
}
message := event.Records[0].SNS.Message
var innerEvent Event
if err = json.Unmarshal([]byte(message), &innerEvent); err != nil {
return
}
return inner(ctx, innerEvent)
}
}