Skip to content

Commit

Permalink
feat(backup): Support backups provided by DevX
Browse files Browse the repository at this point in the history
Adds a new property `withBackup` to `GuStack` to enable backups provided by DevX.

When `true`, all supported resources in the stack will receive a new tag `devx-backup-enabled`.

To opt in/out an individual resource, you can manually apply this tag.

See https://github.com/guardian/aws-backup.
  • Loading branch information
akash1810 committed Nov 8, 2023
1 parent e42afb7 commit 5ee2c09
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 1 deletion.
13 changes: 13 additions & 0 deletions .changeset/lazy-wasps-repair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@guardian/cdk": minor
---

feat(backup): Support backups provided by DevX

Adds a new property `withBackup` to `GuStack` to enable backups provided by DevX.

When `true`, all supported resources in the stack will receive a new tag `devx-backup-enabled`.

To opt in/out an individual resource, you can manually apply this tag.

See https://github.com/guardian/aws-backup.
48 changes: 48 additions & 0 deletions src/aspects/aws-backup.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { App, Tags } from "aws-cdk-lib";
import { Vpc } from "aws-cdk-lib/aws-ec2";
import { DatabaseInstance, DatabaseInstanceEngine } from "aws-cdk-lib/aws-rds";
import { GuStack } from "../constructs/core";
import { GuTemplate } from "../utils/test";

describe("AwsBackupTag aspect", () => {
it("should add the 'devx-backup-enabled' tag as 'true' when backups are enabled", () => {
const app = new App();
const stack = new GuStack(app, "Test", { stack: "test", stage: "TEST", withBackup: true });

const vpc = new Vpc(stack, "TestVpc");
new DatabaseInstance(stack, "MyDatabase", { engine: DatabaseInstanceEngine.POSTGRES, vpc });

GuTemplate.fromStack(stack).hasGuTaggedResource("AWS::RDS::DBInstance", {
additionalTags: [
{
Key: "devx-backup-enabled",
Value: "true",
},
],
});
});

it("should allow the 'devx-backup-enabled' tag to be overridden", () => {
const app = new App();
const stack = new GuStack(app, "Test", {
stack: "test",
stage: "TEST",
withBackup: true, // enable backups on all resources in this stack
});

const vpc = new Vpc(stack, "TestVpc");
const database = new DatabaseInstance(stack, "MyDatabase", { engine: DatabaseInstanceEngine.POSTGRES, vpc });

// Disable backups on this one resource
Tags.of(database).add("devx-backup-enabled", "false");

GuTemplate.fromStack(stack).hasGuTaggedResource("AWS::RDS::DBInstance", {
additionalTags: [
{
Key: "devx-backup-enabled",
Value: "false",
},
],
});
});
});
25 changes: 25 additions & 0 deletions src/aspects/aws-backup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { IAspect } from "aws-cdk-lib";
import { CfnResource, TagManager } from "aws-cdk-lib";
import type { IConstruct } from "constructs";

/**
* Applies the tags that enable backups for supported resources.
*
* @see https://github.com/guardian/aws-backup
*/
export class AwsBackupTag implements IAspect {
/**
* These resources are backed up by https://github.com/guardian/aws-backup.
*/
static resourceTypes: string[] = ["AWS::RDS::DBInstance", "AWS::DynamoDB::Table"];

public visit(node: IConstruct): void {
if (node instanceof CfnResource) {
const { cfnResourceType } = node;

if (AwsBackupTag.resourceTypes.includes(cfnResourceType) && TagManager.isTaggable(node)) {
node.tags.setTag("devx-backup-enabled", "true");
}
}
}
}
22 changes: 21 additions & 1 deletion src/constructs/core/stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Annotations, App, Aspects, CfnParameter, LegacyStackSynthesizer, Stack,
import { Template } from "aws-cdk-lib/assertions";
import type { IConstruct } from "constructs";
import gitUrlParse from "git-url-parse";
import { AwsBackupTag } from "../../aspects/aws-backup";
import { CfnIncludeReporter } from "../../aspects/cfn-include-reporter";
import { CfnParameterReporter } from "../../aspects/cfn-parameter-reporter";
import { Metadata } from "../../aspects/metadata";
Expand Down Expand Up @@ -42,6 +43,15 @@ export interface GuStackProps extends Omit<StackProps, "stackName"> {
* please do not override this.
*/
withoutMetadata?: boolean;

/**
* Set to enable all resources in the stack for backup provided by https://github.com/guardian/aws-backup.
*
* @default false - backups are not enabled
*
* @see https://github.com/guardian/aws-backup
*/
withBackup?: boolean;
}

/**
Expand Down Expand Up @@ -127,7 +137,13 @@ export class GuStack extends Stack implements StackStageIdentity {

// eslint-disable-next-line custom-rules/valid-constructors -- GuStack is the exception as it must take an App
constructor(scope: App, id: string, props: GuStackProps) {
const { cloudFormationStackName = process.env.GU_CFN_STACK_NAME, stack, stage, withoutTags } = props;
const {
cloudFormationStackName = process.env.GU_CFN_STACK_NAME,
stack,
stage,
withoutTags,
withBackup = false,
} = props;

super(scope, id, {
...props,
Expand Down Expand Up @@ -162,6 +178,10 @@ export class GuStack extends Stack implements StackStageIdentity {

Aspects.of(this).add(new CfnIncludeReporter());
Aspects.of(this).add(new CfnParameterReporter());

if (withBackup) {
Aspects.of(this).add(new AwsBackupTag());
}
}

/**
Expand Down

0 comments on commit 5ee2c09

Please sign in to comment.