diff --git a/packages/@aws-cdk/aws-autoscaling/README.md b/packages/@aws-cdk/aws-autoscaling/README.md index a939ddac4a182..d889ce254e437 100644 --- a/packages/@aws-cdk/aws-autoscaling/README.md +++ b/packages/@aws-cdk/aws-autoscaling/README.md @@ -488,6 +488,27 @@ const aspect = new autoscaling.AutoScalingGroupRequireImdsv2Aspect(); Aspects.of(this).add(aspect); ``` +## Warm Pool + +Auto Scaling offers [a warm pool](https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-warm-pools.html) which gives an ability to decrease latency for applications that have exceptionally long boot times. You can create a warm pool with default parameters as below: + +```ts +declare const autoScalingGroup: autoscaling.AutoScalingGroup; + +autoScalingGroup.addWarmPool(); +``` + +You can also customize a warm pool by configuring parameters: + +```ts +declare const autoScalingGroup: autoscaling.AutoScalingGroup; + +autoScalingGroup.addWarmPool({ + minSize: 1, + reuseOnScaleIn: true, +}); +``` + ## Future work * [ ] CloudWatch Events (impossible to add currently as the AutoScalingGroup ARN is diff --git a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts index e2ec8ed39f3b6..b4960ea780593 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts @@ -23,6 +23,7 @@ import { BasicStepScalingPolicyProps, StepScalingPolicy } from './step-scaling-p import { BaseTargetTrackingProps, PredefinedMetric, TargetTrackingScalingPolicy } from './target-tracking-scaling-policy'; import { TerminationPolicy } from './termination-policy'; import { BlockDevice, BlockDeviceVolume, EbsDeviceVolumeType } from './volume'; +import { WarmPool, WarmPoolOptions } from './warm-pool'; /** * Name tag constant @@ -744,6 +745,16 @@ abstract class AutoScalingGroupBase extends Resource implements IAutoScalingGrou }); } + /** + * Add a pool of pre-initialized EC2 instances that sits alongside an Auto Scaling group + */ + public addWarmPool(options?: WarmPoolOptions): WarmPool { + return new WarmPool(this, 'WarmPool', { + autoScalingGroup: this, + ...options, + }); + } + /** * Scale out or in based on time */ @@ -1630,6 +1641,11 @@ export interface IAutoScalingGroup extends IResource, iam.IGrantable { */ addLifecycleHook(id: string, props: BasicLifecycleHookProps): LifecycleHook; + /** + * Add a pool of pre-initialized EC2 instances that sits alongside an Auto Scaling group + */ + addWarmPool(options?: WarmPoolOptions): WarmPool; + /** * Scale out or in based on time */ diff --git a/packages/@aws-cdk/aws-autoscaling/lib/index.ts b/packages/@aws-cdk/aws-autoscaling/lib/index.ts index 8cfcb36d42497..98d1bf94e7933 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/index.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/index.ts @@ -9,6 +9,7 @@ export * from './step-scaling-policy'; export * from './target-tracking-scaling-policy'; export * from './termination-policy'; export * from './volume'; +export * from './warm-pool'; // AWS::AutoScaling CloudFormation Resources: export * from './autoscaling.generated'; diff --git a/packages/@aws-cdk/aws-autoscaling/lib/warm-pool.ts b/packages/@aws-cdk/aws-autoscaling/lib/warm-pool.ts new file mode 100644 index 0000000000000..c08b0fa45c38c --- /dev/null +++ b/packages/@aws-cdk/aws-autoscaling/lib/warm-pool.ts @@ -0,0 +1,104 @@ +import { Lazy, Names, Resource } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { IAutoScalingGroup } from './auto-scaling-group'; +import { CfnWarmPool } from './autoscaling.generated'; + +/** + * Options for a warm pool + */ +export interface WarmPoolOptions { + /** + * Indicates whether instances in the Auto Scaling group can be returned to the warm pool on scale in. + * + * If the value is not specified, instances in the Auto Scaling group will be terminated + * when the group scales in. + * + * @default false + */ + readonly reuseOnScaleIn?: boolean; + + /** + * The maximum number of instances that are allowed to be in the warm pool + * or in any state except Terminated for the Auto Scaling group. + * + * If the value is not specified, Amazon EC2 Auto Scaling launches and maintains + * the difference between the group's maximum capacity and its desired capacity. + * + * @default - max size of the Auto Scaling group + */ + readonly maxGroupPreparedCapacity?: number; + /** + * The minimum number of instances to maintain in the warm pool. + * + * @default 0 + */ + readonly minSize?: number; + /** + * The instance state to transition to after the lifecycle actions are complete. + * + * @default PoolState.STOPPED + */ + readonly poolState?: PoolState; +} + +/** + * Properties for a warm pool + */ +export interface WarmPoolProps extends WarmPoolOptions { + /** + * The Auto Scaling group to add the warm pool to. + */ + readonly autoScalingGroup: IAutoScalingGroup; +} + +/** + * Define a warm pool + */ +export class WarmPool extends Resource { + constructor(scope: Construct, id: string, props: WarmPoolProps) { + super(scope, id, { + physicalName: Lazy.string({ produce: () => Names.uniqueId(this) }), + }); + + if (props.maxGroupPreparedCapacity && props.maxGroupPreparedCapacity < -1) { + throw new Error('\'maxGroupPreparedCapacity\' parameter should be greater than or equal to -1'); + } + + if (props.minSize && props.minSize < 0) { + throw new Error('\'minSize\' parameter should be greater than or equal to 0'); + } + + new CfnWarmPool(this, 'Resource', { + autoScalingGroupName: props.autoScalingGroup.autoScalingGroupName, + instanceReusePolicy: props.reuseOnScaleIn !== undefined ? { + reuseOnScaleIn: props.reuseOnScaleIn, + } : undefined, + maxGroupPreparedCapacity: props.maxGroupPreparedCapacity, + minSize: props.minSize, + poolState: props.poolState, + }); + } +} + +/** + * The instance state in the warm pool + */ +export enum PoolState { + /** + * Hibernated + * + * To use this state, prerequisites must be in place. + * @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/hibernating-prerequisites.html + */ + HIBERNATED = 'Hibernated', + + /** + * Running + */ + RUNNING = 'Running', + + /** + * Stopped + */ + STOPPED = 'Stopped', +} diff --git a/packages/@aws-cdk/aws-autoscaling/package.json b/packages/@aws-cdk/aws-autoscaling/package.json index 09a081cdbd9fe..46b688d3ae6ec 100644 --- a/packages/@aws-cdk/aws-autoscaling/package.json +++ b/packages/@aws-cdk/aws-autoscaling/package.json @@ -122,6 +122,7 @@ "export:@aws-cdk/aws-autoscaling.IAutoScalingGroup", "props-physical-name:@aws-cdk/aws-autoscaling.AutoScalingGroupProps", "props-physical-name:@aws-cdk/aws-autoscaling.ScheduledActionProps", + "props-physical-name:@aws-cdk/aws-autoscaling.WarmPoolProps", "props-default-doc:@aws-cdk/aws-autoscaling.EbsDeviceOptionsBase.iops", "docs-public-apis:@aws-cdk/aws-autoscaling.ScalingProcess.ADD_TO_LOAD_BALANCER", "docs-public-apis:@aws-cdk/aws-autoscaling.ScalingProcess.SCHEDULED_ACTIONS", diff --git a/packages/@aws-cdk/aws-autoscaling/test/integ.warm-pool.expected.json b/packages/@aws-cdk/aws-autoscaling/test/integ.warm-pool.expected.json new file mode 100644 index 0000000000000..67ee9bf5bebd7 --- /dev/null +++ b/packages/@aws-cdk/aws-autoscaling/test/integ.warm-pool.expected.json @@ -0,0 +1,495 @@ +{ + "Resources": { + "VPCB9E5F0B4": { + "Type": "AWS::EC2::VPC", + "Properties": { + "CidrBlock": "10.0.0.0/16", + "EnableDnsHostnames": true, + "EnableDnsSupport": true, + "InstanceTenancy": "default", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/VPC" + } + ] + } + }, + "VPCPublicSubnet1SubnetB4246D30": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.0.0/18", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableFEE4B781": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1RouteTableAssociation0B0896DC": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + } + } + }, + "VPCPublicSubnet1DefaultRoute91CEF279": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet1RouteTableFEE4B781" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet1EIP6AD938E8": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet1NATGatewayE0556630": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet1SubnetB4246D30" + }, + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet1EIP6AD938E8", + "AllocationId" + ] + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/VPC/PublicSubnet1" + } + ] + } + }, + "VPCPublicSubnet2Subnet74179F39": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.64.0/18", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": true, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Public" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Public" + }, + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTable6F1A15F1": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2RouteTableAssociation5A808732": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + } + } + }, + "VPCPublicSubnet2DefaultRouteB7481BBA": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPublicSubnet2RouteTable6F1A15F1" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "GatewayId": { + "Ref": "VPCIGWB7E252D3" + } + }, + "DependsOn": [ + "VPCVPCGW99B986DC" + ] + }, + "VPCPublicSubnet2EIP4947BC00": { + "Type": "AWS::EC2::EIP", + "Properties": { + "Domain": "vpc", + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPublicSubnet2NATGateway3C070193": { + "Type": "AWS::EC2::NatGateway", + "Properties": { + "SubnetId": { + "Ref": "VPCPublicSubnet2Subnet74179F39" + }, + "AllocationId": { + "Fn::GetAtt": [ + "VPCPublicSubnet2EIP4947BC00", + "AllocationId" + ] + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/VPC/PublicSubnet2" + } + ] + } + }, + "VPCPrivateSubnet1Subnet8BCA10E0": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.128.0/18", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1a", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/VPC/PrivateSubnet1" + } + ] + } + }, + "VPCPrivateSubnet1RouteTableBE8A6027": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/VPC/PrivateSubnet1" + } + ] + } + }, + "VPCPrivateSubnet1RouteTableAssociation347902D1": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + } + } + }, + "VPCPrivateSubnet1DefaultRouteAE1D6490": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet1RouteTableBE8A6027" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet1NATGatewayE0556630" + } + } + }, + "VPCPrivateSubnet2SubnetCFCDAA7A": { + "Type": "AWS::EC2::Subnet", + "Properties": { + "CidrBlock": "10.0.192.0/18", + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "AvailabilityZone": "test-region-1b", + "MapPublicIpOnLaunch": false, + "Tags": [ + { + "Key": "aws-cdk:subnet-name", + "Value": "Private" + }, + { + "Key": "aws-cdk:subnet-type", + "Value": "Private" + }, + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/VPC/PrivateSubnet2" + } + ] + } + }, + "VPCPrivateSubnet2RouteTable0A19E10E": { + "Type": "AWS::EC2::RouteTable", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/VPC/PrivateSubnet2" + } + ] + } + }, + "VPCPrivateSubnet2RouteTableAssociation0C73D413": { + "Type": "AWS::EC2::SubnetRouteTableAssociation", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "SubnetId": { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + } + }, + "VPCPrivateSubnet2DefaultRouteF4F5CFD2": { + "Type": "AWS::EC2::Route", + "Properties": { + "RouteTableId": { + "Ref": "VPCPrivateSubnet2RouteTable0A19E10E" + }, + "DestinationCidrBlock": "0.0.0.0/0", + "NatGatewayId": { + "Ref": "VPCPublicSubnet2NATGateway3C070193" + } + } + }, + "VPCIGWB7E252D3": { + "Type": "AWS::EC2::InternetGateway", + "Properties": { + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/VPC" + } + ] + } + }, + "VPCVPCGW99B986DC": { + "Type": "AWS::EC2::VPCGatewayAttachment", + "Properties": { + "VpcId": { + "Ref": "VPCB9E5F0B4" + }, + "InternetGatewayId": { + "Ref": "VPCIGWB7E252D3" + } + } + }, + "FleetInstanceSecurityGroupA8C3D7AD": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "aws-cdk-autoscaling-integ/Fleet/InstanceSecurityGroup", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/Fleet" + } + ], + "VpcId": { + "Ref": "VPCB9E5F0B4" + } + } + }, + "FleetInstanceRoleA605DB82": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": { + "Fn::Join": [ + "", + [ + "ec2.", + { + "Ref": "AWS::URLSuffix" + } + ] + ] + } + } + } + ], + "Version": "2012-10-17" + }, + "Tags": [ + { + "Key": "Name", + "Value": "aws-cdk-autoscaling-integ/Fleet" + } + ] + } + }, + "FleetInstanceProfileC6192A66": { + "Type": "AWS::IAM::InstanceProfile", + "Properties": { + "Roles": [ + { + "Ref": "FleetInstanceRoleA605DB82" + } + ] + } + }, + "FleetLaunchConfig59F79D36": { + "Type": "AWS::AutoScaling::LaunchConfiguration", + "Properties": { + "ImageId": { + "Ref": "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter" + }, + "InstanceType": "t2.micro", + "IamInstanceProfile": { + "Ref": "FleetInstanceProfileC6192A66" + }, + "SecurityGroups": [ + { + "Fn::GetAtt": [ + "FleetInstanceSecurityGroupA8C3D7AD", + "GroupId" + ] + } + ], + "UserData": { + "Fn::Base64": "#!/bin/bash" + } + }, + "DependsOn": [ + "FleetInstanceRoleA605DB82" + ] + }, + "FleetASG3971DFE5": { + "Type": "AWS::AutoScaling::AutoScalingGroup", + "Properties": { + "MaxSize": "1", + "MinSize": "1", + "AutoScalingGroupName": "ASG", + "LaunchConfigurationName": { + "Ref": "FleetLaunchConfig59F79D36" + }, + "Tags": [ + { + "Key": "Name", + "PropagateAtLaunch": true, + "Value": "aws-cdk-autoscaling-integ/Fleet" + } + ], + "VPCZoneIdentifier": [ + { + "Ref": "VPCPrivateSubnet1Subnet8BCA10E0" + }, + { + "Ref": "VPCPrivateSubnet2SubnetCFCDAA7A" + } + ] + }, + "UpdatePolicy": { + "AutoScalingScheduledAction": { + "IgnoreUnmodifiedGroupSizeProperties": true + } + } + }, + "FleetWarmPoolB57F9BC1": { + "Type": "AWS::AutoScaling::WarmPool", + "Properties": { + "AutoScalingGroupName": { + "Ref": "FleetASG3971DFE5" + } + } + } + }, + "Parameters": { + "SsmParameterValueawsserviceamiamazonlinuxlatestamzn2amihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-autoscaling/test/integ.warm-pool.ts b/packages/@aws-cdk/aws-autoscaling/test/integ.warm-pool.ts new file mode 100644 index 0000000000000..29c30f8401eb5 --- /dev/null +++ b/packages/@aws-cdk/aws-autoscaling/test/integ.warm-pool.ts @@ -0,0 +1,27 @@ +#!/usr/bin/env node +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as cdk from '@aws-cdk/core'; +import * as autoscaling from '../lib'; + +/** + * Stack verification steps: + * + * -- aws autoscaling describe-warm-pool --auto-scaling-group-name ASG has 0 and 'Stopped' as MinSize and PoolState, respectively. + */ +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-autoscaling-integ'); + +const vpc = new ec2.Vpc(stack, 'VPC', { + maxAzs: 2, +}); + +const asg = new autoscaling.AutoScalingGroup(stack, 'Fleet', { + vpc, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO), + machineImage: new ec2.AmazonLinuxImage({ generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2 }), + autoScalingGroupName: 'ASG', +}); + +asg.addWarmPool(); + +app.synth(); diff --git a/packages/@aws-cdk/aws-autoscaling/test/warm-pool.test.ts b/packages/@aws-cdk/aws-autoscaling/test/warm-pool.test.ts new file mode 100644 index 0000000000000..041feac6ce30b --- /dev/null +++ b/packages/@aws-cdk/aws-autoscaling/test/warm-pool.test.ts @@ -0,0 +1,85 @@ +import { Template } from '@aws-cdk/assertions'; +import * as ec2 from '@aws-cdk/aws-ec2'; +import * as cdk from '@aws-cdk/core'; +import * as autoscaling from '../lib'; + +describe('warm pool', () => { + test('we can add a warm pool without properties', () => { + // GIVEN + const stack = new cdk.Stack(); + const asg = newASG(stack); + + // WHEN + asg.addWarmPool(); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::WarmPool', { + AutoScalingGroupName: { + Ref: 'ASG46ED3070', + }, + }); + }); + + test('we can add a warm pool with all optional properties', () => { + // GIVEN + const stack = new cdk.Stack(); + const asg = newASG(stack); + + // WHEN + asg.addWarmPool({ + reuseOnScaleIn: true, + maxGroupPreparedCapacity: 5, + minSize: 2, + poolState: autoscaling.PoolState.HIBERNATED, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AutoScaling::WarmPool', { + AutoScalingGroupName: { + Ref: 'ASG46ED3070', + }, + InstanceReusePolicy: { + ReuseOnScaleIn: true, + }, + MaxGroupPreparedCapacity: 5, + MinSize: 2, + PoolState: 'Hibernated', + }); + }); +}); + +test('adding a warm pool with maxGroupPreparedCapacity smaller than -1 throws an error', () => { + // GIVEN + const stack = new cdk.Stack(); + const asg = newASG(stack); + + // WHEN + expect(() => { + asg.addWarmPool({ + maxGroupPreparedCapacity: -42, + }); + }).toThrow(/'maxGroupPreparedCapacity' parameter should be greater than or equal to -1/); +}); + +test('adding a warm pool with negative minSize throws an error', () => { + // GIVEN + const stack = new cdk.Stack(); + const asg = newASG(stack); + + // WHEN + expect(() => { + asg.addWarmPool({ + minSize: -1, + }); + }).toThrow(/'minSize' parameter should be greater than or equal to 0/); +}); + +function newASG(stack: cdk.Stack) { + const vpc = new ec2.Vpc(stack, 'VPC'); + + return new autoscaling.AutoScalingGroup(stack, 'ASG', { + vpc, + instanceType: ec2.InstanceType.of(ec2.InstanceClass.M4, ec2.InstanceSize.MICRO), + machineImage: new ec2.AmazonLinuxImage(), + }); +}