Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(@whook/example): add AWS Lambda build #108

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,4 @@ builds
*.tfstate.d
*.credentials.json
.nx
lambda_layer.zip
6,710 changes: 1,997 additions & 4,713 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"*.plan",
"*.tfstate.d",
"*.credentials.json",
".nx"
".nx",
"lambda_layer.zip"
],
"rootPackage": true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ DEV_MODE=1
"dependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"@whook/authorization": "<current_version>",
"@whook/aws-lambda": "<current_version>",
"@whook/cors": "<current_version>",
"@whook/http-router": "<current_version>",
"@whook/http-server": "<current_version>",
Expand Down Expand Up @@ -435,6 +436,7 @@ DEV_MODE=1
"dependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"@whook/authorization": "<current_version>",
"@whook/aws-lambda": "<current_version>",
"@whook/cors": "<current_version>",
"@whook/http-router": "<current_version>",
"@whook/http-server": "<current_version>",
Expand Down Expand Up @@ -780,6 +782,7 @@ DEV_MODE=1
"dependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"@whook/authorization": "<current_version>",
"@whook/aws-lambda": "<current_version>",
"@whook/cors": "<current_version>",
"@whook/http-router": "<current_version>",
"@whook/http-server": "<current_version>",
Expand Down Expand Up @@ -1108,6 +1111,7 @@ DEV_MODE=1
"dependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"@whook/authorization": "<current_version>",
"@whook/aws-lambda": "<current_version>",
"@whook/cors": "<current_version>",
"@whook/http-router": "<current_version>",
"@whook/http-server": "<current_version>",
Expand Down
4 changes: 4 additions & 0 deletions packages/whook-create/src/services/createWhook.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describe('initCreateWhook', () => {
dependencies: {
..._packageJSON.dependencies,
'@whook/authorization': '<current_version>',
'@whook/aws-lambda': '<current_version>',
'@whook/cors': '<current_version>',
'@whook/http-router': '<current_version>',
'@whook/http-server': '<current_version>',
Expand Down Expand Up @@ -160,6 +161,7 @@ describe('initCreateWhook', () => {
"dependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"@whook/authorization": "<current_version>",
"@whook/aws-lambda": "<current_version>",
"@whook/cors": "<current_version>",
"@whook/http-router": "<current_version>",
"@whook/http-server": "<current_version>",
Expand Down Expand Up @@ -355,6 +357,7 @@ describe('initCreateWhook', () => {
"dependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"@whook/authorization": "<current_version>",
"@whook/aws-lambda": "<current_version>",
"@whook/cors": "<current_version>",
"@whook/http-router": "<current_version>",
"@whook/http-server": "<current_version>",
Expand Down Expand Up @@ -535,6 +538,7 @@ describe('initCreateWhook', () => {
"dependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"@whook/authorization": "<current_version>",
"@whook/aws-lambda": "<current_version>",
"@whook/cors": "<current_version>",
"@whook/http-router": "<current_version>",
"@whook/http-server": "<current_version>",
Expand Down
77 changes: 77 additions & 0 deletions packages/whook-example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,70 @@ npm run dev -- ls
npx whook ls
```

## Deploying with AWS Lambda

### Pushing to S3

First of all, push the lambdas to S3. If you prefer to use it with your own architecture later, you can stop at that step:

```sh
NODE_ENV=production APP_ENV=staging npm run build
APP_ENV=staging bin/lambdas_zip.sh
APP_ENV=staging bin/lambdas_push.sh
```

### Deploy with Terraform

Otherwise, here is a full step by step setup for you.

First install Terraform:

```sh
wget https://releases.hashicorp.com/terraform/1.2.2/terraform_1.2.2_linux_amd64.zip
mkdir .bin
unzip -d .bin terraform_1.2.2_linux_amd64.zip
rm terraform_1.2.2_linux_amd64.zip
```

Then initialize the Terraform configuration:

```sh
cd ./terraform
../.bin/terraform init;
```

Create a new workspace for each `APP_ENV`:

```sh
../.bin/terraform workspace new staging
```

Build the lambdas layer:

```sh
NODE_ENV=production bin/lambda_layer.sh
```

Plan the deployment:

```sh
../.bin/terraform plan -out=terraform.plan -var "node_env=${NODE_ENV}"
```

Apply changes:

```sh
../.bin/terraform apply -var "node_env=${NODE_ENV}" terraform.plan
```

Finally retrieve the API URL and add and enjoy!

```sh
../.bin/terraform output api_url
curl "$(.bin/terraform output api_url)staging/v3/ping"
# {"pong":"pong"}
```

Generate API types:

```sh
Expand All @@ -102,6 +166,19 @@ Debug `knifecycle` internals (dependency injection issues):
DEBUG=knifecycle npm run dev
```

Debug built lambdas:

```sh
## HTTP
APP_ENV=staging npx whook testHTTPLambda --name putEcho \
--parameters '{ "body": { "echo": "Hey!" } }'
## Cron
APP_ENV=staging npx whook testCronLambda --name handleMinutes
## Consumer
APP_ENV=staging npx whook testConsumerLambda --name handleMessages \
--event '{ "Records": [{ "test": "test" }] }'
```

[//]: # (::contents:end)

# Authors
Expand Down
14 changes: 14 additions & 0 deletions packages/whook-example/bin/lambda_layer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
mkdir -p layer/nodejs;
cp package.json layer/nodejs/package.json;
cp package-lock.json layer/nodejs/package-lock.json;
docker run --entrypoint "" -v "$PWD/layer/nodejs":/var/task "public.ecr.aws/lambda/nodejs:20" /bin/sh -c "
dnf update;
dnf install -y gcc gcc-c++ make;
mkdir .npm;
HOME=$(pwd);
npm i --production;
rm -rf .npm;
exit";
env --chdir "$PWD/layer" zip -r ../lambda_layer.zip .;
docker run --entrypoint "" -v "$PWD/layer/nodejs":/var/task "public.ecr.aws/lambda/nodejs:20" /bin/sh -c "rm -rf node_modules; exit";
rm -rf layer/nodejs;
13 changes: 13 additions & 0 deletions packages/whook-example/bin/lambda_push.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash
set -e -o pipefail -u

echo "Sending Signatures..."
aws s3 cp \
--content-type text/plain --recursive \
--exclude "*.zip" --include "*.base64sha256" \
lambdas/$APP_ENV s3://diagrams-lambdas-release-$APP_ENV/;

echo "Sending ZIP files..."
aws s3 cp --recursive --exclude "*.base64sha256" \
--include "*.zip" lambdas/$APP_ENV \
s3://diagrams-lambdas-release-$APP_ENV/
15 changes: 15 additions & 0 deletions packages/whook-example/bin/lambdas_zip.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash
set -e -o pipefail -u

APP_ENV=${APP_ENV:-local}
FILES=$(ls "builds/$APP_ENV")

rm -rf "lambdas/$APP_ENV"
mkdir -p "lambdas/$APP_ENV"

for f in $FILES
do
echo "Processing $f file..."
zip -jr "lambdas/$APP_ENV/$f.zip" builds/$APP_ENV/$f/*
openssl dgst -sha256 -binary "lambdas/$APP_ENV/$f.zip" | openssl enc -base64 | tr -d "\n" > "lambdas/$APP_ENV/$f.zip.base64sha256"
done
10 changes: 9 additions & 1 deletion packages/whook-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@
"testsFiles": "'src/**/*.test.ts'",
"distFiles": "'dist/**/*.js'",
"ignore": [
"dist"
"dist",
"builds",
".bin",
".terraform",
"*.plan",
"*.tfstate.d",
"*.credentials.json",
"lambda_layer.zip"
],
"bundleFiles": [
"bin",
Expand Down Expand Up @@ -80,6 +87,7 @@
"dependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"@whook/authorization": "^18.0.0",
"@whook/aws-lambda": "^18.0.1",
"@whook/cors": "^18.0.0",
"@whook/http-router": "^18.0.0",
"@whook/http-server": "^18.0.0",
Expand Down
57 changes: 57 additions & 0 deletions packages/whook-example/src/build.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { describe, beforeAll, test } from '@jest/globals';
import { exec } from 'child_process';
import { YError } from 'yerror';

describe('build should work', () => {
let env = 'JWT_SECRET=test ';

beforeAll(async () => {
const { stdout } = await execCommand(
`sed -e 's/^//' .env.${process.env.NODE_ENV} || echo ""`,
);

env += stdout.trim();
});

[
['getPing', '{}'],
['getOpenAPI', '{}'],
[
'getParameters',
'{ "aHeader": "true", "pathParam1":"4", "pathParam2":"a,b,c,d" }',
],
['getTime', '{}'],
['getDelay', '{ "duration": 1 }'],
['putEcho', '{"body": { "echo": "YOLO!" }}'],
].forEach(([operationId, parameters]) => {
test(`with ${operationId} http lambdas`, async () => {
await execCommand(
`${env} npx whook testHTTPLambda --name ${operationId} --parameters '${parameters}'`,
);
});
});

test(`with cron lambdas`, async () => {
await execCommand(`${env} npx whook testCronLambda --name handleMinutes`);
});

test(`with consumer lambdas`, async () => {
await execCommand(
`${env} npx whook testConsumerLambda --name handleMessages --event '{ "Records": [{ "test": "test" }] }'`,
);
});
});

async function execCommand(
command,
): Promise<{ stdout: string; stderr: string }> {
return new Promise((resolve, reject) => {
exec(command, (err, stdout, stderr) => {
if (err) {
reject(YError.wrap(err, 'E_COMMAND_FAILURE', stdout, stderr));
return;
}
resolve({ stdout, stderr });
});
});
}
6 changes: 3 additions & 3 deletions packages/whook-example/src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ Per convention a Whook server build file must export
*/
import { Knifecycle, constant, alsoInject } from 'knifecycle';
import { prepareEnvironment } from './index.js';
import { initBuildConstants } from '@whook/whook';
import {
DEFAULT_BUILD_INITIALIZER_PATH_MAP,
initBuildConstants,
runBuild as runBaseBuild,
prepareBuildEnvironment as prepareBaseBuildEnvironment,
} from '@whook/whook';
} from '@whook/aws-lambda';

/* Architecture Note #1.2.1: The `runBuild` function

Expand All @@ -35,7 +35,7 @@ export async function prepareBuildEnvironment<T extends Knifecycle>(
$ = await prepareEnvironment($);

// Usually, here you call the installed build env
$ = await prepareBaseBuildEnvironment($);
$ = await prepareBaseBuildEnvironment<T>($);

// The build often need to know were initializers
// can be found to create a static build and
Expand Down
19 changes: 15 additions & 4 deletions packages/whook-example/src/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@ describe('commands should work', () => {
);

expect({
stdout: stdout.replace(/( |"|')([^ ]+)\/whook\//g, ' /whook/'),
stderr: stderr.replace(/( |"|')([^ ]+)\/whook\//g, ' /whook/'),
}).toMatchInlineSnapshot(`
stdout: stdout.replace(/( |"|')([^ ]+)\/whook\//g, ' /whook/'),
stderr: stderr.replace(/( |"|')([^ ]+)\/whook\//g, ' /whook/')
}).toMatchInlineSnapshot(`
{
"stderr": "⚡ - Loading configurations from /whook/packages/whook-example/dist/config/local/config.js".
🤖 - Initializing the \`$autoload\` service.
On air 🚀🌕
",
"stdout": "

# Provided by "@whook/example": 1 commands
# Provided by "@whook/example": 2 commands
- printEnv: A command printing every env values
- terraformValues: A command printing lambdas informations for Terraform


# Provided by "@whook/whook": 8 commands
Expand All @@ -38,6 +39,16 @@ On air 🚀🌕


# Provided by "@whook/authorization": none


# Provided by "@whook/aws-lambda": 7 commands
- testConsumerLambda: A command for testing AWS consumer lambda
- testCronLambda: A command for testing AWS cron lambda
- testHTTPLambda: A command for testing AWS HTTP lambda
- testKafkaConsumerLambda: A command for testing AWS lambda Kafka consumers
- testS3Lambda: A command for testing AWS consumer lambda
- testS3Lambda: A command for testing AWS consumer lambda
- testTransformerLambda: A command for testing AWS lambda transformers
",
}
`);
Expand Down
Loading
Loading