Skip to content

Commit 419dd37

Browse files
committed
feat(rds): change the default retention policy of Cluster and DB Instance to Snapshot
The 'Snapshot' retention policy is a special one used only for RDS. It deletes the underlying resource, but before doing that, creates a snapshot of it, so that the data is not lost. Use the 'Snapshot' policy instead of 'Retain', for the DatabaseCluster and DbInstance resources. Fixes aws#3298 BREAKING CHANGE: the default retention policy for RDS Cluster and DbInstance is now 'Snapshot'
1 parent be1dd1b commit 419dd37

9 files changed

+66
-66
lines changed

packages/@aws-cdk/aws-rds/lib/cluster.ts

+9-8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { DatabaseClusterAttributes, IDatabaseCluster } from './cluster-ref';
88
import { DatabaseSecret } from './database-secret';
99
import { Endpoint } from './endpoint';
1010
import { ClusterParameterGroup, IParameterGroup } from './parameter-group';
11+
import { applyInstanceDeletionPolicy } from './private/instance-deletion-policy';
1112
import { BackupProps, DatabaseClusterEngine, InstanceProps, Login, RotationMultiUserOptions } from './props';
1213
import { CfnDBCluster, CfnDBInstance, CfnDBSubnetGroup } from './rds.generated';
1314

@@ -124,9 +125,9 @@ export interface DatabaseClusterProps {
124125
* The removal policy to apply when the cluster and its instances are removed
125126
* from the stack or replaced during an update.
126127
*
127-
* @default - Retain cluster.
128+
* @default - Snapshot (remove the cluster and instances, but retain a snapshot of the data)
128129
*/
129-
readonly removalPolicy?: RemovalPolicy
130+
readonly removalPolicy?: RemovalPolicy;
130131

131132
/**
132133
* The interval, in seconds, between points when Amazon RDS collects enhanced
@@ -461,9 +462,11 @@ export class DatabaseCluster extends DatabaseClusterBase {
461462
storageEncrypted: props.kmsKey ? true : props.storageEncrypted,
462463
});
463464

464-
cluster.applyRemovalPolicy(props.removalPolicy, {
465-
applyToUpdateReplacePolicy: true,
466-
});
465+
// if removalPolicy was not specified,
466+
// leave it as the default, which is Snapshot
467+
if (props.removalPolicy) {
468+
cluster.applyRemovalPolicy(props.removalPolicy);
469+
}
467470

468471
this.clusterIdentifier = cluster.ref;
469472

@@ -519,9 +522,7 @@ export class DatabaseCluster extends DatabaseClusterBase {
519522
monitoringRoleArn: monitoringRole && monitoringRole.roleArn,
520523
});
521524

522-
instance.applyRemovalPolicy(props.removalPolicy, {
523-
applyToUpdateReplacePolicy: true,
524-
});
525+
applyInstanceDeletionPolicy(instance, props.removalPolicy);
525526

526527
// We must have a dependency on the NAT gateway provider here to create
527528
// things in the right order.

packages/@aws-cdk/aws-rds/lib/instance.ts

+5-10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { DatabaseSecret } from './database-secret';
1010
import { Endpoint } from './endpoint';
1111
import { IOptionGroup } from './option-group';
1212
import { IParameterGroup } from './parameter-group';
13+
import { applyInstanceDeletionPolicy } from './private/instance-deletion-policy';
1314
import { DatabaseClusterEngine, RotationMultiUserOptions } from './props';
1415
import { CfnDBInstance, CfnDBInstanceProps, CfnDBSubnetGroup } from './rds.generated';
1516

@@ -536,7 +537,7 @@ export interface DatabaseInstanceNewProps {
536537
* The CloudFormation policy to apply when the instance is removed from the
537538
* stack or replaced during an update.
538539
*
539-
* @default RemovalPolicy.Retain
540+
* @default Snapshot (remove the resource, but retain a snapshot of the data)
540541
*/
541542
readonly removalPolicy?: RemovalPolicy
542543

@@ -886,9 +887,7 @@ export class DatabaseInstance extends DatabaseInstanceSource implements IDatabas
886887
const portAttribute = Token.asNumber(instance.attrEndpointPort);
887888
this.instanceEndpoint = new Endpoint(instance.attrEndpointAddress, portAttribute);
888889

889-
instance.applyRemovalPolicy(props.removalPolicy, {
890-
applyToUpdateReplacePolicy: true,
891-
});
890+
applyInstanceDeletionPolicy(instance, props.removalPolicy);
892891

893892
if (secret) {
894893
this.secret = secret.attach(this);
@@ -984,9 +983,7 @@ export class DatabaseInstanceFromSnapshot extends DatabaseInstanceSource impleme
984983
const portAttribute = Token.asNumber(instance.attrEndpointPort);
985984
this.instanceEndpoint = new Endpoint(instance.attrEndpointAddress, portAttribute);
986985

987-
instance.applyRemovalPolicy(props.removalPolicy, {
988-
applyToUpdateReplacePolicy: true,
989-
});
986+
applyInstanceDeletionPolicy(instance, props.removalPolicy);
990987

991988
if (secret) {
992989
this.secret = secret.attach(this);
@@ -1054,9 +1051,7 @@ export class DatabaseInstanceReadReplica extends DatabaseInstanceNew implements
10541051
const portAttribute = Token.asNumber(instance.attrEndpointPort);
10551052
this.instanceEndpoint = new Endpoint(instance.attrEndpointAddress, portAttribute);
10561053

1057-
instance.applyRemovalPolicy(props.removalPolicy, {
1058-
applyToUpdateReplacePolicy: true,
1059-
});
1054+
applyInstanceDeletionPolicy(instance, props.removalPolicy);
10601055

10611056
this.setLogRetention();
10621057
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { CfnDeletionPolicy, RemovalPolicy } from '@aws-cdk/core';
2+
import { CfnDBInstance } from '../';
3+
4+
export function applyInstanceDeletionPolicy(cfnDbInstance: CfnDBInstance, removalPolicy: RemovalPolicy | undefined): void {
5+
const instanceDeletionPolicy = toCfnDeletionPolicy(removalPolicy);
6+
const instanceCfnOptions = cfnDbInstance.cfnOptions;
7+
instanceCfnOptions.deletionPolicy = instanceDeletionPolicy;
8+
instanceCfnOptions.updateReplacePolicy = instanceDeletionPolicy;
9+
}
10+
11+
function toCfnDeletionPolicy(removalPolicy: RemovalPolicy | undefined): CfnDeletionPolicy {
12+
switch (removalPolicy) {
13+
// the CFN default is actually Delete when dbClusterIdentifier is set for DBInstance!
14+
case undefined: return CfnDeletionPolicy.SNAPSHOT;
15+
case RemovalPolicy.RETAIN: return CfnDeletionPolicy.RETAIN;
16+
case RemovalPolicy.DESTROY: return CfnDeletionPolicy.DELETE;
17+
}
18+
}

packages/@aws-cdk/aws-rds/test/integ.cluster-rotation.lit.expected.json

+6-8
Original file line numberDiff line numberDiff line change
@@ -705,9 +705,7 @@
705705
]
706706
}
707707
]
708-
},
709-
"UpdateReplacePolicy": "Retain",
710-
"DeletionPolicy": "Retain"
708+
}
711709
},
712710
"DatabaseInstance1844F58FD": {
713711
"Type": "AWS::RDS::DBInstance",
@@ -726,8 +724,8 @@
726724
"VPCPrivateSubnet2DefaultRouteF4F5CFD2",
727725
"VPCPrivateSubnet3DefaultRoute27F311AE"
728726
],
729-
"UpdateReplacePolicy": "Retain",
730-
"DeletionPolicy": "Retain"
727+
"UpdateReplacePolicy": "Snapshot",
728+
"DeletionPolicy": "Snapshot"
731729
},
732730
"DatabaseInstance2AA380DEE": {
733731
"Type": "AWS::RDS::DBInstance",
@@ -746,8 +744,8 @@
746744
"VPCPrivateSubnet2DefaultRouteF4F5CFD2",
747745
"VPCPrivateSubnet3DefaultRoute27F311AE"
748746
],
749-
"UpdateReplacePolicy": "Retain",
750-
"DeletionPolicy": "Retain"
747+
"UpdateReplacePolicy": "Snapshot",
748+
"DeletionPolicy": "Snapshot"
751749
},
752750
"DatabaseRotationSingleUserSecurityGroupAC6E0E73": {
753751
"Type": "AWS::EC2::SecurityGroup",
@@ -817,4 +815,4 @@
817815
}
818816
}
819817
}
820-
}
818+
}

packages/@aws-cdk/aws-rds/test/integ.cluster-s3.expected.json

+6-8
Original file line numberDiff line numberDiff line change
@@ -667,9 +667,7 @@
667667
]
668668
}
669669
]
670-
},
671-
"UpdateReplacePolicy": "Retain",
672-
"DeletionPolicy": "Retain"
670+
}
673671
},
674672
"DatabaseInstance1844F58FD": {
675673
"Type": "AWS::RDS::DBInstance",
@@ -688,8 +686,8 @@
688686
"VPCPublicSubnet1DefaultRoute91CEF279",
689687
"VPCPublicSubnet2DefaultRouteB7481BBA"
690688
],
691-
"UpdateReplacePolicy": "Retain",
692-
"DeletionPolicy": "Retain"
689+
"UpdateReplacePolicy": "Snapshot",
690+
"DeletionPolicy": "Snapshot"
693691
},
694692
"DatabaseInstance2AA380DEE": {
695693
"Type": "AWS::RDS::DBInstance",
@@ -708,8 +706,8 @@
708706
"VPCPublicSubnet1DefaultRoute91CEF279",
709707
"VPCPublicSubnet2DefaultRouteB7481BBA"
710708
],
711-
"UpdateReplacePolicy": "Retain",
712-
"DeletionPolicy": "Retain"
709+
"UpdateReplacePolicy": "Snapshot",
710+
"DeletionPolicy": "Snapshot"
713711
}
714712
}
715-
}
713+
}

packages/@aws-cdk/aws-rds/test/integ.cluster.expected.json

+6-8
Original file line numberDiff line numberDiff line change
@@ -499,9 +499,7 @@
499499
]
500500
}
501501
]
502-
},
503-
"UpdateReplacePolicy": "Retain",
504-
"DeletionPolicy": "Retain"
502+
}
505503
},
506504
"DatabaseInstance1844F58FD": {
507505
"Type": "AWS::RDS::DBInstance",
@@ -520,8 +518,8 @@
520518
"VPCPublicSubnet1DefaultRoute91CEF279",
521519
"VPCPublicSubnet2DefaultRouteB7481BBA"
522520
],
523-
"UpdateReplacePolicy": "Retain",
524-
"DeletionPolicy": "Retain"
521+
"UpdateReplacePolicy": "Snapshot",
522+
"DeletionPolicy": "Snapshot"
525523
},
526524
"DatabaseInstance2AA380DEE": {
527525
"Type": "AWS::RDS::DBInstance",
@@ -540,8 +538,8 @@
540538
"VPCPublicSubnet1DefaultRoute91CEF279",
541539
"VPCPublicSubnet2DefaultRouteB7481BBA"
542540
],
543-
"UpdateReplacePolicy": "Retain",
544-
"DeletionPolicy": "Retain"
541+
"UpdateReplacePolicy": "Snapshot",
542+
"DeletionPolicy": "Snapshot"
545543
}
546544
}
547-
}
545+
}

packages/@aws-cdk/aws-rds/test/integ.instance.lit.expected.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -694,8 +694,8 @@
694694
}
695695
]
696696
},
697-
"UpdateReplacePolicy": "Retain",
698-
"DeletionPolicy": "Retain"
697+
"UpdateReplacePolicy": "Snapshot",
698+
"DeletionPolicy": "Snapshot"
699699
},
700700
"InstanceLogRetentiontrace487771C8": {
701701
"Type": "Custom::LogRetention",
@@ -1122,4 +1122,4 @@
11221122
"Description": "Artifact hash for asset \"82c54bfa7c42ba410d6d18dad983ba51c93a5ea940818c5c20230f8b59c19d4e\""
11231123
}
11241124
}
1125-
}
1125+
}

packages/@aws-cdk/aws-rds/test/test.cluster.ts

+11-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { expect, haveResource, ResourcePart, SynthUtils } from '@aws-cdk/assert';
1+
import { countResources, expect, haveResource, ResourcePart, SynthUtils } from '@aws-cdk/assert';
22
import * as ec2 from '@aws-cdk/aws-ec2';
33
import { ManagedPolicy, Role, ServicePrincipal } from '@aws-cdk/aws-iam';
44
import * as kms from '@aws-cdk/aws-kms';
@@ -8,7 +8,7 @@ import { Test } from 'nodeunit';
88
import { ClusterParameterGroup, DatabaseCluster, DatabaseClusterEngine, ParameterGroup } from '../lib';
99

1010
export = {
11-
'check that instantiation works'(test: Test) {
11+
'creating a Cluster also creates 2 DB Instances'(test: Test) {
1212
// GIVEN
1313
const stack = testStack();
1414
const vpc = new ec2.Vpc(stack, 'VPC');
@@ -28,20 +28,17 @@ export = {
2828

2929
// THEN
3030
expect(stack).to(haveResource('AWS::RDS::DBCluster', {
31-
Properties: {
32-
Engine: 'aurora',
33-
DBSubnetGroupName: { Ref: 'DatabaseSubnets56F17B9A' },
34-
MasterUsername: 'admin',
35-
MasterUserPassword: 'tooshort',
36-
VpcSecurityGroupIds: [ {'Fn::GetAtt': ['DatabaseSecurityGroup5C91FDCB', 'GroupId']}],
37-
},
38-
DeletionPolicy: 'Retain',
39-
UpdateReplacePolicy: 'Retain',
40-
}, ResourcePart.CompleteDefinition));
31+
Engine: 'aurora',
32+
DBSubnetGroupName: { Ref: 'DatabaseSubnets56F17B9A' },
33+
MasterUsername: 'admin',
34+
MasterUserPassword: 'tooshort',
35+
VpcSecurityGroupIds: [ {'Fn::GetAtt': ['DatabaseSecurityGroup5C91FDCB', 'GroupId']}],
36+
}));
4137

38+
expect(stack).to(countResources('AWS::RDS::DBInstance', 2));
4239
expect(stack).to(haveResource('AWS::RDS::DBInstance', {
43-
DeletionPolicy: 'Retain',
44-
UpdateReplacePolicy: 'Retain',
40+
DeletionPolicy: 'Snapshot',
41+
UpdateReplacePolicy: 'Snapshot',
4542
}, ResourcePart.CompleteDefinition));
4643

4744
test.done();

packages/@aws-cdk/aws-rds/test/test.instance.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,8 @@ export = {
105105
},
106106
],
107107
},
108-
DeletionPolicy: 'Retain',
109-
UpdateReplacePolicy: 'Retain',
110-
}, ResourcePart.CompleteDefinition));
111-
112-
expect(stack).to(haveResource('AWS::RDS::DBInstance', {
113-
DeletionPolicy: 'Retain',
114-
UpdateReplacePolicy: 'Retain',
108+
DeletionPolicy: 'Snapshot',
109+
UpdateReplacePolicy: 'Snapshot',
115110
}, ResourcePart.CompleteDefinition));
116111

117112
expect(stack).to(haveResource('AWS::RDS::DBSubnetGroup', {

0 commit comments

Comments
 (0)