Skip to content
This repository was archived by the owner on Mar 12, 2025. It is now read-only.

Commit d21740e

Browse files
author
Sachin Maheshwari
committed
deploying available code with CI/CD
0 parents  commit d21740e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+5274
-0
lines changed

.circleci/config.yml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
version: 2
2+
3+
default: &default
4+
docker:
5+
- image: circleci/node:8.9.4-stretch
6+
installation_dependency: &installation_dependency
7+
name: Install Serverless and AWS CLI
8+
command: |
9+
sudo apt-get update -y && sudo apt-get install -qq -y python-pip libpython-dev
10+
curl -O https://bootstrap.pypa.io/get-pip.py && sudo python get-pip.py
11+
#installing awscli
12+
sudo pip install awscli --upgrade
13+
#install serverless
14+
sudo npm install -g try-thread-sleep
15+
sudo npm install -g serverless --ignore-scripts spawn-sync
16+
install_deploysuite: &install_deploysuite
17+
name: Installation of install_deploysuite.
18+
command: |
19+
git clone --branch master https://github.com/topcoder-platform/tc-deploy-scripts ../buildscript
20+
cp ./../buildscript/master_deploy.sh .
21+
cp ./../buildscript/buildenv.sh .
22+
cp ./../buildscript/awsconfiguration.sh .
23+
restore_cache: &restore_cache
24+
key: docker-node-{{ checksum "package-lock.json" }}
25+
install_npm: &install_npm
26+
name: Install node_modules
27+
command: |
28+
npm install
29+
save_cache: &save_cache
30+
key: docker-node-{{ checksum "package-lock.json" }}
31+
paths:
32+
- node_modules
33+
build_steps: &build_steps
34+
# Initialization.
35+
- checkout
36+
- run: *installation_dependency
37+
- run: *install_deploysuite
38+
# Restoration of node_modules from cache.
39+
- restore_cache: *restore_cache
40+
- run: *install_npm
41+
# Caching node modules.
42+
- save_cache: *save_cache
43+
# deploy app
44+
- run:
45+
name: Deploy via Masterscript v2
46+
command: |
47+
./awsconfiguration.sh $DEPLOY_ENV
48+
source awsenvconf
49+
./buildenv.sh -e $DEPLOY_ENV -b ${LOGICAL_ENV}-lambda-member-v5user-skill-sync-deployvar
50+
source buildenvvar
51+
./master_deploy.sh -d LAMBDA -e $DEPLOY_ENV -s ${LOGICAL_ENV}-lambda-member-v5user-skill-sync-appvar
52+
53+
jobs:
54+
# Build & Deploy against development backend rera212
55+
"build-dev":
56+
<<: *default
57+
environment:
58+
DEPLOY_ENV: "DEV"
59+
LOGICAL_ENV: "dev"
60+
# deploy app
61+
steps: *build_steps
62+
63+
64+
"build-prod":
65+
<<: *default
66+
environment:
67+
DEPLOY_ENV: "PROD"
68+
LOGICAL_ENV: "prod"
69+
# deploy app
70+
steps: *build_steps
71+
72+
workflows:
73+
version: 2
74+
build:
75+
jobs:
76+
# Development builds are executed on "develop" branch only.
77+
- "build-dev":
78+
context : org-global
79+
filters:
80+
branches:
81+
only:
82+
- develop
83+
# production builds are executed on "master" branch only.
84+
- "build-prod":
85+
context : org-global
86+
filters:
87+
branches:
88+
only:
89+
- master

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
10.16.3

README.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# member-skills-sync-lambda
2+
3+
## Prerequisites
4+
1. Amazon DynamoDB table for the streaming and sending member skills data to Amazon Lambda exists and the ARN is known (please enable the streaming on DynamoDB with View type "New and old images" - see screen-shot in Verification Document if required).
5+
2. Amazon ElasticSearch domain for posting the member skills data already exists and endpoint is known.
6+
3. AWS Lambda IAM role that has the privilege (permission policies) to read the stream from DynamodDB already exists and ARN is known.
7+
4. Amazon ElasticSearch domain access policy has been configured so that AWS Lambda IAM role can do necessary action to post data to ElasticSearch and for unit test purpose, either using IP address or Serverless Agent IAM User below is given access to post data to ElasticSearch.
8+
5. Serverless Framework has been installed. If not yet done, please follow this [link](https://serverless.com/framework/docs/providers/aws/guide/installation/).
9+
6. Serverless Agent IAM User has been configured properly and its access key ID and its agent secret access key is known, please follow this [link](https://serverless.com/framework/docs/providers/aws/guide/credentials/) to setup one if not yet done.
10+
7. All the instructions below are using Node v10.16.3.
11+
12+
## Environment Variables
13+
Please follow this instruction to setup the required environment variables. We are using Linux terminal in this example.
14+
15+
First you need to setup environment variables for AWS credentials. For review purpose, actually you don't need to create a new user for severless agent, just use your AWS account's access key and secret access key which already have full-access to all AWS services.
16+
```
17+
export AWS_ACCESS_KEY_ID=<your access key id>
18+
export AWS_SECRET_ACCESS_KEY=<your secret access key>
19+
```
20+
For the sake of unit testing to run, running your lambda function locally, and deploying your lambda's environment variables, please also setup 9 more environment variables below in your terminal (using export command same as above).
21+
1. ES_URL --> Endpoint of your elastic search
22+
2. ES_REGION --> Region of your elastic search e.g. us-east-1
23+
3. ES_SKILLS_INDEX --> ElasticSearch Index for member entered skills documents
24+
4. ES_SKILLS_MAPPING --> ElasticSearch document type for member entered skills index
25+
7. DB_ENTERED_SKILLS_STREAM --> ARN of DynamoDB table MemberEnteredSkills stream
26+
8. DB_AGGREGATED_SKILLS_STREAM --> ARE of DynamoDB table MemberAggregatedSkills stream
27+
9. LAMBDA_ROLE --> ARN of IAM Role to be used when executing Lambda
28+
29+
## Change the serverless.yml to fit your environment
30+
Please open serverless.yml from the project and modify following value in case you don't want to use environment variables:
31+
1. role (under index.syncSkills) --> ARN of your AWS Lambda IAM Role
32+
2. arn (under stream) --> ARN of your DynamoDB table stream
33+
3. ES_* (under environment) --> This is by default populated by environment variables, feel free to change if needed
34+
4. DB_* (under environment) --> This is by default populated by environment variables, feel free to change if needed
35+
5. Please also modify stage and region to fit your needs
36+
37+
## Install Dependencies and Run Lint
38+
1. run the following command to install the dependencies
39+
```
40+
npm install
41+
```
42+
2. To run linter if required
43+
```
44+
npm run lint
45+
46+
npm run lint:fix # To fix possible lint errors
47+
```
48+
49+
## Unit Test
50+
Make sure you have properly setup the [environment variables](#environment-variables) and [installing dependencies](#install-dependencies-and-run-lint). Run `npm run test` to run the unit test.
51+
52+
## Run lambda locally
53+
1. Make sure you have properly setup the [environment variables](#environment-variables) and [installing dependencies](#install-dependencies-and-run-lint).
54+
55+
2. Run command `npm run test-data` to generate test data.(Important step!! The provided test data have different eventSourceARN which are different with your environment variables. The lambda function need to use eventSourceARN to distinguish ES index.)
56+
57+
3. Then, Run the following to invoke lambda locally.
58+
```
59+
serverless invoke local -f sync -p test_data/createEnteredSkillsEvent.json
60+
serverless invoke local -f sync -p test_data/updateEnteredSkillsEvent.json
61+
serverless invoke local -f sync -p test_data/deleteEnteredSkillsEvent.json
62+
serverless invoke local -f sync -p test_data/createMemberAggregatedSkillsEvent.json
63+
serverless invoke local -f sync -p test_data/updateMemberAggregatedSkillsEvent.json
64+
serverless invoke local -f sync -p test_data/deleteMemberAggregatedSkillsEvent.json
65+
```
66+
Please note that even if the lambda is invoked locally using test data, the data is still being posted to ElasticSearch in AWS based on environment variables above. In order for the local invocation to work properly, the serverless agent that is used by serverless command has to have privilege to post the data to ElasticSearch or the access policy for the ElasticSearch is configured to allow any AWS principal to post the data but restricted by condition e.g. by using IpAddress.
67+
68+
## Deploy
69+
When all the things mentioned above have been done, please run the following:
70+
```
71+
serverless deploy
72+
```
73+
Feel free to use flag `--verbose` after `serverless deploy` if you want to know more information about your deployment.
74+
75+
You can also modify the lambda code if required and the redeployment is as simple as running `serverless deploy` again.
76+
77+
## Verification
78+
Please read verification document for screen shot of enabling DynamoDB table stream, unit test result, deployment results, and results of manual testing on the lambda.

Verification.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Verification
2+
3+
## Setup From scratch
4+
1. You need to install aws-cli on your local environment and properly configure it.
5+
refer https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html and https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html for more information.
6+
2. You can use aws-cli to create `MemberEnteredSkills` and `MemberAggregatedSkills` tables using the following commands:
7+
```bash
8+
# Create MemberEnteredSkills table
9+
aws dynamodb create-table --table-name MemberEnteredSkills --attribute-definitions AttributeName=handleLower,AttributeType=S AttributeName=userId,AttributeType=N --key-schema AttributeName=userId,KeyType=HASH --global-secondary-indexes '[{"IndexName":"handleLower-index","KeySchema":[{"AttributeName":"handleLower","KeyType":"HASH"}],"Projection":{"ProjectionType":"ALL"}, "ProvisionedThroughput": {"ReadCapacityUnits": 2, "WriteCapacityUnits": 2}}]' --provisioned-throughput ReadCapacityUnits=4,WriteCapacityUnits=2
10+
# Create MemberAggregatedSkills table
11+
aws dynamodb create-table --table-name MemberAggregatedSkills --attribute-definitions AttributeName=handleLower,AttributeType=S AttributeName=userId,AttributeType=N --key-schema AttributeName=userId,KeyType=HASH --global-secondary-indexes '[{"IndexName":"handleLower-index","KeySchema":[{"AttributeName":"handleLower","KeyType":"HASH"}],"Projection":{"ProjectionType":"ALL"}, "ProvisionedThroughput": {"ReadCapacityUnits": 2, "WriteCapacityUnits": 2}}]' --provisioned-throughput ReadCapacityUnits=4,WriteCapacityUnits=2
12+
```
13+
3. Now go to AWS console DynamoDB service, you can enable the streaming on DynamoDB with View type "New and old images" after you click `Manage Stream` button. After that you could see `Latest stream ARN` like the following images.
14+
![](doc/images/dynamodb_MemberEnteredSkills.png)
15+
![](doc/images/dynamodb_MemberAggregatedSkills.png)
16+
4. Commonly, You don't need to create ElasticSearch index. By default, the index is created automatically if it doesn’t exist.
17+
5. You need to create IAM role for lambda function. the "Trusted entities" should be `AWS service: lambda`. It should contain 2 policies, one is `AWSLambdaDynamoDBExecutionRole`(AWS managed policy), another is user-created policy with ES permission (you need to create it in IAM service check the 3rd images)
18+
![](doc/images/iam_role.png)
19+
![](doc/images/iam_role_policies.png)
20+
![](doc/images/iam_user_policy.png)
21+
6. Following README.md to deploy the lambda function.
22+
![](doc/images/cloud_watch.png)
23+
![](doc/images/lambda.png)
24+
25+
## Verification
26+
1. Create item in MemberEnteredSkills table
27+
![](doc/images/db_create_member_entered_skills.png)
28+
Check CloudWatch and ElasticSearch
29+
![](doc/images/log_create_member_entered_skills.png)
30+
![](doc/images/es_create_member_entered_skills.png)
31+
2. Update item in MemberEnteredSkills table
32+
![](doc/images/db_update_member_entered_skills.png)
33+
Check CloudWatch and ElasticSearch
34+
![](doc/images/log_update_member_entered_skills.png)
35+
![](doc/images/es_update_member_entered_skills.png)
36+
3. Delete item in MemberEnteredSkills table
37+
![](doc/images/db_delete_member_entered_skills.png)
38+
Check CloudWatch and ElasticSearch
39+
![](doc/images/log_delete_member_entered_skills.png)
40+
![](doc/images/es_delete_member_entered_skills.png)
41+
4. Create item in MemberAggregatedSkills table
42+
![](doc/images/db_create_member_aggregated_skills.png)
43+
Check CloudWatch and ElasticSearch
44+
![](doc/images/log_create_member_aggregated_skills.png)
45+
![](doc/images/es_create_member_aggregated_skills.png)
46+
5. Update item in MemberAggregatedSkills table
47+
![](doc/images/db_update_member_aggregated_skills.png)
48+
Check CloudWatch and ElasticSearch
49+
![](doc/images/log_update_member_aggregated_skills.png)
50+
![](doc/images/es_update_member_aggregated_skills.png)
51+
6. Delete item in MemberAggregatedSkills table
52+
![](doc/images/db_delete_member_aggregated_skills.png)
53+
Check CloudWatch and ElasticSearch
54+
![](doc/images/log_delete_member_aggregated_skills.png)
55+
![](doc/images/es_delete_member_aggregated_skills.png)

config/default.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module.exports = {
2+
ES_URL: process.env.ES_URL || "",
3+
ES_AWS_REGION: process.env.ES_AWS_REGION || "",
4+
ES_API_VERSION: process.env.ES_API_VERSION || "",
5+
ES_SKILLS_INDEX: process.env.ES_SKILLS_INDEX || "",
6+
ES_SKILLS_MAPPING: process.env.ES_SKILLS_MAPPING || "",
7+
DB_ENTERED_SKILLS_STREAM: process.env.DB_ENTERED_SKILLS_STREAM || "",
8+
DB_AGGREGATED_SKILLS_STREAM: process.env.DB_AGGREGATED_SKILLS_STREAM || "",
9+
LAMBDA_ROLE: process.env.LAMBDA_ROLE || "",
10+
TC_API_URL: process.env.TC_API_URL || ""
11+
}

doc/images/cloud_watch.png

91.2 KB
Loading
Loading
107 KB
Loading
Loading
120 KB
Loading
Loading
107 KB
Loading
132 KB
Loading
130 KB
Loading
Loading
63.6 KB
Loading
Loading
46.3 KB
Loading
Loading
64.9 KB
Loading

doc/images/iam_role.png

85.3 KB
Loading

doc/images/iam_role_policies.png

96.3 KB
Loading

doc/images/iam_user_policy.png

141 KB
Loading

doc/images/lambda.png

85.8 KB
Loading
Loading
Loading
Loading
Loading
Loading
Loading

index.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
3+
const AWS = require('aws-sdk')
4+
const skillInsert = require('./reuse/skill.insert')
5+
const skillModify = require('./reuse/skill.modify')
6+
const skillRemove = require('./reuse/skill.remove')
7+
const tag = require('./reuse/tag')
8+
const util = require('./reuse/util')
9+
10+
let fileName = "index"
11+
let cachedTags
12+
13+
/**
14+
* Sync Dynamo DB member skills to Elastic Search
15+
* @param {Object} event the event object
16+
* @param {Object} context the context object
17+
* @param {Func} callback the callback
18+
*/
19+
module.exports.syncSkills = async (event, context, callback) => {
20+
let tagsResponse
21+
if (!cachedTags) {
22+
tagsResponse = await tag.getTags()
23+
if (tagsResponse.statusCode != 200) {
24+
callback(null, util.immediateResponse(null, 503, "Tags service is down", null))
25+
return;
26+
}
27+
cachedTags = tagsResponse.data
28+
}
29+
if (cachedTags) {
30+
for (let count = 0; count < event.Records.length; count++) {
31+
let record = event.Records[count]
32+
if (record.eventName === 'INSERT' || record.eventName === 'MODIFY' || record.eventName === 'REMOVE') {
33+
util.logger(fileName, null, "Get UserId", null)
34+
const keys = AWS.DynamoDB.Converter.unmarshall(record.dynamodb.Keys)
35+
let userId = String(keys.userId)
36+
if (record.eventName === 'INSERT') {
37+
await skillInsert.insert(count, userId, record, cachedTags)
38+
} else if (record.eventName === 'MODIFY') {
39+
await skillModify.modify(count, userId, record, cachedTags)
40+
} else if (record.eventName === 'REMOVE') {
41+
await skillRemove.remove(count, userId, record)
42+
}
43+
}
44+
}
45+
callback(null, util.fetchResponse())
46+
return;
47+
} else {
48+
callback(null, tagsResponse)
49+
return;
50+
}
51+
}

0 commit comments

Comments
 (0)