├── deploy ├── deploy.sh ├── terraform │ ├── versions.tf │ ├── ecs.tf │ ├── outputs.tf │ ├── provider.tf │ ├── webcore-logs.tf │ ├── templates │ │ └── demoapp_app.json.tpl │ ├── roles.tf │ ├── webcore-alb.tf │ ├── security.tf │ ├── variables.tf │ ├── webcore-ecs-service.tf │ ├── network.tf │ └── webcore-auto-scaling.tf ├── create_repo_in_ecr.sh └── push_image_to_ecr_&_deploy_ecs.sh └── demo-app ├── .prettierrc ├── nest-cli.json ├── tsconfig.build.json ├── src ├── app.service.ts ├── main.ts ├── app.module.ts ├── app.controller.ts └── app.controller.spec.ts ├── test ├── jest-e2e.json └── app.e2e-spec.ts ├── Dockerfile ├── tsconfig.json ├── tslint.json ├── .gitignore ├── .dockerignore ├── package.json └── README.md /deploy/deploy.sh: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo-app/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /deploy/terraform/versions.tf: -------------------------------------------------------------------------------- 1 | 2 | terraform { 3 | required_version = ">= 0.12" 4 | } 5 | -------------------------------------------------------------------------------- /demo-app/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src" 4 | } 5 | -------------------------------------------------------------------------------- /deploy/terraform/ecs.tf: -------------------------------------------------------------------------------- 1 | # ecs.tf 2 | resource "aws_ecs_cluster" "main" { 3 | name = "${var.prefix}-api-cluster" 4 | } 5 | -------------------------------------------------------------------------------- /deploy/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | # outputs.tf 2 | 3 | output "demoapp_alb_hostname" { 4 | value = aws_alb.demoapp_lb.dns_name 5 | } 6 | 7 | -------------------------------------------------------------------------------- /demo-app/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /demo-app/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | getHello(): string { 6 | return 'Hello World!'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demo-app/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /deploy/terraform/provider.tf: -------------------------------------------------------------------------------- 1 | # provider.tf 2 | 3 | # Specify the provider and access details 4 | provider "aws" { 5 | shared_credentials_file = "$HOME/.aws/credentials" 6 | profile = "dev" 7 | region = var.aws_region 8 | } 9 | 10 | -------------------------------------------------------------------------------- /demo-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import {NestFactory} from '@nestjs/core'; 2 | import {AppModule} from './app.module'; 3 | 4 | const APP_PORT = 80; 5 | 6 | async function bootstrap() { 7 | const app = await NestFactory.create(AppModule); 8 | await app.listen(APP_PORT); 9 | } 10 | 11 | bootstrap(); 12 | -------------------------------------------------------------------------------- /demo-app/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | @Module({ 6 | imports: [], 7 | controllers: [AppController], 8 | providers: [AppService], 9 | }) 10 | export class AppModule {} 11 | -------------------------------------------------------------------------------- /demo-app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14.3.0-buster-slim 2 | 3 | RUN mkdir -p /demo-app 4 | 5 | WORKDIR /demo-app 6 | 7 | COPY package*.json ./ 8 | 9 | COPY yarn.lock ./ 10 | 11 | RUN yarn install 12 | 13 | COPY . . 14 | 15 | RUN yarn build 16 | 17 | WORKDIR ./ 18 | 19 | # EXPOSE 9001 20 | EXPOSE 80 21 | 22 | RUN mkdir /var/logs 23 | 24 | CMD ["node" , "./dist/main.js", "2>server.log"] 25 | -------------------------------------------------------------------------------- /demo-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "target": "es2017", 9 | "sourceMap": true, 10 | "outDir": "./dist", 11 | "baseUrl": "./", 12 | "incremental": true 13 | }, 14 | "exclude": ["node_modules", "dist"] 15 | } 16 | -------------------------------------------------------------------------------- /demo-app/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get() 9 | getHello(): string { 10 | return this.appService.getHello(); 11 | } 12 | @Get('api/index') 13 | getIndex(): string { 14 | return this.appService.getHello(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /deploy/terraform/webcore-logs.tf: -------------------------------------------------------------------------------- 1 | # logs.tf 2 | # Set up CloudWatch group and log stream and retain logs for 30 days 3 | resource "aws_cloudwatch_log_group" "demoapp_log_group" { 4 | name = "/ecs/demoapp" 5 | retention_in_days = 30 6 | 7 | tags = { 8 | Name = "demoapp-log-group" 9 | } 10 | } 11 | 12 | resource "aws_cloudwatch_log_stream" "cb_log_stream" { 13 | name = "demoapp-log-stream" 14 | log_group_name = aws_cloudwatch_log_group.demoapp_log_group.name 15 | } 16 | -------------------------------------------------------------------------------- /demo-app/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": ["tslint:recommended"], 4 | "jsRules": { 5 | "no-unused-expression": true 6 | }, 7 | "rules": { 8 | "quotemark": [true, "single"], 9 | "member-access": [false], 10 | "ordered-imports": [false], 11 | "max-line-length": [true, 150], 12 | "member-ordering": [false], 13 | "interface-name": [false], 14 | "arrow-parens": false, 15 | "object-literal-sort-keys": false 16 | }, 17 | "rulesDirectory": [] 18 | } 19 | -------------------------------------------------------------------------------- /demo-app/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Tests 17 | /coverage 18 | /.nyc_output 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json -------------------------------------------------------------------------------- /deploy/terraform/templates/demoapp_app.json.tpl: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "${container_name}", 4 | "image": "${app_image}", 5 | "cpu": ${fargate_cpu}, 6 | "memory": ${fargate_memory}, 7 | "networkMode": "awsvpc", 8 | "logConfiguration": { 9 | "logDriver": "awslogs", 10 | "options": { 11 | "awslogs-group": "/ecs/demoapp", 12 | "awslogs-region": "${aws_region}", 13 | "awslogs-stream-prefix": "ecs" 14 | } 15 | }, 16 | "portMappings": [ 17 | { 18 | "containerPort": ${app_port}, 19 | "hostPort": ${app_port} 20 | } 21 | ] 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /demo-app/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(appController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /deploy/create_repo_in_ecr.sh: -------------------------------------------------------------------------------- 1 | AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXX 2 | AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXX 3 | AWS_REGION=XXXXXXXXXXXXXXX 4 | AWS_PROFILE=dev 5 | ECR_REPO_NAME=demo_app_repo 6 | 7 | 8 | # aws configure set varname value [--profile profile-name] 9 | aws configure set aws_access_key_id "$AWS_ACCESS_KEY_ID" --profile "$AWS_PROFILE" 10 | aws configure set aws_secret_access_key "$AWS_SECRET_ACCESS_KEY" --profile "$AWS_PROFILE" 11 | aws configure set region "$AWS_REGION" --profile "$AWS_PROFILE" 12 | aws configure set output json --profile "$AWS_PROFILE" 13 | 14 | 15 | # create repository 16 | aws ecr create-repository --repository-name "$ECR_REPO_NAME" 17 | 18 | # after created repo, it will print the repo path in console. 19 | -------------------------------------------------------------------------------- /demo-app/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from './../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/ (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/') 21 | .expect(200) 22 | .expect('Hello World!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /deploy/terraform/roles.tf: -------------------------------------------------------------------------------- 1 | # ECS task execution role data 2 | data "aws_iam_policy_document" "demoapp_ecs_task_execution_role" { 3 | version = "2012-10-17" 4 | statement { 5 | sid = "" 6 | effect = "Allow" 7 | actions = ["sts:AssumeRole"] 8 | 9 | principals { 10 | type = "Service" 11 | identifiers = ["ecs-tasks.amazonaws.com"] 12 | } 13 | } 14 | } 15 | 16 | # ECS task execution role 17 | resource "aws_iam_role" "demoapp_ecs_task_execution_role" { 18 | name = var.ecs_task_execution_role_name 19 | assume_role_policy = data.aws_iam_policy_document.demoapp_ecs_task_execution_role.json 20 | } 21 | 22 | # ECS task execution role policy attachment 23 | resource "aws_iam_role_policy_attachment" "demoapp_task_execution_role" { 24 | role = aws_iam_role.demoapp_ecs_task_execution_role.name 25 | policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" 26 | } 27 | -------------------------------------------------------------------------------- /deploy/terraform/webcore-alb.tf: -------------------------------------------------------------------------------- 1 | # alb.tf 2 | resource "aws_alb" "demoapp_lb" { 3 | name = "dev-demoapp" 4 | subnets = [data.aws_subnet.public_1a.id,data.aws_subnet.public_1b.id,data.aws_subnet.public_1c.id] 5 | security_groups = [aws_security_group.lb.id] 6 | } 7 | 8 | resource "aws_alb_target_group" "demoapp_target" { 9 | name = "demoapp-target-group" 10 | port = 80 11 | protocol = "HTTP" 12 | vpc_id = data.aws_vpc.main.id 13 | target_type = "ip" 14 | 15 | health_check { 16 | healthy_threshold = "3" 17 | interval = "30" 18 | protocol = "HTTP" 19 | matcher = "200" 20 | timeout = "3" 21 | path = var.demoapp_health_check_path 22 | unhealthy_threshold = "2" 23 | } 24 | } 25 | 26 | # Redirect all traffic from the ALB to the target group 27 | resource "aws_alb_listener" "front_end_demoapp" { 28 | load_balancer_arn = aws_alb.demoapp_lb.id 29 | port = var.app_port 30 | protocol = "HTTP" 31 | 32 | default_action { 33 | target_group_arn = aws_alb_target_group.demoapp_target.id 34 | type = "forward" 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /deploy/terraform/security.tf: -------------------------------------------------------------------------------- 1 | # security.tf 2 | 3 | # ALB Security Group: Edit to restrict access to the application 4 | resource "aws_security_group" "lb" { 5 | name = "${var.prefix}-load-balancer-security-group" 6 | description = "controls access to the ALB" 7 | vpc_id = data.aws_vpc.main.id 8 | 9 | ingress { 10 | protocol = "tcp" 11 | from_port = var.app_port 12 | to_port = var.app_port 13 | cidr_blocks = ["0.0.0.0/0"] 14 | } 15 | 16 | egress { 17 | protocol = "-1" 18 | from_port = 0 19 | to_port = 0 20 | cidr_blocks = ["0.0.0.0/0"] 21 | } 22 | } 23 | 24 | # Traffic to the ECS cluster should only come from the ALB 25 | resource "aws_security_group" "ecs_tasks" { 26 | name = "${var.prefix}-ecs-tasks-security-group" 27 | description = "allow inbound access from the ALB only" 28 | vpc_id = data.aws_vpc.main.id 29 | 30 | ingress { 31 | protocol = "tcp" 32 | from_port = var.app_port 33 | to_port = var.app_port 34 | security_groups = [aws_security_group.lb.id] 35 | } 36 | 37 | egress { 38 | protocol = "-1" 39 | from_port = 0 40 | to_port = 0 41 | cidr_blocks = ["0.0.0.0/0"] 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /deploy/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | # variables.tf 2 | 3 | variable "prefix" { 4 | description = "Prefix for all envirnoments" 5 | default = "demoapp" 6 | } 7 | 8 | variable "aws_region" { 9 | description = "The AWS region things are created in" 10 | default = "ap-southeast-1" 11 | } 12 | 13 | variable "ecs_task_execution_role_name" { 14 | description = "ECS task execution role name" 15 | default = "demoappEcsTaskExecutionRole" 16 | } 17 | 18 | variable "az_count" { 19 | description = "Number of AZs to cover in a given region" 20 | default = "3" 21 | } 22 | 23 | variable "app_port" { 24 | description = "Port exposed by the docker image to redirect traffic to" 25 | default = 80 26 | } 27 | 28 | variable "app_count" { 29 | description = "Number of docker containers to run" 30 | default = 2 31 | } 32 | 33 | #### Commented becoz of it uses Envirnoment variable. 34 | variable "demoapp_image" { 35 | description = "Docker image to run in the ECS cluster" 36 | // default = "XXXXXXXXXX.dkr.ecr.ap-southeast-1.amazonaws.com/dev-demoapp:0.0.4" 37 | } 38 | 39 | variable "demoapp_health_check_path" { 40 | default = "/api/index" 41 | } 42 | 43 | variable "fargate_cpu" { 44 | description = "Fargate instance CPU units to provision (1 vCPU = 1024 CPU units)" 45 | default = "1024" 46 | } 47 | 48 | variable "fargate_memory" { 49 | description = "Fargate instance memory to provision (in MiB)" 50 | default = "2048" 51 | } 52 | 53 | variable "demoapp_container_name" { 54 | description = "Application container name" 55 | default = "demoapp-app" 56 | } 57 | -------------------------------------------------------------------------------- /deploy/terraform/webcore-ecs-service.tf: -------------------------------------------------------------------------------- 1 | data "template_file" "demoapp_app" { 2 | template = file("./templates/demoapp_app.json.tpl") 3 | 4 | vars = { 5 | app_image = "${var.demoapp_image}" # its env variable 6 | app_port = var.app_port 7 | fargate_cpu = var.fargate_cpu 8 | fargate_memory = var.fargate_memory 9 | aws_region = var.aws_region 10 | prefix = var.prefix 11 | container_name = var.demoapp_container_name 12 | } 13 | } 14 | 15 | 16 | resource "aws_ecs_task_definition" "demoapp_task_def" { 17 | family = "demoapp-task" 18 | execution_role_arn = aws_iam_role.demoapp_ecs_task_execution_role.arn 19 | network_mode = "awsvpc" 20 | requires_compatibilities = [ 21 | "FARGATE"] 22 | cpu = var.fargate_cpu 23 | memory = var.fargate_memory 24 | container_definitions = data.template_file.demoapp_app.rendered 25 | } 26 | 27 | 28 | resource "aws_ecs_service" "demoapp_main" { 29 | name = "demoapp-service" 30 | cluster = aws_ecs_cluster.main.id 31 | task_definition = aws_ecs_task_definition.demoapp_task_def.arn 32 | desired_count = var.app_count 33 | launch_type = "FARGATE" 34 | 35 | network_configuration { 36 | security_groups = [ 37 | aws_security_group.ecs_tasks.id] 38 | subnets = [ 39 | data.aws_subnet.public_1a.id, 40 | data.aws_subnet.public_1b.id, 41 | data.aws_subnet.public_1c.id 42 | ] 43 | assign_public_ip = true 44 | } 45 | 46 | load_balancer { 47 | target_group_arn = aws_alb_target_group.demoapp_target.id 48 | container_name = var.demoapp_container_name 49 | container_port = var.app_port 50 | } 51 | 52 | depends_on = [ 53 | aws_alb_listener.front_end_demoapp, 54 | aws_iam_role_policy_attachment.demoapp_task_execution_role] 55 | } 56 | -------------------------------------------------------------------------------- /demo-app/.dockerignore: -------------------------------------------------------------------------------- 1 | # Custom Project 2 | _data/ 3 | # Logs 4 | logs 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | # Optional npm cache directory 10 | .npm 11 | 12 | # Dependency directories 13 | /node_modules 14 | /jspm_packages 15 | /bower_components 16 | /iso-docs/node_modules 17 | **/node_modules 18 | 19 | # Yarn Integrity file 20 | .yarn-integrity 21 | 22 | # Optional eslint cache 23 | .eslintcache 24 | 25 | # dotenv environment variables file(s) 26 | .env 27 | .env.* 28 | 29 | #Build generated 30 | dist/ 31 | build/ 32 | 33 | # Serverless generated files 34 | .serverless/ 35 | 36 | ### SublimeText ### 37 | # cache files for sublime text 38 | *.tmlanguage.cache 39 | *.tmPreferences.cache 40 | *.stTheme.cache 41 | 42 | # workspace files are user-specific 43 | *.sublime-workspace 44 | 45 | # project files should be checked into the repository, unless a significant 46 | # proportion of contributors will probably not be using SublimeText 47 | # *.sublime-project 48 | 49 | 50 | ### VisualStudioCode ### 51 | .vscode/* 52 | .vscode/settings.json 53 | .vscode/tasks.json 54 | .vscode/launch.json 55 | .vscode/extensions.json 56 | 57 | ### Vim ### 58 | *.sw[a-p] 59 | 60 | ### WebStorm/IntelliJ ### 61 | /.idea 62 | modules.xml 63 | *.ipr 64 | *.iml 65 | 66 | ### System Files ### 67 | *.DS_Store 68 | 69 | # Windows thumbnail cache files 70 | Thumbs.db 71 | ehthumbs.db 72 | ehthumbs_vista.db 73 | 74 | # Folder config file 75 | Desktop.ini 76 | 77 | # Recycle Bin used on file shares 78 | $RECYCLE.BIN/ 79 | 80 | # Thumbnails 81 | ._* 82 | 83 | # Files that might appear in the root of a volume 84 | .DocumentRevisions-V100 85 | .fseventsd 86 | .Spotlight-V100 87 | .TemporaryItems 88 | .Trashes 89 | .VolumeIcon.icns 90 | .com.apple.timemachine.donotpresent 91 | 92 | # Gateway 93 | **/.idea/ 94 | **/*.iml 95 | **/target/ 96 | -------------------------------------------------------------------------------- /deploy/terraform/network.tf: -------------------------------------------------------------------------------- 1 | # When you have existing vpc, based on ur avaialbility zone 2 | # update this file. 3 | # Find out - VPC name 4 | # Find out your public subnet name 5 | # Find out your availbitliy zones and replace here 6 | 7 | # network.tf without elp 8 | variable "existing_vpc_name" { 9 | description = "Existing VPC in your aws cloud" 10 | default = "dev-data-stream-vpc" 11 | } 12 | 13 | variable "existing_vpc_public_subnet_name" { 14 | description = "public subnet name which tie up with existing vpc" 15 | default = "dev-data-stream-public-subnet" 16 | } 17 | 18 | data "aws_vpc" "main" { 19 | filter { 20 | name = "tag:Name" 21 | values = [var.existing_vpc_name] 22 | } 23 | } 24 | 25 | data "aws_subnet" "public_1a" { 26 | filter { 27 | name = "tag:Name" 28 | values = [var.existing_vpc_public_subnet_name] 29 | } 30 | 31 | filter { 32 | name = "availability-zone" 33 | values = ["ap-southeast-1a"] # check zone 34 | } 35 | } 36 | 37 | data "aws_subnet" "public_1b" { 38 | filter { 39 | name = "tag:Name" 40 | values = [var.existing_vpc_public_subnet_name] 41 | } 42 | 43 | filter { 44 | name = "availability-zone" 45 | values = ["ap-southeast-1b"] # check zone or replace 46 | } 47 | } 48 | 49 | 50 | data "aws_subnet" "public_1c" { 51 | filter { 52 | name = "tag:Name" 53 | values = [var.existing_vpc_public_subnet_name] 54 | } 55 | 56 | filter { 57 | name = "availability-zone" 58 | values = ["ap-southeast-1c"] # check zone 59 | } 60 | } 61 | 62 | # print vpc and subnet ids 63 | output "aws_vpc_name" { 64 | value = data.aws_vpc.main.id 65 | } 66 | 67 | output "aws_subnet_1a" { 68 | value = data.aws_subnet.public_1a.id 69 | } 70 | 71 | output "aws_subnet_1b" { 72 | value = data.aws_subnet.public_1b.id 73 | } 74 | 75 | output "aws_subnet_1c" { 76 | value = data.aws_subnet.public_1c.id 77 | } 78 | -------------------------------------------------------------------------------- /demo-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "author": "", 6 | "license": "MIT", 7 | "scripts": { 8 | "prebuild": "rimraf dist", 9 | "build": "nest build", 10 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 11 | "start": "nest start", 12 | "start:dev": "nest start --watch", 13 | "start:debug": "nest start --debug --watch", 14 | "start:prod": "node dist/main", 15 | "lint": "tslint -p tsconfig.json -c tslint.json", 16 | "test": "jest", 17 | "test:watch": "jest --watch", 18 | "test:cov": "jest --coverage", 19 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 20 | "test:e2e": "jest --config ./test/jest-e2e.json" 21 | }, 22 | "dependencies": { 23 | "@nestjs/common": "^6.7.2", 24 | "@nestjs/core": "^6.7.2", 25 | "@nestjs/platform-express": "^6.7.2", 26 | "reflect-metadata": "^0.1.13", 27 | "rimraf": "^3.0.0", 28 | "rxjs": "^6.5.3" 29 | }, 30 | "devDependencies": { 31 | "@nestjs/cli": "^6.9.0", 32 | "@nestjs/schematics": "^6.7.0", 33 | "@nestjs/testing": "^6.7.1", 34 | "@types/express": "^4.17.1", 35 | "@types/jest": "^24.0.18", 36 | "@types/node": "^12.7.5", 37 | "@types/supertest": "^2.0.8", 38 | "jest": "^24.9.0", 39 | "prettier": "^1.18.2", 40 | "supertest": "^4.0.2", 41 | "ts-jest": "^24.1.0", 42 | "ts-loader": "^6.1.1", 43 | "ts-node": "^8.4.1", 44 | "tsconfig-paths": "^3.9.0", 45 | "tslint": "^5.20.0", 46 | "typescript": "^3.6.3" 47 | }, 48 | "jest": { 49 | "moduleFileExtensions": [ 50 | "js", 51 | "json", 52 | "ts" 53 | ], 54 | "rootDir": "src", 55 | "testRegex": ".spec.ts$", 56 | "transform": { 57 | "^.+\\.(t|j)s$": "ts-jest" 58 | }, 59 | "coverageDirectory": "./coverage", 60 | "testEnvironment": "node" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /deploy/push_image_to_ecr_&_deploy_ecs.sh: -------------------------------------------------------------------------------- 1 | echo "*************************************************************************" 2 | echo "TO DEPLOY THE CODE, YOU NEED TO HAVE AWS CLI & TERRAFORM CLI INSTALLED." 3 | echo "*************************************************************************" 4 | cd .. 5 | 6 | #it can be any version, depends on user choice either hardcoded version or from war/jar file or packaage.json 7 | PACKAGE_VERSION=$(node -p -e "require('./demo-app/package.json').version") 8 | #PACKAGE_VERSION=1.0.0 9 | 10 | #Update parameters before running 11 | # AWS configuration details 12 | AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXX 13 | AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXX 14 | AWS_REGION=XXXXXXXXXXXXXXX 15 | AWS_PROFILE=dev 16 | #Custom ECR REPO Name. any name for repo 17 | ECR_REPO_NAME=demo_app_repo 18 | #Application Name 19 | APP_NAME=demo-app 20 | # first create ECR repo with create_repo_in_ecr sh file and give the path here 21 | ECR_REPO_PATH=XXXXXXXXXXXXXXX.dkr.ecr.ap-southeast-1.amazonaws.com/demo_app_repo 22 | #Application Folder location 23 | APP_LOCATION=./demo-app/ 24 | 25 | cd $APP_LOCATION 26 | 27 | # go to inside application folder 28 | #docker build -t dev-webcore:"$PACKAGE_VERSION" . 29 | docker build -t "$APP_NAME":"$PACKAGE_VERSION" . 30 | 31 | # aws configure set varname value [--profile profile-name] 32 | aws configure set aws_access_key_id "$AWS_ACCESS_KEY_ID" --profile "$AWS_PROFILE" 33 | aws configure set aws_secret_access_key "$AWS_SECRET_ACCESS_KEY" --profile "$AWS_PROFILE" 34 | aws configure set region "$AWS_REGION" --profile "$AWS_PROFILE" 35 | aws configure set output json --profile "$AWS_PROFILE" 36 | 37 | 38 | # create repository 39 | aws ecr create-repository --repository-name "$ECR_REPO_NAME" 40 | 41 | 42 | echo "docker image created with package version :$PACKAGE_VERSION" 43 | 44 | # tag doker image for ecr image 45 | echo "$APP_NAME:$PACKAGE_VERSION" "$ECR_REPO_PATH:$PACKAGE_VERSION" 46 | docker tag "$APP_NAME:$PACKAGE_VERSION" "$ECR_REPO_PATH:$PACKAGE_VERSION" 47 | 48 | $(aws ecr get-login --no-include-email --no-include-email | sed 's|https://||') 49 | 50 | #push tagged image to ecr 51 | docker push "$ECR_REPO_PATH:$PACKAGE_VERSION" 52 | 53 | 54 | 55 | # Deploy code thru terraform 56 | cd .. 57 | 58 | cd deploy/terraform 59 | 60 | # envirnoment variable demoapp_image for terraform 61 | #export TF_VAR_webcore_image="XXXXX.dkr.ecr.ap-southeast-1.amazonaws.com/demo-app:$PACKAGE_VERSION" 62 | export TF_VAR_demoapp_image="$ECR_REPO_PATH:$PACKAGE_VERSION" 63 | 64 | terraform init 65 | 66 | terraform plan 67 | 68 | terraform apply -auto-approve 69 | -------------------------------------------------------------------------------- /deploy/terraform/webcore-auto-scaling.tf: -------------------------------------------------------------------------------- 1 | # auto_scaling.tf 2 | 3 | resource "aws_appautoscaling_target" "demoapp_target" { 4 | service_namespace = "ecs" 5 | resource_id = "service/${aws_ecs_cluster.main.name}/${aws_ecs_service.demoapp_main.name}" 6 | scalable_dimension = "ecs:service:DesiredCount" 7 | min_capacity = 1 8 | max_capacity = 6 9 | } 10 | 11 | # Automatically scale capacity up by one 12 | resource "aws_appautoscaling_policy" "demoapp_up" { 13 | name = "demoapp_scale_up" 14 | service_namespace = "ecs" 15 | resource_id = "service/${aws_ecs_cluster.main.name}/${aws_ecs_service.demoapp_main.name}" 16 | scalable_dimension = "ecs:service:DesiredCount" 17 | 18 | step_scaling_policy_configuration { 19 | adjustment_type = "ChangeInCapacity" 20 | cooldown = 60 21 | metric_aggregation_type = "Maximum" 22 | 23 | step_adjustment { 24 | metric_interval_lower_bound = 0 25 | scaling_adjustment = 1 26 | } 27 | } 28 | 29 | depends_on = [aws_appautoscaling_target.demoapp_target] 30 | } 31 | 32 | # Automatically scale capacity down by one 33 | resource "aws_appautoscaling_policy" "demoapp_down" { 34 | name = "demoapp_scale_down" 35 | service_namespace = "ecs" 36 | resource_id = "service/${aws_ecs_cluster.main.name}/${aws_ecs_service.demoapp_main.name}" 37 | scalable_dimension = "ecs:service:DesiredCount" 38 | 39 | step_scaling_policy_configuration { 40 | adjustment_type = "ChangeInCapacity" 41 | cooldown = 60 42 | metric_aggregation_type = "Maximum" 43 | 44 | step_adjustment { 45 | metric_interval_upper_bound = 0 46 | scaling_adjustment = -1 47 | } 48 | } 49 | 50 | depends_on = [aws_appautoscaling_target.demoapp_target] 51 | } 52 | 53 | # CloudWatch alarm that triggers the autoscaling up policy 54 | resource "aws_cloudwatch_metric_alarm" "demoapp_service_cpu_high" { 55 | alarm_name = "demoapp_cpu_utilization_high" 56 | comparison_operator = "GreaterThanOrEqualToThreshold" 57 | evaluation_periods = "2" 58 | metric_name = "CPUUtilization" 59 | namespace = "AWS/ECS" 60 | period = "60" 61 | statistic = "Average" 62 | threshold = "85" 63 | 64 | dimensions = { 65 | ClusterName = aws_ecs_cluster.main.name 66 | ServiceName = aws_ecs_service.demoapp_main.name 67 | } 68 | 69 | alarm_actions = [aws_appautoscaling_policy.demoapp_up.arn] 70 | } 71 | 72 | # CloudWatch alarm that triggers the autoscaling down policy 73 | resource "aws_cloudwatch_metric_alarm" "demoapp_service_cpu_low" { 74 | alarm_name = "demoapp_cpu_utilization_low" 75 | comparison_operator = "LessThanOrEqualToThreshold" 76 | evaluation_periods = "2" 77 | metric_name = "CPUUtilization" 78 | namespace = "AWS/ECS" 79 | period = "60" 80 | statistic = "Average" 81 | threshold = "10" 82 | 83 | dimensions = { 84 | ClusterName = aws_ecs_cluster.main.name 85 | ServiceName = aws_ecs_service.demoapp_main.name 86 | } 87 | 88 | alarm_actions = [aws_appautoscaling_policy.demoapp_down.arn] 89 | } 90 | 91 | -------------------------------------------------------------------------------- /demo-app/README.md: -------------------------------------------------------------------------------- 1 |

2 | Nest Logo 3 |

4 | 5 | [travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master 6 | [travis-url]: https://travis-ci.org/nestjs/nest 7 | [linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux 8 | [linux-url]: https://travis-ci.org/nestjs/nest 9 | 10 |

A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.

11 |

12 | NPM Version 13 | Package License 14 | NPM Downloads 15 | Travis 16 | Linux 17 | Coverage 18 | Gitter 19 | Backers on Open Collective 20 | Sponsors on Open Collective 21 | 22 | 23 |

24 | 26 | 27 | ## Description 28 | 29 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 30 | 31 | ## Installation 32 | 33 | ```bash 34 | $ npm install 35 | ``` 36 | 37 | ## Running the app 38 | 39 | ```bash 40 | # development 41 | $ npm run start 42 | 43 | # watch mode 44 | $ npm run start:dev 45 | 46 | # production mode 47 | $ npm run start:prod 48 | ``` 49 | 50 | ## Test 51 | 52 | ```bash 53 | # unit tests 54 | $ npm run test 55 | 56 | # e2e tests 57 | $ npm run test:e2e 58 | 59 | # test coverage 60 | $ npm run test:cov 61 | ``` 62 | 63 | ## Support 64 | 65 | Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). 66 | 67 | ## Stay in touch 68 | 69 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) 70 | - Website - [https://nestjs.com](https://nestjs.com/) 71 | - Twitter - [@nestframework](https://twitter.com/nestframework) 72 | 73 | ## License 74 | 75 | Nest is [MIT licensed](LICENSE). 76 | --------------------------------------------------------------------------------