in packages/@aws-cdk/aws-docdb/lib/cluster.ts [348:492]
constructor(scope: Construct, id: string, props: DatabaseClusterProps) {
super(scope, id);
this.vpc = props.vpc;
this.vpcSubnets = props.vpcSubnets;
// Determine the subnet(s) to deploy the DocDB cluster to
const { subnetIds, internetConnectivityEstablished } = this.vpc.selectSubnets(this.vpcSubnets);
// DocDB clusters require a subnet group with subnets from at least two AZs.
// We cannot test whether the subnets are in different AZs, but at least we can test the amount.
// See https://docs.aws.amazon.com/documentdb/latest/developerguide/replication.html#replication.high-availability
if (subnetIds.length < 2) {
throw new Error(`Cluster requires at least 2 subnets, got ${subnetIds.length}`);
}
const subnetGroup = new CfnDBSubnetGroup(this, 'Subnets', {
dbSubnetGroupDescription: `Subnets for ${id} database`,
subnetIds,
});
// Create the security group for the DB cluster
let securityGroup: ec2.ISecurityGroup;
if (props.securityGroup) {
securityGroup = props.securityGroup;
} else {
securityGroup = new ec2.SecurityGroup(this, 'SecurityGroup', {
description: 'DocumentDB security group',
vpc: this.vpc,
});
// HACK: Use an escape-hatch to apply a consistent removal policy to the
// security group so we don't get errors when trying to delete the stack
(securityGroup.node.defaultChild as CfnResource).applyRemovalPolicy(props.removalPolicy, {
applyToUpdateReplacePolicy: true,
});
}
this.securityGroupId = securityGroup.securityGroupId;
// Create the CloudwatchLogsConfiguratoin
const enableCloudwatchLogsExports: string[] = [];
if (props.exportAuditLogsToCloudWatch) {
enableCloudwatchLogsExports.push('audit');
}
if (props.exportProfilerLogsToCloudWatch) {
enableCloudwatchLogsExports.push('profiler');
}
// Create the secret manager secret if no password is specified
let secret: DatabaseSecret | undefined;
if (!props.masterUser.password) {
secret = new DatabaseSecret(this, 'Secret', {
username: props.masterUser.username,
encryptionKey: props.masterUser.kmsKey,
excludeCharacters: props.masterUser.excludeCharacters,
secretName: props.masterUser.secretName,
});
}
// Default to encrypted storage
const storageEncrypted = props.storageEncrypted ?? true;
if (props.kmsKey && !storageEncrypted) {
throw new Error('KMS key supplied but storageEncrypted is false');
}
// Create the DocDB cluster
this.cluster = new CfnDBCluster(this, 'Resource', {
// Basic
engineVersion: props.engineVersion,
dbClusterIdentifier: props.dbClusterName,
dbSubnetGroupName: subnetGroup.ref,
port: props.port,
vpcSecurityGroupIds: [this.securityGroupId],
dbClusterParameterGroupName: props.parameterGroup?.parameterGroupName,
deletionProtection: props.deletionProtection,
// Admin
masterUsername: secret ? secret.secretValueFromJson('username').toString() : props.masterUser.username,
masterUserPassword: secret
? secret.secretValueFromJson('password').toString()
: props.masterUser.password!.toString(),
// Backup
backupRetentionPeriod: props.backup?.retention?.toDays(),
preferredBackupWindow: props.backup?.preferredWindow,
preferredMaintenanceWindow: props.preferredMaintenanceWindow,
// EnableCloudwatchLogsExports
enableCloudwatchLogsExports: enableCloudwatchLogsExports.length > 0 ? enableCloudwatchLogsExports : undefined,
// Encryption
kmsKeyId: props.kmsKey?.keyArn,
storageEncrypted,
});
this.cluster.applyRemovalPolicy(props.removalPolicy, {
applyToUpdateReplacePolicy: true,
});
this.clusterIdentifier = this.cluster.ref;
this.clusterResourceIdentifier = this.cluster.attrClusterResourceId;
const port = Token.asNumber(this.cluster.attrPort);
this.clusterEndpoint = new Endpoint(this.cluster.attrEndpoint, port);
this.clusterReadEndpoint = new Endpoint(this.cluster.attrReadEndpoint, port);
this.setLogRetention(this, props, enableCloudwatchLogsExports);
if (secret) {
this.secret = secret.attach(this);
}
// Create the instances
const instanceCount = props.instances ?? DatabaseCluster.DEFAULT_NUM_INSTANCES;
if (instanceCount < 1) {
throw new Error('At least one instance is required');
}
for (let i = 0; i < instanceCount; i++) {
const instanceIndex = i + 1;
const instanceIdentifier = props.instanceIdentifierBase != null ? `${props.instanceIdentifierBase}${instanceIndex}`
: props.dbClusterName != null ? `${props.dbClusterName}instance${instanceIndex}` : undefined;
const instance = new CfnDBInstance(this, `Instance${instanceIndex}`, {
// Link to cluster
dbClusterIdentifier: this.cluster.ref,
dbInstanceIdentifier: instanceIdentifier,
// Instance properties
dbInstanceClass: databaseInstanceType(props.instanceType),
});
instance.applyRemovalPolicy(props.removalPolicy, {
applyToUpdateReplacePolicy: true,
});
// We must have a dependency on the NAT gateway provider here to create
// things in the right order.
instance.node.addDependency(internetConnectivityEstablished);
this.instanceIdentifiers.push(instance.ref);
this.instanceEndpoints.push(new Endpoint(instance.attrEndpoint, port));
}
this.connections = new ec2.Connections({
defaultPort: ec2.Port.tcp(port),
securityGroups: [securityGroup],
});
}