Skip to content

Commit 94f9231

Browse files
committed
Update example to upload directly to AWS Lambda
1 parent cf05787 commit 94f9231

15 files changed

+455
-79
lines changed

.github/workflows/release.yml

+40-33
Original file line numberDiff line numberDiff line change
@@ -2,54 +2,61 @@ name: Release
22

33
on:
44
release:
5-
types: [prereleased, edited]
5+
types: [published]
66

77
jobs:
8-
build:
9-
name: Build GitHub action and AWS lambda function
8+
test:
9+
name: Test lambda function
1010
runs-on: ubuntu-latest
1111

1212
steps:
1313
- uses: actions/checkout@v2
14-
with:
15-
ref: "main"
1614

1715
- name: Install dependencies
1816
run: yarn install
1917

20-
- name: Get release tag
21-
id: ref
22-
run: |
23-
TAG=${REF#"$PREFIX"}
24-
echo "::set-output name=tag::$TAG"
25-
env:
26-
REF: ${{ github.ref }}
27-
PREFIX: "refs/tags/"
18+
- name: Test
19+
run: yarn test
2820

29-
- name: Update package.json version
30-
run: |
31-
tmp=$(mktemp)
32-
VERSION=${TAG#"v"}
33-
jq ".version = \"$VERSION\"" package.json > "$tmp"
34-
mv "$tmp" package.json
35-
env:
36-
TAG: ${{ steps.ref.outputs.tag }}
21+
build-and-upload:
22+
name: Build GitHub action and AWS lambda function
23+
needs: test
24+
runs-on: ubuntu-latest
25+
26+
steps:
27+
- uses: actions/checkout@v2
28+
29+
- name: Install dependencies
30+
run: yarn install
3731

3832
- name: Build lambda distribution
3933
run: yarn dist
4034

41-
- name: Setup GitHub git user
42-
run: |
43-
git config user.name github-actions
44-
git config user.email [email protected]
35+
- name: Configure AWS credentials
36+
uses: aws-actions/configure-aws-credentials@v1
37+
with:
38+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
39+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
40+
aws-region: ${{ secrets.AWS_REGION }}
4541

46-
- name: Push build
42+
- name: Upload new build to AWS lambda
4743
run: |
48-
git add package.json ping-slack/dist/index.js terraform/lambda.zip
49-
git commit -m "Build app for release"
50-
git push
44+
aws lambda update-function-code --function-name "$FUNCTION_ARN" --zip-file "fileb://$(pwd)/out/lambda.zip"
45+
env:
46+
FUNCTION_ARN: ${{ secrets.LAMBDA_FUNCTION_ARN }}
5147

52-
- name: Move release tag to latest commit
53-
run: |
54-
git tag --force "${{ steps.ref.outputs.tag }}" $(git rev-parse HEAD)
55-
git push --force --tags
48+
test-new-version:
49+
name: Test uploaded lambda
50+
needs: build
51+
runs-on: ubuntu-latest
52+
53+
steps:
54+
- uses: actions/checkout@v2
55+
56+
- name: Install dependencies
57+
run: yarn install
58+
59+
- name: Integration tests
60+
run: yarn test
61+
env:
62+
LAMBDA_URL: ${{ secrets.LAMBDA_URL }}

.github/workflows/tests.yml

+8-8
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ jobs:
3434
env:
3535
AWS_DEFAULT_REGION: ca-central-1 # https://github.com/hashicorp/terraform/issues/21408#issuecomment-495746582
3636

37-
build:
38-
name: Build lambda function
37+
test:
38+
name: Test lambda function
3939
runs-on: ubuntu-latest
4040

4141
steps:
@@ -44,11 +44,11 @@ jobs:
4444
- name: Install dependencies
4545
run: yarn install
4646

47-
- name: Build lambda distribution
48-
run: yarn dist
47+
- name: Test
48+
run: yarn test
4949

50-
test:
51-
name: Test lambda function
50+
build:
51+
name: Build lambda function
5252
runs-on: ubuntu-latest
5353

5454
steps:
@@ -57,5 +57,5 @@ jobs:
5757
- name: Install dependencies
5858
run: yarn install
5959

60-
- name: Test
61-
run: yarn test
60+
- name: Build lambda distribution
61+
run: yarn dist

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
node_modules
22
/dist
3+
/out
34
.terraform
45
yarn-error.log

CONTRIBUTING.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22

33
## Publish a new release
44

5+
- Bump [`package.json`](./package.json) version (ie.: 1.0.0)
56
- Create a new release (ie.: v1.0.0)
6-
- Check `This is a pre-release`
7-
- A GitHub workflow will run and build the code and update the release.
8-
- Once the build is completed, publish the release.
7+
- A GitHub workflow will automatically run, build and upload the new code to AWS Lambda.
98

109
## Testing
1110

@@ -20,4 +19,4 @@ yarn test
2019

2120
From the [Building Lambda functions with Node.js](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html) documentation:
2221

23-
> Your code runs in an environment that includes the AWS SDK for JavaScript, with credentials from an AWS Identity and Access Management (IAM) role that you manage.
22+
> Your code runs in an environment that includes the AWS SDK for JavaScript, with credentials from an AWS Identity and Access Management (IAM) role that you manage.

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Add the module to your [Terraform](https://www.terraform.io/) project:
1010

1111
```terraform
1212
module "terraform_aws_lambda" {
13-
source = "[email protected]:agendrix/terraform-aws-lambda.git//terraform?ref=v0.1.0"
13+
source = "[email protected]:agendrix/terraform-aws-lambda.git//terraform?ref=v0.2.0"
1414
lambda_name = "my-typescript-lambda"
1515
role_arn = aws_iam_role.iam_for_lambda.role_arn
1616
}

lambda/index.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { APIGatewayEvent, APIGatewayProxyStructuredResultV2, Handler } from "aws-lambda";
1+
import { APIGatewayEvent, APIGatewayProxyStructuredResultV2 } from "aws-lambda";
22
import AWS from "aws-sdk";
3+
import { Handler } from "./types";
34

45
const handler: Handler<APIGatewayEvent, APIGatewayProxyStructuredResultV2> = async (event, context) => {
56
const s3 = new AWS.S3();
@@ -14,7 +15,7 @@ const handler: Handler<APIGatewayEvent, APIGatewayProxyStructuredResultV2> = asy
1415
return {
1516
statusCode: 200,
1617
body: JSON.stringify(data),
17-
headers: { "Content-Type": "text/html; charset=utf-8" },
18+
headers: { "content-type": "application/json" },
1819
isBase64Encoded: false,
1920
};
2021
};

lambda/types.d.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { Context } from "aws-lambda";
2+
3+
export type Handler<TEvent = any, TResult = any> = (event: TEvent, context: Context) => Promise<TResult>;

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"name": "terraform-aws-lambda",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"scripts": {
55
"test": "mocha -r ts-node/register test/**/*.spec.ts",
66
"test:watch": "yarn test --watch --watch-extensions ts",
77
"build": "rm -rf ./dist && yarn tsc",
88
"postbuild": "cp package.json yarn.lock LICENSE README.md ./dist && cd dist && yarn install --production",
9-
"zip": "cd dist && zip -q -FSr ../terraform/lambda .",
9+
"zip": "cd dist && mkdir -p ../out && zip -q -FSr ../out/lambda .",
1010
"dist": "yarn build && yarn zip"
1111
},
1212
"devDependencies": {
@@ -16,6 +16,7 @@
1616
"aws-sdk": "^2.785.0",
1717
"mocha": "^6.2.0",
1818
"prettier": "^2.1.2",
19+
"request": "^2.88.2",
1920
"ts-node": "^9.0.0",
2021
"typescript": "^4.0.3"
2122
},

terraform/lambda.zip

-30.1 KB
Binary file not shown.

terraform/main.tf

+11-8
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1-
locals {
2-
zip_path = "${path.module}/lambda.zip"
1+
data "archive_file" "dummy" {
2+
type = "zip"
3+
output_path = "${path.module}/.terraform/dummy_lambda.zip"
4+
5+
source {
6+
content = "hello"
7+
filename = "dummy.txt"
8+
}
39
}
410

511
resource "aws_lambda_function" "lambda" {
6-
filename = local.zip_path
12+
filename = data.archive_file.dummy.output_path
713
function_name = var.lambda_name
814
role = var.role_arn
915
handler = "index.handler"
1016

11-
source_code_hash = filebase64sha256(local.zip_path)
17+
timeout = var.timeout
18+
memory_size = var.memory_size
1219

1320
runtime = "nodejs12.x"
14-
15-
environment {
16-
variables = {}
17-
}
1821
}
1922

2023
resource "aws_cloudwatch_log_group" "log_group" {

terraform/variables.tf

+12
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@ variable "lambda_name" {
33
type = string
44
}
55

6+
variable "timeout" {
7+
description = "Lambda timeout"
8+
type = number
9+
default = 3
10+
}
11+
12+
variable "memory_size" {
13+
description = "Lambda memory size"
14+
type = number
15+
default = 128
16+
}
17+
618
variable "role_arn" {
719
description = "Lambda IAM role"
820
type = string

terraform/versions.tf

+3
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@ terraform {
44
aws = {
55
source = "hashicorp/aws"
66
}
7+
archive = {
8+
source = "hashicorp/archive"
9+
}
710
}
811
}

test/helpers/post-request.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { post, RequestCallback, Response } from "request";
2+
3+
export async function postRequest<Payload>(url: string, payload: Payload) {
4+
return new Promise<Response>((resolve, reject) => {
5+
const callBack: RequestCallback = (error, response) => {
6+
if (error) return reject(error);
7+
return resolve(response);
8+
};
9+
10+
post(url, { json: payload, headers: { "content-type": "application/json" } }, callBack);
11+
});
12+
}

test/index.spec.ts

+37-21
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,49 @@ import assert from "assert";
33
import "./mocks";
44

55
import { __test__ } from "../lambda/index";
6-
import { APIGatewayProxyStructuredResultV2 } from "aws-lambda";
76
import { S3 } from "aws-sdk";
87
import { MOCKED_BUCKETS, MOCKED_OWNER } from "./mocks/libs/aws-sdk";
8+
import { postRequest } from "./helpers/post-request";
99

1010
const handler = __test__.handler;
1111

12-
describe("exports.handler", () => {
13-
const fakeEvent: any = {};
14-
const fakeContext: any = {};
15-
const fakeCallback = () => {};
12+
const LAMBDA_URL = process.env.LAMBDA_URL;
1613

17-
it("returns 200 with Hello World", async () => {
18-
const response = (await handler(fakeEvent, fakeContext, fakeCallback)) as APIGatewayProxyStructuredResultV2;
14+
if (LAMBDA_URL !== undefined) {
15+
/* Integration tests with the deployed lambda */
16+
describe("lambda POST call", () => {
17+
it("returns 200 with Hello World", async () => {
18+
const response = await postRequest(LAMBDA_URL, {});
1919

20-
assert.strictEqual(response.statusCode, 200);
21-
assert.strictEqual(JSON.parse(response.body || "").text, "Hello World");
20+
assert.strictEqual(response.statusCode, 200);
21+
assert.strictEqual(response.headers?.["content-type"], "application/json");
22+
});
2223
});
23-
24-
it("returns a list of S3 buckets", async () => {
25-
const response = (await handler(fakeEvent, fakeContext, fakeCallback)) as APIGatewayProxyStructuredResultV2;
26-
const listBucketsOutput: S3.ListBucketsOutput = JSON.parse(response.body || "").listBuckets;
27-
28-
assert.strictEqual(response.statusCode, 200);
29-
assert.strictEqual(listBucketsOutput.Owner?.DisplayName, MOCKED_OWNER);
30-
assert.deepStrictEqual(
31-
listBucketsOutput.Buckets?.map(b => b.Name),
32-
MOCKED_BUCKETS
33-
);
24+
} else {
25+
/* Local tests */
26+
describe("exports.handler", () => {
27+
const fakeEvent: any = {};
28+
const fakeContext: any = {};
29+
30+
it("returns 200 with Hello World", async () => {
31+
const response = await handler(fakeEvent, fakeContext);
32+
33+
assert.strictEqual(response.statusCode, 200);
34+
assert.strictEqual(response.headers?.["content-type"], "application/json");
35+
assert.strictEqual(JSON.parse(response.body || "").text, "Hello World");
36+
});
37+
38+
it("returns a list of S3 buckets", async () => {
39+
const response = await handler(fakeEvent, fakeContext);
40+
const listBucketsOutput: S3.ListBucketsOutput = JSON.parse(response.body || "").listBuckets;
41+
42+
assert.strictEqual(response.statusCode, 200);
43+
assert.strictEqual(response.headers?.["content-type"], "application/json");
44+
assert.strictEqual(listBucketsOutput.Owner?.DisplayName, MOCKED_OWNER);
45+
assert.deepStrictEqual(
46+
listBucketsOutput.Buckets?.map(b => b.Name),
47+
MOCKED_BUCKETS
48+
);
49+
});
3450
});
35-
});
51+
}

0 commit comments

Comments
 (0)