Skip to content

Commit

Permalink
Merge pull request #2094 from guardian/aa/riff-raff-yaml-stack-deps
Browse files Browse the repository at this point in the history
feat(riff-raff.yaml): Support cross stack dependencies
  • Loading branch information
akash1810 authored Nov 6, 2023
2 parents 5a89774 + 8541732 commit 6935c82
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 1 deletion.
19 changes: 19 additions & 0 deletions .changeset/chatty-rabbits-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
"@guardian/cdk": minor
---

feat(riff-raff.yaml): Support cross stack dependencies

Currently the `riff-raff.yaml` generator is not able to create dependencies between `cloud-formation` deployments. This means each `cloud-formation` deployment could happen at the same time.

This does not work in the scenario where we have:
- Stack A containing a bucket
- Stack B CODE containing an app that uses A's bucket
- Stack B PROD containing an app that uses A's bucket

That is, we can't guarantee Stack A is deployed first.

In this change we add support for the scenario where we have a shared resources stack.
The generated `riff-raff.yaml` file will describe that Stack B CODE, and Stack B PROD depend on Stack A.

It uses the AWS CDK mechanism https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Stack.html#addwbrdependencytarget-reason.
70 changes: 70 additions & 0 deletions src/riff-raff-yaml-file/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1185,4 +1185,74 @@ describe("The RiffRaffYamlFile class", () => {
"
`);
});

it("Should support cloudformation stacks that depend on other cloudformation stacks", () => {
const app = new App({ outdir: "/tmp/cdk.out" });

class SharedResourceStack extends GuStack {}
class ApplicationStack extends GuStack {}

const sharedResources = new SharedResourceStack(app, "Shared-INFRA", {
env: {
region: "eu-west-1",
},
stack: "deploy",
stage: "INFRA",
});

const codeStack = new ApplicationStack(app, "App-CODE", {
env: {
region: "eu-west-1",
},
stack: "deploy",
stage: "CODE",
});

const prodStack = new ApplicationStack(app, "App-PROD", {
env: {
region: "eu-west-1",
},
stack: "deploy",
stage: "PROD",
});

codeStack.addDependency(sharedResources);
prodStack.addDependency(sharedResources);

const actual = new RiffRaffYamlFile(app).toYAML();

expect(actual).toMatchInlineSnapshot(`
"allowedStages:
- INFRA
- CODE
- PROD
deployments:
cfn-eu-west-1-deploy-shared-resource-stack:
type: cloud-formation
regions:
- eu-west-1
stacks:
- deploy
app: shared-resource-stack
contentDirectory: /tmp/cdk.out
parameters:
templateStagePaths:
INFRA: Shared-INFRA.template.json
cfn-eu-west-1-deploy-application-stack:
type: cloud-formation
regions:
- eu-west-1
stacks:
- deploy
app: application-stack
contentDirectory: /tmp/cdk.out
parameters:
templateStagePaths:
CODE: App-CODE.template.json
PROD: App-PROD.template.json
dependencies:
- cfn-eu-west-1-deploy-shared-resource-stack
"
`);
});
});
10 changes: 9 additions & 1 deletion src/riff-raff-yaml-file/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ export class RiffRaffYamlFile {
return cdkStack.node.findAll().filter((_) => _ instanceof GuAutoScalingGroup) as GuAutoScalingGroup[];
}

private getGuStackDependencies(cdkStack: GuStack): GuStack[] {
return cdkStack.dependencies.filter((_) => _ instanceof GuStack) as GuStack[];
}

// eslint-disable-next-line custom-rules/valid-constructors -- this needs to sit above GuStack on the cdk tree
constructor(app: App) {
this.allCdkStacks = app.node.findAll().filter((_) => _ instanceof GuStack) as GuStack[];
Expand Down Expand Up @@ -235,7 +239,11 @@ export class RiffRaffYamlFile {
].flat();
artifactUploads.forEach(({ name, props }) => deployments.set(name, props));

const cfnDeployment = cloudFormationDeployment(stacks, artifactUploads, this.outdir);
const parentStacks: RiffRaffDeployment[] = this.getGuStackDependencies(stack).map((x) =>
cloudFormationDeployment([x], [], this.outdir),
);

const cfnDeployment = cloudFormationDeployment(stacks, [...artifactUploads, ...parentStacks], this.outdir);
deployments.set(cfnDeployment.name, cfnDeployment.props);

const lambdasWithoutAnAlias = lambdas.filter((lambda) => lambda.alias === undefined);
Expand Down

0 comments on commit 6935c82

Please sign in to comment.