From 5d19137293bd134ee7ba34d2551b5f342a3bb533 Mon Sep 17 00:00:00 2001 From: Luca Pizzini Date: Sat, 24 Feb 2024 18:07:13 +0100 Subject: [PATCH] add instanceRemovalPolicy and securityGroupRemovalPolicy properties --- ...emoval-policy-snapshot-stack.template.json | 4 +- .../integ.cluster-removal-policy-snapshot.ts | 1 + packages/aws-cdk-lib/aws-docdb/README.md | 42 ++++++++- packages/aws-cdk-lib/aws-docdb/lib/cluster.ts | 55 ++++++++++-- .../aws-docdb/test/cluster.test.ts | 90 +++++++++++++++++++ 5 files changed, 181 insertions(+), 11 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-removal-policy-snapshot.js.snapshot/aws-cdk-docdb-removal-policy-snapshot-stack.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-removal-policy-snapshot.js.snapshot/aws-cdk-docdb-removal-policy-snapshot-stack.template.json index 00446c1b294c2..042cd76401b0d 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-removal-policy-snapshot.js.snapshot/aws-cdk-docdb-removal-policy-snapshot-stack.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-removal-policy-snapshot.js.snapshot/aws-cdk-docdb-removal-policy-snapshot-stack.template.json @@ -542,8 +542,8 @@ "VPCPublicSubnet2DefaultRouteB7481BBA", "VPCPublicSubnet2RouteTableAssociation5A808732" ], - "UpdateReplacePolicy": "Delete", - "DeletionPolicy": "Delete" + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" } }, "Parameters": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-removal-policy-snapshot.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-removal-policy-snapshot.ts index fe3a386bcb727..c4a5fea0f4abb 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-removal-policy-snapshot.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-docdb/test/integ.cluster-removal-policy-snapshot.ts @@ -42,6 +42,7 @@ class TestStack extends cdk.Stack { parameterGroup: params, kmsKey, removalPolicy: cdk.RemovalPolicy.SNAPSHOT, + instanceRemovalPolicy: cdk.RemovalPolicy.RETAIN, // Remember to cleanup after running this test enablePerformanceInsights: true, }); diff --git a/packages/aws-cdk-lib/aws-docdb/README.md b/packages/aws-cdk-lib/aws-docdb/README.md index 68eb4ee187527..abff67dd579d1 100644 --- a/packages/aws-cdk-lib/aws-docdb/README.md +++ b/packages/aws-cdk-lib/aws-docdb/README.md @@ -194,7 +194,47 @@ const cluster = new docdb.DatabaseCluster(this, 'Database', { ``` **Note**: A `RemovalPolicy.DESTROY` removal policy will be applied to the -cluster's instances and security group as they don't support the snapshot +cluster's instances and security group by default as they don't support the snapshot removal policy. > Visit [DeletionPolicy](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html) for more details. + +To specify a custom removal policy for the cluster's instances, use the +`instanceRemovalPolicy` property: + +```ts +declare const vpc: ec2.Vpc; + +const cluster = new docdb.DatabaseCluster(this, 'Database', { + masterUser: { + username: 'myuser', + }, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.MEMORY5, ec2.InstanceSize.LARGE), + vpcSubnets: { + subnetType: ec2.SubnetType.PUBLIC, + }, + vpc, + removalPolicy: RemovalPolicy.SNAPSHOT, + instanceRemovalPolicy: RemovalPolicy.RETAIN, +}); +``` + +To specify a custom removal policy for the cluster's security group, use the +`securityGroupRemovalPolicy` property: + +```ts +declare const vpc: ec2.Vpc; + +const cluster = new docdb.DatabaseCluster(this, 'Database', { + masterUser: { + username: 'myuser', + }, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.MEMORY5, ec2.InstanceSize.LARGE), + vpcSubnets: { + subnetType: ec2.SubnetType.PUBLIC, + }, + vpc, + removalPolicy: RemovalPolicy.SNAPSHOT, + securityGroupRemovalPolicy: RemovalPolicy.RETAIN, +}); +``` diff --git a/packages/aws-cdk-lib/aws-docdb/lib/cluster.ts b/packages/aws-cdk-lib/aws-docdb/lib/cluster.ts index c2c9232492530..74cf7f0aec8e2 100644 --- a/packages/aws-cdk-lib/aws-docdb/lib/cluster.ts +++ b/packages/aws-cdk-lib/aws-docdb/lib/cluster.ts @@ -135,6 +135,11 @@ export interface DatabaseClusterProps { * removal policy also applies to the implicit security group created for the * cluster if one is not supplied as a parameter. * + * When set to `SNAPSHOT`, the removal policy for the instances and the security group + * will default to `DESTROY` as those resources do not support the policy. + * + * Use the `instanceRemovalPolicy` and `securityGroupRemovalPolicy` to change the behavior. + * * @default - Retain cluster. */ readonly removalPolicy?: RemovalPolicy; @@ -190,6 +195,28 @@ export interface DatabaseClusterProps { * @default - false */ readonly enablePerformanceInsights?: boolean; + + /** + * The removal policy to apply to the cluster's instances. + * + * Cannot be set to `SNAPSHOT`. + * + * @default - `RemovalPolicy.DESTROY` when `removalPolicy` is set to `SNAPSHOT`, `removalPolicy` otherwise. + * + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html + */ + readonly instanceRemovalPolicy?: RemovalPolicy; + + /** + * The removal policy to apply to the cluster's security group. + * + * Cannot be set to `SNAPSHOT`. + * + * @default - `RemovalPolicy.DESTROY` when `removalPolicy` is set to `SNAPSHOT`, `removalPolicy` otherwise. + * + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html + */ + readonly securityGroupRemovalPolicy?: RemovalPolicy; } /** @@ -422,10 +449,7 @@ export class DatabaseCluster extends DatabaseClusterBase { }); // 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. - // AWS::EC2::SecurityGroup does not support snapshot removal policy - // see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html - const securityGroupRemovalPolicy = !props.removalPolicy || props.removalPolicy !== RemovalPolicy.SNAPSHOT ? - props.removalPolicy : RemovalPolicy.DESTROY; + const securityGroupRemovalPolicy = this.getSecurityGroupRemovalPolicy(props); (securityGroup.node.defaultChild as CfnResource).applyRemovalPolicy(securityGroupRemovalPolicy, { applyToUpdateReplacePolicy: true, }); @@ -508,10 +532,7 @@ export class DatabaseCluster extends DatabaseClusterBase { throw new Error('At least one instance is required'); } - // AWS::DocDB::DBInstance does not support SNAPSHOT removal policy - // see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html - const instanceRemovalPolicy = !props.removalPolicy || props.removalPolicy !== RemovalPolicy.SNAPSHOT ? - props.removalPolicy : RemovalPolicy.DESTROY; + const instanceRemovalPolicy = this.getInstanceRemovalPolicy(props); for (let i = 0; i < instanceCount; i++) { const instanceIndex = i + 1; @@ -561,6 +582,24 @@ export class DatabaseCluster extends DatabaseClusterBase { } } + private getInstanceRemovalPolicy(props: DatabaseClusterProps) { + if (props.instanceRemovalPolicy === RemovalPolicy.SNAPSHOT) { + throw new Error('AWS::DocDB::DBInstance does not support the SNAPSHOT removal policy'); + } + if (props.instanceRemovalPolicy) return props.instanceRemovalPolicy; + return !props.removalPolicy || props.removalPolicy !== RemovalPolicy.SNAPSHOT ? + props.removalPolicy : RemovalPolicy.DESTROY; + } + + private getSecurityGroupRemovalPolicy(props: DatabaseClusterProps) { + if (props.securityGroupRemovalPolicy === RemovalPolicy.SNAPSHOT) { + throw new Error('AWS::EC2::SecurityGroup does not support the SNAPSHOT removal policy'); + } + if (props.securityGroupRemovalPolicy) return props.securityGroupRemovalPolicy; + return !props.removalPolicy || props.removalPolicy !== RemovalPolicy.SNAPSHOT ? + props.removalPolicy : RemovalPolicy.DESTROY; + } + /** * Adds the single user rotation of the master password to this cluster. * diff --git a/packages/aws-cdk-lib/aws-docdb/test/cluster.test.ts b/packages/aws-cdk-lib/aws-docdb/test/cluster.test.ts index fa8a3434f88a3..9e2aabd7a9edb 100644 --- a/packages/aws-cdk-lib/aws-docdb/test/cluster.test.ts +++ b/packages/aws-cdk-lib/aws-docdb/test/cluster.test.ts @@ -998,6 +998,96 @@ describe('DatabaseCluster', () => { UpdateReplacePolicy: 'Delete', }); }); + + test('can specify instances removal policy', () => { + // GIVEN + const stack = testStack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + + // WHEN + new DatabaseCluster(stack, 'Database', { + instances: 1, + masterUser: { + username: 'admin', + password: cdk.SecretValue.unsafePlainText('tooshort'), + }, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), + vpc, + instanceRemovalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + // THEN + Template.fromStack(stack).hasResource('AWS::DocDB::DBInstance', { + DeletionPolicy: 'Delete', + UpdateReplacePolicy: 'Delete', + }); + }); + + test('can specify security group removal policy', () => { + // GIVEN + const stack = testStack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + + // WHEN + new DatabaseCluster(stack, 'Database', { + instances: 1, + masterUser: { + username: 'admin', + password: cdk.SecretValue.unsafePlainText('tooshort'), + }, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), + vpc, + securityGroupRemovalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + // THEN + Template.fromStack(stack).hasResource('AWS::EC2::SecurityGroup', { + DeletionPolicy: 'Delete', + UpdateReplacePolicy: 'Delete', + }); + }); + + test('instances removal policy cannot be set to snapshot', () => { + // GIVEN + const stack = testStack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + + // WHEN + // THEN + expect(() => { + new DatabaseCluster(stack, 'Database', { + instances: 1, + masterUser: { + username: 'admin', + password: cdk.SecretValue.unsafePlainText('tooshort'), + }, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), + vpc, + instanceRemovalPolicy: cdk.RemovalPolicy.SNAPSHOT, + }); + }).toThrow(/AWS::DocDB::DBInstance does not support the SNAPSHOT removal policy/); + }); + + test('security group removal policy cannot be set to snapshot', () => { + // GIVEN + const stack = testStack(); + const vpc = new ec2.Vpc(stack, 'VPC'); + + // WHEN + // THEN + expect(() => { + new DatabaseCluster(stack, 'Database', { + instances: 1, + masterUser: { + username: 'admin', + password: cdk.SecretValue.unsafePlainText('tooshort'), + }, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.SMALL), + vpc, + securityGroupRemovalPolicy: cdk.RemovalPolicy.SNAPSHOT, + }); + }).toThrow(/AWS::EC2::SecurityGroup does not support the SNAPSHOT removal policy/); + }); }); function testStack() {