Pull request #2: AWS Setup
Merge in DCF/mold_cost_calculator from AWS-Setup to master * commit 'ec05c201f2cdcc8ec085353ebca369eda3085a3f': Revert static file fix Try another fix Try another fix Try fix for Font files Fix stack name Add a db handler Remove Mangum Update the API Gateway Resource Policy Update lambda configuration Update stack name Change the App Shortname Initial jenkins version Core App changes to make it AWS compatible
This commit is contained in:
+2
-1
@@ -11,4 +11,5 @@ DB_NAME=mold_cost_development
|
||||
|
||||
# --- Application Configuration ---
|
||||
SECRET_KEY='a_different_secret_key_for_development'
|
||||
FLASK_DEBUG=1
|
||||
FLASK_DEBUG=1
|
||||
HOST_ENV=local # Set AWS for AWS deployments
|
||||
+10
@@ -123,3 +123,13 @@ powerapps/
|
||||
fix_ssl_*.sh
|
||||
quick_ssl_*.sh
|
||||
download_static_files.sh
|
||||
|
||||
# CDK asset staging directory
|
||||
.cdk.staging
|
||||
cdk.out
|
||||
|
||||
# Miscellaneous
|
||||
*.js
|
||||
!jest.config.js
|
||||
*.d.ts
|
||||
node_modules
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
@Library('GlobalJenkinsLibraryNew') _
|
||||
|
||||
def cdkDeployAccount = [
|
||||
'poc': '615817712084',
|
||||
'dev': '030215556060',
|
||||
'uat': '534745294648',
|
||||
'prd': '022438277857',
|
||||
]
|
||||
|
||||
def credentialsIDs = [
|
||||
'poc' : 'ProductDesign Jenkins Deployment POC',
|
||||
'dev' : 'digital-creation Development Jenkins Deployment',
|
||||
'uat' : 'digital-creation UAT Jenkins Deployment',
|
||||
'prd' : 'digital-creation PRD Jenkins Deployment',
|
||||
]
|
||||
|
||||
def mccDatabasePassword = [
|
||||
'poc': 'MCC_POC_DATABASE_PASSWORD',
|
||||
'dev': 'MCC_DEV_DATABASE_PASSWORD',
|
||||
'uat': 'MCC_UAT_DATABASE_PASSWORD',
|
||||
'prd': 'MCC_PRD_DATABASE_PASSWORD',
|
||||
]
|
||||
|
||||
def mccDatabase = [
|
||||
'poc': 'devmccmccdb-devmccmccdbwriterfe3d6e74-nzeetdanw6sx.c8uwkt51amck.eu-west-1.rds.amazonaws.com/mccdb',
|
||||
'dev': 'dev-mld-mccdb.cluster-c8uwkt51amck.eu-west-1.rds.amazonaws.com/mccdb',
|
||||
'uat': 'devmccmccdb-devmccmccdbwriterfe3d6e74-nzeetdanw6sx.c8uwkt51amck.eu-west-1.rds.amazonaws.com/mccdb',
|
||||
'prd': 'devmccmccdb-devmccmccdbwriterfe3d6e74-nzeetdanw6sx.c8uwkt51amck.eu-west-1.rds.amazonaws.com/mccdb',
|
||||
]
|
||||
|
||||
def stageChoices = ['poc', 'dev', 'uat']
|
||||
if (env.BRANCH_NAME == 'main' || env.BRANCH_NAME == 'master') {
|
||||
stageChoices = ['poc', 'dev', 'uat', 'prd']
|
||||
}
|
||||
|
||||
def now = new Date()
|
||||
|
||||
pipeline {
|
||||
environment {
|
||||
DEPLOY_ENV = "${params.STAGE}"
|
||||
HOME = "${WORKSPACE}"
|
||||
CDK_HOME = "${WORKSPACE}"
|
||||
CDK_DEPLOY_ACCOUNT = "${cdkDeployAccount[DEPLOY_ENV]}"
|
||||
CDK_DEPLOY_REGION = 'eu-west-1'
|
||||
CDK_QUALIFIER = "${DEPLOY_ENV}-mld"
|
||||
STACK_NAME = 'MoldCostCalc'
|
||||
POSTGRES_USER = 'mccAdmin'
|
||||
POSTGRES_PASSWORD_ENV = "${mccDatabasePassword[DEPLOY_ENV]}"
|
||||
FLASK_APP = 'run.py'
|
||||
FLASK_ENV = 'production'
|
||||
DATABASE_URL = "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${mccDatabase[DEPLOY_ENV]}"
|
||||
HOST_ENV = 'AWS'
|
||||
}
|
||||
|
||||
agent {
|
||||
label 'ubuntu_node-20_docker-20'
|
||||
}
|
||||
|
||||
parameters {
|
||||
choice(name: 'ACTION', choices: ['Analysis', 'Deploy'], description: 'Deployment Type')
|
||||
choice(name: 'STAGE', choices: stageChoices, description: 'Environment to deploy. Must match with the value in CDK.JSON context')
|
||||
}
|
||||
|
||||
options {
|
||||
disableConcurrentBuilds()
|
||||
parallelsAlwaysFailFast()
|
||||
timestamps()
|
||||
buildDiscarder(logRotator(numToKeepStr: '3'))
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Deploy') {
|
||||
when {
|
||||
expression { return params.ACTION == 'Deploy' }
|
||||
}
|
||||
steps {
|
||||
dir('infra') {
|
||||
withCredentials([
|
||||
string(credentialsId: "${POSTGRES_PASSWORD_ENV}", variable: 'POSTGRES_PASSWORD'),
|
||||
aws(credentialsId: credentialsIDs[env.DEPLOY_ENV], accessKeyVariable: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY')
|
||||
]) {
|
||||
withNvm {
|
||||
sh 'npm install'
|
||||
sh "npx aws-cdk@2.1019.1 bootstrap aws://${env.CDK_DEPLOY_ACCOUNT}/${env.CDK_DEPLOY_REGION} --context config=${env.DEPLOY_ENV} --toolkit-stack-name ${env.DEPLOY_ENV}${env.STACK_NAME} --change-set-name changeset-${env.DEPLOY_ENV}-${currentBuild.number} --qualifier ${env.CDK_QUALIFIER}"
|
||||
sh "npx aws-cdk@2.1019.1 deploy --all --context config=${env.DEPLOY_ENV} --toolkit-stack-name ${env.DEPLOY_ENV}${env.STACK_NAME} --change-set-name ${env.STACK_NAME}-changeset-${env.DEPLOY_ENV}-${currentBuild.number} --require-approval never"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
always {
|
||||
cleanWs()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
import {
|
||||
Stack,
|
||||
StackProps,
|
||||
aws_apigateway as apigateway,
|
||||
aws_lambda,
|
||||
aws_ec2,
|
||||
Duration,
|
||||
} from 'aws-cdk-lib';
|
||||
import * as aws_lambda_python_alpha from '@aws-cdk/aws-lambda-python-alpha';
|
||||
import { Construct } from 'constructs';
|
||||
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
|
||||
import { join } from 'path';
|
||||
import { Runtime } from 'aws-cdk-lib/aws-lambda';
|
||||
import { BuildConfig } from '../utils/build-config';
|
||||
import { Period } from 'aws-cdk-lib/aws-apigateway';
|
||||
import { RetentionDays } from 'aws-cdk-lib/aws-logs';
|
||||
import { VpcEndpoint, VpcEndpointService } from 'aws-cdk-lib/aws-ec2';
|
||||
import { PolicyStatement, PolicyDocument, Effect, AnyPrincipal } from 'aws-cdk-lib/aws-iam';
|
||||
|
||||
|
||||
export class MccAPIInfraStack extends Stack {
|
||||
public readonly api: apigateway.RestApi;
|
||||
|
||||
constructor(
|
||||
scope: Construct,
|
||||
id: string,
|
||||
buildConfig: BuildConfig,
|
||||
props: StackProps,
|
||||
) {
|
||||
super(scope, id, props);
|
||||
|
||||
const stackName = `${buildConfig.Environment}${buildConfig.ApplicationShortName}`;
|
||||
|
||||
const vpc = aws_ec2.Vpc.fromLookup(this, `${stackName}Vpc`, {
|
||||
vpcId: buildConfig.Parameters.VPC,
|
||||
});
|
||||
|
||||
const executeApiVpcEndpointSecurityGroup = new aws_ec2.SecurityGroup(this, `${stackName}ExecuteApiVPCEsg`, {
|
||||
vpc,
|
||||
description: 'Security group for API Gateway VPC Endpoint',
|
||||
allowAllOutbound: true,
|
||||
});
|
||||
|
||||
executeApiVpcEndpointSecurityGroup.addIngressRule(
|
||||
aws_ec2.Peer.ipv4('10.0.0.0/8'),
|
||||
aws_ec2.Port.tcp(443),
|
||||
'Allow inbound HTTPS traffic from 10.0.0.0/8'
|
||||
);
|
||||
|
||||
const executeApiVpcEndpoint = new aws_ec2.InterfaceVpcEndpoint(this, `${stackName}ExecuteApiVPCE`, {
|
||||
vpc,
|
||||
service: aws_ec2.InterfaceVpcEndpointAwsService.APIGATEWAY,
|
||||
privateDnsEnabled: true,
|
||||
subnets: { subnetType: aws_ec2.SubnetType.PRIVATE_WITH_EGRESS },
|
||||
securityGroups: [executeApiVpcEndpointSecurityGroup]
|
||||
});
|
||||
|
||||
const lambdaFunction = new aws_lambda_python_alpha.PythonFunction(this, `${stackName}MCCApp`, {
|
||||
runtime: Runtime.PYTHON_3_11,
|
||||
handler: 'lambda_handler',
|
||||
timeout: Duration.seconds(30),
|
||||
entry: join(__dirname, '../../src'),
|
||||
index: 'run.py',
|
||||
memorySize: 256,
|
||||
vpc,
|
||||
logRetention: RetentionDays.ONE_WEEK,
|
||||
environment: {
|
||||
POSTGRES_USER: process.env.POSTGRES_USER!,
|
||||
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD!,
|
||||
POSTGRES_DB: 'mccdb',
|
||||
FLASK_APP: process.env.FLASK_APP!,
|
||||
FLASK_ENV: process.env.FLASK_ENV!,
|
||||
DATABASE_URL: process.env.DATABASE_URL!,
|
||||
HOST_ENV: process.env.HOST_ENV!,
|
||||
},
|
||||
});
|
||||
|
||||
const lambdaDBUpgradeFunction = new aws_lambda_python_alpha.PythonFunction(this, `${stackName}MCCAppDBUpgrade`, {
|
||||
runtime: Runtime.PYTHON_3_11,
|
||||
handler: 'lambda_handler_upgrade_db',
|
||||
timeout: Duration.minutes(5),
|
||||
entry: join(__dirname, '../../src'),
|
||||
index: 'run.py',
|
||||
memorySize: 1024,
|
||||
vpc,
|
||||
logRetention: RetentionDays.ONE_WEEK,
|
||||
functionName: `${stackName}MCCAppDBUpgrade`,
|
||||
environment: {
|
||||
POSTGRES_USER: process.env.POSTGRES_USER!,
|
||||
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD!,
|
||||
POSTGRES_DB: 'mccdb',
|
||||
FLASK_APP: process.env.FLASK_APP!,
|
||||
FLASK_ENV: process.env.FLASK_ENV!,
|
||||
DATABASE_URL: process.env.DATABASE_URL!,
|
||||
HOST_ENV: process.env.HOST_ENV!,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const api = new apigateway.LambdaRestApi(this, `${stackName}MCCAPI`, {
|
||||
handler: lambdaFunction,
|
||||
proxy: true,
|
||||
deployOptions: {
|
||||
stageName: buildConfig.Environment,
|
||||
},
|
||||
restApiName: `${buildConfig.ApplicationName} API`,
|
||||
description: `API for ${buildConfig.ApplicationName} in ${buildConfig.Environment}`,
|
||||
endpointConfiguration: {
|
||||
types: [apigateway.EndpointType.PRIVATE],
|
||||
vpcEndpoints: [executeApiVpcEndpoint],
|
||||
},
|
||||
policy: new PolicyDocument({
|
||||
statements: [
|
||||
new PolicyStatement({
|
||||
effect: Effect.ALLOW,
|
||||
principals: [new AnyPrincipal()],
|
||||
actions: ['execute-api:Invoke'],
|
||||
resources: ['execute-api:/*/*/*'],
|
||||
// Optionally, restrict by source VPC or VPC endpoint
|
||||
conditions: {
|
||||
StringEquals: { 'aws:SourceVpce': executeApiVpcEndpoint.vpcEndpointId }
|
||||
}
|
||||
}),
|
||||
new PolicyStatement({
|
||||
effect: Effect.DENY,
|
||||
principals: [new AnyPrincipal()],
|
||||
actions: ['execute-api:Invoke'],
|
||||
resources: ['execute-api:/*/*/*'],
|
||||
// Optionally, restrict by source VPC or VPC endpoint
|
||||
conditions: {
|
||||
StringNotEquals: { 'aws:SourceVpce': executeApiVpcEndpoint.vpcEndpointId }
|
||||
}
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
this.api = api;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env node
|
||||
import * as cdk from 'aws-cdk-lib';
|
||||
import { getConfig, BuildConfig } from '../utils/build-config';
|
||||
import { MccAPIInfraStack } from './apiStack';
|
||||
import { MccDBInfraStack } from './rdsStack';
|
||||
|
||||
const app = new cdk.App();
|
||||
const buildConfig: BuildConfig = getConfig(app);
|
||||
|
||||
const qualifier = process.env.CDK_QUALIFIER;
|
||||
|
||||
const stackName = `${buildConfig.Environment}${buildConfig.ApplicationShortName}`;
|
||||
|
||||
cdk.Tags.of(app).add('App', buildConfig.ApplicationName);
|
||||
cdk.Tags.of(app).add('Environment', buildConfig.Environment);
|
||||
cdk.Tags.of(app).add('AppManagerCFNStackKey', stackName);
|
||||
cdk.Tags.of(app).add('purpose', `${buildConfig.Environment}-${buildConfig.ApplicationShortName}`);
|
||||
|
||||
const env: cdk.Environment = {
|
||||
account: buildConfig.Account ?? process.env.CDK_DEPLOY_ACCOUNT ?? process.env.CDK_DEFAULT_ACCOUNT,
|
||||
region: buildConfig.Region ?? process.env.CDK_DEPLOY_REGION ?? process.env.CDK_DEFAULT_REGION,
|
||||
};
|
||||
|
||||
const apiStack = new MccAPIInfraStack(app, `${stackName}MCCAPI`, buildConfig, {
|
||||
env,
|
||||
description: `API stack for ${buildConfig.ApplicationName} in ${buildConfig.Environment}`,
|
||||
synthesizer: new cdk.DefaultStackSynthesizer({
|
||||
qualifier,
|
||||
}),
|
||||
});
|
||||
|
||||
const dbStack = new MccDBInfraStack(app, `${stackName}MCCDB`, buildConfig, {
|
||||
env,
|
||||
description: `Database stack for ${buildConfig.ApplicationName} in ${buildConfig.Environment}`,
|
||||
synthesizer: new cdk.DefaultStackSynthesizer({
|
||||
qualifier,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
Stack,
|
||||
aws_ec2 as ec2,
|
||||
aws_rds as rds,
|
||||
StackProps,
|
||||
RemovalPolicy,
|
||||
} from 'aws-cdk-lib';
|
||||
import { Port } from 'aws-cdk-lib/aws-ec2';
|
||||
import { Construct } from 'constructs';
|
||||
import { BuildConfig } from '../utils/build-config';
|
||||
import { ClusterInstance } from 'aws-cdk-lib/aws-rds';
|
||||
|
||||
export class MccDBInfraStack extends Stack {
|
||||
public readonly mccDB: rds.DatabaseCluster;
|
||||
public readonly mccDBSecurityGroup: ec2.SecurityGroup;
|
||||
|
||||
credentials = rds.Credentials.fromGeneratedSecret('mccAdmin');
|
||||
|
||||
constructor(scope: Construct, id: string, buildConfig: BuildConfig, props: StackProps) {
|
||||
super(scope, id, props);
|
||||
|
||||
const vpc = ec2.Vpc.fromLookup(
|
||||
this,
|
||||
`${buildConfig.Environment}-${buildConfig.ApplicationShortName}MccVPC`,
|
||||
{
|
||||
vpcId: buildConfig.Parameters.VPC,
|
||||
}
|
||||
);
|
||||
|
||||
const dbSecurityGroup = new ec2.SecurityGroup(
|
||||
this,
|
||||
`${buildConfig.Environment}${buildConfig.ApplicationShortName}MccDBSecurityGroup`,
|
||||
{
|
||||
vpc: vpc,
|
||||
securityGroupName: `${buildConfig.Environment}-${buildConfig.ApplicationShortName}DBSecurityGroup`,
|
||||
}
|
||||
);
|
||||
|
||||
const mccDB = new rds.DatabaseCluster(this, `${buildConfig.Environment}${buildConfig.ApplicationShortName}MccDB`, {
|
||||
engine: rds.DatabaseClusterEngine.auroraPostgres({
|
||||
version: rds.AuroraPostgresEngineVersion.VER_17_4,
|
||||
}) ,
|
||||
vpc,
|
||||
writer: ClusterInstance.serverlessV2('writer') ,
|
||||
credentials: this.credentials,
|
||||
defaultDatabaseName: 'mccdb',
|
||||
securityGroups: [dbSecurityGroup],
|
||||
serverlessV2MinCapacity: 0,
|
||||
serverlessV2MaxCapacity: 64,
|
||||
enableDataApi: true,
|
||||
clusterIdentifier: `${buildConfig.Environment}-${buildConfig.ApplicationShortName}-mccdb`,
|
||||
removalPolicy: RemovalPolicy.RETAIN
|
||||
});
|
||||
|
||||
const lambdaSecurityGroup = new ec2.SecurityGroup(
|
||||
this,
|
||||
`${buildConfig.Environment}${buildConfig.ApplicationShortName}MccDBLambdaSecurityGroup`,
|
||||
{
|
||||
vpc: vpc,
|
||||
securityGroupName: `${buildConfig.Environment}-${buildConfig.ApplicationShortName}DBLambdaSecurityGroup`,
|
||||
}
|
||||
);
|
||||
|
||||
dbSecurityGroup.addIngressRule(
|
||||
lambdaSecurityGroup,
|
||||
Port.tcp(mccDB.clusterEndpoint.port),
|
||||
'Allow connection from Lambda Function to DB'
|
||||
);
|
||||
|
||||
dbSecurityGroup.addIngressRule(
|
||||
ec2.Peer.ipv4('10.0.0.0/8'),
|
||||
Port.tcp(mccDB.clusterEndpoint.port),
|
||||
'Allow connection from internal network'
|
||||
);
|
||||
|
||||
this.mccDB = mccDB;
|
||||
this.mccDBSecurityGroup = lambdaSecurityGroup;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"vpc-provider:account=030215556060:filter.vpc-id=vpc-02e23456a3e6064f1:region=eu-west-1:returnAsymmetricSubnets=true": {
|
||||
"vpcId": "vpc-02e23456a3e6064f1",
|
||||
"vpcCidrBlock": "10.157.7.0/24",
|
||||
"ownerAccountId": "030215556060",
|
||||
"availabilityZones": [],
|
||||
"subnetGroups": [
|
||||
{
|
||||
"name": "Private",
|
||||
"type": "Private",
|
||||
"subnets": [
|
||||
{
|
||||
"subnetId": "subnet-014953bcd8175abe5",
|
||||
"cidr": "10.157.7.0/25",
|
||||
"availabilityZone": "eu-west-1a",
|
||||
"routeTableId": "rtb-0e60c84be1611577e"
|
||||
},
|
||||
{
|
||||
"subnetId": "subnet-0fb151177f9b7d430",
|
||||
"cidr": "10.157.7.128/26",
|
||||
"availabilityZone": "eu-west-1b",
|
||||
"routeTableId": "rtb-09dc6ac1e26eb92ba"
|
||||
},
|
||||
{
|
||||
"subnetId": "subnet-0696d516a7e905674",
|
||||
"cidr": "10.157.7.192/26",
|
||||
"availabilityZone": "eu-west-1c",
|
||||
"routeTableId": "rtb-04cc2504f623b4d2e"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"app": "npx ts-node --prefer-ts-exts bin/infra.ts",
|
||||
"watch": {
|
||||
"include": [
|
||||
"**"
|
||||
],
|
||||
"exclude": [
|
||||
"README.md",
|
||||
"cdk*.json",
|
||||
"**/*.d.ts",
|
||||
"**/*.js",
|
||||
"tsconfig.json",
|
||||
"package*.json",
|
||||
"yarn.lock",
|
||||
"node_modules",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
"context": {
|
||||
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
|
||||
"@aws-cdk/core:checkSecretUsage": true,
|
||||
"@aws-cdk/core:target-partitions": [
|
||||
"aws",
|
||||
"aws-cn"
|
||||
],
|
||||
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
|
||||
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
|
||||
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
|
||||
"@aws-cdk/aws-iam:minimizePolicies": true,
|
||||
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
|
||||
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
|
||||
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
|
||||
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
|
||||
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
|
||||
"@aws-cdk/core:enablePartitionLiterals": true,
|
||||
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
|
||||
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
|
||||
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
|
||||
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
|
||||
"@aws-cdk/aws-route53-patters:useCertificate": true,
|
||||
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
|
||||
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
|
||||
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
|
||||
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
|
||||
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
|
||||
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
|
||||
"@aws-cdk/aws-redshift:columnId": true,
|
||||
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
|
||||
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
|
||||
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
|
||||
"@aws-cdk/aws-kms:aliasNameRef": true,
|
||||
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
|
||||
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
|
||||
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
|
||||
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
|
||||
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
|
||||
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
|
||||
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
|
||||
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
|
||||
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
|
||||
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
|
||||
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
|
||||
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
|
||||
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
|
||||
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
|
||||
"@aws-cdk/aws-eks:nodegroupNameAttribute": true,
|
||||
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
|
||||
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
|
||||
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
|
||||
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false,
|
||||
"@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature": false,
|
||||
"@aws-cdk/aws-ecs:disableEcsImdsBlocking": true,
|
||||
"@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true,
|
||||
"@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true,
|
||||
"@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true,
|
||||
"@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true,
|
||||
"@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true,
|
||||
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true,
|
||||
"@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true,
|
||||
"@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true,
|
||||
"@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true,
|
||||
"@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true,
|
||||
"@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true,
|
||||
"@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": true,
|
||||
"@aws-cdk/core:enableAdditionalMetadataCollection": true,
|
||||
"@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": false,
|
||||
"@aws-cdk/aws-s3:setUniqueReplicationRoleName": true,
|
||||
"@aws-cdk/aws-events:requireEventBusPolicySid": true,
|
||||
"@aws-cdk/core:aspectPrioritiesMutating": true,
|
||||
"@aws-cdk/aws-dynamodb:retainTableReplica": true,
|
||||
"@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": true,
|
||||
"@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": true,
|
||||
"@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true,
|
||||
"@aws-cdk/aws-s3:publicAccessBlockedByDefault": true,
|
||||
"@aws-cdk/aws-lambda:useCdkManagedLogGroup": true,
|
||||
"dev": {
|
||||
"Account": "030215556060",
|
||||
"Region": "eu-west-1",
|
||||
"ApplicationName": "Mold Cost Calculator",
|
||||
"ApplicationShortName": "mld",
|
||||
"Environment": "dev",
|
||||
"Version": "1.0.0",
|
||||
"Parameters": {
|
||||
"VPC": "vpc-02e23456a3e6064f1",
|
||||
"TenantID": "3bfeb222-e42c-4535-aace-ea6f7751369b",
|
||||
"ClientID": "bc56643f-7002-4df9-ada5-1d79f55da6e3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
testEnvironment: 'node',
|
||||
roots: ['<rootDir>/test'],
|
||||
testMatch: ['**/*.test.ts'],
|
||||
transform: {
|
||||
'^.+\\.tsx?$': 'ts-jest'
|
||||
}
|
||||
};
|
||||
Generated
+4470
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "infra",
|
||||
"version": "0.1.0",
|
||||
"bin": {
|
||||
"infra": "bin/infra.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"watch": "tsc -w",
|
||||
"test": "jest",
|
||||
"cdk": "cdk"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "22.7.9",
|
||||
"aws-cdk": "2.1019.1",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.2.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "~5.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-cdk/aws-lambda-python-alpha": "^2.202.0-alpha.0",
|
||||
"aws-cdk-lib": "2.202.0",
|
||||
"constructs": "^10.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"lib": [
|
||||
"es2022"
|
||||
],
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"noImplicitThis": true,
|
||||
"alwaysStrict": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": false,
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"experimentalDecorators": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"cdk.out"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import * as cdk from 'aws-cdk-lib';
|
||||
|
||||
interface BuildParameters {
|
||||
readonly VPC: string;
|
||||
readonly TenantID: string;
|
||||
readonly ClientID: string;
|
||||
}
|
||||
|
||||
interface BuildConfig {
|
||||
readonly Account: string;
|
||||
readonly Region: string;
|
||||
readonly ApplicationName: string;
|
||||
readonly ApplicationShortName: string;
|
||||
readonly Environment: string;
|
||||
readonly Version: string;
|
||||
readonly Parameters: BuildParameters;
|
||||
}
|
||||
|
||||
const getValidStringValueOfKey = (buildConfig: any, key: string): string => {
|
||||
if (!buildConfig[key] || buildConfig[key].trim().length === 0) {
|
||||
throw new Error(key + ' does not exist or is empty in build config');
|
||||
}
|
||||
|
||||
return buildConfig[key];
|
||||
};
|
||||
|
||||
const getValidNumberValueOfKey = (buildConfig: any, key: string): number => {
|
||||
if (!buildConfig[key]) {
|
||||
throw new Error(key + ' does not exist or is empty in build config');
|
||||
}
|
||||
|
||||
return buildConfig[key];
|
||||
};
|
||||
|
||||
const getConfig = (app: cdk.App): BuildConfig => {
|
||||
const env = app.node.tryGetContext('config');
|
||||
if (!env) {
|
||||
throw new Error('Context variable missing on CDK command. Pass in as "-c config=env"');
|
||||
}
|
||||
|
||||
const buildConfig: any = app.node.tryGetContext(env);
|
||||
|
||||
if (!buildConfig) {
|
||||
throw new Error('Wrong Context variable passed on CDK command. Pass in as "-c config=env"');
|
||||
}
|
||||
|
||||
const validBuildConfig: BuildConfig = {
|
||||
Account: getValidStringValueOfKey(buildConfig, 'Account'),
|
||||
Region: getValidStringValueOfKey(buildConfig, 'Region'),
|
||||
Version: getValidStringValueOfKey(buildConfig, 'Version'),
|
||||
ApplicationName: getValidStringValueOfKey(buildConfig, 'ApplicationName'),
|
||||
ApplicationShortName: getValidStringValueOfKey(buildConfig, 'ApplicationShortName'),
|
||||
Environment: getValidStringValueOfKey(buildConfig, 'Environment'),
|
||||
Parameters: {
|
||||
VPC: getValidStringValueOfKey(buildConfig['Parameters'], 'VPC'),
|
||||
TenantID: getValidStringValueOfKey(buildConfig['Parameters'], 'TenantID'),
|
||||
ClientID: getValidStringValueOfKey(buildConfig['Parameters'], 'ClientID')
|
||||
},
|
||||
};
|
||||
|
||||
return validBuildConfig;
|
||||
};
|
||||
|
||||
export { BuildConfig, BuildParameters, getConfig };
|
||||
+13
-10
@@ -35,15 +35,17 @@ def create_app(config_class=None):
|
||||
if config_class is None:
|
||||
config_class = get_config()
|
||||
app.config.from_object(config_class)
|
||||
|
||||
|
||||
# Ensure SECRET_KEY is set from environment if not present
|
||||
if not hasattr(config_class, 'SECRET_KEY') or not config_class.SECRET_KEY:
|
||||
env_secret = os.environ.get('SECRET_KEY')
|
||||
if not env_secret:
|
||||
raise ValueError("SECRET_KEY environment variable is not set")
|
||||
config_class.SECRET_KEY = env_secret
|
||||
app.config['SECRET_KEY'] = config_class.SECRET_KEY.encode('utf-8')
|
||||
# Initialize configuration
|
||||
config_class.init_app(app)
|
||||
|
||||
# Ensure secret key is properly set
|
||||
if not app.config.get('SECRET_KEY'):
|
||||
raise ValueError("SECRET_KEY must be set in the configuration")
|
||||
app.config['SECRET_KEY'] = app.config['SECRET_KEY'].encode('utf-8')
|
||||
|
||||
# Ensure instance folder exists
|
||||
try:
|
||||
os.makedirs(app.instance_path)
|
||||
@@ -51,7 +53,8 @@ def create_app(config_class=None):
|
||||
pass
|
||||
|
||||
# Configure logging
|
||||
configure_logging(app)
|
||||
if os.environ.get('HOST_ENV') != 'AWS':
|
||||
configure_logging(app)
|
||||
|
||||
# Initialize extensions with retry logic
|
||||
max_retries = 3
|
||||
@@ -117,8 +120,8 @@ def create_app(config_class=None):
|
||||
handler.flush()
|
||||
|
||||
# Debug: write all registered endpoints to a file
|
||||
with open('logs/endpoints.log', 'w') as f:
|
||||
for rule in app.url_map.iter_rules():
|
||||
f.write(f"{rule.endpoint} {rule.methods} {rule.rule}\n")
|
||||
# with open('logs/endpoints.log', 'w') as f:
|
||||
# for rule in app.url_map.iter_rules():
|
||||
# f.write(f"{rule.endpoint} {rule.methods} {rule.rule}\n")
|
||||
|
||||
return app
|
||||
+9
-8
@@ -164,14 +164,15 @@ class Config:
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = cls.SQLALCHEMY_DATABASE_URI
|
||||
|
||||
# Configure logging
|
||||
if not os.path.exists('logs'):
|
||||
os.mkdir('logs')
|
||||
file_handler = RotatingFileHandler('logs/app.log',
|
||||
maxBytes=10240,
|
||||
backupCount=10)
|
||||
file_handler.setFormatter(logging.Formatter(cls.LOG_FORMAT))
|
||||
file_handler.setLevel(logging.INFO)
|
||||
app.logger.addHandler(file_handler)
|
||||
if os.environ.get('HOST_ENV') != 'AWS':
|
||||
if not os.path.exists('logs'):
|
||||
os.mkdir('logs')
|
||||
file_handler = RotatingFileHandler('logs/app.log',
|
||||
maxBytes=10240,
|
||||
backupCount=10)
|
||||
file_handler.setFormatter(logging.Formatter(cls.LOG_FORMAT))
|
||||
file_handler.setLevel(logging.INFO)
|
||||
app.logger.addHandler(file_handler)
|
||||
app.logger.setLevel(logging.INFO)
|
||||
app.logger.info('Application startup')
|
||||
|
||||
|
||||
Binary file not shown.
+11
-1
@@ -5,6 +5,8 @@ import os
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from flask import render_template
|
||||
from flask_migrate import upgrade
|
||||
import awsgi
|
||||
|
||||
app = create_app()
|
||||
|
||||
@@ -39,4 +41,12 @@ if not app.debug:
|
||||
app.logger.info('Mold Cost Calculator startup')
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=5004)
|
||||
app.run(host='0.0.0.0', port=5004)
|
||||
|
||||
def lambda_handler(event, context):
|
||||
return awsgi.response(app, event, context)
|
||||
|
||||
def lambda_handler_upgrade_db(event, context):
|
||||
with app.app_context():
|
||||
upgrade()
|
||||
return {"statusCode": 200, "body": "Database upgraded successfully"}
|
||||
Reference in New Issue
Block a user