Skip to content

Commit 5c47980

Browse files
authored
Http/202 returned from Logs API call when extension is invoked locally (#37)
1 parent d6e40f5 commit 5c47980

File tree

38 files changed

+503
-50
lines changed

38 files changed

+503
-50
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,6 @@ csharp-*/*.zip
225225
# VSCode
226226
*/.project
227227
*/.settings/
228+
229+
# SAM
230+
.aws-sam/
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
build:
2+
GOOS=linux GOARCH=amd64 go build -o bin/extensions/go-example-logs-api-extension main.go
3+
4+
build-GoExampleLogsApiExtensionLayer:
5+
GOOS=linux GOARCH=amd64 go build -o $(ARTIFACTS_DIR)/extensions/go-example-logs-api-extension main.go
6+
chmod +x $(ARTIFACTS_DIR)/extensions/go-example-logs-api-extension
7+
8+
run-GoExampleLogsApiExtensionLayer:
9+
go run go-example-logs-api-extension/main.go

go-example-logs-api-extension/agent/http.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"time"
1414

1515
"aws-lambda-extensions/go-example-logs-api-extension/logsapi"
16+
1617
"github.com/golang-collections/go-datastructures/queue"
1718
)
1819

@@ -64,6 +65,8 @@ func (h *LogsApiHttpListener) http_handler(w http.ResponseWriter, r *http.Reques
6465
return
6566
}
6667

68+
fmt.Println("Logs API event received:", string(body))
69+
6770
// Puts the log message into the queue
6871
err = h.logQueue.Put(string(body))
6972
if err != nil {

go-example-logs-api-extension/agent/logger.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ func NewS3Logger() (*S3Logger, error) {
7474
bucket, present := os.LookupEnv("LOGS_API_EXTENSION_S3_BUCKET")
7575
if !present {
7676
return nil, errors.New("Environment variable LOGS_API_EXTENSION_S3_BUCKET is not set.")
77+
} else {
78+
fmt.Println("Sending logs to:", bucket)
7779
}
7880
ts := int(time.Now().UnixNano() / 1000000)
7981
timestampMilli := strconv.Itoa(ts)

go-example-logs-api-extension/extension/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func (e *Client) Register(ctx context.Context, filename string) (*RegisterRespon
104104
return nil, err
105105
}
106106
e.ExtensionID = httpRes.Header.Get(extensionIdentiferHeader)
107-
print(e.ExtensionID)
107+
fmt.Println("Extension id:", e.ExtensionID)
108108
return &res, nil
109109
}
110110

go-example-logs-api-extension/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ require (
88
github.com/google/uuid v1.1.2
99
github.com/pkg/errors v0.9.1
1010
github.com/sirupsen/logrus v1.7.0
11+
github.com/uudashr/gopkgs/v2 v2.1.2 // indirect
1112
)

go-example-logs-api-extension/go.sum

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
github.com/aws/aws-sdk-go v1.35.17/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
2+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/golang-collections/go-datastructures v0.0.0-20150211160725-59788d5eb259/go.mod h1:9Qcha0gTWLw//0VNka1Cbnjvg3pNKGFdAm7E9sBabxE=
5+
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
6+
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
7+
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
8+
github.com/karrick/godirwalk v1.12.0 h1:nkS4xxsjiZMvVlazd0mFyiwD4BR9f3m6LXGhM2TUx3Y=
9+
github.com/karrick/godirwalk v1.12.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
10+
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
11+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
12+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
13+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
14+
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
15+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
16+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
17+
github.com/uudashr/gopkgs v1.3.2 h1:ACme7LZyeSNIRIl9HtAA0RsT0eePUsrkHDVb2+aswhg=
18+
github.com/uudashr/gopkgs/v2 v2.1.2 h1:A0+QH6wqNRHORJnxmqfeuBEsK4nYQ7pgcOHhqpqcrpo=
19+
github.com/uudashr/gopkgs/v2 v2.1.2/go.mod h1:O9VKOuPWrUpVhaxcg7N3QiTrlDhgJb/84Y7b3qaX1rI=
20+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
21+
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
22+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
23+
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
24+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
25+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
26+
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use strict';
2+
3+
exports.lambdaHandler = async (event, context) => {
4+
console.log('Inside Lambda function');
5+
6+
return {
7+
'statusCode': 200,
8+
'body': JSON.stringify({
9+
message: 'hello world',
10+
})
11+
};
12+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "hello-world",
3+
"version": "1.0.0",
4+
"main": "app.js",
5+
"license": "MIT-0"
6+
}

go-example-logs-api-extension/logsapi/client.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,9 @@ func (c *Client) Subscribe(types []EventType, bufferingCfg BufferingCfg, destina
120120
}
121121
defer resp.Body.Close()
122122

123-
if resp.StatusCode != http.StatusOK {
123+
if resp.StatusCode == http.StatusAccepted {
124+
fmt.Println("WARNING!!! Logs API is not supported! Is this extension running in a local sandbox?")
125+
} else if resp.StatusCode != http.StatusOK {
124126
body, err := ioutil.ReadAll(resp.Body)
125127
if err != nil {
126128
return nil, errors.Errorf("%s failed: %d[%s]", url, resp.StatusCode, resp.Status)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
AWSTemplateFormatVersion: 2010-09-09
2+
Transform: AWS::Serverless-2016-10-31
3+
Description: >
4+
go-example-logs-api-extension
5+
6+
Sample SAM Template for go-example-logs-api-extension
7+
8+
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
9+
Globals:
10+
Function:
11+
Timeout: 5
12+
13+
Resources:
14+
15+
GoExampleLogsApiExtensionLayer:
16+
Type: AWS::Serverless::LayerVersion
17+
Metadata:
18+
BuildMethod: makefile
19+
Properties:
20+
LayerName: go-example-logs-api-extension
21+
Description: Go Example Logs API Lambda Extension Layer
22+
ContentUri: .
23+
CompatibleRuntimes:
24+
- nodejs12.x
25+
- python3.8
26+
- java11
27+
- dotnetcore3.1
28+
LicenseInfo: MIT-0
29+
RetentionPolicy: Delete
30+
31+
HelloWorldFunction:
32+
Type: AWS::Serverless::Function
33+
Properties:
34+
FunctionName: go-example-logs-api-extension-demo-function
35+
CodeUri: hello-world/
36+
Handler: app.lambdaHandler
37+
Runtime: nodejs12.x
38+
Layers:
39+
- !Ref GoExampleLogsApiExtensionLayer
40+
Environment:
41+
Variables:
42+
LOGS_API_EXTENSION_S3_BUCKET:
43+
Ref: LogExtensionsBucket
44+
Policies:
45+
- S3FullAccessPolicy:
46+
BucketName: !Ref LogExtensionsBucket
47+
48+
LogExtensionsBucket:
49+
Type: 'AWS::S3::Bucket'
50+
Properties:
51+
LifecycleConfiguration:
52+
Rules:
53+
- Id: DeleteAfterSevenDays
54+
Status: "Enabled"
55+
ExpirationInDays: 7
56+
57+
Outputs:
58+
GoExampleLogsApiExtensionLayer:
59+
Description: Go Example Lambda Extension Layer Version ARN
60+
Value: !Ref GoExampleLogsApiExtensionLayer
61+
HelloWorldFunction:
62+
Description: First Lambda Function ARN
63+
Value: !GetAtt HelloWorldFunction.Arn
64+
HelloWorldFunctionIamRole:
65+
Description: Implicit IAM Role created for Hello World function
66+
Value: !GetAtt HelloWorldFunctionRole.Arn
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
# nodejs dependencies
22
node_modules
3+
4+
# zip
5+
*.zip
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
build-NodeJSExampleExtensionLayer:
2+
cp -R extensions "$(ARTIFACTS_DIR)"
3+
cp -R nodejs-example-logs-api-extension "$(ARTIFACTS_DIR)"

nodejs-example-logs-api-extension/README.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,47 @@
11
# Example Logs API Extension in Node.js
2+
23
The provided code sample demonstrates how to get a basic Logs API extension written in Node.js 12 up and running.
34

45
> Note: This extension requires the Node.js 12 runtime to be present in the Lambda execution environment of your function.
56
67
There are two components to this sample:
8+
79
* `extensions/`: This sub-directory should be extracted to /opt/extensions where the Lambda platform will scan for executables to launch extensions
810
* `nodejs-example-logs-api-extension/`: This sub-directory should be extracted to /opt/nodejs-example-logs-api-extension which is referenced by the `extensions/nodejs-example-logs-api-extension` executable and includes a nodejs executable along with all of its necessary dependencies.
911

1012
## Prep Extension Dependencies
13+
1114
Install the extension dependencies locally, which will be mounted along with the extension code.
1215

1316
```bash
14-
$ cd nodejs-example-logs-api-extension
15-
$ chmod +x index.js
16-
$ npm install
17-
$ cd ..
17+
cd nodejs-example-logs-api-extension
18+
chmod +x index.js
19+
npm install
20+
cd ..
1821
```
1922

2023
## Layer Setup Process
24+
2125
The extensions .zip file should contain a root directory called `extensions/`, where the extension executables are located and another root directory called `nodejs-example-logs-api-extension/`, where the core logic of the extension and its dependencies are located.
2226

2327
Creating zip package for the extension:
28+
2429
```bash
25-
$ chmod +x extensions/nodejs-example-logs-api-extension
26-
$ zip -r extension.zip .
30+
chmod +x extensions/nodejs-example-logs-api-extension
31+
zip -r extension.zip ./nodejs-example-logs-api-extension
32+
zip -r extension.zip ./extensions
2733
```
2834

2935
Ensure that you have aws-cli v2 for the commands below.
3036
Publish a new layer using the `extension.zip`. The output of the following command should provide you a layer arn.
37+
3138
```bash
3239
aws lambda publish-layer-version \
3340
--layer-name "nodejs-example-logs-api-extension" \
3441
--region <use your region> \
3542
--zip-file "fileb://extension.zip"
3643
```
44+
3745
Note the LayerVersionArn that is produced in the output.
3846
eg. `"LayerVersionArn": "arn:aws:lambda:<region>:123456789012:layer:<layerName>:1"`
3947

nodejs-example-logs-api-extension/deploy.sh

100644100755
Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
cd nodejs-example-logs-api-extension
22
chmod +x index.js
33
npm install
4-
cd ..
4+
cd -
55

66
chmod +x extensions/nodejs-example-logs-api-extension
7-
zip -r extension.zip .
7+
8+
archive="extension.zip"
9+
if [ -f $archive ] ; then
10+
rm $archive
11+
fi
12+
13+
zip -r $archive ./nodejs-example-logs-api-extension
14+
zip -r $archive ./extensions
815

916
aws lambda publish-layer-version \
1017
--layer-name "nodejs-example-logs-api-extension" \
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use strict';
2+
3+
exports.lambdaHandler = async (event, context) => {
4+
console.log('Inside Lambda function');
5+
6+
return {
7+
'statusCode': 200,
8+
'body': JSON.stringify({
9+
message: 'hello world',
10+
})
11+
};
12+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "hello-world",
3+
"version": "1.0.0",
4+
"main": "app.js",
5+
"license": "MIT-0"
6+
}

nodejs-example-logs-api-extension/nodejs-example-logs-api-extension/http-listener.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
const http = require('http')
1+
const http = require('http');
22

3-
function listen(port) {
3+
function listen(address, port) {
44
const logsQueue = [];
55
// init HTTP server for the Logs API subscription
66
const server = http.createServer(function(request, response) {
@@ -28,9 +28,10 @@ function listen(port) {
2828
response.end("OK");
2929
}
3030
});
31-
server.listen(port, "sandbox");
32-
console.log(`Listening for logs at http://sandbox:${port}`);
33-
return { logsQueue };
31+
32+
server.listen(port, address);
33+
console.log(`Listening for logs at http://${address}:${port}`);
34+
return { logsQueue, server };
3435
}
3536

3637
module.exports = {

nodejs-example-logs-api-extension/nodejs-example-logs-api-extension/index.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ function handleInvoke(event) {
3333
console.log('invoke');
3434
}
3535

36+
const LOCAL_DEBUGGING_IP = "0.0.0.0";
37+
const RECEIVER_NAME = "sandbox";
38+
39+
async function receiverAddress() {
40+
return (process.env.AWS_SAM_LOCAL === 'true')
41+
? LOCAL_DEBUGGING_IP
42+
: RECEIVER_NAME;
43+
}
44+
3645
const BUCKET_NAME = process.env.LOGS_S3_BUCKET_NAME;
3746
const FUNCTION_NAME = process.env.AWS_LAMBDA_FUNCTION_NAME;
3847

@@ -45,7 +54,7 @@ const MAX_ITEMS = 10000 // Maximum number of events that are buffered in memory.
4554
const SUBSCRIPTION_BODY = {
4655
"destination":{
4756
"protocol": "HTTP",
48-
"URI": "http://sandbox:"+RECEIVER_PORT,
57+
"URI": `http://${RECEIVER_NAME}:${RECEIVER_PORT}`,
4958
},
5059
"types": ["platform", "function"],
5160
"buffering": {
@@ -67,11 +76,11 @@ const SUBSCRIPTION_BODY = {
6776

6877
console.log('starting listener');
6978
// listen returns `logsQueue`, a mutable array that collects logs received from Logs API
70-
const { logsQueue } = listen(RECEIVER_PORT);
79+
const { logsQueue, server } = listen(await receiverAddress(), RECEIVER_PORT);
7180

7281
console.log('subscribing listener');
7382
// subscribing listener to the Logs API
74-
await subscribe(extensionId, SUBSCRIPTION_BODY);
83+
await subscribe(extensionId, SUBSCRIPTION_BODY, server);
7584

7685
// function for processing collected logs
7786
async function uploadLogs() {

nodejs-example-logs-api-extension/nodejs-example-logs-api-extension/logs-api.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const fetch = require('node-fetch');
22

33
const baseUrl = `http://${process.env.AWS_LAMBDA_RUNTIME_API}/2020-08-15/logs`;
44

5-
async function subscribe(extensionId, subscriptionBody) {
5+
async function subscribe(extensionId, subscriptionBody, server) {
66
const res = await fetch(baseUrl, {
77
method: 'put',
88
body: JSON.stringify(subscriptionBody),
@@ -12,13 +12,20 @@ async function subscribe(extensionId, subscriptionBody) {
1212
}
1313
});
1414

15-
if (!res.ok) {
16-
console.error('logs subscription failed', await res.text());
17-
} else {
18-
console.error('logs subscription ok', await res.text());
15+
switch(res.status) {
16+
case 200:
17+
console.info('logs subscription ok: ', await res.text());
18+
break;
19+
case 202:
20+
console.warn('WARNING!!! Logs API is not supported! Is this extension running in a local sandbox?');
21+
break;
22+
default:
23+
console.error('logs subscription failed: ', await res.text());
24+
break;
1925
}
2026
}
2127

2228
module.exports = {
2329
subscribe,
2430
};
31+

0 commit comments

Comments
 (0)