in packages/@aws-cdk/aws-redshift-alpha/lib/cluster.ts [576:727]
constructor(scope: Construct, id: string, props: ClusterProps) {
super(scope, id);
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);
this.vpc = props.vpc;
this.vpcSubnets = props.vpcSubnets ?? {
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
};
this.parameterGroup = props.parameterGroup;
this.roles = props?.roles ? [...props.roles] : [];
const removalPolicy = props.removalPolicy ?? RemovalPolicy.RETAIN;
const subnetGroup = props.subnetGroup ?? new ClusterSubnetGroup(this, 'Subnets', {
description: `Subnets for ${id} Redshift cluster`,
vpc: this.vpc,
vpcSubnets: this.vpcSubnets,
removalPolicy: removalPolicy,
});
const securityGroups = props.securityGroups ?? [new ec2.SecurityGroup(this, 'SecurityGroup', {
description: 'Redshift security group',
vpc: this.vpc,
})];
const securityGroupIds = securityGroups.map(sg => sg.securityGroupId);
let secret: DatabaseSecret | undefined;
if (!props.masterUser.masterPassword) {
secret = new DatabaseSecret(this, 'Secret', {
username: props.masterUser.masterUsername,
encryptionKey: props.masterUser.encryptionKey,
excludeCharacters: props.masterUser.excludeCharacters,
});
}
const clusterType = props.clusterType || ClusterType.MULTI_NODE;
const nodeCount = this.validateNodeCount(clusterType, props.numberOfNodes);
if (props.encrypted === false && props.encryptionKey !== undefined) {
throw new Error('Cannot set property encryptionKey without enabling encryption!');
}
this.singleUserRotationApplication = secretsmanager.SecretRotationApplication.REDSHIFT_ROTATION_SINGLE_USER;
this.multiUserRotationApplication = secretsmanager.SecretRotationApplication.REDSHIFT_ROTATION_MULTI_USER;
let loggingProperties;
if (props.loggingProperties) {
loggingProperties = {
bucketName: props.loggingProperties.loggingBucket.bucketName,
s3KeyPrefix: props.loggingProperties.loggingKeyPrefix,
};
props.loggingProperties.loggingBucket.addToResourcePolicy(
new iam.PolicyStatement(
{
actions: [
's3:GetBucketAcl',
's3:PutObject',
],
resources: [
props.loggingProperties.loggingBucket.arnForObjects('*'),
props.loggingProperties.loggingBucket.bucketArn,
],
principals: [
new iam.ServicePrincipal('redshift.amazonaws.com'),
],
},
),
);
}
const nodeType = props.nodeType || NodeType.DC2_LARGE;
if (props.multiAz) {
if (!nodeType.startsWith('ra3')) {
throw new Error(`Multi-AZ cluster is only supported for RA3 node types, got: ${props.nodeType}`);
}
if (clusterType === ClusterType.SINGLE_NODE) {
throw new Error('Multi-AZ cluster is not supported for `clusterType` single-node');
}
}
if (props.resourceAction === ResourceAction.FAILOVER_PRIMARY_COMPUTE && !props.multiAz) {
throw new Error('ResourceAction.FAILOVER_PRIMARY_COMPUTE can only be used with multi-AZ clusters.');
}
if (props.availabilityZoneRelocation && !nodeType.startsWith('ra3')) {
throw new Error(`Availability zone relocation is supported for only RA3 node types, got: ${props.nodeType}`);
}
this.cluster = new CfnCluster(this, 'Resource', {
// Basic
allowVersionUpgrade: true,
maintenanceTrackName: props.maintenanceTrackName,
automatedSnapshotRetentionPeriod: 1,
clusterType,
clusterIdentifier: props.clusterName,
clusterSubnetGroupName: subnetGroup.clusterSubnetGroupName,
vpcSecurityGroupIds: securityGroupIds,
port: props.port,
clusterParameterGroupName: props.parameterGroup && props.parameterGroup.clusterParameterGroupName,
// Admin (unsafeUnwrap here is safe)
masterUsername: secret?.secretValueFromJson('username').unsafeUnwrap() ?? props.masterUser.masterUsername,
masterUserPassword: secret?.secretValueFromJson('password').unsafeUnwrap()
?? props.masterUser.masterPassword?.unsafeUnwrap()
?? 'default',
preferredMaintenanceWindow: props.preferredMaintenanceWindow,
nodeType,
numberOfNodes: nodeCount,
loggingProperties,
iamRoles: Lazy.list({ produce: () => this.roles.map(role => role.roleArn) }, { omitEmpty: true }),
dbName: props.defaultDatabaseName || 'default_db',
publiclyAccessible: props.publiclyAccessible || false,
// Encryption
kmsKeyId: props.encryptionKey?.keyId,
encrypted: props.encrypted ?? true,
classic: props.classicResizing,
elasticIp: props.elasticIp,
enhancedVpcRouting: props.enhancedVpcRouting,
multiAz: props.multiAz,
resourceAction: props.resourceAction,
availabilityZoneRelocation: props.availabilityZoneRelocation,
});
this.cluster.applyRemovalPolicy(removalPolicy, {
applyToUpdateReplacePolicy: true,
});
this.clusterName = this.cluster.ref;
// create a number token that represents the port of the cluster
const portAttribute = Token.asNumber(this.cluster.attrEndpointPort);
this.clusterEndpoint = new Endpoint(this.cluster.attrEndpointAddress, portAttribute);
if (secret) {
this.secret = secret.attach(this);
}
const defaultPort = ec2.Port.tcp(this.clusterEndpoint.port);
this.connections = new ec2.Connections({ securityGroups, defaultPort });
if (props.rebootForParameterChanges) {
this.enableRebootForParameterChanges();
}
// Add default role if specified and also available in the roles list
if (props.defaultRole) {
if (props.roles?.some(x => x === props.defaultRole)) {
this.addDefaultIamRole(props.defaultRole);
} else {
throw new Error('Default role must be included in role list.');
}
}
}