From f98e9c5932f5138a4274d66f6fa5053a21f31845 Mon Sep 17 00:00:00 2001 From: Ashleigh Carr Date: Thu, 11 Jul 2024 13:27:25 +0100 Subject: [PATCH 1/4] feat(asg): Allow setting the UpdatePolicy on ASGs provisioned by our pattern. We want to try new ways of deploying our applications, and we need to be able to control the update policy of the ASGs that are provisioned by our pattern. Co-authored-by: Akash Askoolum --- src/constructs/autoscaling/asg.ts | 5 ++++- src/patterns/ec2-app/base.ts | 9 ++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/constructs/autoscaling/asg.ts b/src/constructs/autoscaling/asg.ts index 0c6b0d6300..bb8a02c969 100644 --- a/src/constructs/autoscaling/asg.ts +++ b/src/constructs/autoscaling/asg.ts @@ -94,6 +94,7 @@ export class GuAutoScalingGroup extends GuAppAwareConstruct(AutoScalingGroup) { vpc, withoutImdsv2 = false, httpPutResponseHopLimit, + updatePolicy, } = props; // Ensure min and max are defined in the same way. Throwing an `Error` when necessary. For example when min is defined via a Mapping, but max is not. @@ -163,7 +164,9 @@ export class GuAutoScalingGroup extends GuAppAwareConstruct(AutoScalingGroup) { // A CDK AutoScalingGroup comes with this update policy, whereas the CFN autscaling group // leaves it to the default value, which is actually false. // { UpdatePolicy: { autoScalingScheduledAction: { IgnoreUnmodifiedGroupSizeProperties: true }} - cfnAsg.addDeletionOverride("UpdatePolicy"); + if (!updatePolicy) { + cfnAsg.addDeletionOverride("UpdatePolicy"); + } Tags.of(launchTemplate).add("App", app); } diff --git a/src/patterns/ec2-app/base.ts b/src/patterns/ec2-app/base.ts index 308c8e5664..955028e22d 100644 --- a/src/patterns/ec2-app/base.ts +++ b/src/patterns/ec2-app/base.ts @@ -1,6 +1,6 @@ /* eslint "@guardian/tsdoc-required/tsdoc-required": 2 -- to begin rolling this out for public APIs. */ import { Duration, SecretValue, Tags } from "aws-cdk-lib"; -import type { BlockDevice } from "aws-cdk-lib/aws-autoscaling"; +import type { BlockDevice, UpdatePolicy } from "aws-cdk-lib/aws-autoscaling"; import { HealthCheck } from "aws-cdk-lib/aws-autoscaling"; import { ProviderAttribute, @@ -298,6 +298,11 @@ export interface GuEc2AppProps extends AppIdentity { * for example when sharing the instance profile with a docker container running on the instance. */ instanceMetadataHopLimit?: number; + + /** + * TODO + */ + updatePolicy?: UpdatePolicy; } function restrictedCidrRanges(ranges: IPeer[]) { @@ -352,6 +357,7 @@ export class GuEc2App extends Construct { privateSubnets = GuVpc.subnetsFromParameter(scope, { type: SubnetType.PRIVATE, app }), publicSubnets = GuVpc.subnetsFromParameter(scope, { type: SubnetType.PUBLIC, app }), instanceMetadataHopLimit, + updatePolicy, } = props; super(scope, app); // The assumption is `app` is unique @@ -400,6 +406,7 @@ export class GuEc2App extends Construct { ...(blockDevices && { blockDevices }), imageRecipe, httpPutResponseHopLimit: instanceMetadataHopLimit, + updatePolicy, }); // This allows automatic shipping of instance Cloud Init logs when using the From d49c69038bc9f3f6a8a1224caf683c5d4d88eb86 Mon Sep 17 00:00:00 2001 From: Ashleigh Carr Date: Thu, 11 Jul 2024 17:11:17 +0100 Subject: [PATCH 2/4] docs(asg): Document what the `updatePolicy` prop does on our App patterns --- src/patterns/ec2-app/base.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/patterns/ec2-app/base.ts b/src/patterns/ec2-app/base.ts index 955028e22d..3d36a6c26c 100644 --- a/src/patterns/ec2-app/base.ts +++ b/src/patterns/ec2-app/base.ts @@ -300,7 +300,12 @@ export interface GuEc2AppProps extends AppIdentity { instanceMetadataHopLimit?: number; /** - * TODO + * Specify an update policy for the ASG created by this pattern. + * + * @see https://docs.aws.amazon.com/cdk/api/latest/docs/aws-autoscaling-readme.html#update-policy + * + * @defaultValue UpdatePolicy.none() - Cloudformation does not attempt to rotate instances in the ASG + * and must rely on riffraff to do so. */ updatePolicy?: UpdatePolicy; } From 3e8dbb5395545c8c204201308777e08af3d59fd2 Mon Sep 17 00:00:00 2001 From: Ashleigh Carr Date: Thu, 11 Jul 2024 17:17:49 +0100 Subject: [PATCH 3/4] test(asg): Verify that UpdatePolicies are correctly cloudformed when specified in a pattern --- src/patterns/ec2-app/base.test.ts | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/patterns/ec2-app/base.test.ts b/src/patterns/ec2-app/base.test.ts index 5850c7af21..71fd009a49 100644 --- a/src/patterns/ec2-app/base.test.ts +++ b/src/patterns/ec2-app/base.test.ts @@ -1,5 +1,5 @@ import { Match, Template } from "aws-cdk-lib/assertions"; -import { BlockDeviceVolume, EbsDeviceVolumeType } from "aws-cdk-lib/aws-autoscaling"; +import { BlockDeviceVolume, EbsDeviceVolumeType, UpdatePolicy } from "aws-cdk-lib/aws-autoscaling"; import { InstanceClass, InstanceSize, InstanceType, Peer, Port, Vpc } from "aws-cdk-lib/aws-ec2"; import { type CfnLoadBalancer } from "aws-cdk-lib/aws-elasticloadbalancingv2"; import { AccessScope, MetadataKeys } from "../../constants"; @@ -1095,4 +1095,32 @@ describe("the GuEC2App pattern", function () { SslPolicy: "ELBSecurityPolicy-TLS13-1-2-2021-06", }); }); + + it("has a defined UpdatePolicy when provided with one", function () { + const stack = simpleGuStackForTesting(); + new GuEc2App(stack, { + applicationPort: 3000, + app: "test-gu-ec2-app", + access: { scope: AccessScope.PUBLIC }, + instanceType: InstanceType.of(InstanceClass.T4G, InstanceSize.MEDIUM), + monitoringConfiguration: { noMonitoring: true }, + userData: "#!/bin/dev foobarbaz", + certificateProps: { + domainName: "domain-name-for-your-application.example", + }, + scaling: { + minimumInstances: 1, + }, + instanceMetadataHopLimit: 2, + updatePolicy: UpdatePolicy.replacingUpdate(), + }); + + Template.fromStack(stack).hasResource("AWS::AutoScaling::AutoScalingGroup", { + UpdatePolicy: { + AutoScalingReplacingUpdate: { + WillReplace: true, + }, + }, + }); + }); }); From 59ffa9de79ce0ad279ccf52fa6c940e81c26fcf4 Mon Sep 17 00:00:00 2001 From: Ashleigh Carr Date: Thu, 11 Jul 2024 17:19:02 +0100 Subject: [PATCH 4/4] docs(asg): Add Changeset for new UpdatePolicy prop on our Patterns --- .changeset/grumpy-experts-divide.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/grumpy-experts-divide.md diff --git a/.changeset/grumpy-experts-divide.md b/.changeset/grumpy-experts-divide.md new file mode 100644 index 0000000000..2f8bdf6f02 --- /dev/null +++ b/.changeset/grumpy-experts-divide.md @@ -0,0 +1,5 @@ +--- +"@guardian/cdk": minor +--- + +feat(asg): Allow setting the UpdatePolicy on ASGs provisioned by our EC2 patterns