-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add Lambda+ApiGateway+Codepipeline example. Fixes #267
This commit adds an example that deploys a Lambda+ApiGateway setup using CodePipeline. Although there's plenty of examples of Lambda already, deploying them via CodePipeline had some non-trivial challenges to sort out. This could mean teams start using CDK and like it, but drop it because they can't adopt Full CI/CD easily when using it
- Loading branch information
Gus El Khoury Seoane
committed
Apr 12, 2020
1 parent
71d87ca
commit d3f0af6
Showing
16 changed files
with
393 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -233,7 +233,6 @@ dist/ | |
downloads/ | ||
eggs/ | ||
.eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
*.js | ||
!jest.config.js | ||
*.d.ts | ||
node_modules | ||
|
||
# CDK asset staging directory | ||
.cdk.staging | ||
cdk.out | ||
*.iml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
*.ts | ||
!*.d.ts | ||
|
||
# CDK asset staging directory | ||
.cdk.staging | ||
cdk.out |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"printWidth": 100, | ||
"semi": false, | ||
"tabWidth": 4 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# What's this? | ||
This is code sample that uses CDK to: | ||
* Create a Lambda function that can be invoked using API Gateway | ||
* Create a CI using CodeSuite that deploys the Lambda+ApiGateway resources using `cdk deploy` | ||
|
||
# How do I start using it? | ||
* Ensure you've followed the [guide to Getting Started to AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html), and you have CDK installed, and the AWS SDK installed and credentials configured. | ||
* [Bootstrap your AWS environment](https://docs.aws.amazon.com/cdk/latest/guide/serverless_example.html#serverless_example_deploy_and_test) | ||
* Create a CodeCommit repository. See [this documentation](https://docs.aws.amazon.com/codecommit/latest/userguide/how-to-create-repository.html) for help. | ||
* Place the contents of this folder inside it | ||
* Set the repository name in the `repositoryName` prop in `bin/ci.ts`. | ||
* Build the stack with `npm run build` | ||
* Deploy the CI stack with `cdk deploy` | ||
* `Todo` summarize permissions | ||
* If you'd like to deploy just the Lambda+ApiGateway stack, you can do so with `cdk deploy -a "npx ts-node bin/lambda.ts"` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/usr/bin/env node | ||
import "source-map-support/register" | ||
import cdk = require("@aws-cdk/core") | ||
import { CIStack } from "../lib/ci-stack" | ||
|
||
const app = new cdk.App() | ||
new CIStack(app, "CDKExampleLambdaApiCIStack", { | ||
repositoryName: "lambda-api-ci", | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#!/usr/bin/env node | ||
import "source-map-support/register" | ||
import cdk = require("@aws-cdk/core") | ||
import { CDKExampleLambdaApiStack } from "../lib/lambda-api-stack" | ||
|
||
export const lambdaApiStackName = "CDKExampleLambdaApiStack" | ||
export const lambdaFunctionName = "CDKExampleWidgetStoreFunction" | ||
|
||
const app = new cdk.App() | ||
new CDKExampleLambdaApiStack(app, lambdaApiStackName, { | ||
functionName: lambdaFunctionName, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
|
||
version: 0.2 | ||
|
||
phases: | ||
install: | ||
runtime-versions: | ||
nodejs: 12 | ||
commands: | ||
- npm install | ||
|
||
build: | ||
commands: | ||
- npm run build | ||
- npm run -- cdk deploy --ci --require-approval never -a "npx ts-node bin/lambda.ts" | ||
artifacts: | ||
files: | ||
- "cdk.out/**/*" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"app": "npx ts-node bin/ci.ts" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module.exports = { | ||
"roots": [ | ||
"<rootDir>/test" | ||
], | ||
testMatch: [ '**/*.test.ts'], | ||
"transform": { | ||
"^.+\\.tsx?$": "ts-jest" | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import { CodeCommitSourceAction, CodeBuildAction } from "@aws-cdk/aws-codepipeline-actions" | ||
import { PolicyStatement } from "@aws-cdk/aws-iam" | ||
import { Construct, Stack, StackProps } from "@aws-cdk/core" | ||
import { PipelineProject, LinuxBuildImage } from "@aws-cdk/aws-codebuild" | ||
import { Artifact, Pipeline } from "@aws-cdk/aws-codepipeline" | ||
import { Repository } from "@aws-cdk/aws-codecommit" | ||
import { lambdaApiStackName, lambdaFunctionName } from "../bin/lambda" | ||
|
||
interface CIStackProps extends StackProps { | ||
repositoryName: string | ||
} | ||
|
||
export class CIStack extends Stack { | ||
constructor(scope: Construct, name: string, props: CIStackProps) { | ||
super(scope, name, props) | ||
|
||
const pipeline = new Pipeline(this, "Pipeline", {}) | ||
|
||
const repo = Repository.fromRepositoryName( | ||
this, | ||
"WidgetsServiceRepository", | ||
props.repositoryName | ||
) | ||
const sourceOutput = new Artifact("SourceOutput") | ||
const sourceAction = new CodeCommitSourceAction({ | ||
actionName: "CodeCommit", | ||
repository: repo, | ||
output: sourceOutput, | ||
}) | ||
pipeline.addStage({ | ||
stageName: "Source", | ||
actions: [sourceAction], | ||
}) | ||
|
||
this.createBuildStage(pipeline, sourceOutput) | ||
} | ||
|
||
private createBuildStage(pipeline: Pipeline, sourceOutput: Artifact) { | ||
const project = new PipelineProject(this, `BuildProject`, { | ||
environment: { | ||
buildImage: LinuxBuildImage.STANDARD_3_0, | ||
}, | ||
}) | ||
|
||
const cdkDeployPolicy = new PolicyStatement() | ||
cdkDeployPolicy.addActions( | ||
"cloudformation:GetTemplate", | ||
"cloudformation:CreateChangeSet", | ||
"cloudformation:DescribeChangeSet", | ||
"cloudformation:ExecuteChangeSet", | ||
"cloudformation:DescribeStackEvents", | ||
"cloudformation:DeleteChangeSet", | ||
"cloudformation:DescribeStacks", | ||
"s3:*Object", | ||
"s3:ListBucket", | ||
"s3:getBucketLocation", | ||
"lambda:UpdateFunctionCode", | ||
"lambda:GetFunction", | ||
"lambda:CreateFunction", | ||
"lambda:DeleteFunction", | ||
"lambda:GetFunctionConfiguration", | ||
"lambda:AddPermission", | ||
"lambda:RemovePermission" | ||
) | ||
cdkDeployPolicy.addResources( | ||
this.formatArn({ | ||
service: "cloudformation", | ||
resource: "stack", | ||
resourceName: "CDKToolkit/*", | ||
}), | ||
this.formatArn({ | ||
service: "cloudformation", | ||
resource: "stack", | ||
resourceName: `${lambdaApiStackName}/*`, | ||
}), | ||
this.formatArn({ | ||
service: "lambda", | ||
resource: "function", | ||
sep: ":", | ||
resourceName: lambdaFunctionName, | ||
}), | ||
"arn:aws:s3:::cdktoolkit-stagingbucket-*" | ||
) | ||
const editOrCreateLambdaDependencies = new PolicyStatement() | ||
editOrCreateLambdaDependencies.addActions( | ||
"iam:GetRole", | ||
"iam:PassRole", | ||
"iam:CreateRole", | ||
"iam:AttachRolePolicy", | ||
"iam:PutRolePolicy", | ||
"apigateway:GET", | ||
"apigateway:DELETE", | ||
"apigateway:PUT", | ||
"apigateway:POST", | ||
"apigateway:PATCH", | ||
"s3:CreateBucket", | ||
"s3:PutBucketTagging" | ||
) | ||
editOrCreateLambdaDependencies.addResources("*") | ||
project.addToRolePolicy(cdkDeployPolicy) | ||
project.addToRolePolicy(editOrCreateLambdaDependencies) | ||
|
||
const buildOutput = new Artifact(`BuildOutput`) | ||
const buildAction = new CodeBuildAction({ | ||
actionName: `Build`, | ||
project, | ||
input: sourceOutput, | ||
outputs: [buildOutput], | ||
}) | ||
|
||
pipeline.addStage({ | ||
stageName: "build", | ||
actions: [buildAction], | ||
}) | ||
|
||
return buildOutput | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { LambdaIntegration, MethodLoggingLevel, RestApi } from "@aws-cdk/aws-apigateway" | ||
import { PolicyStatement } from "@aws-cdk/aws-iam" | ||
import { Function, Runtime, AssetCode, Code } from "@aws-cdk/aws-lambda" | ||
import { Construct, Duration, Stack, StackProps } from "@aws-cdk/core" | ||
import s3 = require("@aws-cdk/aws-s3") | ||
|
||
interface LambdaApiStackProps extends StackProps { | ||
functionName: string | ||
} | ||
|
||
export class CDKExampleLambdaApiStack extends Stack { | ||
private restApi: RestApi | ||
private lambdaFunction: Function | ||
private bucket: s3.Bucket | ||
|
||
constructor(scope: Construct, id: string, props: LambdaApiStackProps) { | ||
super(scope, id, props) | ||
|
||
this.bucket = new s3.Bucket(this, "WidgetStore") | ||
|
||
this.restApi = new RestApi(this, this.stackName + "RestApi", { | ||
deployOptions: { | ||
stageName: "beta", | ||
metricsEnabled: true, | ||
loggingLevel: MethodLoggingLevel.INFO, | ||
dataTraceEnabled: true, | ||
}, | ||
}) | ||
|
||
const lambdaPolicy = new PolicyStatement() | ||
lambdaPolicy.addActions("s3:ListBucket") | ||
lambdaPolicy.addResources(this.bucket.bucketArn) | ||
|
||
this.lambdaFunction = new Function(this, props.functionName, { | ||
functionName: props.functionName, | ||
handler: "handler.handler", | ||
runtime: Runtime.NODEJS_10_X, | ||
code: new AssetCode(`./src`), | ||
memorySize: 512, | ||
timeout: Duration.seconds(10), | ||
environment: { | ||
BUCKET: this.bucket.bucketName, | ||
}, | ||
initialPolicy: [lambdaPolicy], | ||
}) | ||
|
||
this.restApi.root.addMethod("GET", new LambdaIntegration(this.lambdaFunction, {})) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
{ | ||
"name": "lambda-api-ci", | ||
"version": "0.1.0", | ||
"bin": { | ||
"lambda-api-ci": "bin/lambda-api-ci.js" | ||
}, | ||
"scripts": { | ||
"build": "npm run prettier && tsc && npm run build-lambda", | ||
"build-lambda": "cd src && npm run build", | ||
"watch": "tsc -w", | ||
"test": "jest", | ||
"cdk": "cdk", | ||
"prettier": "prettier --write '**/{bin,lib,src,tst}/*.ts'" | ||
}, | ||
"devDependencies": { | ||
"aws-cdk": "*", | ||
"@aws-cdk/core": "*", | ||
"@aws-cdk/assert": "*", | ||
"@aws-cdk/aws-apigateway": "*", | ||
"@aws-cdk/aws-codebuild": "*", | ||
"@aws-cdk/aws-codecommit": "*", | ||
"@aws-cdk/aws-codepipeline": "*", | ||
"@aws-cdk/aws-codepipeline-actions": "*", | ||
"@aws-cdk/aws-cloudformation": "*", | ||
"@types/jest": "^24.0.18", | ||
"@types/node": "^13.7.0", | ||
"jest": "^24.9.0", | ||
"ts-jest": "^24.0.2", | ||
"ts-node": "^8.1.0", | ||
"typescript": "^3.8.3", | ||
"prettier": "^2.0.4" | ||
}, | ||
"dependencies": { | ||
"aws-sdk": "^2.617.0", | ||
"source-map-support": "^0.5.9" | ||
}, | ||
"description": "* `npm run build` compile typescript to js * `npm run watch` watch for changes and compile * `npm run test` perform the jest unit tests * `cdk deploy` deploy this stack to your default AWS account/region * `cdk diff` compare deployed stack with current state * `cdk synth` emits the synthesized CloudFormation template", | ||
"main": "jest.config.js", | ||
"directories": { | ||
"lib": "lib", | ||
"test": "test" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "ISC" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { S3 } from "aws-sdk" | ||
|
||
const bucketName = process.env.BUCKET! | ||
|
||
// From https://docs.aws.amazon.com/cdk/latest/guide/serverless_example.html | ||
const handler = async function (event: any, context: any) { | ||
const S3Client = new S3() | ||
|
||
try { | ||
var method = event.httpMethod | ||
|
||
if (method === "GET") { | ||
if (event.path === "/") { | ||
const data = await S3Client.listObjectsV2({ Bucket: bucketName }).promise() | ||
var body = { | ||
widgets: data.Contents!.map(function (e) { | ||
return e.Key | ||
}), | ||
} | ||
return { | ||
statusCode: 200, | ||
headers: {}, | ||
body: JSON.stringify(body), | ||
} | ||
} | ||
} | ||
|
||
// We only accept GET for now | ||
return { | ||
statusCode: 400, | ||
headers: {}, | ||
body: "We only accept GET /", | ||
} | ||
} catch (error) { | ||
const body = error.stack || JSON.stringify(error, null, 2) | ||
return { | ||
statusCode: 400, | ||
headers: {}, | ||
body: JSON.stringify(body), | ||
} | ||
} | ||
} | ||
|
||
export { handler } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"name": "widgets-lambda", | ||
"version": "1.0.0", | ||
"description": "A simple Lambda function to fetch widgets. See https://docs.aws.amazon.com/cdk/latest/guide/serverless_example.html", | ||
"main": "handler.js", | ||
"scripts": { | ||
"build": "tsc", | ||
"watch": "tsc -w", | ||
"test": "jest" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^13.7.0", | ||
"jest": "^24.9.0", | ||
"ts-jest": "^24.0.2", | ||
"ts-node": "^8.1.0", | ||
"typescript": "~3.8.3" | ||
}, | ||
"dependencies": { | ||
"aws-sdk": "^2.617.0" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/awsdocs/aws-cdk-guide/blob/master/doc_source/serverless_example.md" | ||
}, | ||
"author": "", | ||
"license": "ISC" | ||
} |
Oops, something went wrong.