diff --git a/packages/@aws-cdk/aws-rds/lib/serverless-cluster.ts b/packages/@aws-cdk/aws-rds/lib/serverless-cluster.ts index e849a7c1843fc..493677a1bc997 100644 --- a/packages/@aws-cdk/aws-rds/lib/serverless-cluster.ts +++ b/packages/@aws-cdk/aws-rds/lib/serverless-cluster.ts @@ -108,8 +108,6 @@ export interface ServerlessClusterProps { * The VPC that this Aurora Serverless cluster has been created in. * * @default - No VPC related construct will be created: - * - If subnetGroup is not provided, no DB subnet group will be associated with the DB cluster. - * - If securityGroups is not provided, no VPC security groups will be associated with the DB cluster. */ readonly vpc?: ec2.IVpc; @@ -141,6 +139,7 @@ export interface ServerlessClusterProps { * Security group. * * @default - a new security group is created if a vpc is provided. + * If vpc is not provided, no VPC security groups will be associated with the DB cluster. */ readonly securityGroups?: ec2.ISecurityGroup[]; @@ -162,6 +161,7 @@ export interface ServerlessClusterProps { * Existing subnet group for the cluster. * * @default - a new subnet group will be created if a vpc is provided. + * If vpc is not provided, no DB subnet group will be associated with the DB cluster. */ readonly subnetGroup?: ISubnetGroup; } @@ -394,7 +394,6 @@ export class ServerlessCluster extends ServerlessClusterBase { this.vpc = props.vpc; this.vpcSubnets = props.vpcSubnets; this.subnetGroup = props.subnetGroup; - let securityGroups: ec2.ISecurityGroup[] | undefined = props.securityGroups; this.singleUserRotationApplication = props.engine.singleUserRotationApplication; this.multiUserRotationApplication = props.engine.multiUserRotationApplication; @@ -405,6 +404,7 @@ export class ServerlessCluster extends ServerlessClusterBase { throw new Error('A vpc is required to use vpcSubnets in ServerlessCluster. Please add a vpc or remove vpcSubnets'); } + let securityGroups: ec2.ISecurityGroup[] | undefined = props.securityGroups; if (this.vpc !== undefined) { const { subnetIds } = this.vpc.selectSubnets(this.vpcSubnets); diff --git a/packages/@aws-cdk/aws-rds/test/integ.serverless-cluster-no-vpc.expected.json b/packages/@aws-cdk/aws-rds/test/integ.serverless-cluster-no-vpc.expected.json new file mode 100644 index 0000000000000..def1b8a4cc98b --- /dev/null +++ b/packages/@aws-cdk/aws-rds/test/integ.serverless-cluster-no-vpc.expected.json @@ -0,0 +1,17 @@ +{ + "Resources": { + "ServerlessDatabaseWithoutVPC93F9A752": { + "Type": "AWS::RDS::DBCluster", + "Properties": { + "Engine": "aurora-mysql", + "DBClusterParameterGroupName": "default.aurora-mysql5.7", + "EngineMode": "serverless", + "MasterUsername": "admin", + "MasterUserPassword": "7959866cacc02c2d243ecfe177464fe6", + "StorageEncrypted": true + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/integ.serverless-cluster-no-vpc.ts b/packages/@aws-cdk/aws-rds/test/integ.serverless-cluster-no-vpc.ts new file mode 100644 index 0000000000000..807cf1a894601 --- /dev/null +++ b/packages/@aws-cdk/aws-rds/test/integ.serverless-cluster-no-vpc.ts @@ -0,0 +1,17 @@ +import * as cdk from '@aws-cdk/core'; +import * as rds from '../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-sls-cluster-no-vpc-integ'); + +const cluster = new rds.ServerlessCluster(stack, 'Serverless Database Without VPC', { + engine: rds.DatabaseClusterEngine.AURORA_MYSQL, + credentials: { + username: 'admin', + password: cdk.SecretValue.plainText('7959866cacc02c2d243ecfe177464fe6'), + }, + removalPolicy: cdk.RemovalPolicy.DESTROY, +}); +cluster.connections.allowDefaultPortFromAnyIpv4('Open to the world'); + +app.synth(); diff --git a/packages/@aws-cdk/aws-rds/test/serverless-cluster.test.ts b/packages/@aws-cdk/aws-rds/test/serverless-cluster.test.ts index d807a55b99a96..293dca5fab7d7 100644 --- a/packages/@aws-cdk/aws-rds/test/serverless-cluster.test.ts +++ b/packages/@aws-cdk/aws-rds/test/serverless-cluster.test.ts @@ -392,13 +392,30 @@ describe('serverless cluster', () => { // WHEN const cluster = new ServerlessCluster(stack, 'Database', { engine: DatabaseClusterEngine.AURORA_MYSQL, - credentials: { username: 'admin' }, }); // THEN - expect(() => cluster.addRotationSingleUser()).toThrow(/Cannot add single user rotation for a cluster without vpc/); + expect(() => { + cluster.addRotationSingleUser(); + }).toThrow(/Cannot add single user rotation for a cluster without vpc/); + }); + test('throws when trying to add rotation to a serverless cluster without vpc', () => { + // GIVEN + const stack = new cdk.Stack(); + const secret = new DatabaseSecret(stack, 'Secret', { + username: 'admin', + }); + // WHEN + const cluster = new ServerlessCluster(stack, 'Database', { + engine: DatabaseClusterEngine.AURORA_MYSQL, + }); + + // THEN + expect(() => { + cluster.addRotationMultiUser('someId', { secret }); + }).toThrow(/Cannot add multi user rotation for a cluster without vpc/); }); test('can set deletion protection', () => { @@ -853,39 +870,21 @@ describe('serverless cluster', () => { // WHEN new ServerlessCluster(stack, 'Database', { - engine: DatabaseClusterEngine.AURORA_POSTGRESQL, - parameterGroup: ParameterGroup.fromParameterGroupName(stack, 'ParameterGroup', 'default.aurora-postgresql10'), + engine: DatabaseClusterEngine.AURORA_MYSQL, }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { - Engine: 'aurora-postgresql', - DBClusterParameterGroupName: 'default.aurora-postgresql10', + expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + Engine: 'aurora-mysql', EngineMode: 'serverless', - MasterUsername: { - 'Fn::Join': [ - '', - [ - '{{resolve:secretsmanager:', - { - Ref: 'DatabaseSecret3B817195', - }, - ':SecretString:username::}}', - ], - ], - }, - MasterUserPassword: { - 'Fn::Join': [ - '', - [ - '{{resolve:secretsmanager:', - { - Ref: 'DatabaseSecret3B817195', - }, - ':SecretString:password::}}', - ], - ], - }, + }); + // Expect DbSubnetGroupName and VpcSecurityGroupIds are not defined. + // It can't be done with toHaveResourceLike("", { DbSubnetGroupName: undefined }) + expect(stack).not.toHaveResourceLike('AWS::RDS::DBCluster', { + DbSubnetGroupName: expect.anything(), + }); + expect(stack).not.toHaveResourceLike('AWS::RDS::DBCluster', { + VpcSecurityGroupIds: expect.anything(), }); @@ -898,91 +897,45 @@ describe('serverless cluster', () => { // WHEN new ServerlessCluster(stack, 'Database', { - engine: DatabaseClusterEngine.AURORA_POSTGRESQL, + engine: DatabaseClusterEngine.AURORA_MYSQL, securityGroups: [sg], - parameterGroup: ParameterGroup.fromParameterGroupName(stack, 'ParameterGroup', 'default.aurora-postgresql10'), }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { - Engine: 'aurora-postgresql', - DBClusterParameterGroupName: 'default.aurora-postgresql10', - EngineMode: 'serverless', - MasterUsername: { - 'Fn::Join': [ - '', - [ - '{{resolve:secretsmanager:', - { - Ref: 'DatabaseSecret3B817195', - }, - ':SecretString:username::}}', - ], - ], - }, - MasterUserPassword: { - 'Fn::Join': [ - '', - [ - '{{resolve:secretsmanager:', - { - Ref: 'DatabaseSecret3B817195', - }, - ':SecretString:password::}}', - ], - ], - }, + expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { VpcSecurityGroupIds: ['SecurityGroupId12345'], }); + // Expect DbSubnetGroupName is not defined. + // It can't be done with toHaveResourceLike("", { DbSubnetGroupName: undefined }) + expect(stack).not.toHaveResourceLike('AWS::RDS::DBCluster', { + DbSubnetGroupName: expect.anything(), + }); }); test('can create a Serverless cluster without vpc but with imported subnet group', () => { // GIVEN const stack = testStack(); - const subnetGroup = SubnetGroup.fromSubnetGroupName(stack, 'SubnetGroup12345', 'SubnetGroupId12345'); + const SubnetGroupName = 'SubnetGroupId12345'; + const subnetGroup = SubnetGroup.fromSubnetGroupName(stack, 'SubnetGroup12345', SubnetGroupName); // WHEN new ServerlessCluster(stack, 'Database', { - engine: DatabaseClusterEngine.AURORA_POSTGRESQL, - parameterGroup: ParameterGroup.fromParameterGroupName(stack, 'ParameterGroup', 'default.aurora-postgresql10'), + engine: DatabaseClusterEngine.AURORA_MYSQL, subnetGroup, }); // THEN - expect(stack).toHaveResource('AWS::RDS::DBCluster', { - Engine: 'aurora-postgresql', - DBClusterParameterGroupName: 'default.aurora-postgresql10', - EngineMode: 'serverless', - DBSubnetGroupName: { Ref: 'SubnetGroup12345' }, - MasterUsername: { - 'Fn::Join': [ - '', - [ - '{{resolve:secretsmanager:', - { - Ref: 'DatabaseSecret3B817195', - }, - ':SecretString:username::}}', - ], - ], - }, - MasterUserPassword: { - 'Fn::Join': [ - '', - [ - '{{resolve:secretsmanager:', - { - Ref: 'DatabaseSecret3B817195', - }, - ':SecretString:password::}}', - ], - ], - }, - VpcSecurityGroupIds: ['SecurityGroupId12345'], + expect(stack).toHaveResourceLike('AWS::RDS::DBCluster', { + DBSubnetGroupName: SubnetGroupName, + }); + // Expect VpcSecurityGroupIds is not defined. + // It can't be done with toHaveResourceLike("", { VpcSecurityGroupIds: undefined }) + expect(stack).not.toHaveResourceLike('AWS::RDS::DBCluster', { + VpcSecurityGroupIds: expect.anything(), }); }); - test('can\'t create a Serverless cluster without vpc but with imported vpc subnets', () => { + test("can't create a Serverless cluster without VPC but with imported VPC subnets", () => { // GIVEN const stack = testStack(); @@ -993,10 +946,9 @@ describe('serverless cluster', () => { // THEN expect(() => new ServerlessCluster(stack, 'Database', { - engine: DatabaseClusterEngine.AURORA_POSTGRESQL, - parameterGroup: ParameterGroup.fromParameterGroupName(stack, 'ParameterGroup', 'default.aurora-postgresql10'), + engine: DatabaseClusterEngine.AURORA_MYSQL, vpcSubnets, - })).toThrow(/vpc is required/); + })).toThrow(/A vpc is required to use vpcSubnets in ServerlessCluster. Please add a vpc or remove vpcSubnets/); }); });