From b73f958fd48b5cd554b44e2c0ac21be797f84e29 Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Wed, 19 Jun 2024 22:47:45 +1200 Subject: [PATCH 01/35] initial commit --- .../aws-codepipeline-ecs-lambda/.gitignore | 8 +++ .../aws-codepipeline-ecs-lambda/.npmignore | 6 ++ .../aws-codepipeline-ecs-lambda/README.md | 14 ++++ .../aws-codepipeline-ecs-lambda/cdk.json | 72 +++++++++++++++++++ .../jest.config.js | 8 +++ .../aws-codepipeline-ecs-lambda/lib/app.ts | 15 ++++ .../lib/pipeline-app-stage.ts | 24 +++++++ .../lib/pipeline-async-lambda-stack.ts | 15 ++++ .../lib/pipeline-ecs-fargate-stack.ts | 53 ++++++++++++++ .../lib/pipeline-lambda-api-stack.ts | 15 ++++ .../lib/pipeline-stack.ts | 38 ++++++++++ .../lib/pipeline-vpc-stack.ts | 0 .../aws-codepipeline-ecs-lambda/package.json | 27 +++++++ .../test/aws-codepipeline-ecs-lambda.test.ts | 17 +++++ .../aws-codepipeline-ecs-lambda/tsconfig.json | 31 ++++++++ 15 files changed, 343 insertions(+) create mode 100644 typescript/aws-codepipeline-ecs-lambda/.gitignore create mode 100644 typescript/aws-codepipeline-ecs-lambda/.npmignore create mode 100644 typescript/aws-codepipeline-ecs-lambda/README.md create mode 100644 typescript/aws-codepipeline-ecs-lambda/cdk.json create mode 100644 typescript/aws-codepipeline-ecs-lambda/jest.config.js create mode 100644 typescript/aws-codepipeline-ecs-lambda/lib/app.ts create mode 100644 typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-stage.ts create mode 100644 typescript/aws-codepipeline-ecs-lambda/lib/pipeline-async-lambda-stack.ts create mode 100644 typescript/aws-codepipeline-ecs-lambda/lib/pipeline-ecs-fargate-stack.ts create mode 100644 typescript/aws-codepipeline-ecs-lambda/lib/pipeline-lambda-api-stack.ts create mode 100644 typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts create mode 100644 typescript/aws-codepipeline-ecs-lambda/lib/pipeline-vpc-stack.ts create mode 100644 typescript/aws-codepipeline-ecs-lambda/package.json create mode 100644 typescript/aws-codepipeline-ecs-lambda/test/aws-codepipeline-ecs-lambda.test.ts create mode 100644 typescript/aws-codepipeline-ecs-lambda/tsconfig.json diff --git a/typescript/aws-codepipeline-ecs-lambda/.gitignore b/typescript/aws-codepipeline-ecs-lambda/.gitignore new file mode 100644 index 0000000000..f60797b6a9 --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/.gitignore @@ -0,0 +1,8 @@ +*.js +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/typescript/aws-codepipeline-ecs-lambda/.npmignore b/typescript/aws-codepipeline-ecs-lambda/.npmignore new file mode 100644 index 0000000000..c1d6d45dcf --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/typescript/aws-codepipeline-ecs-lambda/README.md b/typescript/aws-codepipeline-ecs-lambda/README.md new file mode 100644 index 0000000000..9315fe5b9f --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/README.md @@ -0,0 +1,14 @@ +# Welcome to your CDK TypeScript project + +This is a blank project for CDK development with TypeScript. + +The `cdk.json` file tells the CDK Toolkit how to execute your app. + +## Useful commands + +* `npm run build` compile typescript to js +* `npm run watch` watch for changes and compile +* `npm run test` perform the jest unit tests +* `npx cdk deploy` deploy this stack to your default AWS account/region +* `npx cdk diff` compare deployed stack with current state +* `npx cdk synth` emits the synthesized CloudFormation template diff --git a/typescript/aws-codepipeline-ecs-lambda/cdk.json b/typescript/aws-codepipeline-ecs-lambda/cdk.json new file mode 100644 index 0000000000..50556dcaf0 --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/cdk.json @@ -0,0 +1,72 @@ +{ + "app": "npx ts-node --prefer-ts-exts lib/app.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-iam:standardizedServicePrincipals": 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 + } +} diff --git a/typescript/aws-codepipeline-ecs-lambda/jest.config.js b/typescript/aws-codepipeline-ecs-lambda/jest.config.js new file mode 100644 index 0000000000..08263b8954 --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/app.ts b/typescript/aws-codepipeline-ecs-lambda/lib/app.ts new file mode 100644 index 0000000000..bd2756a021 --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/lib/app.ts @@ -0,0 +1,15 @@ +#!/usr/bin/env node +import 'source-map-support/register'; +import * as cdk from 'aws-cdk-lib'; +import { pipelineStack } from './pipeline-stack'; + +const app = new cdk.App(); +const env = { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION } + +const pipeline_stack = new pipelineStack(app, 'aws-codepipeline-stack', { + env, +}); +cdk.Tags.of(pipeline_stack).add('managedBy', 'cdk'); +cdk.Tags.of(pipeline_stack).add('environment', 'dev'); + +app.synth(); diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-stage.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-stage.ts new file mode 100644 index 0000000000..a6c1758b8a --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-stage.ts @@ -0,0 +1,24 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from "constructs"; +import { lambdaApiStack } from './pipeline-lambda-api-stack'; +import { ecsPatternsStack } from './pipeline-ecs-fargate-stack'; +import { asyncLambdaStack } from './pipeline-async-lambda-stack'; + +export class pipelineAppStage extends cdk.Stage { + + constructor(scope: Construct, id: string, props?: cdk.StageProps) { + super(scope, id, props); + + const lambda_api_stack = new lambdaApiStack(this, 'lambdaStack'); + cdk.Tags.of(lambda_api_stack).add('managedBy', 'cdk'); + cdk.Tags.of(lambda_api_stack).add('environment', 'dev'); + + const ecs_stack = new ecsPatternsStack(this, 'ecsPatternsStack'); + cdk.Tags.of(ecs_stack).add('managedBy', 'cdk'); + cdk.Tags.of(ecs_stack).add('environment', 'dev'); + + const async_lambda_stack = new asyncLambdaStack(this, 'asyncLambdaStack'); + cdk.Tags.of(async_lambda_stack).add('managedBy', 'cdk'); + cdk.Tags.of(async_lambda_stack).add('environment', 'dev'); + } +} \ No newline at end of file diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-async-lambda-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-async-lambda-stack.ts new file mode 100644 index 0000000000..b44748e9bf --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-async-lambda-stack.ts @@ -0,0 +1,15 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import { Function, InlineCode, Runtime } from 'aws-cdk-lib/aws-lambda'; + +export class asyncLambdaStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + new Function(this, 'lambdaFunction', { + runtime: Runtime.NODEJS_20_X, + handler: 'index.handler', + code: new InlineCode('exports.handler = _ => "Hello, CDK";') + }); + } +} \ No newline at end of file diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-ecs-fargate-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-ecs-fargate-stack.ts new file mode 100644 index 0000000000..beea267de4 --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-ecs-fargate-stack.ts @@ -0,0 +1,53 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import { ApplicationLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patterns'; +import { IVpc, SubnetType, Vpc } from 'aws-cdk-lib/aws-ec2'; +import { Cluster, ContainerImage } from 'aws-cdk-lib/aws-ecs'; + +export class ecsPatternsStack extends cdk.Stack { + public readonly vpc: IVpc; + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + this.vpc = new Vpc (this, "ecsVpc", { + natGateways: 1, + }); + + const cluster = new Cluster(this, 'ecsCluster', { + vpc: this.vpc, + containerInsights: true, + }); + + // 👇 create a new ecs pattern with an alb 👇 + const loadBalancedFargateService = new ApplicationLoadBalancedFargateService (this, 'ecsPattern', { + cluster: cluster, + cpu: 256, + memoryLimitMiB: 512, + desiredCount: 1, + publicLoadBalancer: true, + taskSubnets: { subnetType: SubnetType.PRIVATE_WITH_EGRESS }, + taskImageOptions: { + image: ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + }, + enableExecuteCommand: true, + }); + + // 👇 auto scale task count 👇 + const scalableTarget = loadBalancedFargateService.service.autoScaleTaskCount({ + minCapacity: 1, + maxCapacity: 6, + }); + // 👇 auto scale cpu trigger 👇 + scalableTarget.scaleOnCpuUtilization('CpuScaling', { + targetUtilizationPercent: 50, + }); + // 👇 auto scale memory trigger 👇 + scalableTarget.scaleOnMemoryUtilization('MemoryScaling', { + targetUtilizationPercent: 50, + }); + // 👇 load balancer health check 👇 + loadBalancedFargateService.targetGroup.configureHealthCheck({ + path: "/", + }); + } +} \ No newline at end of file diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-lambda-api-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-lambda-api-stack.ts new file mode 100644 index 0000000000..42872adda2 --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-lambda-api-stack.ts @@ -0,0 +1,15 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import { Function, InlineCode, Runtime } from 'aws-cdk-lib/aws-lambda'; + +export class lambdaApiStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + new Function(this, 'lambdaFunction', { + runtime: Runtime.NODEJS_20_X, + handler: 'index.handler', + code: new InlineCode('exports.handler = _ => "Hello, CDK";') + }); + } +} diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts new file mode 100644 index 0000000000..0fe0813f3b --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts @@ -0,0 +1,38 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import { CodePipeline, CodePipelineSource, ManualApprovalStep, ShellStep, Wave } from 'aws-cdk-lib/pipelines'; +import { pipelineAppStage } from './pipeline-app-stage'; + +export class pipelineStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const githubOrg = process.env.GITHUB_ORG || "jfan9"; + const githubRepo = process.env.GITHUB_REPO || "jfan9-aws-cdk-examples"; + const githubBranch = process.env.GITHUB_BRANCH || "issue#820"; + const devEnv = process.env.DEV_ENV || "dev"; + + const pipeline = new CodePipeline(this, 'pipeline', { + selfMutation: true, + crossAccountKeys: true, + reuseCrossRegionSupportStacks: true, + synth: new ShellStep('Synth', { + input: CodePipelineSource.connection(`${githubOrg}/${githubRepo}`, `${githubBranch}`,{ + // You need to replace the below code connection: + connectionArn: `arn:aws:codestar-connections:ap-southeast-2:${props?.env?.account}:connection/0ce75950-a29b-4ee4-a9d3-b0bad3b2c0a6` + }), + commands: [ + 'cd typescript/aws-codepipeline-ecs-lambda', + 'npm ci', + 'npm run build', + 'npx cdk synth' + ] + }) + }); + + const devStage = pipeline.addStage(new pipelineAppStage(this, `${devEnv}`, { + env: { account: props?.env?.account, region: props?.env?.region} + })); + devStage.addPost(new ManualApprovalStep('approval')); + } +} \ No newline at end of file diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-vpc-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-vpc-stack.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/typescript/aws-codepipeline-ecs-lambda/package.json b/typescript/aws-codepipeline-ecs-lambda/package.json new file mode 100644 index 0000000000..865d81368a --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/package.json @@ -0,0 +1,27 @@ +{ + "name": "aws-codepipeline-ecs-lambda", + "version": "0.1.0", + "bin": { + "aws-codepipeline-ecs-lambda": "bin/aws-codepipeline-ecs-lambda.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk" + }, + "devDependencies": { + "@types/jest": "^29.5.12", + "@types/node": "20.14.2", + "jest": "^29.7.0", + "ts-jest": "^29.1.4", + "aws-cdk": "2.146.0", + "ts-node": "^10.9.2", + "typescript": "~5.4.5" + }, + "dependencies": { + "aws-cdk-lib": "2.146.0", + "constructs": "^10.0.0", + "source-map-support": "^0.5.21" + } +} \ No newline at end of file diff --git a/typescript/aws-codepipeline-ecs-lambda/test/aws-codepipeline-ecs-lambda.test.ts b/typescript/aws-codepipeline-ecs-lambda/test/aws-codepipeline-ecs-lambda.test.ts new file mode 100644 index 0000000000..8d5a8dd386 --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/test/aws-codepipeline-ecs-lambda.test.ts @@ -0,0 +1,17 @@ +// import * as cdk from 'aws-cdk-lib'; +// import { Template } from 'aws-cdk-lib/assertions'; +// import * as AwsCodepipelineEcsLambda from '../lib/aws-codepipeline-ecs-lambda-stack'; + +// example test. To run these tests, uncomment this file along with the +// example resource in lib/aws-codepipeline-ecs-lambda-stack.ts +test('SQS Queue Created', () => { +// const app = new cdk.App(); +// // WHEN +// const stack = new AwsCodepipelineEcsLambda.AwsCodepipelineEcsLambdaStack(app, 'MyTestStack'); +// // THEN +// const template = Template.fromStack(stack); + +// template.hasResourceProperties('AWS::SQS::Queue', { +// VisibilityTimeout: 300 +// }); +}); diff --git a/typescript/aws-codepipeline-ecs-lambda/tsconfig.json b/typescript/aws-codepipeline-ecs-lambda/tsconfig.json new file mode 100644 index 0000000000..aaa7dc510f --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020", + "dom" + ], + "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" + ] +} From ac4aba1682b0c49d1eb0d2a40f7e1bf9616425fa Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Wed, 19 Jun 2024 22:57:17 +1200 Subject: [PATCH 02/35] remove cd command --- typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts index 0fe0813f3b..432382b780 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts @@ -22,7 +22,6 @@ export class pipelineStack extends cdk.Stack { connectionArn: `arn:aws:codestar-connections:ap-southeast-2:${props?.env?.account}:connection/0ce75950-a29b-4ee4-a9d3-b0bad3b2c0a6` }), commands: [ - 'cd typescript/aws-codepipeline-ecs-lambda', 'npm ci', 'npm run build', 'npx cdk synth' From baa54900116518191fa755d102f4d46faa91b42b Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Wed, 19 Jun 2024 23:01:38 +1200 Subject: [PATCH 03/35] added github project path --- typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts index 432382b780..99bd048496 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts @@ -9,6 +9,7 @@ export class pipelineStack extends cdk.Stack { const githubOrg = process.env.GITHUB_ORG || "jfan9"; const githubRepo = process.env.GITHUB_REPO || "jfan9-aws-cdk-examples"; + const githubProject = process.env.GITHUB_PROJECT || "typescript/aws-codepipeline-ecs-lambda"; const githubBranch = process.env.GITHUB_BRANCH || "issue#820"; const devEnv = process.env.DEV_ENV || "dev"; @@ -17,8 +18,8 @@ export class pipelineStack extends cdk.Stack { crossAccountKeys: true, reuseCrossRegionSupportStacks: true, synth: new ShellStep('Synth', { - input: CodePipelineSource.connection(`${githubOrg}/${githubRepo}`, `${githubBranch}`,{ - // You need to replace the below code connection: + input: CodePipelineSource.connection(`${githubOrg}/${githubRepo}/${githubProject}`, `${githubBranch}`,{ + // You need to replace the below code connection arn: connectionArn: `arn:aws:codestar-connections:ap-southeast-2:${props?.env?.account}:connection/0ce75950-a29b-4ee4-a9d3-b0bad3b2c0a6` }), commands: [ From f4ec8f0bd145f9670700a99ed46c51e29b259f59 Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Wed, 19 Jun 2024 23:25:15 +1200 Subject: [PATCH 04/35] rename stacks --- .../lib/{pipeline-app-stage.ts => app-stage.ts} | 12 ++++++------ ...a-stack.ts => pipeline-app-async-lambda-stack.ts} | 0 ...te-stack.ts => pipeline-app-ecs-fargate-stack.ts} | 2 +- ...api-stack.ts => pipeline-app-lambda-api-stack.ts} | 0 ...peline-vpc-stack.ts => pipeline-app-vpc-stack.ts} | 0 .../lib/pipeline-stack.ts | 11 +++++------ 6 files changed, 12 insertions(+), 13 deletions(-) rename typescript/aws-codepipeline-ecs-lambda/lib/{pipeline-app-stage.ts => app-stage.ts} (58%) rename typescript/aws-codepipeline-ecs-lambda/lib/{pipeline-async-lambda-stack.ts => pipeline-app-async-lambda-stack.ts} (100%) rename typescript/aws-codepipeline-ecs-lambda/lib/{pipeline-ecs-fargate-stack.ts => pipeline-app-ecs-fargate-stack.ts} (97%) rename typescript/aws-codepipeline-ecs-lambda/lib/{pipeline-lambda-api-stack.ts => pipeline-app-lambda-api-stack.ts} (100%) rename typescript/aws-codepipeline-ecs-lambda/lib/{pipeline-vpc-stack.ts => pipeline-app-vpc-stack.ts} (100%) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-stage.ts b/typescript/aws-codepipeline-ecs-lambda/lib/app-stage.ts similarity index 58% rename from typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-stage.ts rename to typescript/aws-codepipeline-ecs-lambda/lib/app-stage.ts index a6c1758b8a..0780bc7f4d 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-stage.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/app-stage.ts @@ -1,23 +1,23 @@ import * as cdk from 'aws-cdk-lib'; import { Construct } from "constructs"; -import { lambdaApiStack } from './pipeline-lambda-api-stack'; -import { ecsPatternsStack } from './pipeline-ecs-fargate-stack'; -import { asyncLambdaStack } from './pipeline-async-lambda-stack'; +import { lambdaApiStack } from './pipeline-app-lambda-api-stack'; +import { ecsFargateStack } from './pipeline-app-ecs-fargate-stack'; +import { asyncLambdaStack } from './pipeline-app-async-lambda-stack'; export class pipelineAppStage extends cdk.Stage { constructor(scope: Construct, id: string, props?: cdk.StageProps) { super(scope, id, props); - const lambda_api_stack = new lambdaApiStack(this, 'lambdaStack'); + const lambda_api_stack = new lambdaApiStack(this, 'LambdaApisStack'); cdk.Tags.of(lambda_api_stack).add('managedBy', 'cdk'); cdk.Tags.of(lambda_api_stack).add('environment', 'dev'); - const ecs_stack = new ecsPatternsStack(this, 'ecsPatternsStack'); + const ecs_stack = new ecsFargateStack(this, 'EcsFargateStack'); cdk.Tags.of(ecs_stack).add('managedBy', 'cdk'); cdk.Tags.of(ecs_stack).add('environment', 'dev'); - const async_lambda_stack = new asyncLambdaStack(this, 'asyncLambdaStack'); + const async_lambda_stack = new asyncLambdaStack(this, 'AsyncLambdasStack'); cdk.Tags.of(async_lambda_stack).add('managedBy', 'cdk'); cdk.Tags.of(async_lambda_stack).add('environment', 'dev'); } diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-async-lambda-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-async-lambda-stack.ts similarity index 100% rename from typescript/aws-codepipeline-ecs-lambda/lib/pipeline-async-lambda-stack.ts rename to typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-async-lambda-stack.ts diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-ecs-fargate-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-ecs-fargate-stack.ts similarity index 97% rename from typescript/aws-codepipeline-ecs-lambda/lib/pipeline-ecs-fargate-stack.ts rename to typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-ecs-fargate-stack.ts index beea267de4..73c89271b0 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-ecs-fargate-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-ecs-fargate-stack.ts @@ -4,7 +4,7 @@ import { ApplicationLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patte import { IVpc, SubnetType, Vpc } from 'aws-cdk-lib/aws-ec2'; import { Cluster, ContainerImage } from 'aws-cdk-lib/aws-ecs'; -export class ecsPatternsStack extends cdk.Stack { +export class ecsFargateStack extends cdk.Stack { public readonly vpc: IVpc; constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-lambda-api-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-lambda-api-stack.ts similarity index 100% rename from typescript/aws-codepipeline-ecs-lambda/lib/pipeline-lambda-api-stack.ts rename to typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-lambda-api-stack.ts diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-vpc-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-vpc-stack.ts similarity index 100% rename from typescript/aws-codepipeline-ecs-lambda/lib/pipeline-vpc-stack.ts rename to typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-vpc-stack.ts diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts index 99bd048496..d0874b13e7 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts @@ -1,16 +1,15 @@ import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { CodePipeline, CodePipelineSource, ManualApprovalStep, ShellStep, Wave } from 'aws-cdk-lib/pipelines'; -import { pipelineAppStage } from './pipeline-app-stage'; +import { pipelineAppStage } from './app-stage'; export class pipelineStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); - const githubOrg = process.env.GITHUB_ORG || "jfan9"; - const githubRepo = process.env.GITHUB_REPO || "jfan9-aws-cdk-examples"; - const githubProject = process.env.GITHUB_PROJECT || "typescript/aws-codepipeline-ecs-lambda"; - const githubBranch = process.env.GITHUB_BRANCH || "issue#820"; + const githubOrg = process.env.GITHUB_ORG || "aws-6w8hnx"; + const githubRepo = process.env.GITHUB_REPO || "aws-codepipeline-ecs-lambda"; + const githubBranch = process.env.GITHUB_BRANCH || "main"; const devEnv = process.env.DEV_ENV || "dev"; const pipeline = new CodePipeline(this, 'pipeline', { @@ -18,7 +17,7 @@ export class pipelineStack extends cdk.Stack { crossAccountKeys: true, reuseCrossRegionSupportStacks: true, synth: new ShellStep('Synth', { - input: CodePipelineSource.connection(`${githubOrg}/${githubRepo}/${githubProject}`, `${githubBranch}`,{ + input: CodePipelineSource.connection(`${githubOrg}/${githubRepo}`, `${githubBranch}`,{ // You need to replace the below code connection arn: connectionArn: `arn:aws:codestar-connections:ap-southeast-2:${props?.env?.account}:connection/0ce75950-a29b-4ee4-a9d3-b0bad3b2c0a6` }), From c5e078c47fee328ac36c44d731fafcb001e0aa92 Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Thu, 20 Jun 2024 09:23:30 +1200 Subject: [PATCH 05/35] rename stage app stacks --- .../aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts | 2 +- ...sync-lambda-stack.ts => stage-app-async-lambda-stack.ts} | 0 ...-ecs-fargate-stack.ts => stage-app-ecs-fargate-stack.ts} | 0 ...pp-lambda-api-stack.ts => stage-app-lambda-api-stack.ts} | 0 .../{pipeline-app-vpc-stack.ts => stage-app-vpc-stack.ts} | 0 .../lib/{app-stage.ts => stage-app.ts} | 6 +++--- 6 files changed, 4 insertions(+), 4 deletions(-) rename typescript/aws-codepipeline-ecs-lambda/lib/{pipeline-app-async-lambda-stack.ts => stage-app-async-lambda-stack.ts} (100%) rename typescript/aws-codepipeline-ecs-lambda/lib/{pipeline-app-ecs-fargate-stack.ts => stage-app-ecs-fargate-stack.ts} (100%) rename typescript/aws-codepipeline-ecs-lambda/lib/{pipeline-app-lambda-api-stack.ts => stage-app-lambda-api-stack.ts} (100%) rename typescript/aws-codepipeline-ecs-lambda/lib/{pipeline-app-vpc-stack.ts => stage-app-vpc-stack.ts} (100%) rename typescript/aws-codepipeline-ecs-lambda/lib/{app-stage.ts => stage-app.ts} (80%) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts index d0874b13e7..2ee7d2300d 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts @@ -1,7 +1,7 @@ import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { CodePipeline, CodePipelineSource, ManualApprovalStep, ShellStep, Wave } from 'aws-cdk-lib/pipelines'; -import { pipelineAppStage } from './app-stage'; +import { pipelineAppStage } from './stage-app'; export class pipelineStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-async-lambda-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-async-lambda-stack.ts similarity index 100% rename from typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-async-lambda-stack.ts rename to typescript/aws-codepipeline-ecs-lambda/lib/stage-app-async-lambda-stack.ts diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-ecs-fargate-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-ecs-fargate-stack.ts similarity index 100% rename from typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-ecs-fargate-stack.ts rename to typescript/aws-codepipeline-ecs-lambda/lib/stage-app-ecs-fargate-stack.ts diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-lambda-api-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-lambda-api-stack.ts similarity index 100% rename from typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-lambda-api-stack.ts rename to typescript/aws-codepipeline-ecs-lambda/lib/stage-app-lambda-api-stack.ts diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-vpc-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-vpc-stack.ts similarity index 100% rename from typescript/aws-codepipeline-ecs-lambda/lib/pipeline-app-vpc-stack.ts rename to typescript/aws-codepipeline-ecs-lambda/lib/stage-app-vpc-stack.ts diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/app-stage.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app.ts similarity index 80% rename from typescript/aws-codepipeline-ecs-lambda/lib/app-stage.ts rename to typescript/aws-codepipeline-ecs-lambda/lib/stage-app.ts index 0780bc7f4d..e4a75874eb 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/app-stage.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app.ts @@ -1,8 +1,8 @@ import * as cdk from 'aws-cdk-lib'; import { Construct } from "constructs"; -import { lambdaApiStack } from './pipeline-app-lambda-api-stack'; -import { ecsFargateStack } from './pipeline-app-ecs-fargate-stack'; -import { asyncLambdaStack } from './pipeline-app-async-lambda-stack'; +import { lambdaApiStack } from './stage-app-lambda-api-stack'; +import { ecsFargateStack } from './stage-app-ecs-fargate-stack'; +import { asyncLambdaStack } from './stage-app-async-lambda-stack'; export class pipelineAppStage extends cdk.Stage { From 191e7cb3d057f46eb27630a6df4abdac6dff9761 Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Thu, 20 Jun 2024 18:45:42 +1200 Subject: [PATCH 06/35] added vpc stack and cross stack reference the vpc in ecs stack --- .../lib/stage-app-ecs-fargate-stack.ts | 19 +++++----- .../lib/stage-app-vpc-stack.ts | 37 +++++++++++++++++++ .../lib/stage-app.ts | 19 ++++++++-- 3 files changed, 61 insertions(+), 14 deletions(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-ecs-fargate-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-ecs-fargate-stack.ts index 73c89271b0..0c660393df 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-ecs-fargate-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-ecs-fargate-stack.ts @@ -4,23 +4,22 @@ import { ApplicationLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patte import { IVpc, SubnetType, Vpc } from 'aws-cdk-lib/aws-ec2'; import { Cluster, ContainerImage } from 'aws-cdk-lib/aws-ecs'; -export class ecsFargateStack extends cdk.Stack { - public readonly vpc: IVpc; - constructor(scope: Construct, id: string, props?: cdk.StackProps) { - super(scope, id, props); +interface vpcStackProps extends cdk.StackProps { + readonly vpc: Vpc; +} - this.vpc = new Vpc (this, "ecsVpc", { - natGateways: 1, - }); +export class ecsFargateStack extends cdk.Stack { + constructor(scope: Construct, id: string, props: vpcStackProps) { + super(scope, id, props); const cluster = new Cluster(this, 'ecsCluster', { - vpc: this.vpc, + vpc: props.vpc, containerInsights: true, }); // 👇 create a new ecs pattern with an alb 👇 const loadBalancedFargateService = new ApplicationLoadBalancedFargateService (this, 'ecsPattern', { - cluster: cluster, + cluster, cpu: 256, memoryLimitMiB: 512, desiredCount: 1, @@ -50,4 +49,4 @@ export class ecsFargateStack extends cdk.Stack { path: "/", }); } -} \ No newline at end of file +} diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-vpc-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-vpc-stack.ts index e69de29bb2..4c42302500 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-vpc-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-vpc-stack.ts @@ -0,0 +1,37 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import { IpAddresses, SubnetType, Vpc } from 'aws-cdk-lib/aws-ec2'; + +export class vpcStack extends cdk.Stack { + + public readonly vpc: Vpc; + + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + // 👇 assign a vpc to the class property + this.vpc = new Vpc(this, 'vpc', { + maxAzs: 3, + natGateways: 1, + enableDnsHostnames: true, + enableDnsSupport: true, + ipAddresses: IpAddresses.cidr('10.0.0.0/16'), + subnetConfiguration: [ + { + cidrMask: 24, + name: 'public', + subnetType: SubnetType.PUBLIC, + }, + { + cidrMask: 24, + name: 'private', + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + }, + { + cidrMask: 28, + name: 'ioslated', + subnetType: SubnetType.PRIVATE_ISOLATED, + }] + }) + } +} diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app.ts index e4a75874eb..80461eeb3e 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app.ts @@ -1,5 +1,6 @@ import * as cdk from 'aws-cdk-lib'; import { Construct } from "constructs"; +import { vpcStack } from './stage-app-vpc-stack'; import { lambdaApiStack } from './stage-app-lambda-api-stack'; import { ecsFargateStack } from './stage-app-ecs-fargate-stack'; import { asyncLambdaStack } from './stage-app-async-lambda-stack'; @@ -9,14 +10,24 @@ export class pipelineAppStage extends cdk.Stage { constructor(scope: Construct, id: string, props?: cdk.StageProps) { super(scope, id, props); + // 👇 vpc stack 👇 + const vpc_stack = new vpcStack(this, 'VpcStack'); + cdk.Tags.of(vpc_stack).add('managedBy', 'cdk'); + cdk.Tags.of(vpc_stack).add('environment', 'dev'); + + // 👇 lambda api stack 👇 const lambda_api_stack = new lambdaApiStack(this, 'LambdaApisStack'); cdk.Tags.of(lambda_api_stack).add('managedBy', 'cdk'); cdk.Tags.of(lambda_api_stack).add('environment', 'dev'); - const ecs_stack = new ecsFargateStack(this, 'EcsFargateStack'); - cdk.Tags.of(ecs_stack).add('managedBy', 'cdk'); - cdk.Tags.of(ecs_stack).add('environment', 'dev'); - + // 👇 ecs fargate stack 👇 + const ecs_fargate_stack = new ecsFargateStack(this, 'EcsFargateStack', { + vpc: vpc_stack.vpc, + }); + cdk.Tags.of(ecs_fargate_stack).add('managedBy', 'cdk'); + cdk.Tags.of(ecs_fargate_stack).add('environment', 'dev'); + + // 👇 async lambda stack 👇 const async_lambda_stack = new asyncLambdaStack(this, 'AsyncLambdasStack'); cdk.Tags.of(async_lambda_stack).add('managedBy', 'cdk'); cdk.Tags.of(async_lambda_stack).add('environment', 'dev'); From 1160de28ce25cc18b01d9eaebd03a8f910672323 Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Thu, 20 Jun 2024 20:30:18 +1200 Subject: [PATCH 07/35] added cdk.context.json to .gitignore --- typescript/aws-codepipeline-ecs-lambda/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/typescript/aws-codepipeline-ecs-lambda/.gitignore b/typescript/aws-codepipeline-ecs-lambda/.gitignore index f60797b6a9..3a671bb3bd 100644 --- a/typescript/aws-codepipeline-ecs-lambda/.gitignore +++ b/typescript/aws-codepipeline-ecs-lambda/.gitignore @@ -6,3 +6,4 @@ node_modules # CDK asset staging directory .cdk.staging cdk.out +cdk.context.json From ef4b1d349e9f881fc5959ba278142df911ffb019 Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Thu, 20 Jun 2024 20:33:14 +1200 Subject: [PATCH 08/35] added synthCodeBuildDefaults to fix build stage permission issue --- .../aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts index 2ee7d2300d..bde75e924e 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts @@ -26,7 +26,14 @@ export class pipelineStack extends cdk.Stack { 'npm run build', 'npx cdk synth' ] - }) + }), + synthCodeBuildDefaults: { + rolePolicy: [ + new PolicyStatement({ + resources: [ '*' ], + actions: [ 'ec2:DescribeAvailabilityZones' ], + }), + ]} }); const devStage = pipeline.addStage(new pipelineAppStage(this, `${devEnv}`, { @@ -34,4 +41,4 @@ export class pipelineStack extends cdk.Stack { })); devStage.addPost(new ManualApprovalStep('approval')); } -} \ No newline at end of file +} From e81e690cd4fcf0470f92cf63609907f992d9cbcd Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Sat, 22 Jun 2024 16:13:02 +1200 Subject: [PATCH 09/35] added architecture diagram --- .../static_images/Architecture_diagram.png | Bin 0 -> 77031 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 typescript/aws-codepipeline-ecs-lambda/static_images/Architecture_diagram.png diff --git a/typescript/aws-codepipeline-ecs-lambda/static_images/Architecture_diagram.png b/typescript/aws-codepipeline-ecs-lambda/static_images/Architecture_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..96a7286ceb47539113a616f23a0f29f4c4347638 GIT binary patch literal 77031 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

>wRt+Z!%4rjmVSW=)ff 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 Date: Sat, 22 Jun 2024 16:15:57 +1200 Subject: [PATCH 10/35] added architecture diagram to README>md --- typescript/aws-codepipeline-ecs-lambda/README.md | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/README.md b/typescript/aws-codepipeline-ecs-lambda/README.md index 9315fe5b9f..eac7da81d6 100644 --- a/typescript/aws-codepipeline-ecs-lambda/README.md +++ b/typescript/aws-codepipeline-ecs-lambda/README.md @@ -1,14 +1,4 @@ -# Welcome to your CDK TypeScript project +# AWS Codepipeline CI/CD Solution for ECS Fargate and Lambda -This is a blank project for CDK development with TypeScript. - -The `cdk.json` file tells the CDK Toolkit how to execute your app. - -## Useful commands - -* `npm run build` compile typescript to js -* `npm run watch` watch for changes and compile -* `npm run test` perform the jest unit tests -* `npx cdk deploy` deploy this stack to your default AWS account/region -* `npx cdk diff` compare deployed stack with current state -* `npx cdk synth` emits the synthesized CloudFormation template +## Architect Design: +image From 528a48004a714fc1436ef2a48b4198ddd8e543a9 Mon Sep 17 00:00:00 2001 From: Kihoon Kwon Date: Thu, 27 Jun 2024 10:52:44 +0900 Subject: [PATCH 11/35] add lambdaApiStack --- .../lib/stage-app-lambda-api-stack.ts | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-lambda-api-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-lambda-api-stack.ts index 42872adda2..e037f788a6 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-lambda-api-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-lambda-api-stack.ts @@ -1,15 +1,33 @@ import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { Function, InlineCode, Runtime } from 'aws-cdk-lib/aws-lambda'; +import { LambdaRestApi } from 'aws-cdk-lib/aws-apigateway' export class lambdaApiStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); - new Function(this, 'lambdaFunction', { + // create a lambda function + const lambdaFunction = new Function(this, 'lambdaFunction', { runtime: Runtime.NODEJS_20_X, handler: 'index.handler', - code: new InlineCode('exports.handler = _ => "Hello, CDK";') + code: new InlineCode(`exports.handler = async (event) => { + const response = { + statusCode: 200, + body: JSON.stringify('Hello from Lambda!'), + }; + return response; +};`) }); + + // create an API Gateway + const api = new LambdaRestApi(this, 'ApiGateway', { + handler: lambdaFunction, + proxy: false, + }); + + // connect the lambda function to API Gateway + api.root.addMethod("GET"); + } } From 8954f3447d3ea960e0dededbac19da0aa41448ea Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Thu, 27 Jun 2024 15:33:23 +1200 Subject: [PATCH 12/35] added import PolicyStatement from cdk lib --- typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts index bde75e924e..7f23bcaa8c 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts @@ -2,6 +2,7 @@ import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { CodePipeline, CodePipelineSource, ManualApprovalStep, ShellStep, Wave } from 'aws-cdk-lib/pipelines'; import { pipelineAppStage } from './stage-app'; +import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; export class pipelineStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { From 044a2798713bb976850cc89881b9e0bf9c28c8f8 Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Thu, 27 Jun 2024 06:55:20 +0000 Subject: [PATCH 13/35] Dockerfile and App code --- .../lib/stage-app-ecs-fargate-stack.ts | 9 +++-- .../src/Dockerfile | 12 +++++++ .../src/flask-app/__init__.py | 1 + .../src/flask-app/app.py | 18 ++++++++++ .../src/flask-app/requirements.txt | 32 ++++++++++++++++++ .../src/flask-app/static/css/style.css | 6 ++++ .../src/flask-app/static/favicon.ico | Bin 0 -> 15406 bytes .../src/flask-app/templates/index.html | 19 +++++++++++ 8 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 typescript/aws-codepipeline-ecs-lambda/src/Dockerfile create mode 100644 typescript/aws-codepipeline-ecs-lambda/src/flask-app/__init__.py create mode 100644 typescript/aws-codepipeline-ecs-lambda/src/flask-app/app.py create mode 100644 typescript/aws-codepipeline-ecs-lambda/src/flask-app/requirements.txt create mode 100644 typescript/aws-codepipeline-ecs-lambda/src/flask-app/static/css/style.css create mode 100644 typescript/aws-codepipeline-ecs-lambda/src/flask-app/static/favicon.ico create mode 100644 typescript/aws-codepipeline-ecs-lambda/src/flask-app/templates/index.html diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-ecs-fargate-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-ecs-fargate-stack.ts index 0c660393df..ee77fe188b 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-ecs-fargate-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-ecs-fargate-stack.ts @@ -2,7 +2,9 @@ import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { ApplicationLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patterns'; import { IVpc, SubnetType, Vpc } from 'aws-cdk-lib/aws-ec2'; -import { Cluster, ContainerImage } from 'aws-cdk-lib/aws-ecs'; +import { Cluster, ContainerImage, EcrImage } from 'aws-cdk-lib/aws-ecs'; +import { DockerImageAsset } from 'aws-cdk-lib/aws-ecr-assets'; +import path = require('path'); interface vpcStackProps extends cdk.StackProps { readonly vpc: Vpc; @@ -16,6 +18,9 @@ export class ecsFargateStack extends cdk.Stack { vpc: props.vpc, containerInsights: true, }); + const asset = new DockerImageAsset(this, 'AppImage', { + directory: path.join(__dirname, '..', 'src') + }); // 👇 create a new ecs pattern with an alb 👇 const loadBalancedFargateService = new ApplicationLoadBalancedFargateService (this, 'ecsPattern', { @@ -26,7 +31,7 @@ export class ecsFargateStack extends cdk.Stack { publicLoadBalancer: true, taskSubnets: { subnetType: SubnetType.PRIVATE_WITH_EGRESS }, taskImageOptions: { - image: ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), + image: EcrImage.fromDockerImageAsset(asset), }, enableExecuteCommand: true, }); diff --git a/typescript/aws-codepipeline-ecs-lambda/src/Dockerfile b/typescript/aws-codepipeline-ecs-lambda/src/Dockerfile new file mode 100644 index 0000000000..0d7f280fb9 --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/src/Dockerfile @@ -0,0 +1,12 @@ +# syntax=docker/dockerfile:1 + +FROM python:3.8-slim-buster + +WORKDIR /flask-app + +COPY flask-app/requirements.txt requirements.txt +RUN pip3 install -r requirements.txt + +COPY flask-app/ . + +CMD [ "python3", "app.py"] \ No newline at end of file diff --git a/typescript/aws-codepipeline-ecs-lambda/src/flask-app/__init__.py b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/__init__.py new file mode 100644 index 0000000000..7e9609681c --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/__init__.py @@ -0,0 +1 @@ +name = 'flask-app' \ No newline at end of file diff --git a/typescript/aws-codepipeline-ecs-lambda/src/flask-app/app.py b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/app.py new file mode 100644 index 0000000000..2df0e24fdb --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/app.py @@ -0,0 +1,18 @@ +from flask import Flask, render_template +from waitress import serve +import datetime +import os + +app = Flask(__name__) + + +@app.route('/') +def index(): + now = datetime.datetime.now() + env = os.getenv('CUSTOM_ENVVAR') if os.getenv('CUSTOM_ENVVAR') else 'Have a nice day!' + bg = os.getenv('BG_COLOR') if os.getenv('BG_COLOR') else '#7b7b7b' + return render_template('index.html', context=now, env=env, bg=bg) + + +if __name__ == "__main__": + serve(app, host='0.0.0.0', port=80) diff --git a/typescript/aws-codepipeline-ecs-lambda/src/flask-app/requirements.txt b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/requirements.txt new file mode 100644 index 0000000000..4dcf47f33c --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/requirements.txt @@ -0,0 +1,32 @@ +bleach==3.3.0 +build==0.3.1.post1 +certifi==2022.12.7 +chardet==4.0.0 +click==7.1.2 +colorama==0.4.4 +docutils==0.17.1 +Flask==1.1.2 +idna==2.10 +importlib-metadata==4.0.1 +itsdangerous==1.1.0 +Jinja2==2.11.3 +keyring==23.0.1 +MarkupSafe==1.1.1 +packaging==20.9 +pep517==0.10.0 +pkginfo==1.7.0 +Pygments==2.9.0 +pyparsing==2.4.7 +readme-renderer==29.0 +requests==2.25.1 +requests-toolbelt==0.9.1 +rfc3986==1.4.0 +six==1.16.0 +toml==0.10.2 +tqdm==4.60.0 +twine==3.4.1 +urllib3==1.26.5 +waitress==2.1.2 +webencodings==0.5.1 +Werkzeug==1.0.1 +zipp==3.4.1 diff --git a/typescript/aws-codepipeline-ecs-lambda/src/flask-app/static/css/style.css b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/static/css/style.css new file mode 100644 index 0000000000..d6d85c1eb0 --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/static/css/style.css @@ -0,0 +1,6 @@ +h1 { + color: #fff; + text-align: center; + padding: 10px; + font-family: Arial, Helvetica, sans-serif; +} diff --git a/typescript/aws-codepipeline-ecs-lambda/src/flask-app/static/favicon.ico b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..07392b43326e3dedc545dec0628ddfe6bcace2d9 GIT binary patch literal 15406 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 literal 0 HcmV?d00001 diff --git a/typescript/aws-codepipeline-ecs-lambda/src/flask-app/templates/index.html b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/templates/index.html new file mode 100644 index 0000000000..cfa7be6922 --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/templates/index.html @@ -0,0 +1,19 @@ + + + + + + + Flask App + + + +

Welcome to Flask App

+

This page was rendered on {{ context.day }}/{{ context.month }}/{{ context.year }} at {{ context.strftime("%H") }}:{{ context.strftime("%M") }}:{{ context.strftime("%S") }} UTC

+

{{ env }}

+ + \ No newline at end of file From dca5a5eab13f6eba4aa49d2ffc1260311f08c75b Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Thu, 27 Jun 2024 07:25:44 +0000 Subject: [PATCH 14/35] lesser requirement --- .../src/flask-app/requirements.txt | 38 +++---------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/src/flask-app/requirements.txt b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/requirements.txt index 4dcf47f33c..63287bc1f5 100644 --- a/typescript/aws-codepipeline-ecs-lambda/src/flask-app/requirements.txt +++ b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/requirements.txt @@ -1,32 +1,6 @@ -bleach==3.3.0 -build==0.3.1.post1 -certifi==2022.12.7 -chardet==4.0.0 -click==7.1.2 -colorama==0.4.4 -docutils==0.17.1 -Flask==1.1.2 -idna==2.10 -importlib-metadata==4.0.1 -itsdangerous==1.1.0 -Jinja2==2.11.3 -keyring==23.0.1 -MarkupSafe==1.1.1 -packaging==20.9 -pep517==0.10.0 -pkginfo==1.7.0 -Pygments==2.9.0 -pyparsing==2.4.7 -readme-renderer==29.0 -requests==2.25.1 -requests-toolbelt==0.9.1 -rfc3986==1.4.0 -six==1.16.0 -toml==0.10.2 -tqdm==4.60.0 -twine==3.4.1 -urllib3==1.26.5 -waitress==2.1.2 -webencodings==0.5.1 -Werkzeug==1.0.1 -zipp==3.4.1 +click==8.0.3 +Flask==2.0.2 +itsdangerous==2.0.1 +Jinja2==3.0.2 +MarkupSafe==2.0.1 +Werkzeug==2.0.2 \ No newline at end of file From c59bb62db4d590a6841e920494f55bbdfddecd54 Mon Sep 17 00:00:00 2001 From: Sonal Date: Thu, 27 Jun 2024 07:40:00 +0000 Subject: [PATCH 15/35] added datastore stack --- .../lib/pipeline-stack.ts | 2 +- .../lib/stage-app-datastore-stack.ts | 40 +++++++++++++++++++ .../lib/stage-app.ts | 7 ++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 typescript/aws-codepipeline-ecs-lambda/lib/stage-app-datastore-stack.ts diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts index bde75e924e..acce37ae61 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts @@ -41,4 +41,4 @@ export class pipelineStack extends cdk.Stack { })); devStage.addPost(new ManualApprovalStep('approval')); } -} +} \ No newline at end of file diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-datastore-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-datastore-stack.ts new file mode 100644 index 0000000000..fb706278ff --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-datastore-stack.ts @@ -0,0 +1,40 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as ec2 from 'aws-cdk-lib/aws-ec2'; +import * as rds from 'aws-cdk-lib/aws-rds'; +import * as kms from 'aws-cdk-lib/aws-kms'; + +interface vpcStackProps extends cdk.StackProps { + readonly vpc: ec2.Vpc; +} + +export class rdsAuroraStack extends cdk.Stack { + constructor(scope: Construct, id: string, props: vpcStackProps) { + super(scope, id, props); + + const kmskey = new kms.Key(this, 'MyKey', { + enableKeyRotation: true, + rotationPeriod: cdk.Duration.days(180), // Default is 365 days + }); + + const cluster = new rds.DatabaseCluster(this, 'Database', { + engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_3_03_0 }), + writer: rds.ClusterInstance.provisioned('writer', { + instanceType: ec2.InstanceType.of(ec2.InstanceClass.R6G, ec2.InstanceSize.XLARGE4), + }), + serverlessV2MinCapacity: 6.5, + serverlessV2MaxCapacity: 64, + readers: [ + // will be put in promotion tier 1 and will scale with the writer + rds.ClusterInstance.serverlessV2('reader1', { scaleWithWriter: true }), + // will be put in promotion tier 2 and will not scale with the writer + rds.ClusterInstance.serverlessV2('reader2'), + ], + credentials: { username: 'clusteradmin' }, + cloudwatchLogsExports: ["error"], + vpc: props.vpc, + storageEncrypted: true, + storageEncryptionKey: kmskey + }); +} +} \ No newline at end of file diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app.ts index 80461eeb3e..d296568dc5 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app.ts @@ -4,6 +4,7 @@ import { vpcStack } from './stage-app-vpc-stack'; import { lambdaApiStack } from './stage-app-lambda-api-stack'; import { ecsFargateStack } from './stage-app-ecs-fargate-stack'; import { asyncLambdaStack } from './stage-app-async-lambda-stack'; +import { rdsAuroraStack } from './stage-app-datastore-stack'; export class pipelineAppStage extends cdk.Stage { @@ -31,5 +32,11 @@ export class pipelineAppStage extends cdk.Stage { const async_lambda_stack = new asyncLambdaStack(this, 'AsyncLambdasStack'); cdk.Tags.of(async_lambda_stack).add('managedBy', 'cdk'); cdk.Tags.of(async_lambda_stack).add('environment', 'dev'); + + const rds_aurora_stack = new rdsAuroraStack(this, 'rdsAuroraStack', { + vpc: vpc_stack.vpc, + }); + cdk.Tags.of(rds_aurora_stack).add('managedBy', 'cdk'); + cdk.Tags.of(rds_aurora_stack).add('environment', 'dev'); } } \ No newline at end of file From 2f5739f3590490cee7a98d67a93b56d442ebe6b0 Mon Sep 17 00:00:00 2001 From: sonal-aws Date: Thu, 27 Jun 2024 13:18:19 +0530 Subject: [PATCH 16/35] Update pipeline-stack.ts --- typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts index acce37ae61..7f23bcaa8c 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts @@ -2,6 +2,7 @@ import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { CodePipeline, CodePipelineSource, ManualApprovalStep, ShellStep, Wave } from 'aws-cdk-lib/pipelines'; import { pipelineAppStage } from './stage-app'; +import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; export class pipelineStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { @@ -41,4 +42,4 @@ export class pipelineStack extends cdk.Stack { })); devStage.addPost(new ManualApprovalStep('approval')); } -} \ No newline at end of file +} From e11b6bccc655850134c17c691c3577e2c0dbaeca Mon Sep 17 00:00:00 2001 From: sonal-aws Date: Thu, 27 Jun 2024 13:20:09 +0530 Subject: [PATCH 17/35] Update pipeline-stack.ts --- typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts index 7f23bcaa8c..bde75e924e 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts @@ -2,7 +2,6 @@ import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { CodePipeline, CodePipelineSource, ManualApprovalStep, ShellStep, Wave } from 'aws-cdk-lib/pipelines'; import { pipelineAppStage } from './stage-app'; -import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; export class pipelineStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { From 30c750c8a7c634372b62c87ad0d05d85a5f17c1f Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Thu, 27 Jun 2024 22:18:54 +1200 Subject: [PATCH 18/35] updated import resource format --- .../lib/stage-app-datastore-stack.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-datastore-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-datastore-stack.ts index fb706278ff..91fdc97875 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-datastore-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-datastore-stack.ts @@ -1,34 +1,34 @@ import * as cdk from 'aws-cdk-lib'; +import { InstanceClass, InstanceSize, InstanceType, Vpc } from 'aws-cdk-lib/aws-ec2'; +import { Key } from 'aws-cdk-lib/aws-kms'; +import { AuroraMysqlEngineVersion, ClusterInstance, DatabaseCluster, DatabaseClusterEngine } from 'aws-cdk-lib/aws-rds'; import { Construct } from 'constructs'; -import * as ec2 from 'aws-cdk-lib/aws-ec2'; -import * as rds from 'aws-cdk-lib/aws-rds'; -import * as kms from 'aws-cdk-lib/aws-kms'; interface vpcStackProps extends cdk.StackProps { - readonly vpc: ec2.Vpc; + readonly vpc: Vpc; } export class rdsAuroraStack extends cdk.Stack { constructor(scope: Construct, id: string, props: vpcStackProps) { super(scope, id, props); - const kmskey = new kms.Key(this, 'MyKey', { + const kmskey = new Key(this, 'MyKey', { enableKeyRotation: true, rotationPeriod: cdk.Duration.days(180), // Default is 365 days }); - const cluster = new rds.DatabaseCluster(this, 'Database', { - engine: rds.DatabaseClusterEngine.auroraMysql({ version: rds.AuroraMysqlEngineVersion.VER_3_03_0 }), - writer: rds.ClusterInstance.provisioned('writer', { - instanceType: ec2.InstanceType.of(ec2.InstanceClass.R6G, ec2.InstanceSize.XLARGE4), + const cluster = new DatabaseCluster(this, 'Database', { + engine: DatabaseClusterEngine.auroraMysql({ version: AuroraMysqlEngineVersion.VER_3_03_0 }), + writer: ClusterInstance.provisioned('writer', { + instanceType: InstanceType.of(InstanceClass.R6G, InstanceSize.XLARGE4), }), serverlessV2MinCapacity: 6.5, serverlessV2MaxCapacity: 64, readers: [ // will be put in promotion tier 1 and will scale with the writer - rds.ClusterInstance.serverlessV2('reader1', { scaleWithWriter: true }), + ClusterInstance.serverlessV2('reader1', { scaleWithWriter: true }), // will be put in promotion tier 2 and will not scale with the writer - rds.ClusterInstance.serverlessV2('reader2'), + ClusterInstance.serverlessV2('reader2'), ], credentials: { username: 'clusteradmin' }, cloudwatchLogsExports: ["error"], From ab43c72433094e0cbf83a039be5c63b09b13ddcf Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Thu, 27 Jun 2024 22:39:28 +1200 Subject: [PATCH 19/35] added waves to eu-west-1 & us-west-2 --- .../lib/pipeline-stack.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts index 7f23bcaa8c..f80c801da5 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts @@ -12,6 +12,9 @@ export class pipelineStack extends cdk.Stack { const githubRepo = process.env.GITHUB_REPO || "aws-codepipeline-ecs-lambda"; const githubBranch = process.env.GITHUB_BRANCH || "main"; const devEnv = process.env.DEV_ENV || "dev"; + const devAccountId = process.env.DEV_ACCOUNT_ID || "117645918752"; // replace with your dev account id + const primaryRegion = process.env.PRIMARY_REGION || "us-west-2"; + const secondaryRegion = process.env.SECONDARY_REGION || "eu-west-1"; const pipeline = new CodePipeline(this, 'pipeline', { selfMutation: true, @@ -41,5 +44,16 @@ export class pipelineStack extends cdk.Stack { env: { account: props?.env?.account, region: props?.env?.region} })); devStage.addPost(new ManualApprovalStep('approval')); + + // add waves: + const devWave = pipeline.addWave(`${devEnv}Wave`); + + devWave.addStage(new pipelineAppStage(this, `${devEnv}-${primaryRegion}`, { + env: { account: `${devAccountId}`, region: `${primaryRegion}`} + })); + + devWave.addStage(new pipelineAppStage(this, `${devEnv}-${secondaryRegion}`, { + env: { account: `${devAccountId}`, region: `${secondaryRegion}`} + })); } } From 1de718876963125e194ac1bc656ea9d00404f3fa Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Thu, 27 Jun 2024 22:54:49 +1200 Subject: [PATCH 20/35] fix container missing waitress module error --- typescript/aws-codepipeline-ecs-lambda/src/flask-app/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/src/flask-app/app.py b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/app.py index 2df0e24fdb..45e894bad1 100644 --- a/typescript/aws-codepipeline-ecs-lambda/src/flask-app/app.py +++ b/typescript/aws-codepipeline-ecs-lambda/src/flask-app/app.py @@ -1,5 +1,5 @@ from flask import Flask, render_template -from waitress import serve + import datetime import os @@ -15,4 +15,4 @@ def index(): if __name__ == "__main__": - serve(app, host='0.0.0.0', port=80) + app.run(host='0.0.0.0', port=80) From f3a9ea29d5cc49a1a77783c69b2bac57f50a070c Mon Sep 17 00:00:00 2001 From: ruchirshetye-aws Date: Thu, 27 Jun 2024 20:13:56 +0530 Subject: [PATCH 21/35] Update README.md --- .../aws-codepipeline-ecs-lambda/README.md | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/typescript/aws-codepipeline-ecs-lambda/README.md b/typescript/aws-codepipeline-ecs-lambda/README.md index eac7da81d6..c2a807e755 100644 --- a/typescript/aws-codepipeline-ecs-lambda/README.md +++ b/typescript/aws-codepipeline-ecs-lambda/README.md @@ -2,3 +2,58 @@ ## Architect Design: image + +## Overview + +This CDK package provides a production-grade template for setting up AWS resources to enable smooth migration from monolithic EC2-based architectures to cloud-native solutions on AWS. It's designed for startups looking to scale their infrastructure efficiently. + +Key features: +- CICD pipeline using AWS CodePipeline +- Lambda functions (async triggered and REST endpoints behind API Gateway) +- ECS Fargate based service with automatic deployment +- Integration with private GitHub repositories +- Reference to CDK created VPCs +- Creates RDS Instances with credentials managed by AWS Secrets Manager +- Event-driven architecture using SQS, SNS, and EventBridge + +## Getting Started + +### Prerequisites + +- Create a Private Github Repository with source code inside 'aws-codepipeline-ecs-lambda' directory. +- Create a connection to GitHub or GitHub Enterprise Cloud, see [Create a connection to GitHub](https://docs.aws.amazon.com/dtconsole/latest/userguide/connections-create-github.html). +- Modify the `connectionArn` in `pipeline-stack.ts` file. +- Modify `githubOrg`, `githubRepo`, `githubBranch` with your private repository details. + +## Project Structure + +The project is organized into six main stacks: + +1. `VpcStack`: Network infrastructure resources +2. `DataStoresStack`: Database resources +3. `PubSubStack`: Event-based infrastructure (SQS, SNS, EventBridge) +4. `AsyncLambdasStack`: Asynchronously triggered Lambda functions +5. `LambdaApisStack`: Lambda functions as REST endpoints behind API Gateway +6. `EcsFargateStack`: ECS Fargate Service, Cluster, Tasks, and Containers + +You can easily customize the infrastructure by modifying or removing specific stacks in the `lib/pipeline-stage.ts` file. + +## Solution overview + +The CDK application is structured as follows: + +`lib/pipeline-stack.ts` contains the definition of the CI/CD pipeline. The main component here is the CodePipeline construct that creates the pipeline for us + +`lib/stage-app.ts` contains definitions of all the six stacks which the pipeline will deploy. + +`lib/stage-app-vpc-stack.ts` creates new vpc resource along with subnets, nat gateways and remaining networking infrastructure. + +`lib/stage-app-datastore-stack.ts` creates a Aurora Serverless V2 Cluster along with KMS key to encrypt the database. + +`lib/stage-app-ecs-fargate-stack.ts` builds the `Dockerfile` and creates ecs fargate service along with loadbalancer. + +`lib/stage-app-lambda-api-stack.ts` creates lambda functions as REST endpoints behind API Gateway resource. + +`lib/PubSubStack.ts` creates sns, eventbridge and lambda function. + +--- From cf2746f6b0c194063a0b7375ef3f66858ec3f89d Mon Sep 17 00:00:00 2001 From: Junseong Jang Date: Fri, 28 Jun 2024 03:28:45 +0900 Subject: [PATCH 22/35] Added sample stack for event sources mapped asynchronous lambda functions It create a SNS topic, a SQS queue, and a EventBridge rule that trigger asynchronous lambda functions. The source of event is the Amazon ECS events that are published into the default EventBridge event bus. We use this event to push messages to the SNS topic and to the SQS queue. --- .../lib/lambda-functions/events_handler.ts | 7 ++ .../lambda-functions/queue_message_handler.ts | 18 +++++ .../lambda-functions/topic_message_handler.ts | 19 ++++++ .../lib/stage-app-async-lambda-stack.ts | 68 +++++++++++++++++-- .../aws-codepipeline-ecs-lambda/package.json | 5 +- 5 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/events_handler.ts create mode 100644 typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/queue_message_handler.ts create mode 100644 typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/topic_message_handler.ts diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/events_handler.ts b/typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/events_handler.ts new file mode 100644 index 0000000000..cf779c1068 --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/events_handler.ts @@ -0,0 +1,7 @@ +import { EventBridgeEvent, Context } from 'aws-lambda'; + +export const handler = async (event: EventBridgeEvent) => { + console.log('LogEvent'); + console.log('Received event:', JSON.stringify(event, null, 2)); + return 'Finished'; +}; diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/queue_message_handler.ts b/typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/queue_message_handler.ts new file mode 100644 index 0000000000..dc0f05aabf --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/queue_message_handler.ts @@ -0,0 +1,18 @@ +import { SQSEvent, SQSBatchResponse, SQSBatchItemFailure } from 'aws-lambda'; + +export const handler = (event: SQSEvent): SQSBatchResponse | void => { + if (event) { + const batchItemFailures: SQSBatchItemFailure[] = []; + event?.Records.forEach(record => { + try { + // process record + } catch (err) { + batchItemFailures.push({ + itemIdentifier: record.messageId + }); + } + }); + + return { batchItemFailures }; + } +}; diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/topic_message_handler.ts b/typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/topic_message_handler.ts new file mode 100644 index 0000000000..74951b72bc --- /dev/null +++ b/typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/topic_message_handler.ts @@ -0,0 +1,19 @@ +import { SNSEvent, SNSEventRecord } from 'aws-lambda'; + +export const handler = async (event: SNSEvent) => { + for (const record of event.Records) { + await processMessageAsync(record); + } + console.info("done"); +}; + +async function processMessageAsync(record: SNSEventRecord) { + try { + const message = JSON.stringify(record.Sns.Message); + console.log(`Processed message ${message}`); + await Promise.resolve(1); //Placeholder for actual async work + } catch (err) { + console.error("An error occurred"); + throw err; + } +} diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-async-lambda-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-async-lambda-stack.ts index b44748e9bf..fd24a24c52 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-async-lambda-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-async-lambda-stack.ts @@ -1,15 +1,75 @@ import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; -import { Function, InlineCode, Runtime } from 'aws-cdk-lib/aws-lambda'; +import * as events from 'aws-cdk-lib/aws-events'; +import * as eventsTargets from 'aws-cdk-lib/aws-events-targets'; +import * as sns from 'aws-cdk-lib/aws-sns'; +import * as sqs from 'aws-cdk-lib/aws-sqs'; +import { SnsEventSource, SqsEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; +import * as path from 'path'; +import { Function, Code, Runtime } from 'aws-cdk-lib/aws-lambda'; export class asyncLambdaStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); - new Function(this, 'lambdaFunction', { + // EventBridge event as an event source for a SNS topic and a SQS queue + const rule = new events.Rule(this, 'Rule', { + eventPattern: { + source: ['aws.ecs'] + } + }); + + // Shared code asset + const code = Code.fromAsset(path.join(__dirname, 'lambda-functions')); + + // Lambda Function that will be invoked asynchronously when there is any event that matches the rule + const eventsFunction = new Function(this, 'EventFunction', { + runtime: Runtime.NODEJS_20_X, + code, + handler: 'events_handler.handler' + }); + const eventsDLQ = new sqs.Queue(this, 'LambdaDLQ'); + rule.addTarget(new eventsTargets.LambdaFunction(eventsFunction, { + deadLetterQueue: eventsDLQ, + maxEventAge: cdk.Duration.minutes(2), + retryAttempts: 2 + })); + + // Lambda Function that will be invoked asynchronously by a SNS topic + const topic = new sns.Topic(this, 'Topic'); + rule.addTarget(new eventsTargets.SnsTopic(topic, { + deadLetterQueue: eventsDLQ, + maxEventAge: cdk.Duration.minutes(2), + retryAttempts: 2 + })); + + const topicFunction = new Function(this, 'TopicLambdaFunction', { + runtime: Runtime.NODEJS_20_X, + code, + handler: 'topic_message_handler.handler' + }); + const topicDLQ = new sqs.Queue(this, 'TopicDLQ'); + topicFunction.addEventSource(new SnsEventSource(topic, { + deadLetterQueue: topicDLQ + })); + + // Lambda Function that will be invoked asynchronously with the event source of a SQS queue + const queue = new sqs.Queue(this, 'JobQueue'); + rule.addTarget(new eventsTargets.SqsQueue(queue, { + deadLetterQueue: eventsDLQ, + maxEventAge: cdk.Duration.minutes(2), + retryAttempts: 2 + })); + + const queueFunction = new Function(this, 'QueueLambdaFunction', { runtime: Runtime.NODEJS_20_X, - handler: 'index.handler', - code: new InlineCode('exports.handler = _ => "Hello, CDK";') + code, + handler: 'queue_message_handler.handler' }); + queueFunction.addEventSource(new SqsEventSource(queue, { + batchSize: 5, + maxBatchingWindow: cdk.Duration.seconds(5), + reportBatchItemFailures: true + })); } } \ No newline at end of file diff --git a/typescript/aws-codepipeline-ecs-lambda/package.json b/typescript/aws-codepipeline-ecs-lambda/package.json index 865d81368a..c28c39d649 100644 --- a/typescript/aws-codepipeline-ecs-lambda/package.json +++ b/typescript/aws-codepipeline-ecs-lambda/package.json @@ -11,11 +11,12 @@ "cdk": "cdk" }, "devDependencies": { + "@types/aws-lambda": "^8.10.140", "@types/jest": "^29.5.12", "@types/node": "20.14.2", + "aws-cdk": "2.146.0", "jest": "^29.7.0", "ts-jest": "^29.1.4", - "aws-cdk": "2.146.0", "ts-node": "^10.9.2", "typescript": "~5.4.5" }, @@ -24,4 +25,4 @@ "constructs": "^10.0.0", "source-map-support": "^0.5.21" } -} \ No newline at end of file +} From 86607f704620ceb6a4cebbc5afbd310dbdca9669 Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Fri, 28 Jun 2024 14:55:03 +1200 Subject: [PATCH 23/35] added @types/aws-lambda: ^8.10.140 --- typescript/aws-codepipeline-ecs-lambda/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/typescript/aws-codepipeline-ecs-lambda/package.json b/typescript/aws-codepipeline-ecs-lambda/package.json index c28c39d649..cdf9c627a5 100644 --- a/typescript/aws-codepipeline-ecs-lambda/package.json +++ b/typescript/aws-codepipeline-ecs-lambda/package.json @@ -22,6 +22,7 @@ }, "dependencies": { "aws-cdk-lib": "2.146.0", + "@types/aws-lambda": "^8.10.140", "constructs": "^10.0.0", "source-map-support": "^0.5.21" } From 92d14c15f68fb2887f74de3b7203ab85cc5c0b71 Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Sat, 29 Jun 2024 10:54:06 +1200 Subject: [PATCH 24/35] updated a new version of diagram --- .../static_images/Architecture_diagram.png | Bin 77031 -> 109126 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/static_images/Architecture_diagram.png b/typescript/aws-codepipeline-ecs-lambda/static_images/Architecture_diagram.png index 96a7286ceb47539113a616f23a0f29f4c4347638..01480dedb344a538ae9fbed1af0d72f7a2ac0340 100644 GIT binary patch literal 109126 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
c|ZYpM>qz!8eSg%O?bU|7Be>-i(L zoODPpAy~CN%{8Kg(Xea&L97HT|7)``4?z{+QX^I+akK%g@(Oy#-(vC4gt0#jzuJJ< zLib&Q9*F5CthV(o#}=(T`;KCj)&jH?Utvr(?_M0*;Rm(<(EPd!jSBK+>$)@q7p zd#S?wsi!7O7wrXKIv#VX=6UvVuZ94{;hDZTi$-N3fzJ}%x7cGIgMU^#HEBbmb&Lj@Kaptyer`MzbALu+6Vcwg~KzUD9 zY~nc3Ee5ljzhs|ZecqK??MRThKERgE2Wdy>s1GLHI$fY^79ap!l2ac9=GoPV!|s_P zZ_lhz>MZeMSBlfS!C8(jUrQIRrLDWRTq7-!tGihtWA0O+H@_lB4vkMjC`Yw4q*zbw zB{`r&Q_hqZY#C`&Lo3b6p!38in%|H%~v^k-K-dOOrZnVp#&aPj@w69skAM z=u7jAS{KWJMLR5J3@o73T4|?*1X#?v>p*aBDc5M**7FeRW=^h*;`-qm;?@uJgGetY zUdpyDp}rNnowSMSg4lRaJ}|8=t25fvZ(B`$5aO_WX!SM3&u{ZB)3v;X9QQS5mGSJ- zy$qDe7l#+7KJ*Nvrq^caRQX8kCuP=oll@F-J%c8e(pgB)2Je$?qkHU5aXnF=N3^`* z>a@anD@ryK3Y~IZyYK+x{?_!p$JrFXcpx3;9#C;;6UvZ^m_Fq)!Pj=jNEM$H_-;M8 zhI|u$DyG2Z3Fh{5j$L5=vn%XJJBpvXx+AEtd^aa0;Uis#0saA(9zLhYDq5k8>;09U zCDg@tbikOJs74AW4M`R6NA7QJlgqeydZxc8dU5W64-EeK37e9S^qQ?{MrAa2Ommc5 za)OQP>!KIN50EOhWKFWX4JD2S3DE2G$!YlBgVx-qSA%h)8fhc}P6l?(v! zTUoICkci~FY)k=zAK#Kz8hh*9=7Y^6k(j*;a;m6A7!ETrV*9| zE_MxIH;#`eEfOOv z{7+Hpok5r;TtZ7TIYAan;pPyA7n@eAWW*Qe+A-(9=R|BdxbC{Ev#)7b3dC#LQQ2{V z{>JKEKCwD-&9IDD7Euwh^zM(vNttx+HdE1*X9B0jKJalUp;%8kgF#2(bk#lO^h8+R=4mzbOe-co^6Bwb zi-qZ4q1O(DX}TW$3Bw}}Dj2z1mJn&wKq*kgRanJ=H^r$t48GIrVtVPqOixPEZCfa~Gq#3g!^tS48%q(J8iC3f;GXEjJV`Qx4704x)3dLv>GYn< zwcZ(jxPi<0wCddasCS;rb>W8hQ`8*H>#}{%yuK?^lY=y?q%|l4AYtwa<{z_eTk-uC z70LJk>KBI&4v>4SV$iyAhCY6Ln?KmcR&q#J93}Bc-hZ^r4UpXqA&1?*;+?x1;$~W_ zXtfOL&+fFGpJ^@4HGN;PFED$x`O_Vi*}NYB&7?&DwK46nT0`eZ>Eda=*LGR2==OxL zg4u`kiKo$Z%@oE5Bu)Ttz-fk#|#Mv}nwZ|2=R(*+Qn>SnrX=S5-H~y=KDY zukDY3tfBQ={Pu*Ec7-TLb}lYVetD&;E~v*c4w=S~`-EdL!gdy^GxUmQ+zsG5Rq?u@$ivJVq&Yl<=Ox`}zV$#)FD? zT)V4SX5W0zJsVmT$-J4P8OPJ99;8K1{nC89G)Lun_F}^`&vLKq~~ABEeboQ?hZF_3EOexbdkX|`=OWj1si07*?{ep zM|(qQ1Dk8~J&oZe6pW&A^FhBZ1+|QLF{2a(O1%rVIX=jj+`Gyf(v3=a5!9`lea~z? z2uuWieSv3h;ny|as7QKTGEMEt%(i(j=;$L&)d#`rgKNU;SFeS=CB01M16cTl44Zt; zKqaj_b!Et9u9BB3pXzCVZD5^rtHm&fNg`VfXy{K(N`Y^{Udr$4k;S<&`>xB^*gZI% zVvqGMjJTC%eU{#GFis{Qa|;Yuiuts{2W~ls+!`6~ssh%Oh4|FlHSC7jm-4S(vzi{t zsvC0GbA~ti`a}l<$JT2|&9|88Ps*$3IOGaEu~L}O_|og2c$syWS6_2zzNv!|E@x`I z)^jT1;I3idIm|J!;VsF`i+5fJHgy!BJ&N=>5MIW;w`;i{ZL}!pPrOVi>16|4brycG z>&d60(fR^|8w^qE@0mZH0$#P@o9)8U^^h+#H&r`!l9e^o6uxDh`6VDWl7l`l+qUMK&Pv*LJ~-lp z^sKJ7)Kt+}t!=AReT-@_3@D~I=2h|+W(Lz%MR<Z0J*}98 z$R914wG*FFX>s0NNk_4{a*Nmo_8F-9&5_U_n7Ccx8JY4Fp=&Zk(T%B=>@p! zE$jVRPp<1nmI5#3t&2OA4%|K&D>7L$RxOGuwVw@89MB}+eKMy0`1Y_P003W2$I0$- zkDvbhoT~-~JT)yNt#u{J$r@-M*noD8k@tMgyplq-9Pg#1j?lghh3XnCsU&u$hh>@Fj_b4a_|bYzN0uHlvW%wYQkZf;%PQH(w;P9xZ!%rY#N!ZgYk`Q zainM`qziEBKi}5tVUuUt;&z_-^2i-MxO82V-dkGE)=DuVK22#_EJnf%NtsUxr>yA1J1M=~g;_ zr9@8K07>gqo8Q|ek~Z1t7Crw^&$JR&1Hi@N>TZ6PJrC%+W78sYuBp+K$3{iauOAm_ zes6#5!p8eXEsQbAbJnu1+tbh>u z)#Fd)dii&$^`AY4x;oCVMQ}idS8novSYPs9s+pA1WYxP|7g_U?4UPdYU{s}2v$<9j zLK-O<5|&+Iv+Xj+VB~mr>MdHIe&BQE2d<#ow5Rk~)HGDyD_x$IFA+S%#U2K zu8a!gEFRa`HIQ^lpihOD7M50fO-SQkF)cCLMmj2(zM~UEPPO-`;>K$E0{dHv3x{9lprwbWCiNY7 zxE2=6i^z0UkBnWJejOmxM*gN8m?Y%KXEGaaG%K)UtTr8azs`1w&d`c+HfX=9w~AYuA{@PM7LEw!i;m8z1Cq z^ST!MCFn)a+ZR#W0fE)smQ-K84RocK<-yx* zt|978?G73FqKnJ5y3R{lH&Y$IEfq+nao4v^W^yg19skFMw+52Zn#SS_Q)athaWC|8 z5^8UlzcAK!X%O2NKE4fs3U*elf6XS=b?NMg^3L#FHn{Iy;QfEH>5B6Zz$=HrG;Slm zQf`-u_u_9qH{}ezQ}r^~a_~gw7ul3JvQUo~PE-*yZ>(<@48+WiLEhzKcLXxR4r|?= z^yJtrZ2EZJF8kQS<9lNAyl1?{s@W7Iyf}m>L+_ZC>#`=zMacBc&+2wbAPo#YuZ z6D2-sZ}A0HMdt~wJnA~2voFgD|$zAH7IhNs^tsr`VqE|NAq&!)u^ z5zTjih1q4IC?+p!26Ih9Gpf&!NrcPq3vlAMxllM8)%R-FkF{oOzDZWH#{AS#2=(d~3kF*2LVaV-j zNg(~nwm3(V(THx*W8XQ;GQd4_`HnF1#3s^HOhE930t~#tzA2nZ$A7;LqRW3EnS8x; zs+9?gSVbSB{#a<(_{a2%>pAS=$oEio@N>658ihhM zvS+C8s8))4m;XZIhuO58>y++Ik21Ofa%*0jj@8)bM>55eJ5fGpN@5;1^d1%veW{mf zbVPKmOryl{BME`xb1&@j=D)ZKxKX~L$xOL8SdS|1Zr5Y-HxA~vfkE@f82RdWskCo9 z8xp-9-t1#Fv(fJ&nZV4(L;10FcgRRQoZ?7sMUlnN-9!X4*PnS8Ak>i&;`Hu3UG6Yf zeyr6#O5W_q5;AV;T|3JZbmZ>dwP)t$H%6nPCQyEGKaF{VHJp?WVHyGzA&ABtmXM>= zs=lY&jeO)$H?+uM&AOAS6Z;G$dSUSynwe&JNF3PH#h$!s8=KUc(-YlW9-VO>j++ zxKTBDM|+Nlh}z5g82RX^XBW672PT)YaZPL~-u+_o?88qJMR!Pw#Xszn>ZiZkOFqGR zgGN5Ct~g}GwkW18M1qb>fWLB#p<#Uq_Lrx92Q6CH8qW1BiZ(xd~ z<|;Ung&YWMYUW(GZSRrF?$oT3W8)#R^Xi>uCqi!1jfd#e6(PO)cU|1jDu)t^?rTY{n>Dyer2zIWQr|ZRL3f(VpweQjT!k~|CxGyW7xdP!((Fs0x?aN za=r-4{V-`jvTs5wUPNhAO8Hb)(pGXvM_934%w5}5CK>(80xhkf=ybJxe0PqYc%i>V zgKxkC57A3q}vjq%1Q_;;p z?IKh1zpdGHnM~d^pgX4VL^Lz(w|;E`^@9pBWrCA_9@aXPLVJ%#g%~~y$*Qe2k1{w2# z2D$H3?AdIxNB-k#o_D=%USOvC@0ND?n_nfHqz)KR^#7t!QdL?ZKX=NYD_0vM=PbWj zA=8FOMqWo=lwyOt^PY6i(0Kou=n<{~z7Qq7K>7Knm9%19ebW+?+vg2?6=zb?S>Hdg z_q*$Pc0^=`K6%fo{;~K`$_caXr1;bE9IC#JK20d+o3VpUtvx$J*R(j3Qoi9YYk73_YCVJApa{!heehIemw)1RVfol1m=6D$hV`BP zhhDt46f3g&$dMnr2yfs&lX)b$Yezoy&27vkxg9fuugfdt58SqbJ&W0QH6wTpwbey> zSDnU+Fwc?VR*KR-Q$j94$x(XX519PoC60Hx2((SJaI9947dv{7QGjWI&#yu>TH#p z=eqnvM8ExD$eb9+z#@xs_@wD~ptwHlaPZp~mf8=?ye{xdw2a?j0ZTvSml-u|=L-}W^hC8I4bV^Isft7Eor2m_Cbx)%%X?Vu3~qzl4|n-99dlLjg7TtH<>*HKDY-MX|soZe@KE5iJoE=sy(69zWFE_q9fn0&o zaeSMeFRZxx#iNjgw7lTR*NCT0R|VkAspA=U%Y!;rDV#LmHovqd+;z16QEnRPb6t?> z6(aPwIoM}$4Zl8S9C^BK0%8{21 z@_ui6wde|Z$gCOKg?dmv!p$t>wm4|?#pKjHrC^3{ihXWJn^n>tm~>{}?&jIg&pG*o z<`=czETVvZw=nuV*M*v{;5N6Wk8{m>*;WRPH3rhXeQBlQmv>tAIVB&(NSzJu>zvtb ztF}W6V-*x9^l0Z>xp*r#R>}+kEj3?6-m28uQ)J!#Rn5k-lcWCOfkE33RTWhnDS7uAMh+4n3; zdoqitQVjj~2T!T(vJ`>InI7{&*uQ5gayGjDI5b&J%5Q3*Bi-}D1NocDs?D>-WcTz^ zC7;Pr4}`%3(zX7!+>Ym_<&@)_nWI))RWX=GlumH@Luvd&4`dgJ9XGsOHZJ; zkngNkc-X%XYi%}QG;>(4ASHlyZR}uhb~Aq{6K6H1y!c#jou*GyhRp`)eRru?296Eh zWbu$5f(_8HCS0Ov|1g2d$zkemqH4ZSa52Tpr4vqbJMo<`;PK>BE=M$@!$O;jk9}5E z_wbiPi=LA!YH}dI_ZjByY^34c;u98`c<;t8|4Zry;~QhGs|v{bU&(qMcWLEQSHtw> zOEunPeh}@w;Qo3qtxL*h+VW<$&)m;v<+gmbs0m}rUhM*fV}q#9=BDRsjJNxYpGhqb zp=TSnkag;&{e1W6q?zwe#v z#|X&DP;PoNB23cQ@X4&cUtnhi6NnV3I zGKomZ7?P4);f)oUC zYHM63HcT+yh-RV<4Q%NRnp59$xayH~hm{)U1}SGOdur~LNYpFW4eJd{&4WqL zmF#MAof(op#h!m@^Te8*1g7`QETJ^>ck2rNkGr=Fi}GvRco9KD=@O(QBt}3<0Rd?k zx@L$WmF|{CLJ2`?0BMGhO|2kq5jrma7@wKPe(XtvC($4My{ zF`vC>1LQrueuiJPT)dNHEiKxs{=}PERAn5vlN^3BL_mePupsL1V4Kr@pan+f9}ROoy+Z(c;$x)H93Z@J`b~PUzqyEYp>WB#oaI zaT43>PDCV{?SiRQ>CQL3%^7)*Uu)fO{4|Wq6o%m^esXgi*S{@uZ3>J#p@XSsuJs=qng1@Yv_+^tkRwRg}?T2@n6e#6%^-{on676NKriPcc_xA%0`Hs=wv(b>G zd*!X4kpe~zmEp9P2jA9WCQbJrx&g$^i|$#OZ&+jD(^yvv_I^4@jAY&?EBTvafeI(2 zxt!E$O?_Je`B&^Rc2e8qrF2Qf*T)J5+3qc#Xyd=nJjW^%ETK_9hNkac%!(L2R6n~D zNzXu|TG8tn3OR9fgApf-MSV7};T>duI2D<5m2+aKAZPI@>WI>-aZup+YPC z9``4upG0b6O-b zG?bUqCm~8Yci4TXhBt&shunj+h&)I}u9dW60gRF(dPA*syg#XU<`HC4cLFgio9(sx zOS*jMeHH7%g)~_g)C->Z)LP;WU;WkLsre*(HjAzufel?8%5fgm+t1#tk;;no#9iH`$^{*5zw5eip~V%WizaqDYo} z@maejJz2kds{kIz##OR~!v>@5DcPKL)wXC{lJw9z4T{y#`pzYMe~=Q zh0*rooN})FhUrDZ36RW$kv@3~`R!|)-e|ARGPjX0a&D?*F?Ov_l;brFS)n?!c6qkT zE8j$i@s&zBu{CtK?Iu4(CQ~TNG8m4EdPha+CPlBjMZIR%6#Ozqsjp@`&hjIH*VpxdK{?HnG_UeyHaMI6?6l{U z30I>m^F3_eK$w&|3rx3K&aEpF`Q*O9l-d1BFH#o^dFLMGarTAIs0nfwaC6uh6-Zej z(+RD;ctrs*^hm+nmp!ROMF1y>DD`Kfe*}5-jpl=Xwc!cV$tTE5k)J>fgVaWiYWH#q zQBe9V{Qzr~Vw=}>Z?*xsVxX=(wy zVzOIsV{CFrl>V@4Mgtin*2JN4scu=eT(_QetGAXkdvoKXh^e+GflZlZKqV`zbOz63 z)yJV0;n?2Q;wv$>%^0lD<#bQ0`WKF1rk3*`GRWe4*B6%rHr|iwAAJss?2KfKOsLbJ zWU2SPU%DZqOsS?g$#O0R6Q|<708~~~R&tmYTeMN1yjxd=NTE>balnU7nJ5_SI6A>g zc&reM4gXy3OC+);StODj&IS`KR=4x=$JQ_|dXOWTQ&FOEZi`2MS1&QR6Z)efxlFm3 z4j!Xlq98um1dXKnS#OAT+IC&71;eg}H@px#o_T@d#x7@($UfBY2W*tEHvCgUojDvN z8>rH(vwU{G`&?ue-V$wO(c*vk%gnF2kVyH94c#!2$gEjcWTA*%CY8jIa^bdf8eRSd zRRvMOL2hAqBJ;xS8)`R4Gz=}|CmEE{-I~2GSz!K~{8M8(*Uv{pocNdI>rLhXM6m1RRX^(%ojH+L3qKYqK*9`r_8#R|%mZ@WJ&$3qZq`lZvVI%g z^nFjeMBbIE4du9i^gY?K{1y^rF|6XXtD2v0lOi2@YpPaXiS*a2;2As7UC-yTeN^>b zPIT_^{#fb^+Y~O>3jkSVwkT-N>~yRx*V~x$3ZnX|HKmfGKOCAqZ$af)w%RKURj1jj zGn|lZ3H_T+cKka}@zQkN7Uv^G*udZZA8 zg)e^SQ4Og%-qf$KJ_FKIR*gEt)4a^8;sR>LF%k>*+n-k6i(onv z{Ox?0ga=>|<{&URl2o0xS77(+#}B`}y16@%4u-Mn7tofr&szM>PJe3`i~j@A2;R?OcqK^1 zXBVCSxS;{OO8{g=ccuAR+ zt!~w|f(l4rRcg;kY8FB!LFqFnDzyt?tA-d@NmUKZx~C~3@1ba=@{i;Eb2`4z!tN=BqfNxIi5SEvE46cl z`Hl+g6q(gqLft-k)ch= zB9(4kcSp5MIuZYofc{5vfhmOLgB*h=yX26iX%EO6e<)u^ZOH-5cR7k5K{yGT5YrI< z5TF10^H(weN@of$hu_`@s-ZarIj~qy_*1@e0AEs-yY8UWuAqpmtCp5gH>IY~__VE0J zJ(~*jDV9-?R6*N>>p2YOM-5mh95~J2F%0})UOQo^^CCygxo*m84JZ{eOTU||h^;2+ z^aCa20H7V$KNh*r!qmqzkuXaL^- z!-@X8F9KQOiVy(Q%oaT!l2rrh450pj_X9D5Bf9wzS`D-$>cfnQ92&w2_sYtvqTc(g zy`LF@siT{(PiHV>UzKMdxEJcjp#R^WEZ=dTb^X>=I*B3TKL1|3SA9I9@=Yu|ky%rP zITBH+1-Z|noe@C*rMl)-o$98#a6p40*SJ*p=zn|u6)^O`h~Vh`b}BP>>gT+S05g2O z>%5lIdR0LC#3cvApY;P=^2OhP>yyd!roB&n%@GXggagn31F0l5~qOls$VCj z)d0VgCFn6zMiRxlKv;Ic|R-i`&0GXKo8!$AE?*cHg zsWv<&2g$2nvKX>C-8ws9A|vR}@3qgDd)2Kv*RAJExBcF331Q$F&MZLD&G>_AD6H;) zhn`*>|6QsBP~x!j{YO-)%#0N~@17t>lUxSv>t7Qrx#bRs! z*7HA;9}D|20mzS|ib)6}m;%q+Fp!W}R7_Y0Pz#wCW(WHIBYLwLVg{VuWhzEU0j26m z_<6nO4YRN@jQt~gHtU{_;eKbqa%FY?YjF*8@2AC%VIz=?QkPSM{a!PU))*3?dK_l- z4@ucenOVjp-}w}%+D^~Ng)xGS<*zZ-t4%8_z2f`L&sU?^`Y|HJqo#we_Xdc?cg!p! zI(sooF`GH~F_HA2fZ~6S(ZW@aKq2n|iZPo3h{Ux>j;u#OwH7ph*%ec^xD5zRre=Cg z_dErVKMY$fU~+Stm%|J8)?_$yrS5PNiSLX`S2-b^TgYA_T)7Wj@FstG3pt{`==L9n@_KLl(l4I73!|nok5|0vk7H>Oc6L z&3RXVtZGzjqMU(EZn(oAD3}zr1}_)G|D{Nlx^nSr#!+}QZsaZ>^!N8*kW*LOR#|Z> znEMk7g`B$pQ56nZEzXOr`WA55!yznY)fqunXCmF`cF;wW4p{B+-XtR7A78Tgrba4^ zM2z@A((JzcI8YkSV<+e1FBQS9NHTW$>;@$=wdMHomq2?vz~A>!0Erm>#Nl+~M?tb-Zw3(g z2#3fGbAyd$9T`KKaDIt!0t4?)z$Bw|&^Di93uifP=@JT;n?WmC#miGy@^`3dx#5Qb zb*`JdXVOlblGbr#;ch=53w6jQYYH&@^Bo4Uv0f&2?*LHA<6*^gKx2LvYf#uFiT5m7pgG14U%|!xA^&gcPZJr!6WW^1|XBRz9$#1*3>J6Oj8OthCNhjb0umgD8nT=G|rLd zjM(H_3?g@b4GJs~{^No1uLYFzJwgktIbjo=!!-;QeyGgI8k6W1MNRaR_}mlin(sXT zJr>amvH__9PaxNO+yr||Ju&W)hoS2f+8i1dU$1piXw=v@Jgn>_KRq){mtR^>P~m+K z^bV^+Ks+(sEO?@*6Io*wjq;9z_{(*W+pxp8c$|!!7pL>x)5^( z02Am0LIwVRS$g~v#Uqq*fKVDkhQ@R8x~&4YzGu)7zC&}4@R&N|6U?7 z?PConFJZ$;IUU;?bQBxZrEeVwKf4(Jeh9km!>AvLR-wBrRUi@BL{x%KOU}2Uub4vR zmq63p^10Nba)ycJSQTSy$hZtg<+s~y?i+~=DBBMeJ48~kk88*%7zu&V-^Q5|G0?xr z{$L1-4r}ocY2oOf=#M!pum8g1#_n+21@SM7KsJj1T7x2R4ig|`WmMZP$^`YXQD~Lz zkNucKpPjnn2=yRlIjU&D2-Gc*MTvq1$|G_*VKwRT357Rp1E}Q-ZLIU(NnUg-G}wI0 zovvbgT{ANbV)1C>DRigT#e$sxm|!K`vt>t8^?!hM0>N3++QNQ3#CDh?TNBL4+()ke zz3b}5>1BhrqcG~$7*dRSc=zIgEe8Qj#n}j0x2nI=*{1@n&Gf_8t3xkwOLCkMOg+bp z+}YI`G6Y?Jk3%U}duno?yb$Hk1KSo|*M$E%V?0yB)BnWMv_L8z($bxzZ66O@KLjC+&`U zmgXiO!nTzLF)S;X$+_XBvPQBlX>fAn{#U@=1?Q1}T;!hl{xb{4 zOHyi{Yr4#=OwWY~UKh=)%bpyTR>=Oky8`ufR8zkXcB?Yami}#y_K(jjiuKO!rNfuGsN^?ThM7B>>eTBN&*LQ0xX^{hWUaW zTP-q*uLaAzA&umxY4)qoA4i4^V9yDWxsv$5ZMkk_T+Wkzd+dS2ikj{AjD97@`i1Hx;u!>0@I&TJ=S4G8XH9d`>$ZNLGdQNm%q%X;)#== zGh8KTg@w`mog=`n&r`d~7!h@E_aT{jJM!Z}BN9=~~`^H5WOK`PxM$EyCdN@MYS~Fq&Dg4Dz9Hhej zXql{=Y6be^v-ZYv931Bt$P#CJhjUplqAv2p82yD$z3*OA%g4Yt zNX&0wio*+Mq&-lzCSnRW+~hKJ8~6-Kue~Uz+#l7ds`ihE>>D3z^>nWXi2_4Tfxj{W zyzznY^e)4fT_XDd_L9H9t@rTNphP2nqO5uJBg%l6L@TYN|I7PZGVHzbWNgZ56~jIp@zG7J+IQ0aI8Y|77bmpfNVLr%d9m zNI&?-%4RrmrCdkvH#Bd)W$-ckKP-R-q{~&Zhw9eIK~u)9A$cabZ;fIn1Q+u3Zcpm*0Clc!}-CY+&EcCkYNg z_Fka*!IGK8#$TX!BsQfTj!y%e@a8pRTQB(|VxjjM$&o*jzFm&+#X)+efl1L@CTkkt z&RcNqI9B|+n<{9UxRJcI0@Py))QuT_%hV&>$D>oA5~X7(V!O;5E8)VYGZ8_(0!_$T z9Bll22bD)IkCtNxPNE9^gVphnz`A?2#K5$XJWFplRt`;BAx}4nhxy6U2XM*?8tVfa z0&2CtP=VifMN>Cn{k%~2?;hEL_Y{1PT2DJ)wR%J+*fG@+g5w)6)1mThOs{gNlW!) zZe1^1n9HU~8p;2fN3d~q3dNtk$Z>8u^1lb!7g*pR<>KA-mv33=*xu}Lfy zJ|W|`jC@c+;m996p&$yJ^MHlZ+th4pR``{Jh#f-~7RlM70o87IGb?-~5Wa^AnRwR@IQnwri1Vn3@_|j*On1J4&g?LS>cnMDw?| zv7hpVu6r*y>r!k8(*V;;_nVY8FHJ9Sf!&W7=|9Fmyppd38TU@8>p{rVL6j3|Y1~(x zTfgBsQITDQh`(!dh6TSljgsS&b2K`aXNScsk2d_ZQtthB>C6olp&D?2Ou$DL$I!rP z&gr^B5RucYS5N0tq&;*$$$`!mUOo7He0v32<)!eCoog3I-0|YPgunf-CyUK*c5v-U z%kx_ckUJ06TO!W76r&||W&PXgmD2r8Co*JFi z#PhjczDo3TrMZ0DAu`~;K$C4nXOG_a10cJlD6t7d;Co6 z3n=yln%dPM;8h^CCE^+$QO8N#i)0Jn8t95l?EbpJmY(ej{BqL3Uf-uQ)Sn`)5=?L< z(h5trerZ4JszT_2{;%?6mEvNjwR5TS!w~X?vykeSe!9~6L-$;7k#tS zMc0@|z$OjQ)+FN9I+p)=9N5`eJXiYYThYmH;5b*PT%x3alh92S{^JcTJ&x>#aaeR9 zP2mn(XwKU>ss}S*78YB`?N+pbo8^9i)8pmWfy6cu`VR?HS9?Oq** zuvO(4Y$J7Jp5mYnJot?SuQriH9uM{(1ttXU{!Mw#)~HQgxy1X^yDbFKbC?&eK5vUp zZF*~1E3%f(A87K@5oAT&C()$<9CR;}WLy&nMG(kFKKFm134tf{zJ*n>bwh{m#7x+` zeq*nAH|RK8y5~duI__qLVczebi!|9x;CW4))@wdek=h@MzbPa@1}5}}lfN1`RM4M< zpcsFu-lYm4O6KlHD%`A|=3;fC)5>U5W%VHw`k+82LZ{Rkbf7Pu6Pp+Z(NPo}=6ick zU<&NJ2PA`HP)3Mjm83xf)>y40=usABxs)3_)ZVT99nMw{@`<=zLx~jNb166m3V|&P z-C5~VJlXD7FS4D^yVikq@*r>Td!Km?)EH!|GItePZT!lg3PKF4_SBDhs9(wrx5Il( zhckX@oJu0Q>yy9??3XQ$bO<2|Ar@Vkc}Jn6=_uj}#5IVVZx$Jz79yMNs2jDNzo@rH zOk@A1&n`riTt95^G*}aN+50Z#p`v1dCcL3@z=(MU7z%}U*$pIVBFLkjHN-y^ zko?)!Y^b(K=*L6=39#D+A$jKL$si4O#R{|qS9Q+ld_m}IB!VEzv`MY>@*XHo2%_XP z^vbmCm6>FH z!;Xrr(2qM5jpT(bp6uqad8RK$*KC2SVRp4|yiM)s!AL)(e_hPMCuRdUS#GAR7}Eb! ziY~`(9z?n>IwE4edr|=$W1?8$V|%XD;uAh6b<44bT$+rMXSkCe7HJB`gPu;z#L0zO zUffmrp&dJ+?zcxbD`At`g@Z=GTWJLfbL9GJ&hobX7<{N79vH8Smb;!6+@{eg+7gU< zdYg4SBzv4K1lY3{HZe|WzXN+(^5nGic?v80H3gpuqlF2uuIPKYzUy;;l7?I4Z*^cqubn@Cgw*2qo?OLsvY9R?5O+rB)YVQ0udK=1M&rBc`bv8=vr2y(B?<)#=gUl4MGMuiP$_# zfN2}FUD?U2KKj&#j0Ewb^~WxBp`uFPKc0Dl#l05U{S_#iE~Li`^r*-FxcY& z=RVd-a`q5eExtB}uvk%>{aJ6y1Wv7y6V4dF)gI03qT%mK=7S`9O6{Sb@bst!mP)OU zxRW+qP!lbhpK@qb9^gftT&idoP+oq`0^aQr_M(V?z+vov5%G9mYM3 zL201m=El;=_42zM%h=#mSEM!K`>>l2SD?=uNA~Las;;VzqXk!>$r&^6y;E)IfnTpt zMAZ>9XvGyRJY-u)-LUey=v}wyyCQ}~GF9bXjz}x#)6j2CLJ!j_UM-@s{9~wtO^^TV zevc62DbC7w)brKl>aNLw$<3UdeR5-uDMngFcWk-cQ>M-=I^NLuTk*zfKoT-#ikD|G z>l%|Eb?HY57`07l%Opsx(S5v`Rxk5j-+&G^R(yAd*>}d%mNjWxVTku;8#Fp+Fw+Zp zeGrl$D^)>%g!C$>Vy!*}u0UtSh1P$|Fsn79UOA7&`4MT?FuwUyw(Dh2F&aX5mJb#% zg?Ip3kmI*W+g0;~hJJfvz+AIq^tj`Ls}u=vMYxE6a%&Y@*4<<#mwD-37Vh403^(2T z+vW=bd{2S~TO+w8R5j05sFsFx%G|OyQDbJ#7xjQce`2S}ZG9};%17?~M85x){0F%K z)-9oMiO&vh1>>G?42r%G$r-ksIP1ZxTbn#}+M7MqwPVF6!47N8-m!~s$8(eql`wdA z{?V;KucG}l<B%9ZO^seTz1T@J&Ywj@c1aFS*9;1_Lwp!6*taeA9^dL$)c#dXXP33dZU1!10dI z&2kIaP`*ziaATiJF|b{Qws1+ivIzCEz{k|0=rE5R*~q&~$u9PlhjVIYKoEQ_hG($a z2(!r&0x`I(oQmDNPE!H>;&PLxPgbKYsat~h6%35+tXeS9eJWyQLFMBh-Bk5=y~-jI zrfsF=&^4br{_O8t`g~8dGt`ubC-owmVg3xW&kj?}{1dOm0@w9zv5#cU82OyQ-9uH6 zw1a?EMY$=(GR2h6Ej(~opy?KrYaJF3F$b0^bp+p{bw|BB29Z_QZ zY^TlnWEVjx;f*(r2a<>y={`-UlJjV9)qr(wY1wF)9= zovWX)=kvhnFxAbNAP7mEk*e&2H&m8-EjT9N9YO z??%EV@mos>?!ygF(meiTf#%Mnn;b0AK?_a;_w1w`1s_YF`&x&w_gWGP{iC!^S9;SG zbb{@4TDWAok%GQ3NuL*B|3pB2Aus7@_#p{|Ly5Mfz;CersT$8L5(y`@7zZ>P0xeKJ z4>=b8yy(`y&-wHGKKyX|o^49fdcghNPl-%8?c^?J#W3->#>{UaMvvb}aB$YPXDqXv zw3~#Id_%4Vj&Q+8rrDKZ;47ISi1jGem?&YP;@)@&n!z47`j4sY81G5ADxC-oPidK3 z6@4e}_GVvllh%|K$b4c-Ha4NWe_p--2{&Ze{=+C_ zfe-rA;mu&n#7e&i{?}gdA@@YyqdDwLW zE4+}6N|-P_kgV>Fr5_?0xObWL$oL9h+$k+T<6|@usp91+1$n(CcvGCMp{mj9(c&ye zDyLhHUZBt4mhpC^!6XMaNL@_uBpCncM(+Gb5Wp!_$a#2{sNSf}H_aA{IJj@uz8Tqt7nOcn-&AG0yV#An!fmv-=#HDbTVMDO z#YI$oqg|_D;vkAgjrin{yU&c%UVR1FOx8TmOukeuLX^~JDe3-RP#KG|pMP?Q-#Bv7 zq2(tx+a3FnjPPVdc2?R$Aa`Vg$g|~el#-_#`4U7Cvg_)!E?Qt*?mMik3{G4H(zaV+ zC1G~E(;6pxG`1>Wpi{S0`DB4~M}OX78I9Xdhsk8C+EL!enC!;Wcu34VbmL=A7wrF>y|Bt(Ahez8{QxA%0qeEb~TMS@~EhsFmb|FeDgRa@mqn4QI7OscFf8jMB*uMn1>>sZLUHyHhCr0 z2(&v2J!ABe|Hn8>6j)u|o9C&DLX-E>lqUil5}B$-NeivWm|qg4l(&aPT#bl=2+;Zycx*Q|@X_i3DoVME9qQ zU{a6X*?CX#b>p994bmDMIs#kz?lJPykBd3uAuYZq{CSG+go`rVpvFB3 z;RphBC27CZTZHWc$d=>pjQs3zBP{rD`N;AEE%XN6CQXc=y1_;Gtrc+fDgtgpn=mr? zt-wE{$&j`|S0pCXBnlR~maSnqZ<(@9`z3WDqnCQtnoTwHqYulh(HpFhA$@K=1{ zigvyN?@u`n@jvW8`W!b%r9}vuix=CnF+;3-YT^0sq+K#d34joIX8TRX4o(3-F3UNps}MwPeLKWmJO!s zb+?hc*;~#z^Z0uN3;ZYva?$Suj7`>nrN&2^d(A29zlWC7>6JcM=O-p&%CuSIC+-F! z=}_!;o+{wJq%dUyOqOkhVuER6CxmM&iE#xQ@V)H@tm1_-@LzvDGkGruyvsmY=I`=} z@Mt4C`d#S8lS$Q9e8qu^v*qWl#Ymgl1a=-U|LDx(oG2T~XAdBV+Xe2r8A7>-Nsv!a znileA3uWqS@CwuXn<#nSgqyZ}P`cVBNe$eC7;t>H+kg1|z>@Y3)f-NsSI;~Nl2!w? zZ5zos#EP;Io7MUnM+rzgg_m z@ddETd6rM+^ZM26cj9U`+B5%dENY}~?(fhwnjA+n#s41S100o=)x7m-wW>E=@9oLUH=%2`6UWUq`dpX)GWl6!d+VHB|2=We0q&p* zTqWI4U-!!i>Kt@%92?N7?p#8;+eNobVoj)T4^{fv60{ddy(a<}LR0UVb1 z{7xIacPING65b^CL8p@6=Ju^CYBz@#or_-_{GAU9x;o$psPvXXa%+X5Tld8Jbfitw z>l}*u-}7q^%iwZa_EIL#yoSwIUaD|w)i9gSS8`vHFh1zN!9G49qBSqRpEFjX)5til zV%$!B0m0!n^p2O_xgli0YOmv&t zd&kVIBrIQzf*-*IQ+%sCq8~rxDeB4tFfEPW-%fm@`q2VQRbwJ&~gd*+x_T>>+`%zbnq8!T`T=q|a+_|sDy~BA=`pHMj`~h+C9BVH7FQeU& zuE2f8?O2Ht8SrivUSq@Qliw!O)%#f2zKbkGdvnNMDb0k>=WbFb^p6sFjX$eu2-_=Z z)Z3_5h#lu}Q}t>`KRA(R68G9AGhy1VFq9SeiBKBnMi%uf-x~e+IB}!HGg}j#F;!xn za;n>4_UGeukL;h}=RbA+nlIbV{`#CG;0G({!)+KrDNC3`dN&2uixDvjHP1WTF1Ghu zZNGdxsM(GVXopyLeo5Ild4Ht3Zl#=38EVtnFljqnbr#Mmd*PAC5`K7FEt>M-GgBr> zD1{@sHhg6fI_@Yv)Jv!@EdeGH!ywgIjE9RwgT?1iAJa}LM4LJNpv0G`V_kwd zT9YqS0v|&jx4eeLX9X>bcdA<$LiRaY7oj!Q!pWw-FJvYJ=SovsZSDxMX+CbhaW&`E zb}sz}k}9xm?Y;W9jH&N>Zx(vOGXQ#i%=VhY$YwU5w#IGv;l#j@SkF4TSRd%z%q~wc8_pab10h z93NbG|3c*B7X(Mqw=CX*&#~@|%cT4w5x-16>#|*}q}sp4o_w@C^)C5M$5!>dJ<+zo z@(?}9V6xsI)fD{6j3tvoQ#rbN>au4?!e*Ve#%OZqy`E15VeR;)qX_#Jfdk!y9%(3t!}ZIPjH=hU%6=%)Zk2wW7xDg$Xwv+wZZEEsZwK z9JSB(z7x!&rFf%r1k1FYxv*Ta{>dbzOv|5NRAZ|>>itav-=^wxc>o)$S^Ug%@qI$Q zlKh453%~OP^?RMnpOy)5f0iKp6W^ExjB#BOQp9ntQXmA5oyfSmD@A(PNhihzg7AvtetG)M4 zpz%v~yxH|X1C7{TOL^_wZ2@eNpzD&{!4ViYgzu=X_)CF_uZ6#VtP!=T?x}9I_thjd@|kJaPUowI#b9uoDesG1&xdDC6!y>GYmWCzQi;M>QbQG6ptJ(BGU8G{Rn+Ll3Ju301PzBX`$3xD z>UiXZu--||iIbasNjqDM%)W1Zo46^DLp9gLI9sJR{&4zjqsP^oL_z;AD#}Tago;yB+u=UHI8VxT%IrRzZKeWe~uRdTcK~G&qF81o--X(7EV>AF`1{W7y97d;rn`f+_ zbMdIgdOy%^bkp+UXoP+GlM~ks7c6x7@0Sz<*^gflGl(iMG@a2fQdyRXpu*iB5G-g_ zFD4$JmPZP@bV78?e_bDUJ}2tpk;Zrb{X1yg_T^tjY_xx^BT-D+BYC!l-JwEuqI=@+ z>vpd~K)*ssbnRcN1qsnTy-z+JoBcE~Hv3#FjJ$+bp&{f~G2``Xox(fy_|I@il5?*= zX&s=#i_N3!SX4_C*S)mXZw(^XKb<}vx9r;T1UrzHcG>l4zE*NkD^e70D*Ga#Onox? zJkEr6DqB5URAun#WGL)mhIFP0oy~Ns>8G17UJPbsx~=tdCG^5743Ts~c5<7eMehjV zgp{1jZq+%=z$b_ z`%|@Ee!x1{E!Wq=Vlj^1I#VuR%fx{<_p_4|KkKxUfqio&C#U4y>EX(+wBzVNmYLtc zVuE2sPc5>OBP!iKwx8#kK->b}UMu{&_siR;=4lKPorVvV0#KzSA};c4PZI`%1WBkN zgD|!R3#vFd-`4KHfq5;feU8_VJMiY0#I)!JxYilp@5-vdhmTwA#IfSy*7F@oECg#z zy(>QkL#o$!$a8Q_=sPYvi>8IjhI+@hZFcf$!(31bp7vTP>qe3JHL_n-^E(t<3m(1~ zAmB@dD-z|D8}LVGaF=M$J$!05?Y+rLK02Q)Al{Z|ahNL2GN@t`^lSb{t!wzNj1tW< z0<{3kyGSmBvFq2-BR3Za+tW1T-kvRPA&X51)h+(GueHhw%=@R0i5yQPLtQ}26$HirHMc=?sI7>WgCWBPwsfUz)PrLbQn)y`q6-Rk*lxao-iX%~fNOhnen#9aMauuO1^@ zKcMzZ51(6R8(3WEPnjdMz93ancy*2*oN&rV-NStmhikUF53bm+?__6+Mw7;-PBoZUmWn$Sa8Pu2t9_)OkpCT(_m^zJrIP@Z6kPUIu<>PCZM z`3dE8;Q^sEI%}8H*+C1Qks_6J2Me(+Mj|vbfnyo00!MfAM`a%VX1uvH{(ZyG{*4W` z_@ahbykFnavNprwY=j-BYp`9u*yG%P7R=cl6ml;(ZV;CgCyR&w;RXQJ^T^qun(r7T z!tjWm>hfEER|TP(ELWZre^;89YQ<0b$xxMLUnkR#$Y1xbqyt{gn}LIYUo>fvR-OWZ z(G6W{K9h_jcidd1U$0-Oap@EuJ5l(KB{J<(yi?VfP$NeiOhaTRXCdlsgu1syz*JeA z8G-&#sF4j1W3Xz}9QxPV9YuS$?MZy#J?A?H1n_UYH{uqfJR3r4mjcZH+_^C5s8}b= z3ZfKvX}&rYwbE!YQSa&x*~hDnlYxl7SV?pJPPN^XLQ!Qh9<>;CB0*77i0K#eY=Z=Q z8=BkVGLuT}$kRc|y};bYH(@%=bO`A`R4z-MMeNQE*wxGI;vr^kL|d?WV(pf%dt9OmGgq_vZjZOo$#K1*ta>Iz-XKD!Ev)7Q*g(zh0azO4X`o_BZ{$?CTd&io}#J8?a`vP~9Oz z^k42GM(GU~jB)fsXZd~!Kc+@>+!^G;B7go0?>1qx`a(&_U%atbZ>XHep((tX-L)Zc zth17oHA~+v<-!$|U2rqsob+$}NL$EU%-^`BKO0Q-LBcO%gLe7>*&?A}gjbxadxXNz zudMYJ7ssO-3oawjRrf-KZy9~??5--5r^$lyIJxEnp%45=b{7D+}?MDb@E?kfH!Vxgp? zI_&c`cO)Skb%Pola}DNkSj`@P`m14x>9f&4l$s_tt%*6sb< zt@GOPvKuIa7pECvHy>I#zBPMbSZuLT%{XA+MXYb$u=JC0CEnmh6XC{xinMp(761#T z8q#?6(kGiO6cYbbE#vhq+LQsl?j>A(GQ2%MsA=`>mZ7eJ33c!&f{!m9rrEvX$pFi{B5ksrP&j zM%?F0rj;Y60eg9DiZY*q{!{ZO;S|H}@~?5>2c{?5SIKlXy=TRF{rMJsm3_K^Oy z?hARC5?BjA{V0lNu=2~4vc}cFaZMGGa;D7RfIYPvc1zhT#A{WBrs%LBij4fL0_3Z3 zX|n;AZut)j&-9Wob(kYQoNss$`s9m+mu1YJ<^X6ro+U+x%LAtfR%(1 z7v~0tpM|H3#5C2ZXVF4TI6wB{>*pgu4q^Fi#g0fBq39+=3k)7LEn)xfRAh$~2}Zb& z*t8!{;2|O>L?mVP)rJ-cx2;b~?wD%)r@VWxbfaqQ@?c`Z&c4e|a>T2J_+~*F^S2Bd zWCU588`5P_x0!Et$HdxEuY(Gi1xc-h4@zEu z)Q}^|WBQMU6dhFFV)FJCmnr_nJEL91p5phb290*Hx|e)=u)5 zbyt{MtMza3BO*Zq%73S-uB|)r0r%WU7xuB|@*#0bJp~mHYHF*!yuf!V3#mJ(?8U&^ zUj&MEc2OdEr-xMdEZKKdmel?6X5oDp%gp=x^ADBY`o_E9ZZmPw=)YAZ>(D1~jwz5Z zi7B`USVCr6UCV_*7SWap)>h1{l)d^l9y&F{%qS8zEa>@EI)8CygE{*B@$h2QjFCCY z`2EhR3E^(v4%S`6Y>Mdz@Xnp!-bb<9%P0i1VxLb)?OMP>{oyTASs zG(Qxwm?SXww!-6Y!iVU9(%m?-XK#Mt{UCqQfVz1%uD%wR3cc`?jo}nEB|d+BZz!8l zw^hSJs4T(-_g2`EN!#FV7~cCEv>(tmr_<@3$~L;?)M~Mb-7f62H|Rd4GBkIvi^iOM zGJh)mTLBD`H;`t&s~-cy-*j)lW!ea{I3V*m(!c%JMh_-w*Egc9+OQ$6?&M0R2RE+LH4OcXZXn0XC|V%bk?m$bak=rH+sR*@`sf{1TrkIko; zt#Q?VmLTop=_aUV8Sp!O)aLQu1DF4Uvp0{2@_pZjXNC!5Uj}6fr7#I4+4n7#>|+^A z_N)`hAPm`6mJ%&?$~uS`TaB%xgd$6p86|u6C8FmVy+5Dt=llIF&+~fzsPr0h-}iOh z*L7a!d7Q_2oJfEwZZI(Z%Q%d%#Grm|%2?0m33~y{9HvruZ(C{_v!Z*YA_SOyY0KlR zEJ}5BLEblr`(US~kP;GlkJU9C7XiBpUyDGG&I!(MGNDsZf-HpPS8*GJ8r7_HtSE97JLXg?`U8(aiBa;0;RK~;@M z{Cu<4^EtRl2>b$0 z&tOSUt?lz2i0ytWDe>3rr`Ury>ly;>pkJ^obn6-8!411)@@?dkNBQGv(d0K$Z9#bg zUEO|!_PLf-KmJY31;VrIcZ2MmH~dP)XWugt9cfW1t7(?+>hqi<6O=A%_aHk`{kF>R z#jwBjX5gkYMrN$!8S+LSTg;2;gjqkU5>giGq4R2aswmO%kY!#_04}Za%O&CSSkaQe zd#*ticNC8!Fd8iIppF-h>S)@K)hBhbd%C}zNiFm{xU))la@yE;Zh`|-HR_9TEF~dr z0BfhWA>38(GMb89wu6psHB#5A7LPRPs>Zmo6Nv6Uv#S6%(|3Knf#*Fya`hU|3@;?O z*3h-nOLn?&ymM;*=$zS_mJ^9|zb}T>`ALI{y&i+QnRO(5-rA}M%N?_SV!}WTY!n+$ zkk@d3In1oYgzhI)&1pTbp05wIAZ5WCmI7@TOugyakRiKr81G}e2F7wpyDYZrZ0n^N z0rU)!zb#FNTCp7+u4Y61+?W+uWz>^Id#tF*s`a&eQ-^$Pb80}QKDMI^dSr50Ska=f zXHVW@Q1-_0V*nweTVmXrpQ@pgA}1WSjPPbcKP4o;yjj9({H2U+U8e5E)f@VH-Z9%l z={RpM#R%m~PPqBm((C5)X5)gGP3z)vf%ejj>t{0!;W?+=L;5bM4J>h1uQczB%7Jrk z7Mfp%tBg9bM2C@~K`EZ7WFC+-CMOKg$hILX>=>(N-+pX3BAJ@gZWsbqpVen}ga#yp>teQ-aj<5>Ice~+I8X%e_0eyzcrU64itrT?bHH2%C;&iz`AS{E;rJq^Tk98o-7$hts z#^NF9GD5iWpc9d!r*!_7kH#>MAtk}zWwKPXEp6NSO4eoR1CvvdLp3?r5a6!Q*`KHT zr2Q(vDyE&6l3*&I+KHAqi;)SRFAmGjupwV&zCungCy88snB%gxIaU+&l~9S6{hFDz zz`58frTOqf0clJ^joq9-_Swlvs+>AQqS2;i_>ka*bi&-2eerNXPI(WlAC?SuI^#kVRXG=0LL-^6 zQ6gz%O2kNx)L&H?!+pE^le<@U{{NaDw)^lO>EW}Os;UcGB_*m%^>CVndQD(ee3uHq zGb*$7Y4VgzoaDZ=T#zu0)d}q6U^UY5_<-%W@20+M80i;Ri#pzhY)qg!QkK#-ZRB>jdPzs{%( zmkFzk<;lF9>%CoKCU#NsaX(M!!q7`STxe6B2Xpr=e_``dv#b+Ie3#nZT*_k|4Ardn zbA6s=J7P+S{MEL3lJ%EfQs@jDSp z&d##&WBFjA%a0VYe@NNs4!;;Tpa}`cg>6kGGL9W4$WMGJVbuZ?S8>;C)G5T`jJZpkqh>wMA z+4lb)=4tO?xfL3fTmAA#{dM@_a0vccx0R4Ov^Ca( zV0Y#7$$Ru)?b8&yuf1z3?tfv6KB7>>lZLV#9n-XS7eJtU3vPAmUHdenQFF7+wZU)d zXz;6UPTAmA*{)C<7LRda*6gTUcx{^*kNf%HRz4(_}j9w)^A^v?gfY5E`KR%Yof{xb}Tb_*j4&(ebLkZc*Nj^mlIH0wF@cM2`J= z>D+^qGLv1>+SqL$h534j#iFD+FLB009?gXv97KT*T`>a|&kjcXgJ=Zg$KTgZB@x_70YX9cV73a%Ssox6TS zI#sUR?$U6z3q#XS%0s=shaJ_K<+c|zE~y_#RzVN>ckG?6`EnbWqI0Ox!2|}3%wus4 z#?+6KOLplvItkroUIMN;WPL$OEY`hoP6kXHFR-Es@ag~&60+d z`-ff@TWlD>2){RXkM>u2U#!AG1VbAFkCX7{mby9Bw9X80iJSD!Fqjsh2d7j03Yz4c zP7C~@9A`edY{G#b+Mbo0JLkZqGB#;!aR0*6*(DwXTgMw}nR@5RX{`EI$%nCZ(R*$vol|v6QP)W>~+?55+pv%wG zXL~NDyU#Zi(vqOW7eBsTmJNSHt9au(Z@|L1Jje2HcmU^xc)hRxc@rP3o~56g2Qs5Uu&a>wJcf&Wwsc*p)3~Hs^Ia61nqYBu5gY z3k5U3>SXdMqqF8m-kPN&O9%T1Qkw|YOaP4=e9zE`<92u$g`yv`ZkW^xc5x0@4E=FR zWevk>U4=aq5PU7?+MkB`kJyf1A1y+8Q?a5t3U0ARHF_-rEgUt(S;knW-wY-xM;cxZ zZ#q-!cENcMN!~_*wvnGMb?F0JqpZWY2M?_S8ZwfpHp+-6m797=9Q1}v_^T7 zA<5)~$BR$on+ z&18;@R5WIsC3lqtCDOV_@Vu=I8!NuqEm7mQp?E~;+9BbmMaE;+{ZqdzM8p@9WjJS- z`+UVO{>ZU#zE*E7eyC!$8af}>oI)Bp} zBdqKu(eI&YYJXVQ#emzqfg6va_texlGag%t&I?mfivQK~=nqRbZq3`^zT!*$g21<5PjiUe(6kOciJbZGx2i|Et73Mcl7k{Y*R zL$P)r__8GMBKUK54{{<@s})LtuXT56V0xq#S#g55XO9+6XkRshknuJ;@wM^e{^A)R?jn=<1ZnG>S##|(CcoJDcJ*`^w`Yo~ z?TGWI_9s8X76O$*E+62s_ikd+k=Q)^dgO~w+>j2#&~mx3>wtfcw*Fbl=o!$8!9h;YfRkWisa#@y4rYes1TW#5YYM zZD|=7e~y8)antA25J&2L@W{9_{G65CYaB2VJv_$dDkv4XZn=L5=ic2cDve zcpmq(F4XO~J1O^C7a#4f6#D+8>db2+qsah1Cl-FC-La(x3(gVPr>pmLiH_W`Xza0d z-_bsf5#RBUn9xa z*n>GQ%Cd4gy?)c?eLO>Gp6)P#ye!E4P+bcIiD%G_-goNG!)MPqHr-Qv9FtpTdZCxY zn?NBn$l0Lo+;%+V6ImeRWxl5xpC-tJb}X!kyv|DyV@qpv{yScxZUpYvr?!jl5thd4 zTYCVa*!tA-=U-d^Dwg2rQm0$tq^A{wqPzrwMc7g6Sq*SlCotRf1282zL3N`A+i{W- zWC>=^0nTxWdcFG&@85h5D8m2TwWTiMt90PNH`wm>pKR=i4d z)HrTiDvjw2a2Yy%aE-}})N63sm$p_L7yidJq%l{Cd2^uvNho=6dAtGu8D6d$T%5#J z;CFlijqijAOjJ_m80fBfVXg7TEC=OryIHYGhsVe3XAMPKk(l(L-|b1%(^o`J%DR60 ztM84eF3_&+aPj*?(snc@iI+8A-rmwNLVwH}Yx=mkzWU;3)2-PR6V9KgFyz02ZC(N~ z{LO-UXBLXG;ridF!x({vn7e*_dcn5wW+5ZbY5I`Q&n{mCk}r8T7f0g%R9L^1D7Sx| z^)XxR3efjCD```Ehf2E*FQo z)+gUUkKT^!mggl*WZ~ z0!(@i3n}ZGxFhF%E&<}(Gr**yPX?+`*+$)u&s_r=+3S;uQlCKQ_@eMX`!!eF4wYC| z<&_U}iy7fJmnN=2lvk`HY0=NRgA#R{&*+@e`TG2c>6W4QwLzYc;#pIHqHfdfBEYqn zT-vr8vmS58BF?uWf`f4q0R-cI*>lJq_L3Ivf2e^eiz@mCet{V6Y*p%7t! zluE5>UydvpEITKleB;5~06|0CWK=&f{gsreeVrq8EDQB~Y7rk6_GgdPdTm_Ul=!K^ z3!kAi$kOx4zekn5N`SzdDZ9ZeVO<2F(y0?3hG%4jH8E`be8^9@)iit7 zW31}r< zcU5NVf9|SW=~FX3YJUn&S-qf7N(O&jXwh zQ$PW^23G3>DSOo;@>n4O#T)vhv~;`Rc#ASijhQu&CFRMIUcOOm+bEZI$}06u(oreR zdlF_v>LB|n)tw=ub%%}!Q$OqWk-sZTMd_}9(nFAF6hL{WC2<}nnlz7pyYXZ6$_>-5 z2b$@Up5sVzDXv5D#+GehZ|m^=nXE~3v4A7FbG@_#mun+Yl*wt&^Ip$VzCAbk>So=W zzv0nmvvlet|K`0v*wh5Bt}8UbH=pgUO=$6)-q*dY_sW1zZ45Y7qgU+10+M}R&7P1^ zZMynH5~KSbU7oA7)Tsk}<^m8b9-FQA+g@W9xitI4_w{qYEzAMo>1R@UqtO$?W-+au zby+{7D~iA?P#Jv5dM>2qv_7zL_KOJK=?)pQik;kWb>Py~4R(mvDi;H(tI(yu8ZOcz+V0Wy%{NRMF zf1Ym}&M0ZzLl3a&_j1H83%d@lFS_^GWgXo0wDPAD9toOgDS10wWSd~*AY@!~e45?# zaM**dF~oT8=ca9*X$aLP?lylk-33GgXMtp2{u!69UZCN}eO(n05`fGu?5U7;kx{ND zU^@X}G#DW#C%fFHF5xw3S^dOs`s3wCXWgDjn3wc61kAS!^Vee%kf;+5cY(L5)s==y zhe+a8o*QPK=^kK5t&H8{5rG3Rs1MW*D7cwLB5O{@4-M1L`Ox_c0wQ*8ab zo}MPS>Dujr%AAiqFAXA5QUN|FJg33^otIQnxK8*A{l8x2y`%t(y~4M1P}1V-Cl(`g zdu*btk1T$alzi-vneT{5rz9MSHdjlsD7WyA6>xBmLRl=H7j!SLi4UCdpcS=PR~IGF;M)!EB6p3v^ehG(;{GDzV^p?X`-kJpqzTIP7hqF6 zE1)4)TBG4Dy9Iz9(Vwr!dPF6-W;rY(JpoC40eBEl&m(eP`hXxK`s+_s`p7Q(Z5W!3 zh4xB{LPb(8;E&tkykxz%>6sS?GpSs&hAjbIK#9(WCr(_9lz_-|zFvI;!ezpZ>%!wZ zS8C&)qCgP{kDZVh>SVvniJH5#=r3hfT7X27 z584|>14UYq)t4lv4tn(d{;qEWP=c&j!K5gloxcM9iZS3+?oDV2TC&j+VM!wR`0TL# zsmF(xzHYK-s)a{%4GXYTlto7@rI(7Xg>@;cgF0s|y zG`vziF3v>1Xn-!hGW+RSE<24q$1ewmqfOq<#kSQ55L)YzkcYXW`0$8_#p0>Muul*I zd1Xa=AHa{6A4rmi%c{YA_Dl-km{N+S_mDU?$J=K?ew_F6-2B5vn%|^U@DKk_{G8F>6kRAe`(nb!qH>O8C%e{sMI!9X)K^v{;xg+jq?DaG^@!rAH#e zelT59$W-U~iG=K97k-u=qciGQE!5$#9o3|jK9%In@Rj0LpCuc$uBmW1I7hO}1o~U`M9Ps$eXsTTltCb{<#o?{w>}M-) z5eJThOu2_jNo@&DQj=KYwoCDY!>_4l63DXdz8uie`Va+v*ti2w#_7J2jfSDC19pVB zh)URw1e??nXCYSS^aPb{*O_u`@{1rYDFcbAetO2mpy6FBbn{c;MQWwdr}Db-BUN7k z58QNTeO7YA&Gm_4_DK;;--YnnXljffp>3?bkuGRUf`kxo)%k}BxO}GqJ2UFJH`u{a zy6L9^Lc?A(l{?i=OPFcA(^7kfVo*7lR8A!+SqOT-Jz7y*jKQ z%f+~IO4fW&*;-n|tnYtZ2ZWq~Nsp}GU^~o22qZ8bDq$=F227K_G)j2zHK(zAN_`XTK^J^+q4eZ)Kc79&aHssq+*R>9!VM{0< z*t6+^e5NeU=IRo4I+~(VyMCUgWnbrCO$1OmMR|)!AOgF}<=JVoKZgGRiywZsCNo}yv6&_~IYS{JtQlNPQZ)stZG z_zQ@-W?L(*4p52qN7U))tQ4wyAW#ZKuui+OK-5eX^VY}XQ6R*{%aRKI8U*%P;;Ki4NV&)}glbgLe z8}p{mEDPJ<)6#BF{MpxA9C?`!REhC`c&^@&fXs)|95MpT z^>(+JI`6Nwwa*Vq?SSz06%d0|Ox51};ZXls44G>R%v-VPb3?luCwDSbLmP+;fg?57 zjCU7CUZTc;Hgtck7G2L9i{X^PCC;>3-T5b!1pBTK>Cp%73{s{Q1`{S(I7s-g6PXD7mT z%kh%LUjsf+j1}Oyj)B6!hfhWx-JUW8J5bns^mr%0INse{e$E+#+MoHD|AM+nlHC!w z&^W|E&!V@+o6_)=KrVGyqi9Nn;~}<|qafvs(A$=9ZB**93#A%L&!$=$>=dBg7Dmv} z**U?KQHPk1Xgj^VR|uH3#^4B=q1G9UvuVY$6dn!#ZTve%P>_Qj9(lg4FJG_2_op%N zrY@zQw#@_Lv2nV0kBEl7^8PBf^ZovncFG?h7@SKDEAAXV3!=dO8c=TFF;IdG-55Ye z(fk6nud^UK3@Vl6`}4Ew#)A_@8?ot zUdr8_rSb$^QyVE^mcxEb*9bUj)Ebo=Yu)ETELKGajy@nxZt7)# zxPp?GR)3d?SD{oD>|6%rXzACQ(TMme3iQ5GpwG^78*4Er-J}-4u;&Z{c6QW>-f$2d zwQ7Bf=CNa=sP!@*sq-2(89EE1*}g$wP&+27!g~4bcsJ$-I76eK_!hiO$4+g2_2mf# zbv6d1YJJg9{FPFQfj_C_?_4l!V=zlmvD8Kh9bpDg*My43|#;^>nF?YnyqK( z+-Z}Fu)Akdrx81`hJc1HGBnW~66i5Xl)1~VU{dxzOC^NfPA7v^NLbcmq(&Unk7_I! z--)lx2FmFg3n4REB07j8Ky!DS$us&B2q{(+esKF_H;xbm&u;=iuPW$aBQS$j5_=M7 zFnzw4heZAYdd5^^2#~Xhb3W&s0jKdu(fE=N+J#LN~3T%n+`uTdNnKhnEBKu|Qv5yZ_hT)`a+#EajRT zvnK2b_rbYi-FUNHKBKJ>PKJaEc@=?rx&DR#PpBwW)E?@E?F<7V+VJ)kN`re}o=>_d z?n@LS<48k5sXW$qs8WM}d^!&&73X?y-xf40A|&h_QTW=J{c zRJ(S!^XDL0mLO*<`LdMhGb4ErkV6jiHLV(Z1893BU9fv%P(2&R&N*TU1{RVhChZfQUf2mcd(+iGjB~fjU64Q1UZ*NBO@{ppiz% z@18B;Jx>+1lmdTpk)h#lBN{*udsCFrMvVib@rnb_fD0!Z-K9}r*`pSz#d-{}%+O$? zv{Z9?Cp!|OPa-sZA!s%cxOBD4dv3}vND}?`$IzVC@H*d#)5;hKntZtml&r~c*F**$ zQhnlUQoBI(Z;W znqQ3|)%OIHbb?(gLWH%k&zKR$Tn#6@~SP zdwxFhqda0p>JTnGoQrfSN)Y;(jAxa;t)-|eXJo_^duvt)ijUT=5~El7wNTwd%MZas z3^FvGN+xj&(ZkuGBQ#`4@&&-Zezk1KP`BCnh$fV6leRayH%ni1gdZZAeDu%=$&^lr zX6;ZDei6@x*WV7mb+3}YA6z!VjZxiLo)22n+JYpVJgyr<$EDd%78j)?FooRrJOqh$ z1P|+=iQ_;x^Z?WzuHOb^|8*cqLxV#T6e_2)?uySgV2k$89Jz0In9#26Dj0Pz?NMmb zyp~x}4j?S}%G4bJROWEcoZ#1uw%)c2Ya=WqBrbtmxAg`l1%q($k3#i9nm~#rMV^g@ zp6tNb8_CaIlo(Yi-!D=?Bgi99rZFOYg~vLQkWm;C4g4zC4C^>6o}GA=F4i!LOnwbP z5Ch=CJiMHC()7nP2yFb%+QxjG14BhI5dtsNZNC3j-)3Z?;C1M=WP%A9El3;zazp6W zp{tt22A*-L3_ia)^<(_2P*pNCQlS+ETZ3t9+Nd8oY!S)EX~ot6_dPr+OFMJ>Ydl#2 zwr3ai%Z1%l6omYTZ|`)b-wuLuJJ|My|M>6--WmB;2b_p)A9-q{2`b9xqz^%uAsm{S z5Z+#2B8N9iFFPCa5o$qUlw}JieEcF_ecNlO^6ENJ;s<=_@&QW~__RbS`w3v)&z=nF!od!R5x-grcyxm|wHp!+JHTJ> z=~*JwY5fA$=LfIgo{{U;0r5GE1Qi(PmcP_$3J;PsA_u^F@u&=nD1PEQO)43ifZ^HZ zr!$R03fseTm{%QS-FwwZG&L<7ZOdiJXc?M*h^9~tje_r@Ak%Qr9+Qz!_Ht0GNf~5!@>$+jXJK6*sB+Si30LQi z$``VdQohj}I1?~Lp9nkEk}HTmv;#HLV04d?5yfbjIF#}1sIv&*V8Ui_&)A#9MLIT` z9Es6e(5tQG%_|bF>P%4&-WeUvf5^*9aY^|B;f?qO`*4*Nng@~36{=&}Ol4xHp?`GF z^C4bQtW$}m2YKh{N8K{!&Bs*^-pNe7<8sR;(1l1{;^QOd#e@$_aty4Q8-a$h ze0)J>OSpZa$<_!frYEo-|C<}azD%$Fk)7X8Cd(ck8~WjJES4*3p6uZBQb-%|&Jikg z=bfEQIIasa#LK9Bo?HcOW^;vIPF_E60`1g@L#$S+E8#-5CBS(^A3=Aoilhr+Q_pl? zW3Jn@2#_v~y$+RxWJ8vmTNaggdFq|ZxRm4^eOuaSmzj*-3avk+u!wh*#7*XGGxv`pjT`&sjkK5qlQ+HHg-0U&blUHQk+aSW6J?Yz&r#@$j3 z?cs=WZmFivy@=^Qh|gnC9$WFB#t#$3($bXYvRg;n#9HtKI*NP)rscu ziNVxLmR79Jo}IBAtPSb%$?xq!>{tmq1U|XQDkXt((D_Txo^^~(J2#~WKF&EP%mSh2 zmTIDhKwBC_O7O`?>h4s>bAjNAE(>MReA?tex7N1POI7EX^M?`!TIya4& zg9bywgz69pRzAoYnIG&`CTc(-sJFfAx28t?p!XdGtnyHp%#!9tSfG;dzb61Vv&;5W z2m-Kw!le{y@stFoS)%Yi7iXruVm*kX;0-CcpRP-T97a1+mx)0<#+Y*gx1z}2=ZsIVv4AFtE-ZAjRTsvD!p z)H4L^j~J@)8iLX=p}|Bmr7$sp!-B7cOzo`nz3+Ebq{Rp@F1$--bo=#2BIW z^kxgo>4SBED_ejsfLJf6Z|{tc2D7CKdOIvX}as_I2Fv z_h_c+5d083od}08oF`t z43UNSxwfaYG${oAhaS0E?1YQ~Jtbh*->*uzR?GP@-Qg^7)B|J;na^4t?$4RMG=Q9m ze)924FU-M)tj|1J_xB-+z+f2Wx~RmDu743fE&}3*0>h!QBH&n9%;n!>CsUib2m&44)5uT*>$& zs-lX`<1Ii3(K$bk%MYZ{o0$dj3;BsWQYIj$l$KQJ2sxO{VoT1S_YVduBnAdcU&_^@ z%fg8GPnuB$tACHm7JoCSq8=1m2H1 z0||49#|r)@1}f~cLDJW`vdU)%rvt9Im(`S?puo-{Gz$o;)7`|6U1YHGM; zA@d5m%zH=G1YdR!SG$P=c;exkH@}aDEXV{eHE$IG9T4MPmCzkoph9zZ|JU@Y(RCR_SKa^byB`;t zL4&EEefDIgFx}4JnIVcvG~Ls&e{$aU8A#{$QT1T}FNs5KqSl*_+DC12 z`433y7y)3(9Zbvv#Q=|y*Wqd}Jq8k9?aZNIW*PF=MM8l^0Oj7X!Ru=P%`^V^RKa#J z(LD=wn~wDv2$tjbDd%9`Vf(+_N-*_j0FIjI+LLt|Trm%Ul;{PDAY!%+H)I2UVj6&; zOfgU-GHbl)O&MG1H@(8(4!4o11G+JGGqjMiKpl`$|H5}db#A!&c`t32W`=#PW_0_| zGX7nr()myTpqN#=UfuyKm%i5%tX)mcnb$xD31qD}o0nS|MSjyYXnX?#N{EnWhM5vO z4Yz*1*jD<&`gm!m>NY@;PwpVjPw0C}QMv#T>humk`X1ojN)iTl$2^t5W}g4$7yfrM z>wp)bVO1=B^UQzW{a^#(QOj5{`&YM85!8mz_>+x+OWbr={@*>BN_ys{7RY5GY}PwN9uWgbZ8IkNP6eD6$weIWVZR)uaL z?8G3vc;Q|I{yP=B>ZKXU%Mn=6wq5^Rik8aV0;(-K|7Y+@;a}jD+HcT08NgQ|Kt{^* z%RUfK;GC~>Xn{eae!klEVK=!6gozsMlJo^|%@fUX;J|LmlG>uCk;>GKxEzQa57Hco z9ON{|&sJ0(CVef;?&uFHCu|w2VTDad+o}Kg6jJx=PP;%KtM~@dqg$`1s_-MT>$Vp zGKawhyms2LDmxB>L$-r5ZXa?_Seag$HW-%tt};h|4epGn+tf+mVi!Dvzpz|jocUl7 z*ob#OWNu8RhvEZpuZYwslSa-wY>1SLnYjK|p!KUvO~I_44j*48FDN#Q_jp_bN>VO! zM|S@`hd&~hccghFPtgk=0n>C;?BtcK0;a=f;-ZQ>%OkPb3jRNO0q))2ll-0r;5#@| z$R#pf3S={#*-T(bX}|-BS7q~yV&mlBzn*g?MI?IpCiD`77lP4PHII5JWaR_kEZYjd z<3yMZrTjKq5y&McD2sl3n{*`-bq0Ex+y$)B>DHJ#0O%FgKa~iR;=08Wnv?&OuStih zH3UI482~(sEtu9FMubVzR6GPAe|(QI51y9I6?FK9IS7kOA~efDqq(yb9q=m^oVZ${ z2uXD|gb+&&9SJIQA1nIl7A2FzXEEh0-Hy-#Lry8ks}cr9tm(iBQE_+Uz3S^X8`f$+ zlaTsmaH@dBcD^SRh&1HQ&mG6xGNCyfQi2i=HobceelS*oip8w|98(%zAO}J5k++o~ z?*NGd&ZgdK27%(#AvHpSbaNsUi6&V#KUfkY|1wmJya*!#q!S~#tbXlNLHxhrm~k2U zmX>fRSHsOk1MmU6Q!C>k1*2pXE!{r0j&IoZ2vEF;J=J>?^c9U-<{34lwT!}^0F11| zn22eFI$f(9+li?8BFbK5y>K9-Y-B70iB4HvTJLykM-wEamm3S0W=LiTa&y+0;cjxl zerC&I^*I?iO}`!jSXeq4Y3rB>H>SB`_AEc(zQ{7h9RQzJL#*`iUjgV@xfbo6>lwZ< zBqMVG%QT&2IjVYU$K1SWe97+XYo}H7+G4S&V$-oc) zL;ywr)iN(A&45}YJHfAtL)be9`YPv^ zgg5)xJBikWWK-JSL+`BkWZi|x&=K;^Rh&g9xTXh0n9U@LU&JRN3Y8lwbex`z4gzza zwUfh3E(1ZlWGvj@eY`0Mq%$-FlF!1Ip$w7N$Ikr4OwbZk%mly1urmjgb~4n27+b@W z%XkD8XOz|zy`prW4@vjw|4*37FX=BVi`cKM%2tq))@~lQ6Qd)16^=1=?3!Iz#s?*n z8(~``hOZaaJTVm;-}XWF8hDBcJ85pj|8<6P*a(*FJip8#Ob|Y2jW{-z*|-4~NqSq2 z83tu*XQ)2JD58!L!7^2JTQ&UG6c1E{(D+j^#h+IA6mL%j%ppqQ7={5(Q6v_=_0Wjp zO6`Ez@!oHC++ThGt(18PQkC(y&h|3?vd&>h-Pe_XDCoaPR6f!t8g)^YXL0exUkJi@SPwL9x+v2--vJ=s+PxV%!UH>=Zb}+)Tnl}j; zK9v_%wqUd4TGIK~SC@A0PQOL88&JQR_P_fo2^+no9x0Kdq`gety3AuSoxg5ZDnyZH zjo2n#ne6*pPSi#q>7V0yKov^8@`GnKtMjQ+AR|55^ZYG_p{EWAaVPOLQs0RU{2-zR zCKHc@-%v2Ks!E%$g!xeE+`BH>_TRcTl*4`#^~>&{8jXQdtJkBvc1J&B!A;|*$oFFs z7-HpfSFiD=M}oJOTbx5DAFl!4Yn;tXKt)<~R~|hQWAhGMsFL1pLXyO!EYq+}O=gs$ z6`zZRoU7wjT`Tz`Q~}D|XmK0VaPq%mH2g$t3FJb@vo{gcfa72l)fa+{)3MaE-#~9d zpN0#gw3`3|jhp{+6l8NJZf1Fa1aX_R{qdPMi>|9jf-uoP3&_E1z;%eX=_MY22=gJ+ zkr~txIR^5q$k{#^EBEsY{=@#FRh#)Yt6hkBW2u!I2j;E45Egc{WYWYe>p?9wc-f;a zgDKs2kI$779|{bHzK<+zP2>{S1dh4rD1M;W^yEn8)!WM=hZX#eBPFfPMlYRn?qs_T zmnSFz#$Yp1UT0SD5pEk;1W8;ZnxwY7P@T638XarxZY@1OartpCs-j86 zXR>u*mY1&1UuAR1X)<9|>eaU9o%nrf4)#s^%gJSF4Zb1fj4H3;GuHzYm2I)-f)lvJ zXF#SX58NcNR`&{Wwmy=t*5Ups;vs=h%8%1S%ql?0>J&i@q){6y8Q|#r*TDbNOn-Yc z9D`9w@*KF{p%nH7q*1OvIWk8&J^JuaPYzJ1OXTa9HUtu?K-BX3Se&mjLbEG%5S6pG|9qD>6-rfX^%m(E@s1?qdxBvVb#{5)}j- z4VYrn#uuB(Ka|B);C=T3s^)A5k3E-BK1j~N2-K#|vbbf3zOCokVFuttD+GV2Io=s? zpguE5ljySz9Mb_A`}0&k{lujVp*bjlY`q0C!3;AI^AjN7cnNSTazVP?2)N_$OD+7% z&$Z(@9B=*R-?Jz-iN;0;QS+Qxq6}zkv@#hX^Z_WsX@~p1-jh4$Jz_>;!z1y>)iZ!A zePWnl$h6E-1`sVy-#v0Jq1I>208kFt6iFfQ^Wj%NQZsUF^yn8_Ujco|=)=*gdi2eY zLC7Ep$obTQh|WM7sUxRb%t*`_b~{vlMGQ;_#nH~2iwJXd0qB71-0HLe(&2yDo#kDl z4#pNly9qUJw%`19j(`clT_NLNJHO+AN*J+;&{*R6MIhYxLjVVk#Y=)v;25qJqO82y zdF+Cq6hsi5*;LI*27Q{0$TLoFn5g7*;W|dLCKy8M2ivDuk^+fcKV79{BJ0bg&fyek!*JQqWfqA00C5U*#C%8z z0B^2Leg6ooGZi-x5hHKqCh)`{;|l+6PRdj?apYlW5*rup8dc*=#0!4)luOqG_FSc0 zc&_@gGxPy;L7JNc!^cy(YDe3?!n007fKDHMM8ru(gkOVD%BdzfGJF;E?oP~ZJ3Y!p zOH7q<(q;F!ty)NjPxJ{i$IJ%>{{9GuFx7SGUb`FF*@=s)8u2wWK`|PmutL_9wn!KELG0#zp$yR2YFv=!`8Y%RxrfVA`A=su_Tw86?`hfWcz zvM<@?#jRg{S2+<)KICp#5iFVJ%8}oHlGOI~75u*IA3~E`Xz*0#HK>kAc4vLk7Y^zw zI^5{9yY5eWj5Ecctec)tJ=o?;||(tQirWaTF^UJ#eK-oVfc!T!-+q3|1*hw1UDw zW}1DRPhT~WmY{!T7jO>}nnaer$OGNaa_g4~fQKRF5VycbQOr~dyho3QoMK}E!m!fU znPpRlMHcA6_CWy;&^WSE;JcfKeG$zzAV6_A3)v%9pOWqYAe_)&VQ>FERKstCP7tS# z7dbu^&BFvS1vx?;7M}0XEj=kufRJRajr&LBdi1Xxsi=LS2lNUKkQ81*`%ZU$n6erL z;Yue)lR*>5aasY;t~NsOdU1JRM~n*|oN$$}DkD;lmieh4Z=EV(y6_tlOJT#)gG4`X zGBWb1(DJk7aq(sS2s&L5U+WazZ4#&RhNwf3uzb9yfaaJ(m8CRRh50BvnKY$&FIe)kP6@~*1Jxg>nKYV!HY0Be=erU`^ z?0am-90IE%N{wDbP=Q^4BNnzi$k7tq#p9{}vX-N`(GWZepeIT67yZ7yPqv@d2pYo_w|nE=bcm-E<1Elv=M#nji%GBAQngC`ibV=|9HjJ3tD~a&zPFW- zRystw89+ek?otE<24p}Qm7zPTU4Qyf6VNBV z^?lzkl22@UO~WKR9lFOXjAU9{`8kz(4(F76m7MwOQhTyJND0Wue2@{{L9xo*QQIaZ z{&9`R=SmmN%1HL6AOrHC4G)ejyRF0_$_wuA%1i^z2t_(4b$<2ttO2CXF@%Tlt)=ZE zD%jT7`dwYaUYD^j|IzK^*2}ghlnhAKFs)uly2^a45h2aDI}V?>85SK9=3`SO%IyJG za}UJ|zj$_Ww-W5d=c3MiPFcOcSLVz!yKdiL?X{FScKc9P6xLXxm^6QGegCh^ngnn} z0HvdiK_Jv1aywc9iVgJzKipUTxVF>6KbHeMbQcfs<@jJ-e`&kVH3B@SW`Ks#@mp+k z4Fquvb%rXtJWQ1`S{zAEBNfh>*IwLRs$|{V3mo(VSvC)}uj1L=YT4yX+>6J-q!In5XEIh9M6)F{j9$F^t1MtuMrkN zsr9n0*l_BuN7Gm6`Ech`?gHKn4aJ~(aNlP5Ll_TbGmpo$ApRP&CBw_f)3p3f=3`OHBxd+KTsby zPFt=)Op!mcR|v#8uavG*tdBr$eB`H8tYKGNCgjH)1@=^VBYbFX$a`0gpQ2qvPhwD_ zN`cFZfH@)=wCC@FcJ0~cvcx@mV48&ZhRn+|+!ohAuCvNPGnv17HG94Jy8NTl`R9dq zgwPrP^c75K88Wlx5r2+G)~8s>1FxDi2jwDNPd_CAqSpHF?|;j>yuy2`UHM`#q~h%z`;d25;E-h*_asslsz15jf`i5 z(^evAS`w^)D^}py?o@_zRRiprb>%la_M%XC1$`*@t1n(cORfYm@G0F+# zZ6a(MKpuO+IL1WqBe+69%L4`1Pl}+1;8bi@sHvHyuRbcl&?@0i3&&;U>&N8qcljfbV+vD1EgAE(WUu-Q@ z-amV2-N`uo?RZkHqK5r`ZiA29%n~rA(6>1)y<+!}XrRqss*puprt-yT(7VEo%$$m+ zzE-koc1;}nwoylRcLm-cCVXWY%ei&}KGi1Xwi3kq#bIBQRzv8n&$c{bW9g=|?r~?k z8?P>s+6NhW^t8%9rzmGA2&7hH3Z|J8!7bS+HOl<40uYUVQgU88?_3~(RZ+p;z{W+4 zFC+MB8wH&KP9GE1zasLy2e=3V*lXQFd`t3;*}ZwZLCLzQ0q?RL4>6#^^08~|DUgRl zT|IR6#w6-)*c{5oEVTfJqJdN58|#LJs@xf-GZ$Q@I5AJ};>)+OZ3A=dy{MH#%_qmY)(trwN=#$JgIE$4&qV55&24h5wQB z>c6UV!0uT1Y2ZQG^kmh*W|Q}Iw-UDHdp=j%z1&3Gz^m`h9u+?e73Gg=r*=(L0vNRK zp=g!1Ew2qn?OrpR{gQ(T7?~m@dqTGY8$ko5G1(JLIx%ngb|PhauzlMsx>;_LJS#6Y|rtNuFwxHIz~JFu4)k_ zB3}X693Q@H_1hJ>0#{U0##t-I{k+FYiTkdYPQwEUEyq!+=>?L9*KaPmtUr!AU&9$= zKm|x6Iy?8S@I)sjU?U~-S@(yWNvi!INmXqKyu3d`!mEe&Cxc*xx1Sk4R`Ris($#P4 z*JweaN!u-JDF-0XXo&e-1>&&)ug@8v1|qRBw)cY<^@VwN#IN`sE7#5$zGS+nnJ4i{ z_4vnH527~^&Q|I@fEx_xEWT;sGT};H_k_Ooy8RsO+IR*G*$gxK@yyRmfoQ9^lhu7B z74)vmqGx?E;K4eE5IOX*5UYUZP-X^5Y;_CF32$6`R74sfe{41y>$%dIaOjcGTyB6M zeR--l&%GT}#bIE6<_+yP@qcf?@U#=iho~W{(Z`RMjyc7zHAxe9Axug(woHJppncMO z2I>PiurwGLkUiGnEN7(5$2O{aKPZ~n^j^1Ya2K1n1>6PjH*_PET{M8LuwQ!YH|~F7 z-ccClR+4#P1I)-O3E{svU_`mt3t>69tU#ns?D}4=d%K@i@!h>1cUaADKWKp!!Zj?u z_t8aQj%RuNAv6rojPCTd03Cdhv`m6C;S^Rj8ofiVzIAf^4yhIG9VgIx%o~HH!2M2x z|It4{^)q6Seoy@w9r=^j#((FuG+SoWZf?vym{L39)D?7(9+% z0wQXcHy;4Mtvm2+zuT2-Ib}QecTCR5x-{(6tl)o5&A0${X@HPm@auxdhcwrovOw9_ zG&NGyfk5PNFS=v|I0}8SDAyD4qW`ON(tnG55w8EEzLKwhnXOi&^eTqF!n${4a2oA4 zi1Z!5U#baoWSlE8KpM)FvH0Z{x&8Qsy*@Y3cjyv9&{reI7h~IJD4p^(07+Dq1nG4*$-VF5F*5vTMjgCU;~*8X#&fcquqyt$ys)Zw zLigIMo&ssr8D%h?7xxb88Uhva)q88jV9F6Ly+^Y^TNdnr4-TIWqcApeve^&l#~$zt zC|)j{UY7h{vy3aQ+CqHLe+%YB16_pXXwc%))pq-O5uieKBp0JNFyV(W9t^Av6bReRT-N8w zFrv_suhi!$$j?*gEype^YH{m*VL95Sx!X1^i1yGApbKfBj9%MTb?5552KLtO3@aQ} zQ^ZqcRmNL5V&hd-$<VV$mOKs-MMsf7<={Z#?!^dqP93i7*Y4GFMA{9JOc!Lb=>{}`o&JphU0cZ zPMyc5SHwoiyK|UbaS-}o|4mLHYCkQ=R)fWB@<{&|AdqYf_2_Io`x=E=&Tx9V_vQA) zfXCgMegvw9{nT{HMqMF-@3!>H z^2uU~0KLN62*G~l`O?sMBD`H$v?O_sOA>60;oh?`Eo8WsnG@(*v?7{|{6kbOkPmt< zj)7m>=P{jMyX(Idu~GGg`F_XBksxaFMV}+I{El6LP8teSnY}W~5$aEP4cI15Klz*m ziVgvu;!ZO0qF14L+@l>*beQdDdpb_8>R;n9f&#p!x1hSv*!G!Ht&WYrU}8x?VPrF!H4Mmb}CM&1djLh3ouI@R{u|R3npGJXc)qmcyKC?gox=&nnFDJDYV3x9;4@ zklaJYs}5O-F-~_*-I@fIUFqJWjTR55#+b^iBYqe=ZOUzYz9nCe@6zJrWOmp7rf$Vu z+p*%KWr?)MxLeJ?{9SLYrv?fwNyoi!Rw!E0dQ?u}gILsdh2vJ3x^ua9KLAF6wS%Fa z8BhS~#1gRc_K^jk#?*jvw<*8`y3N@|D|lz}eqomVPoK~48a*5sWxUJSRJ~`tpv_=6 z#K%F9k`|EORL{r>*qh=`YXNLHZ;-#gOrog`_OF5WC9$DEapV-huxvn$N>vu9myL6LQPosP1Q|UteIf~4Gn>@hs9O^#O9k{p5OjAEiN6s4N?0EuXSev zmWG_VrX7EYaA@tSZJi^t4JeYUQF9SM)jt)e$qBTuX?1Swc)%1?+j_WXu=b%fr`)`L zSV+D--F$rEileWSMyXkIWZk{MSEV)`FY00AZ$94|sT8>+YhC!E*shCUzgiwQYO3bi z-RfEIDYSH*BTyG{w|sZhqV_8aB46hm@U5~Vz@^UId-`o6pRp}dAA)u$qrrp^0u8Vc zTg$N{u~)DHdkhtNZF)orKJthe=x=O&bJKg|*%sgNQ`$5oxsAP!{mJ~|TT();&9(Ji zm*hr85m(MHZ+oK={_mg|?7-A>C(Pewi@sC^s3A3A+uUhj&<|Su_ECU7_7NTTLR;V6 z4p0Kd^R=kVYVRd)G<;6j12sZ-0No7^=vdx^B5yd$odH&43@Y*S-SI=fK1EJECT%}| z=&X(Ag~e?Fo>nbLHcnj|k+so$e4`&+*BKEZun!|7R zkWYLgeZ{FefXeB|i4TSVB9CI*pINCk?Hj-6yVZy~mG{Hor`=uo>?R&dyF+d-iRS!RO=Nub1}>z!w{0bg3%?zMxlY;2%aYspHW4xA=RL2Nd3D zBMFoC?5y(TDviVIp=!{YD%gDg9kOAR74D8|%MwZ7DQY zRLEJZH=EXwy9}&^p&W^_?*P6c)4Z6Xvkn+bank8}#RgnF z3d3^yu(YbsBA@|Mh2Y=aOeX@EVjgF;g1roIN#(or?=9u;X2u)$`CCu40|db-CBUKPJFd)90L`umni5%h&t7F-{T^7t+kV(+3i?o;E6wSb+4Ijr;Ck0L3h7<;XPexa~z145(khRpNJ9mMU{!?`hAd9Yj z>4Ogj1zf;Kt+dl9MJBkjU(K&c~3 z%Nx5_hB7}@{aoG(_?)B^Y)+-v4LE%ao!EFk(q2oTPKr8mu&C7vh{pd~c6&{7Ex_^k zx&}}EKnfc1=%!HZeAuHY=H?CSZQ2$q7UV&<-RBR|$R@9bU;Wm{%TA@*J$9p``o>;~ zv+J3!PVK)lneE@m_%(Sl{$=K=xc9+1O#L0BW}m@_X<}iuFFe0}9I?ojpYkrLR_Sn` z7%7+u5?T^}8P_J$x1yydQh2)pY$pGNc;_-2fx|=}!G#0-jR;55a1^V2H@i-P{L#H* zutC}eFw+_U*Ut$4I=oC0v`E0d1pEsM7~!aSI}kBQ%L_4Km01FT$c_Eer?5-jar?lP zyeCR}bu(l7qRjdyPLdzz&MV&^FH0A5`8WgqN0>(jps-7v#MV8>6Qyp;dzCeer{v-;O>=6A5 z5J^oz2|27RS`Z~g#wClI*4m5@r`GSLs@9ao2X96F}$aW@M8*_69J5>eMh(j$Fo~u3dTpjIA zI)9CsW%JkLYi7?Ba3g0Nvs@Y;oiOnEBnFPkAOF-@!<@X@#aZZqE}N}QsbQngSxVKr;!h!^ZHMCzGT#k; zG?#pDx5o;7E+#)VS<)R+ALGttI>($L-093He4Q)JJIl}XWh9STYpSm6jVVgtD^wlb zf6<8Bb!uYTet3_Uk64;Del?Rp9i6hs`Y$t(!M{Yi@vV)}fuPoZvSOe&w;Ay(a|kt; z0+~vI1Rrxki7m;gL$Nc{C+Bch{RtOT)^%kz7HPMx*!G8JsRc$5)5Ibm1WcYuG8qxd zPw}Uy`zS8bVD;6^Bhi3>ZVOaD+5!NrR@a@H2O0>-V>}Ma!&z&hd@a{ZOm=EEtUwK> z?nTzS&h00AWGMW%+#7=CO&_$VIEfjqXrAN5*-Cy~1;`X>`pSFwoo^BaWd-h(8kH`K zy7cKPnIwq-!t514Ak~rijJ+$2-cpAJZ4e^Si94K`7LuuC>&k*ZANF0DGWk4kdrN?l z?_nwz9dGMiz)OoIMFZ~gFm~%{I=U*Lak#IWFGA3#Z52lwZ*?=|+LSU4#Mtw*G|1}h zZ#rbUMOKl;28!rb@_67kdI6P-ibSz>mnzR?ze$mZF$+;QD)nXJ6FYMa(-5*aaBftjqLw1HAihQ2Ri8?={VkUcT4!@Mh^gzzJ{e6P$ zx@Gg17W;34N^p?{wVpuWiC-t{DvU}R%BfUsD`&>*TA94M^Tx}15Y8C} zZ%|MgjD#o@*_NlHE%g4?NUfTSkT-+0OWR3;j7_BAoj!qE$VxBA{#u8P;lNq=Qpwm} zPN~dvt%f>gS7KMwG^nj%ZN=cbnNH0~cJ!JDSoL5S^v~^UA*o-p^5S7vO1$Us-W`(H zx*d73KkXi*six{P{4NSLnLJ%lI5lmbYy%{yS;o+d3O`-zzw8soxuUjMSt*x@PkR?K z!O{B0NZ6{gQbLl5QY1k-^G9*^w#bKUaS>hju(Rl02V|v_eP=k4_m~m1G0YhK98?dV zf;|+<04w*~%z4%GF62tcN<_qlO7sOzbyUQ&FC5CT$}-W!`Q#g^TPoay7zc8O+Nc&* z6+ElZq)(@Z(&^F`_Lp!X2Wdsa_vV6v);<)cmgdwrBry>domopCjf+|Gn`c$|oc%Q2 z^q*Cg_5|*>&5+;iyWUNUAJZ$z`fxI`C+IM!I_q8+mC}^x&|Vv`43rn$j{vzxJFb|O zq(@JLM}R()>`LYb$z{#vaz_F>Tz9+m11;U!q@K5hjs!BTO6GpiO+!)vRuzLfQn zmCuR^&gRv&5?riXVhM!H7$O&|Ex&!8oAsW_tCqQ4;JNAqyKrO8+kB>@w)tCiYO=nF zzvV5T;ep=W^N|qd|%3OUCclKJqGaEL<}`eRK1m z;&I?7D~Q>vcp1&7HHJxyPST!NQ7*8Y*nwP(vl{{4Hvg9jC=$hX(j+z9hbX4fFkRB7 z0jtWO`h@zuIT`6IboUzM9k`&OnPTTB1RE)ZEsd^X zBNh86?8LaJ;ZhZR*D^WL5QxY44E{bYIH+#A*{( zepQotSiwq$bfuA1sSmz|!fyp@BjeUbUTc6dJCJL|b{A-(@Gwo@QMGcsgP*1k3Zvw~2jU+%6QGd8mmb8IOWiJ>HP>qarF zl3P)*6$5+=tOwE6p{HFiz_-nb2&xu<@zdxQYZg1UIe74&&W(TF7Vmd9v_nM-RO zm8n{lrUo);RpkOng(F#$8d~L%Lb4piebyW&E&xJKY8Fo~31!3X2OLIN-NLP-!^P=A(|`!N zd2Z#iFQs}S7qm0?iXLMM>&{{ zEJ?|{7FNj#@$+IsjC6rQpHK9wvGOt z;exu7d_E-+ha?}rnow$i=3`q}H@&?7!#h@Pf+x2YO+P#aW?9O1ZNHaY6i!c4AuMk@ zm=q8upe@i6LD<&ao2i(Td`RiNWz!bL{KJ6`t|f?<^Hukrl}j`a*ut_Nl8n=@hT+Y|(*=;FqP`fH7#0iT#@{o1zL^4{ z$Z8@s!(&$gsWDSDqVGn>O}DIg1218ysO7<@6yMhIcwBLzG8#3dz)%Qz92+0PI8 zpb_MS8PEBJan>wMt=5*2yhTOYV?_@N9vBN2J##1HKI;n1L7jOs4Tk3M7Zb!@gDV<) zcZggzL+CZu+(V(plnzh6A{`Qa(kB~w^||x~#P&nMroB1J{LzSwSG5B9N$v?;XIIKB zRR6`F1Vgj<)d`$k31lk4mGVUl@4n~BJF=aDZvmyYE*pZ}JG)jTsO9>EbV;_jMhGN{%Bw4Yv zA?f*{eZZeqp8OS@A6*UO!xIUn4z@8|A{Ko~zWjpc3K#UnmW_@(*X7Sou4|(agqO0F z=|HA5gpgv?`kHcBfu099xU0LMCAIRj6AHV=6qm?B*8Jc_?cv0iyHl_fNMjA@%L{0P zFfYM}$VF{MOQUkEl|;Y3fLA;DA)Ht1?Cp``f&&94bdZa^Bh6;%kYBE(rL<^7r6$2> z!lL#$J)?r(Cvjs>%Kvi`C)E8k0X+3AHZe`)L6=C`9ZqN`JCmXe7?z4gh~L991@au1 z>v`~EEcj@DOFV~0%rIz!qlE{tN9S*Nn1RFqu94U8qqT_cRTanRrx0T8PZPQLi|J}( zii`@HCWt=LG;UG*S#5x`j(d+oZi(($?oZJjWOKWzl3>Z;gFX>q{eL|=rU~}8^jS?% zd^v;gz`~o%2yUlB-F-lB6|jyf7BphHpFXzE@pbj z4PUdG7AW6}K@JLkN%`uGAL!Q7hNEK&q;0{_?)M{CBOkTCefuRR#i;*CBFu_y^NsQo zC*F@%C5?lalorA0ZMn(IpEYQ2P}k!xJz9>fFOT1l{g^rI-Mqt+#Ch%unoNb-9QBlZAYQK2o_aoO)mtX)w8i-dR8JiywK|M z0jg{I2#9c>_b>le6*?dFSRSZ*x=wcH+Yr>-13u11%ExZnmka&_V7OPskk?^QBt->o z9bDO4KjYx*9E>*K;=)ZKl7&njtEV&%s~iSKF_fvG1q27zq0wLpq?pI`?7bAfX|t?I zg7Hn}>04s&lpJ!F?Jrd_aPa5AIt|KQC~&N{r;?h~H~L}dY?~69&qtXEn3mWN{GVPR zKfrt?f*b23M9y56$Ypk~9*r~qmI65qT89Qq-=HJbLh9+|^ zo!J(ij0;Y=xjUC$O9||sF@*;Zm#AN`DA9XDjcu#yE+nG+m+d5NAr}Cb?pk8VKK~ND z%|%u!4T${x%D!oO&Gl*X%?j(`NQ;AgO=S4UOg$|Z)L*4I7TZxpyjV8!ofq2Q)pC=- zz~*6joZI-&niV5a>#7ieYa;hHMaNt}kj4W!cPj>5qYeqb)IC%;3*X#d)JE<5?fbg- z`UyT1+7$s|-<^&AV`~azzG|ZiH-o~^L0zQPd%57!`fLH$%5O=*Rro3Drs@Jv5zSjqPmckfDJQ6E$O{{sD-jSM2U z=&>h+)H<&RLbVB~aJW*}ir)_u^)I%;YbxKZs>mZ_&YnI?4Z2Vq^c|=8!Joc!ojrZ; zv!^eo;2sfS-(*TB1j$t&tF0*1u)~}lXPn-cj=qBmuCL?I+Jw855>=4t*4ujAnyJUP zOXWkXq99Hr+CGuXr_yM|&qSqh3QeT#V_k{gQw3W^5KBlcSKswvhd~Z(-apo`Jx_xb z?w_qDz0WCh?|q|u2`2QNT|*VGR+JX^K4*3aRY8<;6Tvk5>KB_AFs(ZL*bVg$Ig)g@ znKDaponBt|8{~w(5VK%Z{ZB0H4qW|SCG~@05HYqPP=nUh$QyC0jVaopoNY=xZf<5z zZhS}HGIANF5)3AO7hpzCN~NYH9Wb<$-3>M;86A6Fa>L)oz^(KC=la{eFA5r7r?fui z8}r}4*nN}B83&hAujjWtUbpHj0t;T!{s$^&_K2N3T(4$NiVt3HUX0!6pDl7G(XO)` zF?=G-A=;|pZufqZ1C2O!pqyAK1MWcqs`+1CB^Q(vDxL*m9iyoJvbOW3rL%$QoH;-PXv(dT8}tv% z^xPw-X{m=_Uj4G7CD}SzO9L>9&ZtSBVB_iqd zCan#xmA+qG92&D5H`&J{!t|20VncO ziR2C<7E?v2B2yqB5AUCke&CYbwu1hWk9;e}Z04WrkondrlM0)z z7|%_#r_iGrl9=1bDm>7hSQ{KWReK6MKf}{#a4syD{GTRs@yM|)q7j~Y-2Dho+iAqCGLu$t%9Ke6P`Qv#5+m&h61%TCS+X1&>1ON#whQJU4*KgObRA@wvX7qH6rpKp~&od9tRhPjs+PAF4OSJipn6f#XT0$xPZ zCkLj^qU6NC-ND&du-1LO*)s}+3Y-9Ce}wx3AZOB)>c3Q1zYa7?t$=KPMk^>&07)(> zP|K1&3$op)T-h&KcnugjG}D^N_{lWgUv)zg0gz73O)ph(z#pk(Yu`6-Geu(A}{ z)SvCU%z%Y?HUK(Y`)#(_3?Pp)6Gi*|kcu14E*~TOHe2iEKxrQe++|^a3wMBK^E+#y z@b>_BgoCHh$}-j{4RUREB(wsR$s8KAW-DY8GnOC^n`REa+Ul5W)dCf6g~p4eHgQ2k z^NcJfw2{=4!Ms<_T+SJlBAV}fJDo}&0*G!;J2$PqB8zsapY>+2GfrZwdao-ZMd7{> z!*lruR#$ku#0b(1wjMBF?glcC8vw5uzGq7=4mK`|3x}Da;ORj3We(ct4q&xuMwgcP zXS&a?7n^w74!LdsJqjn>3#7t$Q$Vg}^BaRW{F&G4yPm#hhXd2&p5n z#c1 zwYM+daI>0v?ygV02jz^m09(tbbD8@n)Vwa5M&^cbs-FV;FDwPB+=|3A3+b~z09f6u z%Qrrc<-sZd9Zmzq8;6tQeVSq?N}Q9wTysEHJ` z0-==NP=5d+V!@TNG>E|0;+4E8=4x~ADXzkht3n69ck%&H%BmCt&d(MNQc=%7CKR4WRz(GsOh9#wkuU?$(||HZEU& zLv-E)h~hMGc^ECiv#MF$)I|3N659@^zmO*sqJ)lHT3Na0eyXgMr8d}$T#a%LnLu3HlXU;-F3mcGCcCDHvy^j!3?>4A*Qi_K3(gkYraguft-`9+!ewUc!}{i-;qN2YVxULKmI7a3t7$!9e~l3ztH`wpj0ydnM> zn0-KhN7o2MtuTVT6zqh=)i;v_V=$q8K(I2Yu~Rwikx5zV`A%b+VaNG) zfIe0OH7eAhe;rRdRy2~*EfP;rU$Y-{b?S&+Cv6*^0NNeHTKffaCb8N@WKWJ1+S24x~Z`sirx$38d8R@kQ~n2}Qx;TmM8&nUnDt zR0hu2^|7XsQ}r^5N&{UU?k*fY*EeT$Z( zQz#u~g`wWQm>1ry9N%#Ym{V@)a?$Jyq5EJYZst>16L`Z9m0wrZJq{hbQ)L|iQ1`b* zKA&EPe49(BBfNHmfM@&7>9pOjX5rsD_FYiXY%^qQTp;QF3Fet8EZ-g&{WCk#zKjhM zJx(On6mgPD{(4vrO#L%kC0*L{7>VA;j4Oc|Bkfh>>f#S0@OiC6@!NQyCXzK=Vfe0~ z(AAs?E@d4@6cxyn9fw_%?jxe^#=%eT8Ry6+BJuAg zjq?;0=YgO1BS z)xIFPxaQ;hXkQ1J|E8F=A_d0RXQi+Ad*zM&CYQlc>Z1`=JJ<-YW{W!)(%H$R^aB*{ zH>q)wScjh@F;gXRFmV+mKvoyo=@2l`34EuZwr#HDRYz7Xg_2UE^z~S#F7*+9tn7M` zDk%ArF7LG&bkbcZ!?vUHad?fg$qgRAX8l*V8)%w^@5FGa?XZoj0-ecfa22gVm=+hb z52oZ<84&5D+|rb`PIuHhB`9p2oOr;B8!n$hqtu?HITL659fhdu>4v~(+07y)$k+lN z%21?pd~XJBWL6w<)gxk%9=XzTq=0m7lil2ar{~_dTID3WXZv9ELgBYmI7z(S;KdK{l7J8Ei(T11CPCCJ(^(zV2*? zn*vE6<*#()f)c|OsT2NM;{+3SKM9_T%X?wb6K^tKe+t)8Qgt=fm_VAF+Y`4JXwWB} zyvE6blhCHxmbfNdh$y2{#jUX9)&a`B99kxs?&J7(9jy9M?8Bvs)2}P_7k&( z!VUX~eSUjYGRn|$rcAaJovp?GUav|n!kDmN#rULvU^rXX1-E%L&5>&dK3&-ejBOzF z+U=xESI7rDifkvz72qus6z?Uf6(qaDnt;;6=c7bhD;eUb-IP3)S;qE@8BY3rl(f5q zmk-#AEYEXH?<^8W;as!$(7_;Wi&JG3QT18=<6c;|q9GS>_S{pGpmL2NrsJuoOX&@E zooO3CQ~dx%_0hZCS3C$~LS0|DqDVc^81_g)20(R8m8KrfV)KaDOYP~K6rP|YvFWmU zZHq}N2we}QsCczZrhP?yKnbO(f@fmAOgzOuXvRs5nI{(&eo5Aw6j=F~SSyB<^JNS@ zg)Dkmij<__;6#@1Ngx2B`!#JTq87DjM?K^IdHm6ey?8&W=&hAvgcX6e5L6a;hn<|? zJbFD&Q%!6l=@+5KOLDW?oL8W~F;ZG;4u?WX{hUKW<->wje;V53opd$Hhz)6h!S{mj z;c;I%GBZ?KtW=BeFtOAa*U-mTQc@>mkc^3- zr(o60S~$vpufJbP`YJuGc84`5$;V=TL#B4XRCM*p4QpHHWUEkFl@iuM%N%E`z|L!1 zPDYiSK*U!wbGv6~+Y>_qWa4CRg?^*j1=xp@RIWHD6bGmD!GA2kS;;4Qk|L!7aI%3T zJJmcL?rcvPq(s>!n|6$QpegzNq@)a41xILNs<5}6vswZ4d}lIAa5uO zSkpm5hvP36V_kTSlK=e{MDR>Vi_cc7ckxnc4ZH=;Doy{19#ElM@#|jL5RqarMsv!*d zeX0h+2n_%N$v}XMAKMoww=^7KVv28>6F)%~Fnng*NNh}DMksr1c}&vES;5K?FHLI{ z3e>k^(pEmkXR^2?4o5;L#mf=b4gk}CUv^m<*2|0Jk5nhmqpfYHpv`KkaMnsr;k2Lz z45o&MmmLggixciC4QOedGBiZlf~AR>ZyU4&Ui4NduqH9n$9tNA_cZ8_I0pkS5WhI| ziH(bQO~TsG{Hqhd5LcAPe1hu0SBOnm`x(@+G6U!GCk{4D^(zyg=2m{jbZ|w&>c>!T zpgQo?V}{4J_A|DFAP@Ixlr0*Gu!M^*10Ag8(_kSRA;6T0qmU?TfGtauC6MiDQ?vyV zeBuPZ+M6QKa8)Z9&;;BP_F$2MG-H${8n@Iq!FtAi#t9y(6)Yw+473H+2-Sg^3AnYS z9cT%f5NsZxL9J{M2AJuBQmo;Iwm>P_!Oy~NuNUD&-38B$Q<5uI{CA5SZUodV4tgkSv?ZeI`To`ai8mgAH$wjTz?R z<~jpZ=C3+B-?;|Hm_MMvO4k6`^W}SgS!S5JpCjU-O*?SynCIX{i+&>_53& zcy|MD_aGMw=aB%>a6_NgbDFHIQAW0gXspL{+G<_b$qm4l`FEo=e0#cmG@tk_XSw{ssF8uycS}fMe9tmIgM^53uK0CrK5KG-g$>MZ?fgL=&*E z;7Id#ZaMHwwhbB%)o%s2Mu78fOk@F+r2Nb0bs@WAtAgQP@D3iRY9(m{G3tNYJ_1Ao zVcW(*jT3|>U`)>no5_i%3s?DOI4|zU@y&R?_syhM9E$$3U*-C_m0TIK!OM@|IWBwX zIKO)Z2Jjl_hk*bOzr7|GmnyHSgS57ZwWSSGN=u9N`$IkaQvbuG1@27n$5tTU9q4m$ z{65l_O z_6Q3k3}YBT;D|N;9gSbujlaQB;K#pgfSUp?yyLK9XS*Emw^H+S@&3K>BkL|+yc$}i z{MXLHpB<#S#>oHQh;{l8P+fm^Fl*$0-$)DM))-z};7>tTK$V-14Nr2d1S>c_{W}jz z*O$ikWE|c!{9!6a9%f-+1UqGg__rLF{vB{zqZv7QxbYxb0XR-P2v@=F?__@02isrB z`>9(Q{)E~8uGP4}p*+Vgf4EnlHt^xx(G`%x2PkyQe2OC z|C5ewlx+=E!QX#LaQok(6j^cZ)v{7tW@A>M4G+K{Tq%AZVtHt%s0)LX_zvXtWy|nUo-qG=Qz!d*-2o9#49P8@EAMgd=^|AUF z9wq*ZUhw@}fcBe5iR&L=|1(Y#1%C%egp0YwUNNMByH6F`!mfDB>~D*(<-}=tybi#> z6SBfq3F0qQUAf=HhZp=f`^{yOy2>qDW2}C4E{qW19%0-~pMWBOF46uW=4b7NUz~U{ zf5imh=VImjaN&UYBh3%aD_S)_Shp2iLuk1wCJ}$}j3M^+W$02GYZC)zh>irm;(r_k zlLS6O)DWvN%mRS~D2B31!;yAy=sM;V&vB$J^w<(&cECl|%Vq7rW%w{q2e_>nhPDNy z5Vo3M&1VR1p#GIC_PD!|p>}vcM=^~c!9~GvYu4q<7T0zHe9wR>%o0~6q!~bA=uJmf z+&wu^{^jdu@y`(8dSd9C1)zr{i=wFU z?^^wCrT8=K_fHM`{VC2rRh<77rTF`}!=F*qD{nenH@4%C>eGL26g59E(%;$XH9qD4 zxBJA`TH*P+h1B5>y$G#VU zD+LJ{9JBqduhaeYCf6LkeAguQI`#jdNp5ha3cfRU`Ad9%TQa!Zn6J+LPdUH)hTV0S zBmeI>i1`UhEHIY;g}%Oc4Ji5X)Wk9<0qP*^{dJ(^$44yvT9ZEzC^5kOl`o~oF9GPw zy%oUOISA)i@dXr^r$z`EIMV|`5SRzJ0>C7L3lO1{mO?dY0s>kB8HrPBm-Gh1wq#JB ztGmJf=+stX`tf$Bl64B$N&CLDNT8ONfbW=%bb1LM-jq?oZ}9{=koD z>*4tUVHlf!fit``c=5fqmdvoQO=zd)V5emq97yeBrRzAaIi~LGN9p+{t&O*^HkKVnucpCj>%@23@EKaSdSnTN5 zjJ;*D!PH;FuJ0kaFn*Q2vBjjlQFRNV(ZkA`Yi%n=(vdNmdS6qvG<%7P@~ZGZ7!YG& zdiCzzn$FVt9p1ohAb3lHe;cyM`iA0-&czBTS&|Fh9uzM z*WzAeprrz*ioUtjVBBOM5I#OccdKhO;&#^2$M>rpN%|uWpM7v;m7Hh^+|%~-G6%-$ z=1j;HT05wgM_!C#b?q6g zM}-F;8fn;B&?quL$Dz~RC)#*db;?$BZbV5j?Fo5!Tepy%`P}$o&tTiPMMU%s;Q?3v z3p1OL-EDknrv_N+s`ZYHIKR?s@0#L!3lmKZc1_iTA%(hAH3kQW_nco6#19@nv$Q7& zsota!rL>#1UFv0Ra;M!HRqC!8_-toX*~OwUlZvGZ`as+FZj0@Tt%}o~a!$^v!pQI% zyNs7hQ&j0Gg;`7*A}@;Frk}80ddF_je2yZMIKmC#%U*D^+^P5jpJ^L>aFk&(qiI)r zt8uFtE2XIQ?R>X)ZIi<}OBpdVRLxwaCk;kyENb(WbUFl0o=PrVG_o7d%NXw_cFQJ? z@J$!=ZI9=0i8``oTKdHz6ErPdk7p63olV}S8%S$u+Z!riFjF3ol64W~YN|RpWVol; z1@UcX+naMs)jLfz3v-OoQKRV;?HN6MBBeKwsksvexRT24Dt#TJD&%&YcvnEp-yW{j z!=dvvZ6mUIgr)OC(0$jbP#xqA)dz3ZCFp83%9TCZuyH>)v8szq)4 z`|eU_UhBks*PsH3O&(M8vpMQNv@7ovHt z@6&pwbXyLskprzCiMxbBjw(g8XIDz$g9}PY){*5duk4If6=Ccn=s?zmqTqV-7qx6L zv#%4DhOQ0Hx#g6_XzZF9jb~fX@&7o*_WX5w;WV4he0ax$euqh?n=RA6BAJInQjBYo zb8nGoUVNBU!73M7;Py^8IlPBoxL)${)mt;STt3(u*!hUNb<$PPbd1yJ41V}LShrAt znz8X{;Z({uvpMwqy2A9}cFNR_nXDI*b4@&D?@g3g(tTMkRuHo}F4V=PP8OxjHQ(74 zY7kL4o|XS>sE@C0wygQKPKpI;SN=7l4TS zt6OkklljtNH+01@H{nm_LF&R4eJ7ee(j+4t?WV4mD?N>&q3kE>jm!+p=;POV7k(b* zrZe_Zb;#}QdCUAQyLx3b7RRotOjczsW%(vMRo-n*60e}ExL882c#DrZ-hS3w!>Z72 zYF=k3-)lI7;bxeTzV}RPxTa%7_|{B`X`NVIb8ck81L5>`RP*BbPR%M4s+8d+rjRr@ z(IB#R1s3*iwIjMsDoc*dX9jJPSr@)&ojGVXyXP|bnzfig=O@j={}?vl0FTjsyMV4u z*s|C}-0Eucp?7xX4dqD73*Mlon--s^xGZ#CazgsISuB2=v}W(r9`BAu_Fp%#_x~7d z-&Cn5I?m45;L##jm0%J<8R_sb5*>+f9v~>Dh{fAG$^D*(o!ItA!60T$&Je>UO%R zHFCH{J7rNd$23D??3D(MS^ncblM2gAkGvN79$UICcIr^M71SOnOmD9tv9fsXQcS`( z+0@YXt=`+fVg7A2)3yZ^UD=UI(T+gJUbMa9*VE};dLlHb??bPzR*HUm+UE*0N3cBR;j>OHp;DH8;caz?Rc4E;+|n6tMi{#o@9(Wnn{Xbb zNGU|gk%*QfJ&&G$G*#m!Izm_B$uSjt(Qi*ND%?@QcY4R{Vy#(WTClx#hjBBkE&S7S zmwIo$vB|d$ImDseZx`8PV~jkvet0KZPxW zVMO#mYG@68q}CeqQch&xW|E__f6Kk%1@ztOm=1wXE}i}=Zlq^>{&`zZ#(_6F=?k-O zhm-0@bgl9hMzS@U{b-WRY$DRG9{<+2v7*VVCDd}EJFJPk@bfbdy1AI_!6p))3_;g# z+#weecr85j@{zSJ6^(L*`^~oNIbK%CD=i9gm6;dGEKacq1lHg{wpQLvuj;2AA`3z~ z{izSxSkJTfS2M+ks;j%p2K;3_PUcv3cVUds$5Zh@5T2nvgV!z`Qj1cr`F(N zxAYm%ka{}V`}E;FM1@>_`vFJ8K?-LE%FQO?c$qG{T=S&T^nq6`dledA>O8#~Au02E zlK+s|T6bf^R(ht&vn<(8+wJalpAntDlJC;(EgVav(#veCOup6+qxIY39%vxw?L!+_ z6Db?EMh>)on>Q3$A5?p?XKguI1%lM`X5XqDz8le1F<;G{BAf1=ITCO4!k(Njf9vgy zYfIBAqX~r#9CCsx*d6uzXx4glVzhe*Z*|FY)NEb*ghj3ZGI28=1yR69;klA~DZR19#2~tCejSsr7)@20l zT%=+wLGQH3_pNQ5>oD+giMYMjy3b<4TWJH!j96#4t!+Gbw-}4bmEbdL%z;>>Jkjnl zcpACO8bwOrE)S~Weks4!E233q1Ty5(@+vqDfD}jS9@OF5s^>7rse{U)GUGA3fp1z& z1ytZ=Ks>tv(-)u15@T@wFiWMA6LIByOJX68Vl+CarXkYu=`DDH1e1YIdWIBZIvGjj ze#ZaMUE(6^uWl0DiyLtx5>p2q*7AQ$g^|0dOyoUjOQt~5asg6&Em}ZoCXx^AFtuDD zBMg(U=1;wCS{#fYyL1ScA&XbFyD(MyOIm9}k8_qEdx6KSX|>xik5eVB)u6{s_{U!X zLI*>bConqz!|hYM<*kU0n$eWdZvA4w~RwcaWm-t2=rct*jfbB z*-I4f3W}_iE0QU9WzLxBZkAi#wYXP;d=FJru=^V!(r1Q^6O%7&G+Op33HOtzeX|ez ztbSY5N6zOPu3Hlfmo<}9h7Jfq6|)mn?|ygc?d~!?c?kwQqKjxRYx->PR>c%}!J^gi^6qQP;0pjF_`DZWY`2FPIh`PD>Y?$4Hh$4mdlAPsk0W9VNx~h> zpt-Zi@3Om9`n9g1KE(|+ z_cbXsK;lZz`AdE$Y(89%^{R_~W*}%f_=Rf3yN-yQ-V5i&N45$>1*FLui0k$t|5W?Q1RVRo#PH_*kq-ezV)eUWq z?M;m;o-I=AC{S(gF)YsBSDXZ3;ABgA>?#&)YMx+}XSGjrS^j^8pNXF!w#!)_a%F)vi$#GL^HRE;_3ciceUNS{4hPjiG2OgVgF(Dcss;J_q z0ObQMrq7If-oWNs`Waxix}J_RZ^0^- zxSaqpw#awV5M(YL`T8||s*z^N`AYt`QS*ldE}@S;758|(K6BgURwsu`pp~9=Ro7r8 z726KgP%f-IHf&Y91%`9F*oYZQsHU?T5$A7l3xAd!I~yC}^nPJ3CTNK=_uZy${gQiO z#$uvQ1Ftns3+uNsdrmYx5mB8OV{56!>>AK+5(sD#(Vm9rm*wzHptxxaUu53Px zMMMlEus)eHy(fW!a33R8_MB>)YD1{Li4TOZTu8U z@H{0}!8$PgCE9!k#-+Ju0pUj{&5DUaI>N_a^8e17utUmU!NnQB&*6I)z|axL_1}S030vMI08^$)9sSo z7+u6@^(m97QNdonJFmH9;+_JFFO8_k*!~2&(+Rcu{(>b>A&o~0WE%a!aZUv6paFC5`bB)>wpvBOuDuio3dTy0TcqSqrfKLoym=gAnXAl5ay(E;;1f}c z)oT)q!N%0dmX5{x;pg8JoyOiV4P`dyrHi=CHX_r@$>Z}Qm1zS-=LWf*zLoPE*12o? zTnok8D&-4cEfG@QOvbDw$lix*7IbCbRm z>BJBZQ|>-3koK6Cnm>h zlv=UV@Y+c*D}cw+DjTVtx=>s5${H!@0@C0c&@|vKJ3Qx)<8QP z)bxmH1M@o{aY2x-gRDJMps&cRsnd9s)KGsyie=wc+48`xiv#*gW}l0_)5GlBo7i%` zkj-^^v7hgs>{y)Sr(kF|3Q$1yRz6f@k-K{=-?8qTMyCGKY=h$aaJR*o;U3YYnF@>R z`_!NMACVH;(2}6Rp4Szq<5>Na!Op(Lfz*V1&z<~{5tmEHY;l%mNi+u2NywZ!{uXMg_mV8CS+EUJgV7|cfq2_TKfAqY@? zGBq)7bSS&SG8G%Ypt%_PF5J~?k#hyos{eD8jk%VbjqMHj|ZL(JlzoFAVnrbMATbIwtro{w%;-Gn1NbWU(FfMhGW41~JxI@~9`%OQyln178KlGpMuD40Qui$(Ikz;c(O~=B z$E&+SO-X$2scXN>N6M*mFa({io@_yhP9=Qy6>S%p?L9JOlKO_6n^=uX2bG_>r_`oG zINKt@KxqmPz9l1lcID_DnZGfDmTq)|+Ph4W)_8gFBQ z^voLc-CSCP?3xj=%8ATGSoO^;1cY0~DzOCVfW4Pr+@x_eJ<{${I`QQR1yHrryw(}{ z6Q8_>I}jfI8AaB#SL&u_jDwkP^@P|qQCE8x`d-oRt1M&7OO(qL0V+XPJEQuJuR!Nk zr-?d^q3#e=R*jNqo22o?`KQ%U4ELpcsfVs5x+dR65}Qay*f+wqPzVOTIM<&Zex%;! z6-7jIR57o@_P30=c3a2~0hHF$jIrPE8 zWjmYL*!Nvu4g+)*&7JuvCpT{~gtX)Al5qA_(H3wYj0L7vl-!0_wpvspes z`gQrti6NTxAy2pLq8^mU{%}t~-_VZp8cLWZv#Ok(nI8_5-B@{ zqZ5_b`%uIR!YPLi%{C3B6b`pbCz<$MHGFlAm~HlC8B4n6(&7RW;_T^j6-NgcbX7#k z(JwbqG(LsBZcQ(pWl=M9GNZN3_YN~pJ|esH$~xu{R%o=l08mfH)i7c{s@)L-+cU0b zV6QZD!>q_!B~eKff`SmyS3bZfmy+5u?LX zQzyCEH)!JxF|FT0(4Lv;ll_?GBoP_5KXA9AiS^&%||t2}gh&gPdQ7J=|hYPu^`MdZl`_de5eTA|3>d|EJ>cO;;`rQbZ%)_e8g* zJlJ$G!GC;&zvOYSStP5#+_u!Y7&TN8noIN(5<3pDI>;Iut6K~$@daUUxWmx#_5Q{Ox>qT#<1^cRagjwGW-t4~^weZyBmi zcs(Y?a+5(4=b4GwLV`4uI|I=tFDTs~8O$UFNi|(wtWmg=Jd|W}`1rN}J*u>0bY-wp zDm!r+_%HzWwgUkVF&bF%#ZApc~}{r0gv7DzVl) zdl$b2xe10Plx0K@4eL26*l2~EAnzG!PTCyAlP|==QS|G1rFDP^cy@ny*^-$@^2)@~ zHaqJ|$C1F-ccO3$E>Z}z- zVF2pmI6{5%CP?qN5R#Qg+;)oY;?9Rd`NmzY;ZHB%(B>t5q8B<1cE3W#oThn(o}w1f zOODlSkr#lVH*s%mg9NR-iBNJcD1Ph`xgnTSXcZYr0fLRnTpQ9^%9Ml@dQMl`&{J@a zw*#Mp;S$yc+{znp6$OHdGI>s1g?v;$5qFLb#BjYAy}<$hR&Tb#o7mdKBKLre_9B+n zdO{{m_u%s_P)mIGBWv8SEWk{e8=7r~s4!Z6!iy3%dsA;qe`qMk*ohI(?l?&2)v=Ub z!mRO7vAtD?&afQ0v)$4v7q`t|hP1E@iBvY^+uaQl+h5fx19FrNZy2Y+p&kiPZ_skH z9r9u0PP!{b<=g{e(P(GbPm3GPZZMkb92>(hVsKZF%25GTjFMt|NkFvftXxck&^t=Y zz`r0R#_7a z?$v`EJ~(n?m9p*vn7!bve(}2w^&l1|D;+Dokh+c5*<0@Z#X;8MUm0u@x=Auu@b7CR%azGwJ{emWJHl0LhA(9n|VzAyt8M?%_;e(0QBsL_ufWw}H)*M&q*`(B=smv=u0L zh4R)g1`6DuU|3Q`Bd*{KXr!ksq8{@xawCB5Fc!MFeV2oOtS+YGy|M(Ut@}tf;YA<~ zIB`LSr)pRcyaOT#;C{3j51vjSg14WGwJ;s<+W^|!?1zA;AV^5=&^i*>jFIc!K%ilt zK7MG3dsGVby}f7$#Mg*9fLhDUPdMV%1@j3|PZO*364Y6>`=H>fVz?=;;1y`c*eZtJ z1r-bf1qYBW#khhcL7=bti@5bg1=ioiUHG1kfz9p*uaiSq1Zn_LCEsFRo-A3=)1&S& zSbszspd~c)FfV?xcKd>Asrf%(ot+(^TJFp8d$9f4ur&k3niO+l#I?#Hh58;2`aZ^J zd>P7H3aL`PmMR+@KANCGTb`PQJgmr9p#6P(Y~nb3v^NwK{J1&F2`bnx|1sQYIMrz+ zzoGuTje?GOQfB|{E2;Ps96wGVt>PC5oEgLqX$`N3?=3W@5+G7R!;d^TC4-qO3~Jvn z?bc!0QsKYP?bi5rF*WguFQ!f?8hCYmB#Ak<<-;@{Ao${u(?mW({mduEN{-~)@?>c; zrVW;FR>SQx@R z=qf&JMO~N0PJ=1FMj%Lq10!x(5)j1$GjLuwgo_T3416on(qP(m)5hWji#MUCR zGASCDjJ~TChu+YAoIBDgbLOTm{llR49EX{$i+0kxq)qV#>~AKpaVA(QW!Pn%Wc zCYe7@wIr==er`Tt&lYF)Cqa*FT&bMdsjTbbcmp--D@SsAsIxCeeK)L4+)_vq-n1h1wxIV*iCV$C&ekOWT(`b5y(PiKaDDPi81i#?@$gb-|OIt_qi4 zh(XWo2ea08=R)8S84fuCn=f*am}<3)9E0QMt!cF`W&Sa z*Fd+MUD%*XY-;z2R-4mh;au)`GY=4gbm2+{Y4u*uPmGcoQlxWgJIuRkyq*vF+t1A? zvs)&>v!$Bm^v_q^YDkZ7iZ=V~VN%x_dXCAd7LoX7^y#GVOKSMjJ4+&tq-Fi&h;57B z0gsC5nxk*I$i8|u*wYPlhdpn>d8%-5lG$m=rG$B7t4pY z)LP1H>Xhw8)*LLLGw_d?N{$8Ar41>xPnP5@AQ2RWnQ1N2g9{)8NMZn1H>i?1Dv@qH zD%td@s1I7tbcq;?Z+*q(dr2A~T)3#g1cHQtd-QRmO

>wRt+Z!%4rjmVSW=)ff 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 Date: Sat, 29 Jun 2024 14:05:25 +1200 Subject: [PATCH 25/35] Update README.md --- typescript/aws-codepipeline-ecs-lambda/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/README.md b/typescript/aws-codepipeline-ecs-lambda/README.md index c2a807e755..3191aab496 100644 --- a/typescript/aws-codepipeline-ecs-lambda/README.md +++ b/typescript/aws-codepipeline-ecs-lambda/README.md @@ -1,7 +1,7 @@ # AWS Codepipeline CI/CD Solution for ECS Fargate and Lambda ## Architect Design: -image +image ## Overview From 564af7977e704ba81bcf34bb1999a3b18f378918 Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Sat, 29 Jun 2024 14:08:42 +1200 Subject: [PATCH 26/35] relocate async lambda function code to assets --- .../{lib => assets}/lambda-functions/events_handler.ts | 0 .../{lib => assets}/lambda-functions/queue_message_handler.ts | 0 .../{lib => assets}/lambda-functions/topic_message_handler.ts | 0 .../lib/stage-app-async-lambda-stack.ts | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) rename typescript/aws-codepipeline-ecs-lambda/{lib => assets}/lambda-functions/events_handler.ts (100%) rename typescript/aws-codepipeline-ecs-lambda/{lib => assets}/lambda-functions/queue_message_handler.ts (100%) rename typescript/aws-codepipeline-ecs-lambda/{lib => assets}/lambda-functions/topic_message_handler.ts (100%) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/events_handler.ts b/typescript/aws-codepipeline-ecs-lambda/assets/lambda-functions/events_handler.ts similarity index 100% rename from typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/events_handler.ts rename to typescript/aws-codepipeline-ecs-lambda/assets/lambda-functions/events_handler.ts diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/queue_message_handler.ts b/typescript/aws-codepipeline-ecs-lambda/assets/lambda-functions/queue_message_handler.ts similarity index 100% rename from typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/queue_message_handler.ts rename to typescript/aws-codepipeline-ecs-lambda/assets/lambda-functions/queue_message_handler.ts diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/topic_message_handler.ts b/typescript/aws-codepipeline-ecs-lambda/assets/lambda-functions/topic_message_handler.ts similarity index 100% rename from typescript/aws-codepipeline-ecs-lambda/lib/lambda-functions/topic_message_handler.ts rename to typescript/aws-codepipeline-ecs-lambda/assets/lambda-functions/topic_message_handler.ts diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-async-lambda-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-async-lambda-stack.ts index fd24a24c52..7fbb7087a0 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-async-lambda-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/stage-app-async-lambda-stack.ts @@ -20,7 +20,7 @@ export class asyncLambdaStack extends cdk.Stack { }); // Shared code asset - const code = Code.fromAsset(path.join(__dirname, 'lambda-functions')); + const code = Code.fromAsset(path.join(__dirname, '../assets/lambda-functions')); // Lambda Function that will be invoked asynchronously when there is any event that matches the rule const eventsFunction = new Function(this, 'EventFunction', { From aa0a2fa14a35ba85481a939f7d61f577ab56e0fd Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Wed, 18 Sep 2024 13:40:33 +1200 Subject: [PATCH 27/35] explicit add account id in the pipeline --- .../lib/pipeline-stack.ts | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts index f80c801da5..360a381331 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts @@ -8,14 +8,16 @@ export class pipelineStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); - const githubOrg = process.env.GITHUB_ORG || "aws-6w8hnx"; - const githubRepo = process.env.GITHUB_REPO || "aws-codepipeline-ecs-lambda"; - const githubBranch = process.env.GITHUB_BRANCH || "main"; - const devEnv = process.env.DEV_ENV || "dev"; - const devAccountId = process.env.DEV_ACCOUNT_ID || "117645918752"; // replace with your dev account id - const primaryRegion = process.env.PRIMARY_REGION || "us-west-2"; - const secondaryRegion = process.env.SECONDARY_REGION || "eu-west-1"; - + const pipelineAccountId = process.env.PIPELINE_ACCOUNT_ID || "111111111111"; // replace with your pipeline account id + const pipelineRegion = process.env.PIPELINE_REGION || "us-east-1"; // replace with your pipeline region + const githubOrg = process.env.GITHUB_ORG || "aws-6w8hnx"; // replace with your GitHub Org + const githubRepo = process.env.GITHUB_REPO || "aws-codepipeline-ecs-lambda"; // replace with your GitHub Repo + const githubBranch = process.env.GITHUB_BRANCH || "main"; // replace with your GitHub repo branch + const devEnv = process.env.DEV_ENV || "dev"; // replace with your environment + const devAccountId = process.env.DEV_ACCOUNT_ID || "222222222222"; // replace with your dev account id + const primaryRegion = process.env.PRIMARY_REGION || "us-west-2"; // replace with your primary region + const secondaryRegion = process.env.SECONDARY_REGION || "eu-west-1"; // replace with your secondary region + const pipeline = new CodePipeline(this, 'pipeline', { selfMutation: true, crossAccountKeys: true, @@ -23,7 +25,7 @@ export class pipelineStack extends cdk.Stack { synth: new ShellStep('Synth', { input: CodePipelineSource.connection(`${githubOrg}/${githubRepo}`, `${githubBranch}`,{ // You need to replace the below code connection arn: - connectionArn: `arn:aws:codestar-connections:ap-southeast-2:${props?.env?.account}:connection/0ce75950-a29b-4ee4-a9d3-b0bad3b2c0a6` + connectionArn: `arn:aws:codestar-connections:${pipelineRegion}:${pipelineAccountId}:connection/0ce75950-a29b-4ee4-a9d3-b0bad3b2c0a6` }), commands: [ 'npm ci', @@ -41,7 +43,7 @@ export class pipelineStack extends cdk.Stack { }); const devStage = pipeline.addStage(new pipelineAppStage(this, `${devEnv}`, { - env: { account: props?.env?.account, region: props?.env?.region} + env: { account: `${pipelineAccountId}`, region: `${pipelineRegion}`} })); devStage.addPost(new ManualApprovalStep('approval')); From 7cf8ea7bd6398b5ab09237b36c0aaa69b1b791f7 Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Tue, 15 Oct 2024 22:32:33 +1300 Subject: [PATCH 28/35] moved variables to app.ts --- .../aws-codepipeline-ecs-lambda/lib/app.ts | 23 +++++++++- .../lib/pipeline-stack.ts | 42 ++++++++++--------- 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/app.ts b/typescript/aws-codepipeline-ecs-lambda/lib/app.ts index bd2756a021..bbfdfb0099 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/app.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/app.ts @@ -3,11 +3,30 @@ import 'source-map-support/register'; import * as cdk from 'aws-cdk-lib'; import { pipelineStack } from './pipeline-stack'; -const app = new cdk.App(); -const env = { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION } +const app = new cdk.App(); +const env = { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION } +const pipelineAccountId = process.env.PIPELINE_ACCOUNT_ID || "111111111111"; // replace with your pipeline account id +const pipelineRegion = process.env.PIPELINE_REGION || "us-east-1"; // replace with your pipeline region +const githubOrg = process.env.GITHUB_ORG || "aws-6w8hnx"; // replace with your GitHub Org +const githubRepo = process.env.GITHUB_REPO || "aws-codepipeline-ecs-lambda"; // replace with your GitHub Repo +const githubBranch = process.env.GITHUB_BRANCH || "main"; // replace with your GitHub repo branch +const devEnv = process.env.DEV_ENV || "dev"; // replace with your environment +const devAccountId = process.env.DEV_ACCOUNT_ID || "222222222222"; // replace with your dev account id +const primaryRegion = process.env.PRIMARY_REGION || "us-west-2"; // replace with your primary region +const secondaryRegion = process.env.SECONDARY_REGION || "eu-west-1"; // replace with your secondary region + const pipeline_stack = new pipelineStack(app, 'aws-codepipeline-stack', { env, + pipelineAccountId, + pipelineRegion, + githubOrg, + githubRepo, + githubBranch, + devEnv, + devAccountId, + primaryRegion, + secondaryRegion, }); cdk.Tags.of(pipeline_stack).add('managedBy', 'cdk'); cdk.Tags.of(pipeline_stack).add('environment', 'dev'); diff --git a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts index 360a381331..314bfab308 100644 --- a/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts +++ b/typescript/aws-codepipeline-ecs-lambda/lib/pipeline-stack.ts @@ -4,28 +4,30 @@ import { CodePipeline, CodePipelineSource, ManualApprovalStep, ShellStep, Wave } import { pipelineAppStage } from './stage-app'; import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; +export interface pipelineProps extends cdk.StackProps { + readonly pipelineAccountId: string; + readonly pipelineRegion: string; + readonly githubOrg: string; + readonly githubRepo: string; + readonly githubBranch: string; + readonly devEnv: string; + readonly devAccountId: string; + readonly primaryRegion: string; + readonly secondaryRegion: string; +} + export class pipelineStack extends cdk.Stack { - constructor(scope: Construct, id: string, props?: cdk.StackProps) { + constructor(scope: Construct, id: string, props: pipelineProps) { super(scope, id, props); - - const pipelineAccountId = process.env.PIPELINE_ACCOUNT_ID || "111111111111"; // replace with your pipeline account id - const pipelineRegion = process.env.PIPELINE_REGION || "us-east-1"; // replace with your pipeline region - const githubOrg = process.env.GITHUB_ORG || "aws-6w8hnx"; // replace with your GitHub Org - const githubRepo = process.env.GITHUB_REPO || "aws-codepipeline-ecs-lambda"; // replace with your GitHub Repo - const githubBranch = process.env.GITHUB_BRANCH || "main"; // replace with your GitHub repo branch - const devEnv = process.env.DEV_ENV || "dev"; // replace with your environment - const devAccountId = process.env.DEV_ACCOUNT_ID || "222222222222"; // replace with your dev account id - const primaryRegion = process.env.PRIMARY_REGION || "us-west-2"; // replace with your primary region - const secondaryRegion = process.env.SECONDARY_REGION || "eu-west-1"; // replace with your secondary region const pipeline = new CodePipeline(this, 'pipeline', { selfMutation: true, crossAccountKeys: true, reuseCrossRegionSupportStacks: true, synth: new ShellStep('Synth', { - input: CodePipelineSource.connection(`${githubOrg}/${githubRepo}`, `${githubBranch}`,{ + input: CodePipelineSource.connection(`${props.githubOrg}/${props.githubRepo}`, `${props.githubBranch}`,{ // You need to replace the below code connection arn: - connectionArn: `arn:aws:codestar-connections:${pipelineRegion}:${pipelineAccountId}:connection/0ce75950-a29b-4ee4-a9d3-b0bad3b2c0a6` + connectionArn: `arn:aws:codestar-connections:${props.pipelineRegion}:${props.pipelineAccountId}:connection/0ce75950-a29b-4ee4-a9d3-b0bad3b2c0a6` }), commands: [ 'npm ci', @@ -42,20 +44,20 @@ export class pipelineStack extends cdk.Stack { ]} }); - const devStage = pipeline.addStage(new pipelineAppStage(this, `${devEnv}`, { - env: { account: `${pipelineAccountId}`, region: `${pipelineRegion}`} + const devStage = pipeline.addStage(new pipelineAppStage(this, `${props.devEnv}`, { + env: { account: `${props.pipelineAccountId}`, region: `${props.pipelineRegion}`} })); devStage.addPost(new ManualApprovalStep('approval')); // add waves: - const devWave = pipeline.addWave(`${devEnv}Wave`); + const devWave = pipeline.addWave(`${props.devEnv}Wave`); - devWave.addStage(new pipelineAppStage(this, `${devEnv}-${primaryRegion}`, { - env: { account: `${devAccountId}`, region: `${primaryRegion}`} + devWave.addStage(new pipelineAppStage(this, `${props.devEnv}-${props.primaryRegion}`, { + env: { account: `${props.devAccountId}`, region: `${props.primaryRegion}`} })); - devWave.addStage(new pipelineAppStage(this, `${devEnv}-${secondaryRegion}`, { - env: { account: `${devAccountId}`, region: `${secondaryRegion}`} + devWave.addStage(new pipelineAppStage(this, `${props.devEnv}-${props.secondaryRegion}`, { + env: { account: `${props.devAccountId}`, region: `${props.secondaryRegion}`} })); } } From 8421b472734dd908ae72e4e3de4d0db602ffa499 Mon Sep 17 00:00:00 2001 From: Jacky Fan Date: Tue, 15 Oct 2024 22:34:50 +1300 Subject: [PATCH 29/35] upgrade cdk app version to v2.162.1 --- typescript/aws-codepipeline-ecs-lambda/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/package.json b/typescript/aws-codepipeline-ecs-lambda/package.json index cdf9c627a5..5f23bf83f8 100644 --- a/typescript/aws-codepipeline-ecs-lambda/package.json +++ b/typescript/aws-codepipeline-ecs-lambda/package.json @@ -14,14 +14,14 @@ "@types/aws-lambda": "^8.10.140", "@types/jest": "^29.5.12", "@types/node": "20.14.2", - "aws-cdk": "2.146.0", + "aws-cdk": "2.162.1", "jest": "^29.7.0", "ts-jest": "^29.1.4", "ts-node": "^10.9.2", "typescript": "~5.4.5" }, "dependencies": { - "aws-cdk-lib": "2.146.0", + "aws-cdk-lib": "2.162.1", "@types/aws-lambda": "^8.10.140", "constructs": "^10.0.0", "source-map-support": "^0.5.21" From 972e3cb10db52160580c2624048ad71f5ce712e1 Mon Sep 17 00:00:00 2001 From: Michael Kaiser Date: Sat, 2 Nov 2024 10:29:19 -0500 Subject: [PATCH 30/35] Update README.md Update diagram image --- typescript/aws-codepipeline-ecs-lambda/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/README.md b/typescript/aws-codepipeline-ecs-lambda/README.md index 3191aab496..0c97fce8e3 100644 --- a/typescript/aws-codepipeline-ecs-lambda/README.md +++ b/typescript/aws-codepipeline-ecs-lambda/README.md @@ -1,7 +1,7 @@ # AWS Codepipeline CI/CD Solution for ECS Fargate and Lambda ## Architect Design: -image +![](./static_images/Architecture_diagram.png) ## Overview From 3f29f8ccee4c2bc3ee8ad36f851c90d20d60d174 Mon Sep 17 00:00:00 2001 From: Michael Kaiser Date: Sat, 2 Nov 2024 10:31:24 -0500 Subject: [PATCH 31/35] Delete typescript/aws-codepipeline-ecs-lambda/test/aws-codepipeline-ecs-lambda.test.ts No test implemented --- .../test/aws-codepipeline-ecs-lambda.test.ts | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 typescript/aws-codepipeline-ecs-lambda/test/aws-codepipeline-ecs-lambda.test.ts diff --git a/typescript/aws-codepipeline-ecs-lambda/test/aws-codepipeline-ecs-lambda.test.ts b/typescript/aws-codepipeline-ecs-lambda/test/aws-codepipeline-ecs-lambda.test.ts deleted file mode 100644 index 8d5a8dd386..0000000000 --- a/typescript/aws-codepipeline-ecs-lambda/test/aws-codepipeline-ecs-lambda.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -// import * as cdk from 'aws-cdk-lib'; -// import { Template } from 'aws-cdk-lib/assertions'; -// import * as AwsCodepipelineEcsLambda from '../lib/aws-codepipeline-ecs-lambda-stack'; - -// example test. To run these tests, uncomment this file along with the -// example resource in lib/aws-codepipeline-ecs-lambda-stack.ts -test('SQS Queue Created', () => { -// const app = new cdk.App(); -// // WHEN -// const stack = new AwsCodepipelineEcsLambda.AwsCodepipelineEcsLambdaStack(app, 'MyTestStack'); -// // THEN -// const template = Template.fromStack(stack); - -// template.hasResourceProperties('AWS::SQS::Queue', { -// VisibilityTimeout: 300 -// }); -}); From 194c0e56dd22adc04794b3839f38a98b524300e2 Mon Sep 17 00:00:00 2001 From: Michael Kaiser Date: Sat, 2 Nov 2024 10:32:02 -0500 Subject: [PATCH 32/35] Update package.json No test implemented --- typescript/aws-codepipeline-ecs-lambda/package.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/package.json b/typescript/aws-codepipeline-ecs-lambda/package.json index 5f23bf83f8..f5353766df 100644 --- a/typescript/aws-codepipeline-ecs-lambda/package.json +++ b/typescript/aws-codepipeline-ecs-lambda/package.json @@ -7,16 +7,12 @@ "scripts": { "build": "tsc", "watch": "tsc -w", - "test": "jest", "cdk": "cdk" }, "devDependencies": { "@types/aws-lambda": "^8.10.140", - "@types/jest": "^29.5.12", "@types/node": "20.14.2", "aws-cdk": "2.162.1", - "jest": "^29.7.0", - "ts-jest": "^29.1.4", "ts-node": "^10.9.2", "typescript": "~5.4.5" }, From bc619a2518e70f42b5313bacfb45b9d694dee6b4 Mon Sep 17 00:00:00 2001 From: Michael Kaiser Date: Sat, 2 Nov 2024 10:32:49 -0500 Subject: [PATCH 33/35] Delete typescript/aws-codepipeline-ecs-lambda/jest.config.js No test implemented --- typescript/aws-codepipeline-ecs-lambda/jest.config.js | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 typescript/aws-codepipeline-ecs-lambda/jest.config.js diff --git a/typescript/aws-codepipeline-ecs-lambda/jest.config.js b/typescript/aws-codepipeline-ecs-lambda/jest.config.js deleted file mode 100644 index 08263b8954..0000000000 --- a/typescript/aws-codepipeline-ecs-lambda/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - testEnvironment: 'node', - roots: ['/test'], - testMatch: ['**/*.test.ts'], - transform: { - '^.+\\.tsx?$': 'ts-jest' - } -}; From 0cb89cd03d009d2a97afe928f1c28dd7af9de5fc Mon Sep 17 00:00:00 2001 From: Michael Kaiser Date: Sat, 2 Nov 2024 10:45:14 -0500 Subject: [PATCH 34/35] Update Dockerfile update python 3.12 --- typescript/aws-codepipeline-ecs-lambda/src/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typescript/aws-codepipeline-ecs-lambda/src/Dockerfile b/typescript/aws-codepipeline-ecs-lambda/src/Dockerfile index 0d7f280fb9..6a8f56a0ff 100644 --- a/typescript/aws-codepipeline-ecs-lambda/src/Dockerfile +++ b/typescript/aws-codepipeline-ecs-lambda/src/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -FROM python:3.8-slim-buster +FROM python:3.12-slim-buster WORKDIR /flask-app @@ -9,4 +9,4 @@ RUN pip3 install -r requirements.txt COPY flask-app/ . -CMD [ "python3", "app.py"] \ No newline at end of file +CMD [ "python3", "app.py"] From 62ff40856dd421e9aa3f0b6a17ff53603e6ed1c3 Mon Sep 17 00:00:00 2001 From: Michael Kaiser Date: Sat, 2 Nov 2024 10:50:56 -0500 Subject: [PATCH 35/35] fix: add DNA for cross account and lookup --- typescript/aws-codepipeline-ecs-lambda/DO_NOT_AUTOTEST | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 typescript/aws-codepipeline-ecs-lambda/DO_NOT_AUTOTEST diff --git a/typescript/aws-codepipeline-ecs-lambda/DO_NOT_AUTOTEST b/typescript/aws-codepipeline-ecs-lambda/DO_NOT_AUTOTEST new file mode 100644 index 0000000000..e69de29bb2