Skip to content

Commit

Permalink
chore: sync from upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
cplee committed Nov 13, 2024
1 parent 5df4c9e commit 88094a4
Show file tree
Hide file tree
Showing 10 changed files with 862 additions and 600 deletions.
189 changes: 121 additions & 68 deletions examples/cdk-application-pipeline/infrastructure/src/deployment.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
import { ApplicationLoadBalancedCodeDeployedFargateService } from '@cdklabs/cdk-ecs-codedeploy';
import { CfnOutput, Duration, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';
import { EcsDeploymentConfig, IEcsDeploymentConfig } from 'aws-cdk-lib/aws-codedeploy';
import {
CfnOutput,
Duration,
RemovalPolicy,
Stack,
StackProps,
} from 'aws-cdk-lib';
import {
IEcsDeploymentConfig,
EcsDeploymentConfig,
} from 'aws-cdk-lib/aws-codedeploy';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import { FlowLog, FlowLogResourceType, Port } from 'aws-cdk-lib/aws-ec2';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import { FlowLog, FlowLogResourceType } from 'aws-cdk-lib/aws-ec2';
import { AssetImage, AwsLogDriver, Secret } from 'aws-cdk-lib/aws-ecs';
import { PolicyStatement } from 'aws-cdk-lib/aws-iam';
import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs';
import { Credentials, DatabaseClusterEngine, DatabaseSecret, ServerlessCluster } from 'aws-cdk-lib/aws-rds';
import {
AuroraMysqlEngineVersion,
ClusterInstance,
Credentials,
DatabaseCluster,
DatabaseClusterEngine,
DatabaseSecret,
} from 'aws-cdk-lib/aws-rds';
import { Construct } from 'constructs';
import { ApplicationLoadBalancedCodeDeployedFargateService } from '@cdklabs/cdk-ecs-codedeploy';
import * as ecs from 'aws-cdk-lib/aws-ecs';

export interface DeploymentProps extends StackProps {
deploymentConfigName?: string;
Expand All @@ -24,25 +40,35 @@ export class DeploymentStack extends Stack {

const image = new AssetImage('.', { target: 'build' });

const appName = Stack.of(this).stackName.toLowerCase().replace(`-${Stack.of(this).region}-`, '-');
const appName = Stack.of(this)
.stackName.toLowerCase()
.replace(`-${Stack.of(this).region}-`, '-');
const vpc = new ec2.Vpc(this, 'Vpc', {
maxAzs: 3,
natGateways: props?.natGateways,
});
new FlowLog(this, 'VpcFlowLog', { resourceType: FlowLogResourceType.fromVpc(vpc) });
new FlowLog(this, 'VpcFlowLog', {
resourceType: FlowLogResourceType.fromVpc(vpc),
});

const dbName = 'fruits';
const dbSecret = new DatabaseSecret(this, 'AuroraSecret', {
username: 'fruitapi',
secretName: `${appName}-DB`,
});
const db = new ServerlessCluster(this, 'AuroraCluster', {
engine: DatabaseClusterEngine.AURORA_MYSQL,
vpc,

const db = new DatabaseCluster(this, 'Database', {
engine: DatabaseClusterEngine.auroraMysql({
version: AuroraMysqlEngineVersion.VER_3_07_1,
}),
credentials: Credentials.fromSecret(dbSecret),
writer: ClusterInstance.serverlessV2('writer'),
defaultDatabaseName: dbName,
deletionProtection: false,
serverlessV2MaxCapacity: 2,
serverlessV2MinCapacity: 0.5,
vpc,
clusterIdentifier: appName,
storageEncrypted: true,
});

const cluster = new ecs.Cluster(this, 'Cluster', {
Expand All @@ -57,64 +83,86 @@ export class DeploymentStack extends Stack {
});
let deploymentConfig: IEcsDeploymentConfig | undefined = undefined;
if (props?.deploymentConfigName) {
deploymentConfig = EcsDeploymentConfig.fromEcsDeploymentConfigName(this, 'DeploymentConfig', props.deploymentConfigName);
deploymentConfig = EcsDeploymentConfig.fromEcsDeploymentConfigName(
this,
'DeploymentConfig',
props.deploymentConfigName,
);
}
const appConfigEnabled = props?.appConfigRoleArn !== undefined && props.appConfigRoleArn.length > 0;
const service = new ApplicationLoadBalancedCodeDeployedFargateService(this, 'Api', {
cluster,
capacityProviderStrategies: [
{
capacityProvider: 'FARGATE_SPOT',
weight: 1,
const appConfigEnabled =
props?.appConfigRoleArn !== undefined &&
props.appConfigRoleArn.length > 0;
const service = new ApplicationLoadBalancedCodeDeployedFargateService(
this,
'Api',
{
cluster,
capacityProviderStrategies: [
{
capacityProvider: 'FARGATE_SPOT',
weight: 1,
},
],
minHealthyPercent: 50,
maxHealthyPercent: 200,
desiredCount: 3,
cpu: 512,
memoryLimitMiB: 1024,
taskImageOptions: {
image,
containerName: 'api',
containerPort: 8080,
family: appName,
logDriver: AwsLogDriver.awsLogs({
logGroup: appLogGroup,
streamPrefix: 'service',
}),
secrets: {
SPRING_DATASOURCE_USERNAME: Secret.fromSecretsManager(
dbSecret,
'username',
),
SPRING_DATASOURCE_PASSWORD: Secret.fromSecretsManager(
dbSecret,
'password',
),
},
environment: {
SPRING_DATASOURCE_URL: `jdbc:mysql://${db.clusterEndpoint.hostname}:${db.clusterEndpoint.port}/${dbName}`,
APPCONFIG_AGENT_APPLICATION:
this.node.tryGetContext('workloadName'),
APPCONFIG_AGENT_ENVIRONMENT:
this.node.tryGetContext('environmentName'),
APPCONFIG_AGENT_ENABLED: appConfigEnabled.toString(),
},
},
],
minHealthyPercent: 50,
maxHealthyPercent: 200,
desiredCount: 3,
cpu: 512,
memoryLimitMiB: 1024,
taskImageOptions: {
image,
containerName: 'api',
containerPort: 8080,
family: appName,
logDriver: AwsLogDriver.awsLogs({
logGroup: appLogGroup,
streamPrefix: 'service',
}),
secrets: {
SPRING_DATASOURCE_USERNAME: Secret.fromSecretsManager( dbSecret, 'username' ),
SPRING_DATASOURCE_PASSWORD: Secret.fromSecretsManager( dbSecret, 'password' ),
},
environment: {
SPRING_DATASOURCE_URL: `jdbc:mysql://${db.clusterEndpoint.hostname}:${db.clusterEndpoint.port}/${dbName}`,
APPCONFIG_AGENT_APPLICATION: this.node.tryGetContext('workloadName'),
APPCONFIG_AGENT_ENVIRONMENT: this.node.tryGetContext('environmentName'),
APPCONFIG_AGENT_ENABLED: appConfigEnabled.toString(),
deregistrationDelay: Duration.seconds(5),
responseTimeAlarmThreshold: Duration.seconds(3),
targetHealthCheck: {
healthyThresholdCount: 2,
unhealthyThresholdCount: 2,
interval: Duration.seconds(60),
path: '/actuator/health',
},
deploymentConfig,
terminationWaitTime: Duration.minutes(5),
apiCanaryTimeout: Duration.seconds(5),
apiTestSteps: [
{
name: 'getAll',
path: '/api/fruits',
jmesPath: 'length(@)',
expectedValue: 5,
},
],
},
deregistrationDelay: Duration.seconds(5),
responseTimeAlarmThreshold: Duration.seconds(3),
targetHealthCheck: {
healthyThresholdCount: 2,
unhealthyThresholdCount: 2,
interval: Duration.seconds(60),
path: '/actuator/health',
},
deploymentConfig,
terminationWaitTime: Duration.minutes(5),
apiCanaryTimeout: Duration.seconds(5),
apiTestSteps: [{
name: 'getAll',
path: '/api/fruits',
jmesPath: 'length(@)',
expectedValue: 5,
}],
});
);

if (appConfigEnabled) {
service.taskDefinition.addContainer('appconfig-agent', {
image: ecs.ContainerImage.fromRegistry('public.ecr.aws/aws-appconfig/aws-appconfig-agent:2.x'),
image: ecs.ContainerImage.fromRegistry(
'public.ecr.aws/aws-appconfig/aws-appconfig-agent:2.x',
),
essential: false,
logging: AwsLogDriver.awsLogs({
logGroup: appLogGroup,
Expand All @@ -129,13 +177,18 @@ export class DeploymentStack extends Stack {
portMappings: [{ containerPort: 2772 }],
});

service.taskDefinition.addToTaskRolePolicy(new PolicyStatement({
actions: ['sts:AssumeRole'],
resources: [props!.appConfigRoleArn!],
}));
service.taskDefinition.addToTaskRolePolicy(
new PolicyStatement({
actions: ['sts:AssumeRole'],
resources: [props!.appConfigRoleArn!],
}),
);
}

service.service.connections.allowTo(db, Port.tcp(db.clusterEndpoint.port));
service.service.connections.allowTo(
db,
ec2.Port.tcp(db.clusterEndpoint.port),
);

this.apiUrl = new CfnOutput(this, 'endpointUrl', {
value: `http://${service.listener.loadBalancer.loadBalancerDnsName}`,
Expand Down
Loading

0 comments on commit 88094a4

Please sign in to comment.