Skip to content

Commit

Permalink
Merge pull request #768 from umccr/implement-long-term-tier2-backup-r…
Browse files Browse the repository at this point in the history
…etention-production

Implemented OrcaBus database tier-2 backup retention
  • Loading branch information
victorskl authored Dec 10, 2024
2 parents 64cd84d + ace6611 commit 851b012
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 0 deletions.
3 changes: 3 additions & 0 deletions config/stacks/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ const getDatabaseConstructProps = (stage: AppStage): ConfigurableDatabaseProps =
enablePerformanceInsights: true,
removalPolicy: RemovalPolicy.DESTROY,
backupRetention: Duration.days(1),
createT2BackupRetention: false,
};
case AppStage.GAMMA:
return {
Expand All @@ -167,6 +168,7 @@ const getDatabaseConstructProps = (stage: AppStage): ConfigurableDatabaseProps =
enablePerformanceInsights: true,
removalPolicy: RemovalPolicy.DESTROY,
backupRetention: Duration.days(1),
createT2BackupRetention: false,
};
case AppStage.PROD:
return {
Expand All @@ -178,6 +180,7 @@ const getDatabaseConstructProps = (stage: AppStage): ConfigurableDatabaseProps =
enablePerformanceInsights: true,
removalPolicy: RemovalPolicy.RETAIN,
backupRetention: Duration.days(7),
createT2BackupRetention: true,
};
}
};
Expand Down
49 changes: 49 additions & 0 deletions lib/workload/stateful/stacks/shared/constructs/database/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import * as rds from 'aws-cdk-lib/aws-rds';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ssm from 'aws-cdk-lib/aws-ssm';
import * as sm from 'aws-cdk-lib/aws-secretsmanager';
import * as backup from 'aws-cdk-lib/aws-backup';
import * as events from 'aws-cdk-lib/aws-events';
import { SecurityGroup } from 'aws-cdk-lib/aws-ec2';
import { DatabaseCluster } from 'aws-cdk-lib/aws-rds';

Expand Down Expand Up @@ -90,9 +92,17 @@ export type ConfigurableDatabaseProps = MonitoringProps & {
*/
secretRotationSchedule: Duration;
/**
* Tier 1 backup - using built-in RDS system capability
*
* RDS aurora automated backup retention (in Duration)
*/
backupRetention: Duration;
/**
* Tier 2 backup - leveraging another AWS Backup service is intentional redundancy
*
* Create long term tier-2 RDS aurora backup using AWS Backup service (in boolean)
*/
createT2BackupRetention?: boolean;
};

/**
Expand Down Expand Up @@ -199,5 +209,44 @@ export class DatabaseConstruct extends Construct {
description: 'orcabus rds writer cluster endpoint host',
parameterName: props.clusterEndpointHostParameterName,
});

/**
* See compliance rule
* https://trello.com/c/RFnECxRa
* https://github.com/umccr/orcabus/issues/178
*
* Backup weekly and keep it for 6 weeks
* Cron At 17:00 on every Sunday UTC = AEST/AEDT 3AM/4AM on every Monday
* cron(0 17 ? * SUN *)
*/
if (props.createT2BackupRetention) {
const t2BackupVault = new backup.BackupVault(this, 'OrcaBusDatabaseTier2BackupVault', {
backupVaultName: 'OrcaBusDatabaseTier2BackupVault',
removalPolicy: RemovalPolicy.RETAIN,
});

const t2BackupPlan = new backup.BackupPlan(this, 'OrcaBusDatabaseTier2BackupPlan', {
backupPlanName: 'OrcaBusDatabaseTier2BackupPlan',
backupVault: t2BackupVault,
});
t2BackupPlan.applyRemovalPolicy(RemovalPolicy.RETAIN);

// https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk-lib/aws-backup/lib/rule.ts
t2BackupPlan.addRule(
new backup.BackupPlanRule({
ruleName: 'Weekly',
scheduleExpression: events.Schedule.cron({
hour: '17',
minute: '0',
weekDay: 'SUN',
}),
deleteAfter: Duration.days(42),
})
);

t2BackupPlan.addSelection('OrcaBusDatabaseTier2BackupSelection', {
resources: [backup.BackupResource.fromRdsServerlessCluster(this.cluster)],
});
}
}
}
1 change: 1 addition & 0 deletions test/stateful/pipeline/deployment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ function applyNagSuppression(stackId: string, stack: Stack) {
'/SharedStack/EventBusConstruct/UniversalEventArchiver/UniversalEventArchiver/ServiceRole/Resource',
'/SharedStack/EventBusConstruct/UniversalEventArchiver/UniversalEventArchiver/ServiceRole/DefaultPolicy/Resource',
'/SharedStack/DatabaseConstruct/Cluster/MonitoringRole/Resource',
'/SharedStack/DatabaseConstruct/OrcaBusDatabaseTier2BackupPlan/OrcaBusDatabaseTier2BackupSelection/Role/Resource',
],
[
{
Expand Down
51 changes: 51 additions & 0 deletions test/stateful/shared/databaseConstruct.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,54 @@ test('Test other SG Allow Ingress to DB SG', () => {
},
});
});

test('Test tier-2 backup created for DBCluster and it is compliance in production configuration', () => {
const prodConfig = getEnvironmentConfig(AppStage.PROD);
if (!prodConfig) throw new Error('No construct config for the test');

expect(prodConfig).toBeTruthy();
const dbProps = prodConfig.stackProps.statefulConfig.sharedStackProps.databaseProps;

new DatabaseConstruct(stack, 'TestDatabaseConstruct', {
vpc,
...dbProps,
});
const template = Template.fromStack(stack);

template.hasResourceProperties('AWS::RDS::DBCluster', {
DBClusterIdentifier: 'orcabus-db',
DatabaseName: 'orcabus',
DBClusterParameterGroupName: dbProps.parameterGroupName,
ServerlessV2ScalingConfiguration: {
MaxCapacity: dbProps.maxACU,
MinCapacity: dbProps.minACU,
},
});

// Assert that we are compliance with long term tier-2 backup plan

template.hasResource('AWS::Backup::BackupVault', {
DeletionPolicy: 'Retain',
});
template.hasResourceProperties('AWS::Backup::BackupVault', {
BackupVaultName: 'OrcaBusDatabaseTier2BackupVault',
});

template.hasResource('AWS::Backup::BackupPlan', {
DeletionPolicy: 'Retain',
});
template.hasResourceProperties('AWS::Backup::BackupPlan', {
BackupPlan: {
BackupPlanName: 'OrcaBusDatabaseTier2BackupPlan',
BackupPlanRule: [
{
Lifecycle: {
DeleteAfterDays: 42,
},
RuleName: 'Weekly',
ScheduleExpression: 'cron(0 17 ? * SUN *)',
},
],
},
});
});

0 comments on commit 851b012

Please sign in to comment.