Build Cicd Pipeline For Aws Lambda With Terraform, Github & Circleci - Part 1, AWS Lambda
0CICD is a method of incremental deployment of applications to customers by automating application development stages. CI stands for Continuous Integration
while CD is Continuous Delivery
. To make this work, various continuous monitoring tools, test-driven development and core development tasks are brought together in simple automated stages, architected and supported by DevOps or Site Reliability Engineering Teams.
In this series of articles, I am going to show you how I built a simple lambda function with typescript and node, and then created a ci/cd pipeline for the lambda on AWS Cloud using Terraform as the Infrastructure as code tool. Circle CI would help us run tests and ship our code provided all tests pass. The steps are very simple, easy to follow and concise.
Let’s begin.
CREATE A LAMBDA FUNCTION
AWS Lambda, image credit - educative.io
In this section, we would create a lambda function that prints the request header, method and body using Node and Typescript. This function responds based on two event types, either GET OR POST.
The following steps assume that you have git
and node
installed in your development environment:
Initialize a new node project using the npm init -y
command in your terminal
Install the dev dependencies for the project like so:
npm install –save-dev @types/mocha, @types/node aws-lambda chai lambda-tester mocha prettier ts-node typescript
Now to the development proper:
Since we are using typescript, we need to create the header interfaces:
// src/domain/interfaces/response.interface.ts
export type ResponseHeader = { [header: string]: string | number | boolean; }
export interface IResponse {
statusCode: number;
headers: ResponseHeader;
body: string | number | boolean | {[header: string]: string | number | boolean | object};
}
And the response header constant:
// src/domain/constants/headers.constant.ts
import { ResponseHeader } from "../interfaces/response.interface";
export const responseHeaders: ResponseHeader = {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true,
};
Next up, we create an enum to handle our status codes:
// src/domain/enums/statusCode.enum.ts
export enum statusCode {
ok = 200,
badrequest = 400,
notaccepted = 406,
error = 500,
}
We need a factory function to handle our response messages, so we go ahead and create one:
// src/factory/response.factory.ts
import { responseHeaders } from "../domain/constants/headers.constant";
import { IResponse } from "../domain/interfaces/response.interface";
export const createResponse = ({
code = 200,
headers = responseHeaders,
body = {},
}): IResponse => {
return {
statusCode: code,
headers: headers,
body: JSON.stringify(body),
};
};
At this point, we need to create our main logic. This would be made of a simple method that accepts an event and returns a promise which resolves to the result returned from the lambda function:
// src/logic/index.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
import { statusCode } from "../domain/enums/statusCode.enum";
import { createResponse } from "../factory/response.factory";
export const createEvent = async (
event: APIGatewayProxyEvent = {}
): Promise<APIGatewayProxyResult> => {
return createResponse({
code: statusCode.ok,
headers: event.headers,
body: {
response:
"Welcome to the terraform-lambda API, here are the details of your request:",
headers: event.headers,
method: event.httpMethod,
body: event.body,
},
});
};
The function returns a simple JSON response created by the response factory function. We need a simple barrel file to export our lambda function. We create one like so:
// src/index.ts
export { createEvent } from "./logic/index";
We need to write some simple tests for our function. We can do so like this:
// tests/index.ts
import { expect } from "chai";
// lambda tester simplifies the writing of unit tests for lambdas using Nodejs
import lambdaTester from "lambda-tester";
import * as lambda from "../src";
import { responseHeaders } from "../src/domain/constants/headers.constant";
// mock data for creating mock requests
const mockData = {
headers: responseHeaders,
httpMethod: "POST",
body: { username: "xyz", password: "xyz" },
};
const mockData1 = {
headers: responseHeaders,
httpMethod: "GET",
body: { username: "xyz", password: "xyz" },
};
// The tests are pretty descriptive for the POST and GET events. It just creates
// an event and checks that a success response was returned:
describe("EVENT TESTS", () => {
it("should return a successful result: POST", async () => {
await lambdaTester(lambda.createEvent)
.event(mockData)
.expectResult((result) => {
expect(result.statusCode).to.eql(200);
const body = JSON.parse(result.body);
expect(body.response).to.eql(
"Welcome to the terraform-lambda API, here are the details of your request:"
);
expect(body.method).to.eql("POST");
});
});
it("should return a successful result: GET", async () => {
await lambdaTester(lambda.createEvent)
.event(mockData1)
.expectResult((result) => {
expect(result.statusCode).to.eql(200);
const body = JSON.parse(result.body);
expect(body.response).to.eql(
"Welcome to the terraform-lambda API, here are the details of your request:"
);
expect(body.method).to.eql("GET");
});
});
});
To test our functions, we need to include a script in our package.json
:
"scripts": {
…
"test": "npx mocha \"tests/**/**/*.ts\" --require ts-node/register",
…
},
You can now run npm test
to watch the tests pass.
Now, to prepare our code for deployment, we need to build and zip the built files into a build folder using some build scripts in the package.json
file:
"build": "npm run clean && tsc && cp ./package.json ./build/package.json && cd ./build && zip -r terraform-lambda.zip . && cd ..",
"clean": "rm -rf build",
"tsc": “tsc"
To build the code from the source files, we can run npm run build
which removes the old build folder and builds the files with tsc
, then zips the contents of the folder into the root directory as a single terraform-lambda.zip
file.
If you made it to this point with your lambda function correctly setup and tests passing, congratulations!!! In the next article, we will start creating resources for our lambda function in Terraform configuration files.
devops awssite reliability engineering 0 1Great~ Edwin Echenim