Skip to content

Commit

Permalink
Merge pull request #392 from guardian/aa-apply-app-tag
Browse files Browse the repository at this point in the history
fix: Improve GuAutoScalingGroup's support of a multi-app stack
  • Loading branch information
akash1810 authored Apr 8, 2021
2 parents 28c65ba + ad9aea6 commit d896ada
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 6 deletions.
42 changes: 41 additions & 1 deletion src/constructs/autoscaling/asg.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import "@aws-cdk/assert/jest";
import "../../utils/test/jest";
import { SynthUtils } from "@aws-cdk/assert/lib/synth-utils";
import { InstanceType, UserData, Vpc } from "@aws-cdk/aws-ec2";
import { ApplicationProtocol } from "@aws-cdk/aws-elasticloadbalancingv2";
import { Stack } from "@aws-cdk/core";
import { Stage } from "../../constants";
import { simpleGuStackForTesting } from "../../utils/test";
import { TrackingTag } from "../../constants/library-info";
import type { SynthedStack } from "../../utils/test";
import { alphabeticalTags, simpleGuStackForTesting } from "../../utils/test";
import { GuSecurityGroup } from "../ec2";
import { GuApplicationTargetGroup } from "../loadbalancing";
import type { GuAutoScalingGroupProps } from "./asg";
Expand All @@ -32,6 +34,44 @@ describe("The GuAutoScalingGroup", () => {
app: "testing",
};

test("Uses the AppIdentity to create the logicalId and tag the resource", () => {
const stack = simpleGuStackForTesting();
new GuAutoScalingGroup(stack, "MyAutoScalingGroup", defaultProps);

expect(stack).toHaveResourceOfTypeAndLogicalId(
"AWS::AutoScaling::AutoScalingGroup",
/MyAutoScalingGroupTesting[A-Z0-9]+/
);

expect(stack).toHaveResource("AWS::AutoScaling::AutoScalingGroup", {
Tags: alphabeticalTags([
{
Key: "App",
PropagateAtLaunch: true,
Value: "testing",
},
{
Key: "Stack",
PropagateAtLaunch: true,
Value: "test-stack",
},
{
Key: "Stage",
PropagateAtLaunch: true,
Value: {
Ref: "Stage",
},
},
{
Key: "Name",
PropagateAtLaunch: true,
Value: "Test/MyAutoScalingGroupTesting",
},
{ ...TrackingTag, PropagateAtLaunch: true },
]),
});
});

test("adds the AMI parameter if no imageId prop provided", () => {
const stack = simpleGuStackForTesting();

Expand Down
6 changes: 4 additions & 2 deletions src/constructs/autoscaling/asg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { ApplicationTargetGroup } from "@aws-cdk/aws-elasticloadbalancingv2
import { Stage } from "../../constants";
import type { GuStack } from "../core";
import { GuAmiParameter, GuInstanceTypeParameter } from "../core";
import type { AppIdentity } from "../core/identity";
import { AppIdentity } from "../core/identity";
import { GuHttpsEgressSecurityGroup } from "../ec2";

// Since we want to override the types of what gets passed in for the below props,
Expand Down Expand Up @@ -104,7 +104,7 @@ export class GuAutoScalingGroup extends AutoScalingGroup {
securityGroup: GuHttpsEgressSecurityGroup.forVpc(scope, props),
};

super(scope, id, mergedProps);
super(scope, AppIdentity.suffixText(props, id), mergedProps);

mergedProps.targetGroup && this.attachToApplicationTargetGroup(mergedProps.targetGroup);

Expand All @@ -117,5 +117,7 @@ export class GuAutoScalingGroup extends AutoScalingGroup {
cfnAsg.addDeletionOverride("UpdatePolicy");

if (mergedProps.overrideId) cfnAsg.overrideLogicalId(id);

AppIdentity.taggedConstruct(props, this);
}
}
1 change: 1 addition & 0 deletions src/utils/test/alphabetical-tags.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
interface Tag {
Key: string;
Value: unknown;
PropagateAtLaunch?: boolean;
}

export function alphabeticalTags(tags: Tag[]): Tag[] {
Expand Down
41 changes: 41 additions & 0 deletions src/utils/test/jest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { SynthUtils } from "@aws-cdk/assert";
import type { GuStack } from "../../constructs/core";
import type { SynthedStack } from "./synthed-stack";

declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace -- custom Jest matcher
namespace jest {
interface Matchers<R> {
toHaveResourceOfTypeAndLogicalId(resourceType: string, logicalId: string | RegExp): R;
}
}
}

expect.extend({
/**
* A helper function to assert a stack has a resource of a particular type and logicalId.
* Useful for when the logicalId is auto-generated.
* @param stack the stack to make an assertion on
* @param resourceType the AWS resource type string, for example "AWS::AutoScaling::AutoScalingGroup"
* @param logicalId a string or regex pattern to match against the resource's logicalId
*/
toHaveResourceOfTypeAndLogicalId(stack: GuStack, resourceType: string, logicalId: string | RegExp) {
const json = SynthUtils.toCloudFormation(stack) as SynthedStack;

const matchResult = Object.entries(json.Resources).find(([key, { Type }]) => {
const logicalIdMatch = logicalId instanceof RegExp ? logicalId.test(key) : key === logicalId;
const typeMatch = Type === resourceType;
return logicalIdMatch && typeMatch;
});

return matchResult
? {
pass: true,
message: () => "",
}
: {
pass: false,
message: () => `No resource found matching logicalId ${logicalId.toString()} and Type ${resourceType}`,
};
},
});
18 changes: 15 additions & 3 deletions src/utils/test/synthed-stack.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
import type { Stage } from "../../constants";

interface Parameter {
Type: string;
Description: string;
Default?: string | number;
AllowedValues?: Array<string | number>;
}

type ResourceProperty = Record<string, unknown>;
type Resource = Record<string, { Type: string; Properties: ResourceProperty }>;

export interface SynthedStack {
Parameters: Record<string, { Properties: Record<string, unknown> }>;
Mappings: Record<string, unknown>;
Resources: Record<string, { Properties: Record<string, unknown> }>;
Parameters: Record<string, Parameter>;
Mappings: Record<Stage, unknown>;
Resources: Resource;
}

0 comments on commit d896ada

Please sign in to comment.