in source/infrastructure/lib/distributed-load-testing-on-aws-stack.ts [63:348]
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
// CFN template format version
this.templateOptions.templateFormatVersion = '2010-09-09';
// CFN Parameters
// Admin name
const adminName = new CfnParameter(this, 'AdminName', {
type: 'String',
description: 'Admin user name to access the Distributed Load Testing console',
minLength: 4,
maxLength: 20,
allowedPattern: '[a-zA-Z0-9-]+',
constraintDescription: "Admin username must be a minimum of 4 characters and cannot include spaces"
});
// Admin email
const adminEmail = new CfnParameter(this, 'AdminEmail', {
type: 'String',
description: 'Admin user email address to access the Distributed Load Testing Console',
allowedPattern: '^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$',
constraintDescription: 'Admin email must be a valid email address',
minLength: 5
});
// Existing VPC ID
const existingVpcId = new CfnParameter(this, 'ExistingVPCId', {
type: 'String',
description: 'Existing VPC ID',
allowedPattern: '(?:^$|^vpc-[a-zA-Z0-9-]+)',
});
const existingSubnetA = new CfnParameter(this, 'ExistingSubnetA', {
type: 'String',
description: 'First existing subnet',
allowedPattern: '(?:^$|^subnet-[a-zA-Z0-9-]+)'
});
const existingSubnetB = new CfnParameter(this, 'ExistingSubnetB', {
type: 'String',
description: 'Second existing subnet',
allowedPattern: '(?:^$|^subnet-[a-zA-Z0-9-]+)'
});
// VPC CIDR Block
const vpcCidrBlock = new CfnParameter(this, 'VpcCidrBlock', {
type: 'String',
default: '192.168.0.0/16',
description: 'CIDR block of the new VPC where AWS Fargate will be placed',
allowedPattern: '(?:^$|(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2}))',
constraintDescription: 'The VPC CIDR block must be a valid IP CIDR range of the form x.x.x.x/x.',
minLength: 9,
maxLength: 18
});
// Subnet A CIDR Block
const subnetACidrBlock = new CfnParameter(this, 'SubnetACidrBlock', {
type: 'String',
default: '192.168.0.0/20',
description: 'CIDR block for subnet A of the AWS Fargate VPC',
allowedPattern: '(?:^$|(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2}))',
constraintDescription: 'The subnet CIDR block must be a valid IP CIDR range of the form x.x.x.x/x.',
minLength: 9,
maxLength: 18
});
// Subnet B CIDR Block
const subnetBCidrBlock = new CfnParameter(this, 'SubnetBCidrBlock', {
type: 'String',
default: '192.168.16.0/20',
description: 'CIDR block for subnet B of the AWS Fargate VPC',
allowedPattern: '(?:^$|(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2}))',
constraintDescription: 'The subnet CIDR block must be a valid IP CIDR range of the form x.x.x.x/x.'
});
// Egress CIDR Block
const egressCidrBlock = new CfnParameter(this, 'EgressCidr', {
type: 'String',
default: '0.0.0.0/0',
description: 'CIDR Block to restrict the ECS container outbound access',
minLength: 9,
maxLength: 18,
allowedPattern: '(?:^$|(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2}))',
constraintDescription: 'The Egress CIDR block must be a valid IP CIDR range of the form x.x.x.x/x.'
});
// CloudFormation metadata
this.templateOptions.metadata = {
'AWS::CloudFormation::Interface': {
ParameterGroups: [
{
Label: { default: 'Console access' },
Parameters: [adminName.logicalId, adminEmail.logicalId]
},
{
Label: { default: 'Enter values here to use your own existing VPC' },
Parameters: [existingVpcId.logicalId, existingSubnetA.logicalId, existingSubnetB.logicalId]
},
{
Label: { default: 'Or have the solution create a new AWS Fargate VPC' },
Parameters: [vpcCidrBlock.logicalId, subnetACidrBlock.logicalId, subnetBCidrBlock.logicalId, egressCidrBlock.logicalId]
}
],
ParameterLabels: {
[adminName.logicalId]: { default: '* Console Administrator Name' },
[adminEmail.logicalId]: { default: '* Console Administrator Email' },
[existingVpcId.logicalId]: { default: 'The ID of an existing VPC in this region. Ex: `vpc-1a2b3c4d5e6f`' },
[existingSubnetA.logicalId]: { default: 'The ID of a subnet within the existing VPC. Ex: `subnet-7h8i9j0k`' },
[existingSubnetB.logicalId]: { default: 'The ID of a subnet within the existing VPC. Ex: `subnet-1x2y3z`' },
[vpcCidrBlock.logicalId]: { default: 'AWS Fargate VPC CIDR Block' },
[subnetACidrBlock.logicalId]: { default: 'AWS Fargate Subnet A CIDR Block' },
[subnetBCidrBlock.logicalId]: { default: 'AWS Fargate Subnet A CIDR Block' },
[egressCidrBlock.logicalId]: { default: 'AWS Fargate SecurityGroup CIDR Block' }
}
}
};
// CFN Mappings
const solutionMapping = new CfnMapping(this, 'Solution', {
mapping: {
Config: {
CodeVersion: 'CODE_VERSION',
KeyPrefix: 'SOLUTION_NAME/CODE_VERSION',
S3Bucket: 'CODE_BUCKET',
SendAnonymousUsage: 'Yes',
SolutionId: 'SO0062',
URL: 'https://metrics.awssolutionsbuilder.com/generic'
}
}
});
const sendAnonymousUsage = solutionMapping.findInMap('Config', 'SendAnonymousUsage');
const solutionId = solutionMapping.findInMap('Config', 'SolutionId');
const solutionVersion = solutionMapping.findInMap('Config', 'CodeVersion');
const sourceCodeBucket = Fn.join('-', [solutionMapping.findInMap('Config', 'S3Bucket'), Aws.REGION]);
const sourceCodePrefix = solutionMapping.findInMap('Config', 'KeyPrefix');
const metricsUrl = solutionMapping.findInMap('Config', 'URL');
// CFN Conditions
const sendAnonymousUsageCondition = new CfnCondition(this, 'SendAnonymousUsage', {
expression: Fn.conditionEquals(sendAnonymousUsage, 'Yes')
});
const createFargateVpcResourcesCondition = new CfnCondition(this, 'CreateFargateVPCResources', {
expression: Fn.conditionEquals(existingVpcId.valueAsString, '')
});
const usingExistingVpc = new CfnCondition(this, 'BoolExistingVPC', {
expression: Fn.conditionNot(Fn.conditionEquals(existingVpcId.valueAsString, ''))
});
// Fargate VPC resources
const fargate = new FargateVpcContruct(this, 'DLTVpc', {
solutionId: solutionId,
subnetACidrBlock: subnetACidrBlock.valueAsString,
subnetBCidrBlock: subnetBCidrBlock.valueAsString,
vpcCidrBlock: vpcCidrBlock.valueAsString,
});
Aspects.of(fargate).add(new ConditionAspect(createFargateVpcResourcesCondition));
this.fargateVpcId = Fn.conditionIf(createFargateVpcResourcesCondition.logicalId,
fargate.DLTfargateVpcId,
existingVpcId.valueAsString
).toString();
this.fargateSubnetA = Fn.conditionIf(createFargateVpcResourcesCondition.logicalId,
fargate.subnetA,
existingSubnetA.valueAsString
).toString();
this.fargateSubnetB = Fn.conditionIf(createFargateVpcResourcesCondition.logicalId,
fargate.subnetB,
existingSubnetB.valueAsString
).toString();
// Fargate ECS resources
const fargateECSResources = new FargateECSTestRunnerContruct(this, 'DLTEcs', {
DLTfargateVpcId: this.fargateVpcId,
securityGroupEgress: egressCidrBlock.valueAsString,
solutionId: solutionId,
});
const existingVpc = Fn.conditionIf(usingExistingVpc.logicalId, true, false).toString();
const commonResources = new CommonResourcesContruct(this, 'DLTCommonResources', {
dltEcsTaskExecutionRole: fargateECSResources.dltTaskExecutionRole,
solutionId: solutionId,
sourceCodeBucket,
sourceCodePrefix,
solutionVersion,
sendAnonymousUsageCondition,
existingVpc,
metricsUrl
});
const dltConsole = new DLTConsoleContruct(this, 'DLTConsoleResources', {
customResource: commonResources.customResourceLambda,
s3LogsBucket: commonResources.s3LogsBucket,
solutionId: solutionId
});
const dltStorage = new ScenarioTestRunnerStorageContruct(this, 'DLTTestRunnerStorage', {
ecsTaskExecutionRole: fargateECSResources.dltTaskExecutionRole,
s3LogsBucket: commonResources.s3LogsBucket,
cloudFrontDomainName: dltConsole.cloudFrontDomainName,
solutionId,
});
const stepLambdaFunctions = new TestRunnerLambdasConstruct(this, 'DLTLambdaFunction', {
cloudWatchLogsPolicy: commonResources.cloudWatchLogsPolicy,
dynamoDbPolicy: dltStorage.dynamoDbPolicy,
ecsTaskExecutionRoleArn: fargateECSResources.dltTaskExecutionRole.roleArn,
ecsCloudWatchLogGroup: fargateECSResources.dltCloudWatchLogGroup,
ecsCluster: fargateECSResources.dltEcsClusterName,
ecsTaskDefinition: fargateECSResources.dltTaskDefinitionArn,
ecsTaskSecurityGroup: fargateECSResources.dltSecurityGroupId,
scenariosS3Policy: dltStorage.scenariosS3Policy,
subnetA: this.fargateSubnetA,
subnetB: this.fargateSubnetB,
metricsUrl,
sendAnonymousUsage,
solutionId,
solutionVersion,
sourceCodeBucket: commonResources.sourceBucket,
sourceCodePrefix,
testScenariosBucket: dltStorage.scenariosBucket.bucketName,
testScenariosTable: dltStorage.scenariosTable,
uuid: commonResources.uuid,
})
const taskRunnerStepFunctions = new TaskRunnerStepFunctionConstruct(this, 'DLTStepFunction', {
taskStatusChecker: stepLambdaFunctions.taskStatusChecker,
taskRunner: stepLambdaFunctions.taskRunner,
resultsParser: stepLambdaFunctions.resultsParser,
taskCanceler: stepLambdaFunctions.taskCanceler,
solutionId
});
const dltApi = new DLTAPI(this, 'DLTApi', {
ecsCloudWatchLogGroup: fargateECSResources.dltCloudWatchLogGroup,
cloudWatchLogsPolicy: commonResources.cloudWatchLogsPolicy,
dynamoDbPolicy: dltStorage.dynamoDbPolicy,
taskCancelerInvokePolicy: stepLambdaFunctions.taskCancelerInvokePolicy,
scenariosBucketName: dltStorage.scenariosBucket.bucketName,
scenariosS3Policy: dltStorage.scenariosS3Policy,
scenariosTableName: dltStorage.scenariosTable.tableName,
ecsCuster: fargateECSResources.dltEcsClusterName,
ecsTaskExecutionRoleArn: fargateECSResources.dltTaskExecutionRole.roleArn,
taskRunnerStepFunctionsArn: taskRunnerStepFunctions.taskRunnerStepFunctions.stateMachineArn,
tastCancelerArn: stepLambdaFunctions.taskCanceler.functionArn,
metricsUrl,
sendAnonymousUsage,
solutionId,
solutionVersion,
sourceCodeBucket: commonResources.sourceBucket,
sourceCodePrefix,
uuid: commonResources.uuid
});
const cognitoResources = new CognitoAuthConstruct(this, 'DLTCognitoAuth', {
adminEmail: adminEmail.valueAsString,
adminName: adminName.valueAsString,
apiId: dltApi.apiId,
cloudFrontDomainName: dltConsole.cloudFrontDomainName,
scenariosBucketArn: dltStorage.scenariosBucket.bucketArn,
});
new CustomResourcesConstruct(this, 'DLTCustomResources', {
apiEndpoint: dltApi.apiEndpointPath,
customResourceLambda: commonResources.customResourceLambda.functionArn,
cognitoIdentityPool: cognitoResources.cognitoIdentityPoolId,
cognitoUserPool: cognitoResources.cognitoUserPoolId,
cognitoUserPoolClient: cognitoResources.cognitoUserPoolClientId,
consoleBucketName: dltConsole.consoleBucket.bucketName,
scenariosBucket: dltStorage.scenariosBucket.bucketName,
sourceCodeBucketName: commonResources.sourceBucket.bucketName,
sourceCodePrefix
});
//Outputs
new CfnOutput(this, 'Console', {
description: 'Console URL',
value: dltConsole.cloudFrontDomainName
});
new CfnOutput(this, 'SolutionUUID', {
description: 'Solution UUID',
value: commonResources.uuid
})
}