From 06ad7981f29ca8f48d975d334f09bbb0197a7ecd Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 2 Jul 2020 18:45:35 -0400 Subject: [PATCH 01/35] preliminary API --- packages/@aws-cdk/aws-lambda/lib/alias.ts | 36 +++++++++- .../@aws-cdk/aws-lambda/lib/lambda-version.ts | 55 +++++++++++++++- .../lib/scalable-version-attribute.ts | 65 +++++++++++++++++++ packages/@aws-cdk/aws-lambda/package.json | 2 + 4 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 packages/@aws-cdk/aws-lambda/lib/scalable-version-attribute.ts diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index 638f52e11a09f..9a3e1f1594b7c 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -1,9 +1,12 @@ +import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; -import { Construct } from '@aws-cdk/core'; +import * as iam from '@aws-cdk/aws-iam'; +import { Construct, Stack} from '@aws-cdk/core'; import { EventInvokeConfigOptions } from './event-invoke-config'; import { IFunction, QualifiedFunctionBase } from './function-base'; import { extractQualifierFromArn, IVersion } from './lambda-version'; import { CfnAlias } from './lambda.generated'; +import { EnableScalingProps, IScalableVersionAttribute, ScalableVersionAttribute } from './scalable-version-attribute'; export interface IAlias extends IFunction { /** @@ -129,6 +132,9 @@ export class Alias extends QualifiedFunctionBase implements IAlias { protected readonly canCreatePermissions: boolean = true; + private provisionedConcurrency: boolean; + private scalableAlias?: ScalableVersionAttribute; + constructor(scope: Construct, id: string, props: AliasProps) { super(scope, id, { physicalName: props.aliasName, @@ -147,6 +153,8 @@ export class Alias extends QualifiedFunctionBase implements IAlias { provisionedConcurrencyConfig: this.determineProvisionedConcurrency(props), }); + this.provisionedConcurrency = props.provisionedConcurrentExecutions ? true : false; + this.functionArn = this.getResourceArnAttribute(alias.ref, { service: 'lambda', resource: 'function', @@ -193,6 +201,32 @@ export class Alias extends QualifiedFunctionBase implements IAlias { }); } + public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute { + if (!this.provisionedConcurrency) { + throw new Error('Autoscaling is available for aliases with provisioned concurrency only'); + } + if (this.scalableAlias) { + throw new Error('Autoscaling already enabled for this alias'); + } + // Use a Service Linked Role + // https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html + const role = iam.Role.fromRoleArn(this, 'ScalingRole', Stack.of(this).formatArn({ + service: 'iam', + region: '', + resource: 'role/aws-service-role/lambda.application-autoscaling.amazonaws.com', + resourceName: 'AWSServiceRoleForApplicationAutoScaling_LambdaConcurrency', + })); + + return this.scalableAlias = new ScalableVersionAttribute(this, 'VersionScaling', { + serviceNamespace: appscaling.ServiceNamespace.LAMBDA, + dimension: 'lambda:function:ProvisionedConcurrency', + minCapacity: props.minCapacity, + maxCapacity: props.maxCapacity, + resourceId: `function:${this.lambda.functionName}:${this.aliasName}`, + role, + }); + } + /** * Calculate the routingConfig parameter from the input props */ diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts index 09c62a5530690..138ad4e0a70fc 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts @@ -1,10 +1,13 @@ +import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; -import { Construct, Fn, RemovalPolicy } from '@aws-cdk/core'; +import * as iam from '@aws-cdk/aws-iam'; +import { Construct, Fn, RemovalPolicy, Stack } from '@aws-cdk/core'; import { Alias, AliasOptions } from './alias'; import { EventInvokeConfigOptions } from './event-invoke-config'; import { Function } from './function'; import { IFunction, QualifiedFunctionBase } from './function-base'; import { CfnVersion } from './lambda.generated'; +import { EnableScalingProps, IScalableVersionAttribute, ScalableVersionAttribute } from './scalable-version-attribute'; import { addAlias } from './util'; export interface IVersion extends IFunction { @@ -25,6 +28,12 @@ export interface IVersion extends IFunction { * @param options Alias options */ addAlias(aliasName: string, options?: AliasOptions): Alias; + + /** + * Enables autoscaling on a version with provisioned concurrency + * @param props The properties for autoscaling + */ + autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute; } /** @@ -128,6 +137,10 @@ export class Version extends QualifiedFunctionBase implements IVersion { public addAlias(name: string, opts: AliasOptions = { }): Alias { return addAlias(this, this, name, opts); } + + public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute { + return this.autoScaleProvisionedConcurrency(props); + } } return new Import(scope, id); } @@ -147,6 +160,11 @@ export class Version extends QualifiedFunctionBase implements IVersion { public addAlias(name: string, opts: AliasOptions = { }): Alias { return addAlias(this, this, name, opts); } + + public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute { + return this.autoScaleProvisionedConcurrency(props); + } + } return new Import(scope, id); } @@ -159,6 +177,9 @@ export class Version extends QualifiedFunctionBase implements IVersion { protected readonly qualifier: string; protected readonly canCreatePermissions = true; + private provisionedConcurrency: boolean; + private scalableVersion?: ScalableVersionAttribute; + constructor(scope: Construct, id: string, props: VersionProps) { super(scope, id); @@ -177,6 +198,8 @@ export class Version extends QualifiedFunctionBase implements IVersion { }); } + this.provisionedConcurrency = props.provisionedConcurrentExecutions ? true : false; + this.version = version.attrVersion; this.functionArn = version.ref; this.functionName = `${this.lambda.functionName}:${this.version}`; @@ -223,6 +246,36 @@ export class Version extends QualifiedFunctionBase implements IVersion { return addAlias(this, this, aliasName, options); } + /** + * Enable autoscaling for the given lambda version. Requires that lambda version has provisioned concurrency. + * @param props The properties for autoscaling + */ + public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute { + if (!this.provisionedConcurrency) { + throw new Error('Autoscaling is available for versions with provisioned concurrency only'); + } + if (this.scalableVersion) { + throw new Error('Autoscaling already enabled for this version'); + } + // Use a Service Linked Role + // https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html + const role = iam.Role.fromRoleArn(this, 'ScalingRole', Stack.of(this).formatArn({ + service: 'iam', + region: '', + resource: 'role/aws-service-role/lambda.application-autoscaling.amazonaws.com', + resourceName: 'AWSServiceRoleForApplicationAutoScaling_LambdaConcurrency', + })); + + return this.scalableVersion = new ScalableVersionAttribute(this, 'VersionScaling', { + serviceNamespace: appscaling.ServiceNamespace.LAMBDA, + dimension: 'lambda:function:ProvisionedConcurrency', + minCapacity: props.minCapacity, + maxCapacity: props.maxCapacity, + resourceId: `function:${this.lambda.functionName}:${this.version}`, + role, + }); + } + /** * Validate that the provisionedConcurrentExecutions makes sense * diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-version-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-version-attribute.ts new file mode 100644 index 0000000000000..60e08fa128951 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-version-attribute.ts @@ -0,0 +1,65 @@ +import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; + +/** + * Interface for scalable attributes + */ +export interface IScalableVersionAttribute { + /** + * Scale out or in to keep utilization at a given level + */ + scaleOnUtilization(props: UtilizationScalingProps): void; + /** + * Add scheduled scaling for this scaling attribute + */ + scaleOnSchedule(id: string, actions: appscaling.ScalingSchedule): void; +} + +/** + * Properties for enabling Lambda utilization tracking + */ +export interface UtilizationScalingProps extends appscaling.BaseTargetTrackingProps { + /** + * Target utilization percentage for the attribute + */ + readonly targetUtilizationPercent: number; +} + +/** + * A scalable lambda version attribute + */ +export class ScalableVersionAttribute extends appscaling.BaseScalableAttribute { + /** + * Scale out or in to keep utilization at a given level + */ + public scaleOnUtilization(props: UtilizationScalingProps) { + if (props.targetUtilizationPercent < 0 || props.targetUtilizationPercent > 1) { + throw new Error('target utilization is a percentage and must be between 0 and 1.'); + } + super.doScaleToTrackMetric('Tracking', { + targetValue: props.targetUtilizationPercent, + predefinedMetric: appscaling.PredefinedMetric.LAMBDA_PROVISIONED_CONCURRENCY_UTILIZATION, + ...props, + }); + } + + /** + * Scale out or in based on time + */ + public scaleOnSchedule(id: string, action: appscaling.ScalingSchedule) { + super.doScaleOnSchedule(id, action); + } +} + +/** + * Properties for enabling Lambda capacity scaling + */ +export interface EnableScalingProps { + /** + * Minimum capacity to scale to + */ + minCapacity: number; + /** + * Maximum capacity to scale to + */ + maxCapacity: number; +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 73416597604ba..705ea983249db 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -83,6 +83,7 @@ "sinon": "^9.0.2" }, "dependencies": { + "@aws-cdk/aws-applicationautoscaling": "0.0.0", "@aws-cdk/aws-cloudwatch": "0.0.0", "@aws-cdk/aws-ec2": "0.0.0", "@aws-cdk/aws-events": "0.0.0", @@ -97,6 +98,7 @@ }, "homepage": "https://github.com/aws/aws-cdk", "peerDependencies": { + "@aws-cdk/aws-applicationautoscaling": "0.0.0", "@aws-cdk/aws-cloudwatch": "0.0.0", "@aws-cdk/aws-ec2": "0.0.0", "@aws-cdk/aws-events": "0.0.0", From 2aeebb48162e76c0dbb92e1d9b5548aeba5ae14b Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 2 Jul 2020 19:36:14 -0400 Subject: [PATCH 02/35] fixed awslint issues --- packages/@aws-cdk/aws-lambda/lib/alias.ts | 4 ++++ packages/@aws-cdk/aws-lambda/lib/index.ts | 1 + packages/@aws-cdk/aws-lambda/lib/lambda-version.ts | 10 +++++----- .../aws-lambda/lib/scalable-version-attribute.ts | 4 ++-- packages/@aws-cdk/aws-lambda/package.json | 5 ++++- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index 9a3e1f1594b7c..ccfa61a5a0ba7 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -201,6 +201,10 @@ export class Alias extends QualifiedFunctionBase implements IAlias { }); } + /** + * Enable autoscaling for the given lambda version. Requires that lambda version has provisioned concurrency. + * @param props The properties for autoscaling + */ public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute { if (!this.provisionedConcurrency) { throw new Error('Autoscaling is available for aliases with provisioned concurrency only'); diff --git a/packages/@aws-cdk/aws-lambda/lib/index.ts b/packages/@aws-cdk/aws-lambda/lib/index.ts index b494e924c604a..ca77a6f309ed8 100644 --- a/packages/@aws-cdk/aws-lambda/lib/index.ts +++ b/packages/@aws-cdk/aws-lambda/lib/index.ts @@ -12,6 +12,7 @@ export * from './event-source'; export * from './event-source-mapping'; export * from './destination'; export * from './event-invoke-config'; +export * from './scalable-version-attribute'; export * from './log-retention'; diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts index 138ad4e0a70fc..97ade5c671a9d 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts @@ -29,11 +29,11 @@ export interface IVersion extends IFunction { */ addAlias(aliasName: string, options?: AliasOptions): Alias; - /** - * Enables autoscaling on a version with provisioned concurrency - * @param props The properties for autoscaling - */ - autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute; + // /** + // * Enables autoscaling on a version with provisioned concurrency + // * @param props The properties for autoscaling + // */ + // autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute; } /** diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-version-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-version-attribute.ts index 60e08fa128951..c9a11cd89349b 100644 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-version-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-version-attribute.ts @@ -57,9 +57,9 @@ export interface EnableScalingProps { /** * Minimum capacity to scale to */ - minCapacity: number; + readonly minCapacity: number; /** * Maximum capacity to scale to */ - maxCapacity: number; + readonly maxCapacity: number; } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 705ea983249db..dc66d1a27b082 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -167,7 +167,10 @@ "props-default-doc:@aws-cdk/aws-lambda.Permission.sourceArn", "docs-public-apis:@aws-cdk/aws-lambda.ResourceBindOptions", "docs-public-apis:@aws-cdk/aws-lambda.VersionAttributes", - "props-physical-name:@aws-cdk/aws-lambda.EventInvokeConfigProps" + "props-physical-name:@aws-cdk/aws-lambda.EventInvokeConfigProps", + "props-struct-name:@aws-cdk/aws-lambda.IScalableVersionAttribute", + "construct-ctor-props-type:@aws-cdk/aws-lambda.ScalableVersionAttribute", + "construct-interface-extends-iconstruct:@aws-cdk/aws-lambda.IScalableVersionAttribute" ] }, "stability": "stable", From 721f9b3b62917876eee5037d337326290ef56ce3 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 2 Jul 2020 19:47:29 -0400 Subject: [PATCH 03/35] added dependency --- packages/@aws-cdk/aws-lambda/lib/alias.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index ccfa61a5a0ba7..b2dfeda4fc96a 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -221,7 +221,7 @@ export class Alias extends QualifiedFunctionBase implements IAlias { resourceName: 'AWSServiceRoleForApplicationAutoScaling_LambdaConcurrency', })); - return this.scalableAlias = new ScalableVersionAttribute(this, 'VersionScaling', { + this.scalableAlias = new ScalableVersionAttribute(this, 'VersionScaling', { serviceNamespace: appscaling.ServiceNamespace.LAMBDA, dimension: 'lambda:function:ProvisionedConcurrency', minCapacity: props.minCapacity, @@ -229,6 +229,9 @@ export class Alias extends QualifiedFunctionBase implements IAlias { resourceId: `function:${this.lambda.functionName}:${this.aliasName}`, role, }); + + // Make sure that alias is created before the autoscale resources + this.scalableAlias.node.addDependency(this); } /** From 9d50cd35c8faeb550f4ba7ea6dfdb5cff5c93c52 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 6 Jul 2020 10:45:46 -0400 Subject: [PATCH 04/35] removed autoscaling from lambda-version --- .../@aws-cdk/aws-lambda/lib/lambda-version.ts | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts index 97ade5c671a9d..5070060845cf1 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts @@ -1,13 +1,10 @@ -import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; -import * as iam from '@aws-cdk/aws-iam'; import { Construct, Fn, RemovalPolicy, Stack } from '@aws-cdk/core'; import { Alias, AliasOptions } from './alias'; import { EventInvokeConfigOptions } from './event-invoke-config'; import { Function } from './function'; import { IFunction, QualifiedFunctionBase } from './function-base'; import { CfnVersion } from './lambda.generated'; -import { EnableScalingProps, IScalableVersionAttribute, ScalableVersionAttribute } from './scalable-version-attribute'; import { addAlias } from './util'; export interface IVersion extends IFunction { @@ -137,10 +134,6 @@ export class Version extends QualifiedFunctionBase implements IVersion { public addAlias(name: string, opts: AliasOptions = { }): Alias { return addAlias(this, this, name, opts); } - - public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute { - return this.autoScaleProvisionedConcurrency(props); - } } return new Import(scope, id); } @@ -160,11 +153,6 @@ export class Version extends QualifiedFunctionBase implements IVersion { public addAlias(name: string, opts: AliasOptions = { }): Alias { return addAlias(this, this, name, opts); } - - public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute { - return this.autoScaleProvisionedConcurrency(props); - } - } return new Import(scope, id); } @@ -177,9 +165,6 @@ export class Version extends QualifiedFunctionBase implements IVersion { protected readonly qualifier: string; protected readonly canCreatePermissions = true; - private provisionedConcurrency: boolean; - private scalableVersion?: ScalableVersionAttribute; - constructor(scope: Construct, id: string, props: VersionProps) { super(scope, id); @@ -198,8 +183,6 @@ export class Version extends QualifiedFunctionBase implements IVersion { }); } - this.provisionedConcurrency = props.provisionedConcurrentExecutions ? true : false; - this.version = version.attrVersion; this.functionArn = version.ref; this.functionName = `${this.lambda.functionName}:${this.version}`; @@ -246,36 +229,6 @@ export class Version extends QualifiedFunctionBase implements IVersion { return addAlias(this, this, aliasName, options); } - /** - * Enable autoscaling for the given lambda version. Requires that lambda version has provisioned concurrency. - * @param props The properties for autoscaling - */ - public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute { - if (!this.provisionedConcurrency) { - throw new Error('Autoscaling is available for versions with provisioned concurrency only'); - } - if (this.scalableVersion) { - throw new Error('Autoscaling already enabled for this version'); - } - // Use a Service Linked Role - // https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html - const role = iam.Role.fromRoleArn(this, 'ScalingRole', Stack.of(this).formatArn({ - service: 'iam', - region: '', - resource: 'role/aws-service-role/lambda.application-autoscaling.amazonaws.com', - resourceName: 'AWSServiceRoleForApplicationAutoScaling_LambdaConcurrency', - })); - - return this.scalableVersion = new ScalableVersionAttribute(this, 'VersionScaling', { - serviceNamespace: appscaling.ServiceNamespace.LAMBDA, - dimension: 'lambda:function:ProvisionedConcurrency', - minCapacity: props.minCapacity, - maxCapacity: props.maxCapacity, - resourceId: `function:${this.lambda.functionName}:${this.version}`, - role, - }); - } - /** * Validate that the provisionedConcurrentExecutions makes sense * From 595b4086a5cca41e942a3ef2518fe0d7651b4f3c Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 6 Jul 2020 13:16:26 -0400 Subject: [PATCH 05/35] modified things based off of code review --- packages/@aws-cdk/aws-lambda/lib/alias.ts | 39 ++++--- packages/@aws-cdk/aws-lambda/lib/index.ts | 2 +- .../@aws-cdk/aws-lambda/lib/lambda-version.ts | 2 +- .../lib/scalable-function-attribute.ts | 106 ++++++++++++++++++ packages/@aws-cdk/aws-lambda/package.json | 6 +- 5 files changed, 130 insertions(+), 25 deletions(-) create mode 100644 packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index b2dfeda4fc96a..43da459c90d08 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -1,4 +1,3 @@ -import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as iam from '@aws-cdk/aws-iam'; import { Construct, Stack} from '@aws-cdk/core'; @@ -6,7 +5,7 @@ import { EventInvokeConfigOptions } from './event-invoke-config'; import { IFunction, QualifiedFunctionBase } from './function-base'; import { extractQualifierFromArn, IVersion } from './lambda-version'; import { CfnAlias } from './lambda.generated'; -import { EnableScalingProps, IScalableVersionAttribute, ScalableVersionAttribute } from './scalable-version-attribute'; +import { EnableScalingProps, IScalableFunctionAttribute, ScalableFunctionAttribute } from './scalable-function-attribute'; export interface IAlias extends IFunction { /** @@ -133,7 +132,8 @@ export class Alias extends QualifiedFunctionBase implements IAlias { protected readonly canCreatePermissions: boolean = true; private provisionedConcurrency: boolean; - private scalableAlias?: ScalableVersionAttribute; + private scalableAlias?: ScalableFunctionAttribute; + private readonly scalingRole: iam.IRole; constructor(scope: Construct, id: string, props: AliasProps) { super(scope, id, { @@ -154,6 +154,14 @@ export class Alias extends QualifiedFunctionBase implements IAlias { }); this.provisionedConcurrency = props.provisionedConcurrentExecutions ? true : false; + // Use a Service Linked Role + // https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html + this.scalingRole = iam.Role.fromRoleArn(this, 'ScalingRole', Stack.of(this).formatArn({ + service: 'iam', + region: '', + resource: 'role/aws-service-role/lambda.application-autoscaling.amazonaws.com', + resourceName: 'AWSServiceRoleForApplicationAutoScaling_LambdaConcurrency', + })); this.functionArn = this.getResourceArnAttribute(alias.ref, { service: 'lambda', @@ -202,36 +210,27 @@ export class Alias extends QualifiedFunctionBase implements IAlias { } /** - * Enable autoscaling for the given lambda version. Requires that lambda version has provisioned concurrency. + * Enable autoscaling for provisioned concurrency lambda functions. Tracks the predefined metric + * LambdaProvisionedConcurrencyUtilization. More information about lambda performance metrics here: + * https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html#monitoring-metrics-concurrency * @param props The properties for autoscaling */ - public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute { + public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableFunctionAttribute { + if (!this.provisionedConcurrency) { throw new Error('Autoscaling is available for aliases with provisioned concurrency only'); } if (this.scalableAlias) { throw new Error('Autoscaling already enabled for this alias'); } - // Use a Service Linked Role - // https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html - const role = iam.Role.fromRoleArn(this, 'ScalingRole', Stack.of(this).formatArn({ - service: 'iam', - region: '', - resource: 'role/aws-service-role/lambda.application-autoscaling.amazonaws.com', - resourceName: 'AWSServiceRoleForApplicationAutoScaling_LambdaConcurrency', - })); - this.scalableAlias = new ScalableVersionAttribute(this, 'VersionScaling', { - serviceNamespace: appscaling.ServiceNamespace.LAMBDA, - dimension: 'lambda:function:ProvisionedConcurrency', + return this.scalableAlias = new ScalableFunctionAttribute(this, 'AliasScaling', { minCapacity: props.minCapacity, maxCapacity: props.maxCapacity, resourceId: `function:${this.lambda.functionName}:${this.aliasName}`, - role, + dimension: 'lambda:function:ProvisionedConcurrency', + role: this.scalingRole, }); - - // Make sure that alias is created before the autoscale resources - this.scalableAlias.node.addDependency(this); } /** diff --git a/packages/@aws-cdk/aws-lambda/lib/index.ts b/packages/@aws-cdk/aws-lambda/lib/index.ts index ca77a6f309ed8..d6302463ef22e 100644 --- a/packages/@aws-cdk/aws-lambda/lib/index.ts +++ b/packages/@aws-cdk/aws-lambda/lib/index.ts @@ -12,7 +12,7 @@ export * from './event-source'; export * from './event-source-mapping'; export * from './destination'; export * from './event-invoke-config'; -export * from './scalable-version-attribute'; +export * from './scalable-function-attribute'; export * from './log-retention'; diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts index 5070060845cf1..3e1dcbfda0b53 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts @@ -1,5 +1,5 @@ import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; -import { Construct, Fn, RemovalPolicy, Stack } from '@aws-cdk/core'; +import { Construct, Fn, RemovalPolicy } from '@aws-cdk/core'; import { Alias, AliasOptions } from './alias'; import { EventInvokeConfigOptions } from './event-invoke-config'; import { Function } from './function'; diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts new file mode 100644 index 0000000000000..1e543465a6889 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts @@ -0,0 +1,106 @@ +import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; +import { IRole } from '@aws-cdk/aws-iam'; +import { Construct } from '@aws-cdk/core'; + +/** + * Interface for scalable attributes + */ +export interface IScalableFunctionAttribute { + /** + * Scale out or in to keep utilization at a given level + */ + scaleOnUtilization(props: UtilizationScalingProps): void; + /** + * Add scheduled scaling for this scaling attribute + */ + scaleOnSchedule(actions: appscaling.ScalingSchedule): void; +} + +/** + * Properties for enabling Lambda utilization tracking + */ +export interface UtilizationScalingProps extends appscaling.BaseTargetTrackingProps { + /** + * Target utilization percentage for the attribute + */ + readonly targetUtilizationPercent: number; +} + +/** + * A scalable lambda function attribute + */ +export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute { + constructor(scope: Construct, id: string, props: AttributeScalingProps) { + super(scope, id, { + serviceNamespace: appscaling.ServiceNamespace.LAMBDA, + ...props, + }); + } + + /** + * Scale out or in to keep utilization at a given level. This uses the predefined metric + * LambdaProvisionedConcurrencyUtilization, which is a percentage. + */ + public scaleOnUtilization(props: UtilizationScalingProps) { + if (props.targetUtilizationPercent < 0 || props.targetUtilizationPercent > 1) { + throw new Error('The tracked metric, LambdaProvisionedConcurrencyUtilization, is a percentage and must be between 0 and 1.'); + } + super.doScaleToTrackMetric('Tracking', { + targetValue: props.targetUtilizationPercent, + predefinedMetric: appscaling.PredefinedMetric.LAMBDA_PROVISIONED_CONCURRENCY_UTILIZATION, + ...props, + }); + } + + /** + * Scale out or in based on time + */ + public scaleOnSchedule(action: appscaling.ScalingSchedule) { + super.doScaleOnSchedule('Scheduling', action); + } +} + +/** + * Properties for creating a scalable function attribute + */ +export interface AttributeScalingProps { + /** + * Minimum capacity to scale to + */ + readonly minCapacity: number; + + /** + * Maximum capacity to scale to + */ + readonly maxCapacity: number; + + /** + * The id of the resource - should be 'function::' + */ + readonly resourceId: string; + + /** + * The iam role that executes autoscaling rules + */ + readonly role: IRole; + + /** + * The dimension - should always be 'lambda:function:ProvisionedConcurrency' + */ + readonly dimension: string; +} + +/** + * Properties for enabling Lambda capacity scaling + */ +export interface EnableScalingProps { + /** + * Minimum capacity to scale to + */ + readonly minCapacity: number; + + /** + * Maximum capacity to scale to + */ + readonly maxCapacity: number; +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index dc66d1a27b082..d39e477b253bd 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -168,9 +168,9 @@ "docs-public-apis:@aws-cdk/aws-lambda.ResourceBindOptions", "docs-public-apis:@aws-cdk/aws-lambda.VersionAttributes", "props-physical-name:@aws-cdk/aws-lambda.EventInvokeConfigProps", - "props-struct-name:@aws-cdk/aws-lambda.IScalableVersionAttribute", - "construct-ctor-props-type:@aws-cdk/aws-lambda.ScalableVersionAttribute", - "construct-interface-extends-iconstruct:@aws-cdk/aws-lambda.IScalableVersionAttribute" + "props-struct-name:@aws-cdk/aws-lambda.IScalableFunctionAttribute", + "construct-ctor-props-type:@aws-cdk/aws-lambda.ScalableFunctionAttribute", + "construct-interface-extends-iconstruct:@aws-cdk/aws-lambda.IScalableFunctionAttribute" ] }, "stability": "stable", From 7a2c60f66b7b8d2de9d70e1422187d733ea824e3 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 6 Jul 2020 15:07:09 -0400 Subject: [PATCH 06/35] added unit tests --- packages/@aws-cdk/aws-lambda/lib/alias.ts | 6 - .../@aws-cdk/aws-lambda/test/test.alias.ts | 127 +++++++++++++++++- 2 files changed, 125 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index 43da459c90d08..8989e242817cb 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -131,7 +131,6 @@ export class Alias extends QualifiedFunctionBase implements IAlias { protected readonly canCreatePermissions: boolean = true; - private provisionedConcurrency: boolean; private scalableAlias?: ScalableFunctionAttribute; private readonly scalingRole: iam.IRole; @@ -153,7 +152,6 @@ export class Alias extends QualifiedFunctionBase implements IAlias { provisionedConcurrencyConfig: this.determineProvisionedConcurrency(props), }); - this.provisionedConcurrency = props.provisionedConcurrentExecutions ? true : false; // Use a Service Linked Role // https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html this.scalingRole = iam.Role.fromRoleArn(this, 'ScalingRole', Stack.of(this).formatArn({ @@ -216,10 +214,6 @@ export class Alias extends QualifiedFunctionBase implements IAlias { * @param props The properties for autoscaling */ public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableFunctionAttribute { - - if (!this.provisionedConcurrency) { - throw new Error('Autoscaling is available for aliases with provisioned concurrency only'); - } if (this.scalableAlias) { throw new Error('Autoscaling already enabled for this alias'); } diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index abbb8c1716b0f..77fc4ce9dc8a0 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -1,6 +1,7 @@ -import { beASupersetOfTemplate, expect, haveResource, haveResourceLike } from '@aws-cdk/assert'; +import { beASupersetOfTemplate, expect, haveResource, haveResourceLike, objectLike, arrayWith } from '@aws-cdk/assert'; +import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; -import { Stack } from '@aws-cdk/core'; +import { Stack, } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import * as lambda from '../lib'; @@ -430,4 +431,126 @@ export = { test.done(); }, + + 'can enable AutoScaling on aliases'(test: Test): void { + // GIVEN + const stack = new Stack(); + const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('hello()'), + handler: 'index.hello', + runtime: lambda.Runtime.NODEJS_10_X, + }); + + const version = fn.addVersion('1', undefined, 'testing'); + + const alias = new lambda.Alias(stack, 'Alias', { + aliasName: 'prod', + version, + }); + + // WHEN + alias.autoScaleProvisionedConcurrency({minCapacity: 1, maxCapacity: 5}); + + // THEN + expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalableTarget', { + MinCapacity: 1, + MaxCapacity: 5, + ResourceId: objectLike({ + 'Fn::Join': arrayWith(arrayWith( + 'function:', + { Ref: 'MyLambdaCCE802FB' }, + ':prod', + )), + }), + })); + + test.done(); + }, + + 'cannot enable AutoScaling twice on same property'(test: Test): void { + // GIVEN + const stack = new Stack(); + const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('hello()'), + handler: 'index.hello', + runtime: lambda.Runtime.NODEJS_10_X, + }); + + const version = fn.addVersion('1', undefined, 'testing'); + + const alias = new lambda.Alias(stack, 'Alias', { + aliasName: 'prod', + version, + }); + + // WHEN + alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 5 }); + + // THEN + test.throws(() => alias.autoScaleProvisionedConcurrency({ minCapacity: 3, maxCapacity: 8 }), /Autoscaling already enabled for this alias/); + + test.done(); + }, + + 'error when specifying invalid utilizationPercent when AutoScaling on utilization'(test: Test): void { + // GIVEN + const stack = new Stack(); + const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('hello()'), + handler: 'index.hello', + runtime: lambda.Runtime.NODEJS_10_X, + }); + + const version = fn.addVersion('1', undefined, 'testing'); + + const alias = new lambda.Alias(stack, 'Alias', { + aliasName: 'prod', + version, + }); + + // WHEN + const target = alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 5 }); + + // THEN + test.throws(() => target.scaleOnUtilization({targetUtilizationPercent: 50}), /The tracked metric, LambdaProvisionedConcurrencyUtilization, is a percentage and must be between 0 and 1./); + + test.done(); + }, + + 'can autoscale on a schedule'(test: Test): void { + // GIVEN + const stack = new Stack(); + const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('hello()'), + handler: 'index.hello', + runtime: lambda.Runtime.NODEJS_10_X, + }); + + const version = fn.addVersion('1', undefined, 'testing'); + + const alias = new lambda.Alias(stack, 'Alias', { + aliasName: 'prod', + version, + }); + + // WHEN + const target = alias.autoScaleProvisionedConcurrency({minCapacity: 1, maxCapacity: 5}); + target.scaleOnSchedule({ + schedule: appscaling.Schedule.cron({}), + maxCapacity: 10, + }); + + // THEN + expect(stack).to(haveResourceLike('AWS::ApplicationAutoScaling::ScalableTarget', { + ScheduledActions: [ + { + ScalableTargetAction: { MaxCapacity: 10 }, + Schedule: 'cron(* * * * ? *)', + ScheduledActionName: 'Scheduling', + }, + ], + })); + + test.done(); + }, }; From b3c5c113c77ce09e8c55092a8e2d3d6c74b998de Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 6 Jul 2020 21:21:35 -0400 Subject: [PATCH 07/35] added integ test --- packages/@aws-cdk/aws-lambda/lib/alias.ts | 9 +- .../lib/scalable-function-attribute.ts | 12 +- .../test/integ.autoscaling.expected.json | 157 ++++++++++++++++++ .../aws-lambda/test/integ.autoscaling.ts | 37 +++++ .../@aws-cdk/aws-lambda/test/test.alias.ts | 4 +- 5 files changed, 208 insertions(+), 11 deletions(-) create mode 100644 packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json create mode 100644 packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index 8989e242817cb..b4692091e6e4c 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -217,14 +217,17 @@ export class Alias extends QualifiedFunctionBase implements IAlias { if (this.scalableAlias) { throw new Error('Autoscaling already enabled for this alias'); } - - return this.scalableAlias = new ScalableFunctionAttribute(this, 'AliasScaling', { + const name = this.stack.parseArn(this.functionArn, ':').resourceName!; + this.scalableAlias = new ScalableFunctionAttribute(this, 'AliasScaling', { minCapacity: props.minCapacity, maxCapacity: props.maxCapacity, - resourceId: `function:${this.lambda.functionName}:${this.aliasName}`, + resourceId: `function:${name}:${this.aliasName}`, dimension: 'lambda:function:ProvisionedConcurrency', role: this.scalingRole, }); + + // this.scalableAlias.node.tryFindChild('Target')?.node.addDependency(this); + return this.scalableAlias; } /** diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts index 1e543465a6889..4f6669fae16bb 100644 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts @@ -13,7 +13,7 @@ export interface IScalableFunctionAttribute { /** * Add scheduled scaling for this scaling attribute */ - scaleOnSchedule(actions: appscaling.ScalingSchedule): void; + scaleOnSchedule(id: string, actions: appscaling.ScalingSchedule): void; } /** @@ -42,11 +42,11 @@ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute * LambdaProvisionedConcurrencyUtilization, which is a percentage. */ public scaleOnUtilization(props: UtilizationScalingProps) { - if (props.targetUtilizationPercent < 0 || props.targetUtilizationPercent > 1) { - throw new Error('The tracked metric, LambdaProvisionedConcurrencyUtilization, is a percentage and must be between 0 and 1.'); + if (props.targetUtilizationPercent < 10 || props.targetUtilizationPercent > 90) { + throw new Error('The tracked metric, LambdaProvisionedConcurrencyUtilization, is a percentage and the target value must be between 10% and 90%.'); } super.doScaleToTrackMetric('Tracking', { - targetValue: props.targetUtilizationPercent, + targetValue: props.targetUtilizationPercent/100, predefinedMetric: appscaling.PredefinedMetric.LAMBDA_PROVISIONED_CONCURRENCY_UTILIZATION, ...props, }); @@ -55,8 +55,8 @@ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute /** * Scale out or in based on time */ - public scaleOnSchedule(action: appscaling.ScalingSchedule) { - super.doScaleOnSchedule('Scheduling', action); + public scaleOnSchedule(id: string, action: appscaling.ScalingSchedule) { + super.doScaleOnSchedule(id, action); } } diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json new file mode 100644 index 0000000000000..d64961427166d --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json @@ -0,0 +1,157 @@ +{ + "Resources": { + "MyLambdaServiceRole4539ECB6": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "MyLambdaCCE802FB": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "foo" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "MyLambdaServiceRole4539ECB6", + "Arn" + ] + }, + "Runtime": "nodejs10.x" + }, + "DependsOn": [ + "MyLambdaServiceRole4539ECB6" + ] + }, + "MyLambdaVersion16CDE3C40": { + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "MyLambdaCCE802FB" + }, + "Description": "integ-test" + } + }, + "Alias325C5727": { + "Type": "AWS::Lambda::Alias", + "Properties": { + "FunctionName": { + "Ref": "MyLambdaCCE802FB" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MyLambdaVersion16CDE3C40", + "Version" + ] + }, + "Name": "prod" + } + }, + "AliasAliasScalingTarget7449FF0E": { + "Type": "AWS::ApplicationAutoScaling::ScalableTarget", + "Properties": { + "MaxCapacity": 50, + "MinCapacity": 1, + "ResourceId": { + "Fn::Join": [ + "", + [ + "function:", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "Alias325C5727" + } + ] + } + ] + }, + ":prod" + ] + ] + }, + "RoleARN": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::", + { + "Ref": "AWS::AccountId" + }, + ":role/aws-service-role/lambda.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_LambdaConcurrency" + ] + ] + }, + "ScalableDimension": "lambda:function:ProvisionedConcurrency", + "ServiceNamespace": "lambda", + "ScheduledActions": [ + { + "ScalableTargetAction": { + "MinCapacity": 20 + }, + "Schedule": "cron(0 8 * * ? *)", + "ScheduledActionName": "ScaleUpInTheMorning" + }, + { + "ScalableTargetAction": { + "MaxCapacity": 20 + }, + "Schedule": "cron(0 20 * * ? *)", + "ScheduledActionName": "ScaleDownAtNight" + } + ] + } + }, + "AliasAliasScalingTargetTrackingA7718D48": { + "Type": "AWS::ApplicationAutoScaling::ScalingPolicy", + "Properties": { + "PolicyName": "awslambdaautoscaling2AliasAliasScalingTargetTrackingC12C84AC", + "PolicyType": "TargetTrackingScaling", + "ScalingTargetId": { + "Ref": "AliasAliasScalingTarget7449FF0E" + }, + "TargetTrackingScalingPolicyConfiguration": { + "PredefinedMetricSpecification": { + "PredefinedMetricType": "LambdaProvisionedConcurrencyUtilization" + }, + "TargetValue": 0.5 + } + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts new file mode 100644 index 0000000000000..56244339bacdc --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts @@ -0,0 +1,37 @@ +import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; +import * as cdk from '@aws-cdk/core'; +import * as lambda from '../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-lambda-autoscaling-2'); + +const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('foo'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, +}); + +const version = fn.addVersion('1', undefined, 'integ-test'); + +const alias = new lambda.Alias(stack, 'Alias', { + aliasName: 'prod', + version, +}); + +const scalingTarget = alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 50 }); + +scalingTarget.scaleOnUtilization({ + targetUtilizationPercent: 50, +}); + +scalingTarget.scaleOnSchedule('ScaleUpInTheMorning', { + schedule: appscaling.Schedule.cron({ hour: '8', minute: '0'}), + minCapacity: 20, +}); + +scalingTarget.scaleOnSchedule('ScaleDownAtNight', { + schedule: appscaling.Schedule.cron({ hour: '20', minute: '0'}), + maxCapacity: 20, +}); + +app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index 77fc4ce9dc8a0..232dc51b9ddd9 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -512,7 +512,7 @@ export = { const target = alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 5 }); // THEN - test.throws(() => target.scaleOnUtilization({targetUtilizationPercent: 50}), /The tracked metric, LambdaProvisionedConcurrencyUtilization, is a percentage and must be between 0 and 1./); + test.throws(() => target.scaleOnUtilization({targetUtilizationPercent: 150}), /The tracked metric, LambdaProvisionedConcurrencyUtilization, is a percentage and must be between 0 and 100./); test.done(); }, @@ -535,7 +535,7 @@ export = { // WHEN const target = alias.autoScaleProvisionedConcurrency({minCapacity: 1, maxCapacity: 5}); - target.scaleOnSchedule({ + target.scaleOnSchedule('Scheduling', { schedule: appscaling.Schedule.cron({}), maxCapacity: 10, }); From e78c0f4f406cb20395179f0ead536803ca823cc5 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 6 Jul 2020 21:42:49 -0400 Subject: [PATCH 08/35] fixed linting issues --- packages/@aws-cdk/aws-lambda/lib/alias.ts | 2 ++ .../lib/scalable-function-attribute.ts | 4 ++-- .../@aws-cdk/aws-lambda/test/test.alias.ts | 18 ++++++++++++------ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index b4692091e6e4c..e38af8bdc59a1 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -217,6 +217,8 @@ export class Alias extends QualifiedFunctionBase implements IAlias { if (this.scalableAlias) { throw new Error('Autoscaling already enabled for this alias'); } + // This is a hack to get the ScalableTarget resource to depend on Alias. + // this.functionArn depends on the Alias resource. Otherwise, name could easily be this.lambda.functionName const name = this.stack.parseArn(this.functionArn, ':').resourceName!; this.scalableAlias = new ScalableFunctionAttribute(this, 'AliasScaling', { minCapacity: props.minCapacity, diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts index 4f6669fae16bb..237659f179526 100644 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts @@ -43,10 +43,10 @@ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute */ public scaleOnUtilization(props: UtilizationScalingProps) { if (props.targetUtilizationPercent < 10 || props.targetUtilizationPercent > 90) { - throw new Error('The tracked metric, LambdaProvisionedConcurrencyUtilization, is a percentage and the target value must be between 10% and 90%.'); + throw new Error('The tracked metric, LambdaProvisionedConcurrencyUtilization, must be between 10% and 90%.'); } super.doScaleToTrackMetric('Tracking', { - targetValue: props.targetUtilizationPercent/100, + targetValue: props.targetUtilizationPercent / 100, predefinedMetric: appscaling.PredefinedMetric.LAMBDA_PROVISIONED_CONCURRENCY_UTILIZATION, ...props, }); diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index 232dc51b9ddd9..7731db75002b6 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -1,7 +1,7 @@ -import { beASupersetOfTemplate, expect, haveResource, haveResourceLike, objectLike, arrayWith } from '@aws-cdk/assert'; +import { arrayWith, beASupersetOfTemplate, expect, haveResource, haveResourceLike, objectLike } from '@aws-cdk/assert'; import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; -import { Stack, } from '@aws-cdk/core'; +import { Stack } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import * as lambda from '../lib'; @@ -457,8 +457,14 @@ export = { MaxCapacity: 5, ResourceId: objectLike({ 'Fn::Join': arrayWith(arrayWith( - 'function:', - { Ref: 'MyLambdaCCE802FB' }, + 'function:', + objectLike({ + 'Fn::Select': arrayWith( + {'Fn::Split': arrayWith( + {'Ref': 'Alias325C5727' }), + }, + ), + }), ':prod', )), }), @@ -512,7 +518,7 @@ export = { const target = alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 5 }); // THEN - test.throws(() => target.scaleOnUtilization({targetUtilizationPercent: 150}), /The tracked metric, LambdaProvisionedConcurrencyUtilization, is a percentage and must be between 0 and 100./); + test.throws(() => target.scaleOnUtilization({targetUtilizationPercent: 150}), /The tracked metric, LambdaProvisionedConcurrencyUtilization, must be between 10% and 90%./); test.done(); }, @@ -550,7 +556,7 @@ export = { }, ], })); - + test.done(); }, }; From 02e1c862d80067d887472e571e4557869e269b1e Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 6 Jul 2020 22:13:22 -0400 Subject: [PATCH 09/35] added readme comments --- packages/@aws-cdk/aws-lambda/README.md | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index 55ada8d6da307..5294c3442fbb0 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -256,6 +256,33 @@ const fn = new lambda.Function(this, 'MyFunction', { See [the AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/concurrent-executions.html) managing concurrency. +### Lambda with Application AutoScaling + +Application AutoScaling can be added on to Lambda Aliases using the `alias.autoScaleProvisionedConcurrency()` API: + +```ts +import * as lambda from '@aws-cdk/aws-lambda'; + +const fn = new lambda.Function(this, 'MyFunction', { + runtime: lambda.Runtime.NODEJS_10_X, + handler: 'index.handler', + code: lambda.Code.fromInline('exports.handler = function(event, ctx, cb) { return cb(null, "hi"); }'), +}); + +const alias = new lambda.Alias(stack, 'Alias', { + aliasName: 'prod', + version, +}); + +alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 50 }); +``` + +[Example of Lambda AutoScaling usage](test/integ.autoscaling.ts) + +Autoscaling on a Lambda Version is still possible, but it is a sharp edge that is not exposed in the L2 construct. + +See [the AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/invocation-scaling.html) on autoscaling lambda functions. + ### Log Group Lambda functions automatically create a log group with the name `/aws/lambda/` upon first execution with From 614f109217cd041ea7882602a0cee540cf7b46b0 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 7 Jul 2020 10:42:07 -0400 Subject: [PATCH 10/35] updated alias with implicit dependency --- packages/@aws-cdk/aws-lambda/lib/alias.ts | 10 ++-------- .../aws-lambda/test/integ.autoscaling.expected.json | 2 +- packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index e38af8bdc59a1..7dde4aac252f4 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -217,19 +217,13 @@ export class Alias extends QualifiedFunctionBase implements IAlias { if (this.scalableAlias) { throw new Error('Autoscaling already enabled for this alias'); } - // This is a hack to get the ScalableTarget resource to depend on Alias. - // this.functionArn depends on the Alias resource. Otherwise, name could easily be this.lambda.functionName - const name = this.stack.parseArn(this.functionArn, ':').resourceName!; - this.scalableAlias = new ScalableFunctionAttribute(this, 'AliasScaling', { + return this.scalableAlias = new ScalableFunctionAttribute(this, 'AliasScaling', { minCapacity: props.minCapacity, maxCapacity: props.maxCapacity, - resourceId: `function:${name}:${this.aliasName}`, + resourceId: `function:${this.functionName}`, dimension: 'lambda:function:ProvisionedConcurrency', role: this.scalingRole, }); - - // this.scalableAlias.node.tryFindChild('Target')?.node.addDependency(this); - return this.scalableAlias; } /** diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json index d64961427166d..340ad461a3fe6 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json @@ -140,7 +140,7 @@ "AliasAliasScalingTargetTrackingA7718D48": { "Type": "AWS::ApplicationAutoScaling::ScalingPolicy", "Properties": { - "PolicyName": "awslambdaautoscaling2AliasAliasScalingTargetTrackingC12C84AC", + "PolicyName": "awslambdaautoscalingAliasAliasScalingTargetTrackingD339330D", "PolicyType": "TargetTrackingScaling", "ScalingTargetId": { "Ref": "AliasAliasScalingTarget7449FF0E" diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts index 56244339bacdc..03956e0adf134 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts @@ -3,7 +3,7 @@ import * as cdk from '@aws-cdk/core'; import * as lambda from '../lib'; const app = new cdk.App(); -const stack = new cdk.Stack(app, 'aws-lambda-autoscaling-2'); +const stack = new cdk.Stack(app, 'aws-lambda-autoscaling'); const fn = new lambda.Function(stack, 'MyLambda', { code: new lambda.InlineCode('foo'), From 4c64b6c897cecf0e41a099eaf45ee9207e79c4e4 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 7 Jul 2020 10:55:17 -0400 Subject: [PATCH 11/35] typo --- packages/@aws-cdk/aws-lambda/test/test.alias.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index 7731db75002b6..3af79030a3a76 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -461,7 +461,7 @@ export = { objectLike({ 'Fn::Select': arrayWith( {'Fn::Split': arrayWith( - {'Ref': 'Alias325C5727' }), + {Ref: 'Alias325C5727' }), }, ), }), From 710a623545f72170c48f955ba0dba0aafdff5190 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 7 Jul 2020 13:25:25 -0400 Subject: [PATCH 12/35] updated readme --- packages/@aws-cdk/aws-lambda/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index 5294c3442fbb0..45951703f1767 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -279,7 +279,7 @@ alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 50 }); [Example of Lambda AutoScaling usage](test/integ.autoscaling.ts) -Autoscaling on a Lambda Version is still possible, but it is a sharp edge that is not exposed in the L2 construct. +Autoscaling on a Lambda Version is still [possible](https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk/aws-applicationautoscaling#lambda-provisioned-concurrency-auto-scaling), but there are some limitations to this approach. Versions do not support updates so adding autoscaling to an existing version or updating autoscaling configuration is not possible. In addition, replacing a version by changing the logical ID could strand previous autoscaling configurations if the version is set to retain. See [the AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/invocation-scaling.html) on autoscaling lambda functions. From 66b4ee9eca59314d73c6e1082737f492b9f065b5 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 7 Jul 2020 15:00:16 -0400 Subject: [PATCH 13/35] made minor edits --- packages/@aws-cdk/aws-lambda/README.md | 6 +- .../@aws-cdk/aws-lambda/lib/lambda-version.ts | 6 -- .../lib/scalable-version-attribute.ts | 65 ------------------- .../@aws-cdk/aws-lambda/test/test.alias.ts | 48 ++++++++++++++ 4 files changed, 50 insertions(+), 75 deletions(-) delete mode 100644 packages/@aws-cdk/aws-lambda/lib/scalable-version-attribute.ts diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index 45951703f1767..2aee1bcf6fb7c 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -256,9 +256,9 @@ const fn = new lambda.Function(this, 'MyFunction', { See [the AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/concurrent-executions.html) managing concurrency. -### Lambda with Application AutoScaling +### Lambda with Provisioned Concurrency AutoScaling -Application AutoScaling can be added on to Lambda Aliases using the `alias.autoScaleProvisionedConcurrency()` API: +Provisioned Concurrency AutoScaling can be added on to Lambda Aliases using the `alias.autoScaleProvisionedConcurrency()` API: ```ts import * as lambda from '@aws-cdk/aws-lambda'; @@ -279,8 +279,6 @@ alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 50 }); [Example of Lambda AutoScaling usage](test/integ.autoscaling.ts) -Autoscaling on a Lambda Version is still [possible](https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk/aws-applicationautoscaling#lambda-provisioned-concurrency-auto-scaling), but there are some limitations to this approach. Versions do not support updates so adding autoscaling to an existing version or updating autoscaling configuration is not possible. In addition, replacing a version by changing the logical ID could strand previous autoscaling configurations if the version is set to retain. - See [the AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/invocation-scaling.html) on autoscaling lambda functions. ### Log Group diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts index 3e1dcbfda0b53..09c62a5530690 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda-version.ts @@ -25,12 +25,6 @@ export interface IVersion extends IFunction { * @param options Alias options */ addAlias(aliasName: string, options?: AliasOptions): Alias; - - // /** - // * Enables autoscaling on a version with provisioned concurrency - // * @param props The properties for autoscaling - // */ - // autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableVersionAttribute; } /** diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-version-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-version-attribute.ts deleted file mode 100644 index c9a11cd89349b..0000000000000 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-version-attribute.ts +++ /dev/null @@ -1,65 +0,0 @@ -import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; - -/** - * Interface for scalable attributes - */ -export interface IScalableVersionAttribute { - /** - * Scale out or in to keep utilization at a given level - */ - scaleOnUtilization(props: UtilizationScalingProps): void; - /** - * Add scheduled scaling for this scaling attribute - */ - scaleOnSchedule(id: string, actions: appscaling.ScalingSchedule): void; -} - -/** - * Properties for enabling Lambda utilization tracking - */ -export interface UtilizationScalingProps extends appscaling.BaseTargetTrackingProps { - /** - * Target utilization percentage for the attribute - */ - readonly targetUtilizationPercent: number; -} - -/** - * A scalable lambda version attribute - */ -export class ScalableVersionAttribute extends appscaling.BaseScalableAttribute { - /** - * Scale out or in to keep utilization at a given level - */ - public scaleOnUtilization(props: UtilizationScalingProps) { - if (props.targetUtilizationPercent < 0 || props.targetUtilizationPercent > 1) { - throw new Error('target utilization is a percentage and must be between 0 and 1.'); - } - super.doScaleToTrackMetric('Tracking', { - targetValue: props.targetUtilizationPercent, - predefinedMetric: appscaling.PredefinedMetric.LAMBDA_PROVISIONED_CONCURRENCY_UTILIZATION, - ...props, - }); - } - - /** - * Scale out or in based on time - */ - public scaleOnSchedule(id: string, action: appscaling.ScalingSchedule) { - super.doScaleOnSchedule(id, action); - } -} - -/** - * Properties for enabling Lambda capacity scaling - */ -export interface EnableScalingProps { - /** - * Minimum capacity to scale to - */ - readonly minCapacity: number; - /** - * Maximum capacity to scale to - */ - readonly maxCapacity: number; -} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index 3af79030a3a76..1174298c486ca 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -473,6 +473,54 @@ export = { test.done(); }, + 'can enable AutoScaling on aliases with Provisioned Concurrency set'(test: Test): void { + // GIVEN + const stack = new Stack(); + const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('hello()'), + handler: 'index.hello', + runtime: lambda.Runtime.NODEJS_10_X, + }); + + const version = fn.addVersion('1', undefined, 'testing'); + + const alias = new lambda.Alias(stack, 'Alias', { + aliasName: 'prod', + version, + provisionedConcurrentExecutions: 10, + }); + + // WHEN + alias.autoScaleProvisionedConcurrency({minCapacity: 1, maxCapacity: 5}); + + // THEN + expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalableTarget', { + MinCapacity: 1, + MaxCapacity: 5, + ResourceId: objectLike({ + 'Fn::Join': arrayWith(arrayWith( + 'function:', + objectLike({ + 'Fn::Select': arrayWith( + {'Fn::Split': arrayWith( + {Ref: 'Alias325C5727' }), + }, + ), + }), + ':prod', + )), + }), + })); + + expect(stack).to(haveResourceLike('AWS::Lambda::Alias', { + ProvisionedConcurrencyConfig: { + ProvisionedConcurrentExecutions: 10, + }, + })); + + test.done(); + }, + 'cannot enable AutoScaling twice on same property'(test: Test): void { // GIVEN const stack = new Stack(); From 0b53cf822ad1aa79365e5e03a8ad7bc7fd45e502 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 7 Jul 2020 15:31:50 -0400 Subject: [PATCH 14/35] typo --- packages/@aws-cdk/aws-lambda/test/test.alias.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index 1174298c486ca..9fa75b8fe7f1f 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -517,7 +517,6 @@ export = { ProvisionedConcurrentExecutions: 10, }, })); - test.done(); }, From 77884afe2b68d792a7611ef611589b612ff0bec2 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 16 Jul 2020 14:31:53 -0400 Subject: [PATCH 15/35] made requested changes --- packages/@aws-cdk/aws-lambda/README.md | 23 ++++---- packages/@aws-cdk/aws-lambda/lib/alias.ts | 8 +-- .../lib/scalable-function-attribute.ts | 53 ++++--------------- .../test/integ.autoscaling.expected.json | 4 +- .../aws-lambda/test/integ.autoscaling.ts | 12 +++-- .../@aws-cdk/aws-lambda/test/test.alias.ts | 3 +- 6 files changed, 40 insertions(+), 63 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index 2aee1bcf6fb7c..84ebc899a3b8b 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -256,25 +256,26 @@ const fn = new lambda.Function(this, 'MyFunction', { See [the AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/concurrent-executions.html) managing concurrency. -### Lambda with Provisioned Concurrency AutoScaling +### Configure Provisioned Concurrency AutoScaling -Provisioned Concurrency AutoScaling can be added on to Lambda Aliases using the `alias.autoScaleProvisionedConcurrency()` API: +You can configure provisioned concurrency autoscaling on a function alias using the `autoScaleProvisionedConcurrency()` method: ```ts -import * as lambda from '@aws-cdk/aws-lambda'; - -const fn = new lambda.Function(this, 'MyFunction', { - runtime: lambda.Runtime.NODEJS_10_X, - handler: 'index.handler', - code: lambda.Code.fromInline('exports.handler = function(event, ctx, cb) { return cb(null, "hi"); }'), -}); - const alias = new lambda.Alias(stack, 'Alias', { aliasName: 'prod', version, }); -alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 50 }); +// Configure autoscaling to track utilization +alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 50 }).scaleOnUtilization({ + targetUtilizationPercent: 50, +}); + +// or you can configure schedule scaling +alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 50}).scaleOnSchedule('ScaleUpInTheMorning', { + schedule: appscaling.Schedule.cron({ hour: '8', minute: '0'}), + minCapacity: 20, +}); ``` [Example of Lambda AutoScaling usage](test/integ.autoscaling.ts) diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index 7dde4aac252f4..383c9a9dc2e76 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -1,3 +1,4 @@ +import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as iam from '@aws-cdk/aws-iam'; import { Construct, Stack} from '@aws-cdk/core'; @@ -208,9 +209,9 @@ export class Alias extends QualifiedFunctionBase implements IAlias { } /** - * Enable autoscaling for provisioned concurrency lambda functions. Tracks the predefined metric - * LambdaProvisionedConcurrencyUtilization. More information about lambda performance metrics here: - * https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html#monitoring-metrics-concurrency + * Configure provisioned concurrency autoscaling on a function alias. Returns a scalable attribute that can call + * `scaleOnUtilization()` and `scaleOnSchedule()`. + * * @param props The properties for autoscaling */ public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableFunctionAttribute { @@ -222,6 +223,7 @@ export class Alias extends QualifiedFunctionBase implements IAlias { maxCapacity: props.maxCapacity, resourceId: `function:${this.functionName}`, dimension: 'lambda:function:ProvisionedConcurrency', + serviceNamespace: appscaling.ServiceNamespace.LAMBDA, role: this.scalingRole, }); } diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts index 237659f179526..5a8352ff4cfcd 100644 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts @@ -1,5 +1,4 @@ import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; -import { IRole } from '@aws-cdk/aws-iam'; import { Construct } from '@aws-cdk/core'; /** @@ -30,66 +29,36 @@ export interface UtilizationScalingProps extends appscaling.BaseTargetTrackingPr * A scalable lambda function attribute */ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute { - constructor(scope: Construct, id: string, props: AttributeScalingProps) { - super(scope, id, { - serviceNamespace: appscaling.ServiceNamespace.LAMBDA, - ...props, - }); + constructor(scope: Construct, id: string, props: appscaling.BaseScalableAttributeProps) { + super(scope, id, props); } /** - * Scale out or in to keep utilization at a given level. This uses the predefined metric - * LambdaProvisionedConcurrencyUtilization, which is a percentage. + * Scale out or in to keep utilization at a given level. The utilization is tracked by the + * LambdaProvisionedConcurrencyUtilization metric, emitted by lambda. See: + * https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html#monitoring-metrics-concurrency + * + * Allowed values: 0.1 - 0.9. */ public scaleOnUtilization(props: UtilizationScalingProps) { - if (props.targetUtilizationPercent < 10 || props.targetUtilizationPercent > 90) { - throw new Error('The tracked metric, LambdaProvisionedConcurrencyUtilization, must be between 10% and 90%.'); + if (props.targetUtilizationPercent < 0.1 || props.targetUtilizationPercent > 0.9) { + throw new Error('TargetUtilizationPercent should be between 0.1 and 0.9.'); } super.doScaleToTrackMetric('Tracking', { - targetValue: props.targetUtilizationPercent / 100, + targetValue: props.targetUtilizationPercent, predefinedMetric: appscaling.PredefinedMetric.LAMBDA_PROVISIONED_CONCURRENCY_UTILIZATION, ...props, }); } /** - * Scale out or in based on time + * Scale out or in based on time. */ public scaleOnSchedule(id: string, action: appscaling.ScalingSchedule) { super.doScaleOnSchedule(id, action); } } -/** - * Properties for creating a scalable function attribute - */ -export interface AttributeScalingProps { - /** - * Minimum capacity to scale to - */ - readonly minCapacity: number; - - /** - * Maximum capacity to scale to - */ - readonly maxCapacity: number; - - /** - * The id of the resource - should be 'function::' - */ - readonly resourceId: string; - - /** - * The iam role that executes autoscaling rules - */ - readonly role: IRole; - - /** - * The dimension - should always be 'lambda:function:ProvisionedConcurrency' - */ - readonly dimension: string; -} - /** * Properties for enabling Lambda capacity scaling */ diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json index 340ad461a3fe6..5c3826d035d8e 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json @@ -35,7 +35,7 @@ "Type": "AWS::Lambda::Function", "Properties": { "Code": { - "ZipFile": "foo" + "ZipFile": "exports.handler = async () => {\nconsole.log('hello world');\n};" }, "Handler": "index.handler", "Role": { @@ -78,7 +78,7 @@ "Type": "AWS::ApplicationAutoScaling::ScalableTarget", "Properties": { "MaxCapacity": 50, - "MinCapacity": 1, + "MinCapacity": 3, "ResourceId": { "Fn::Join": [ "", diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts index 03956e0adf134..74cd6fad30c7f 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts @@ -2,11 +2,17 @@ import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cdk from '@aws-cdk/core'; import * as lambda from '../lib'; +/** + * Stack verification steps: + * aws application-autoscaling describe-scalable-targets --service-namespace lambda --resource-ids function:aws-lambda-autoscaling-:prod + * has a minCapacity of 3 and maxCapacity of 50 + */ + const app = new cdk.App(); const stack = new cdk.Stack(app, 'aws-lambda-autoscaling'); const fn = new lambda.Function(stack, 'MyLambda', { - code: new lambda.InlineCode('foo'), + code: new lambda.InlineCode('exports.handler = async () => {\nconsole.log(\'hello world\');\n};'), handler: 'index.handler', runtime: lambda.Runtime.NODEJS_10_X, }); @@ -18,10 +24,10 @@ const alias = new lambda.Alias(stack, 'Alias', { version, }); -const scalingTarget = alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 50 }); +const scalingTarget = alias.autoScaleProvisionedConcurrency({ minCapacity: 3, maxCapacity: 50 }); scalingTarget.scaleOnUtilization({ - targetUtilizationPercent: 50, + targetUtilizationPercent: 0.5, }); scalingTarget.scaleOnSchedule('ScaleUpInTheMorning', { diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index 9fa75b8fe7f1f..7c18aac47d4ff 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -565,8 +565,7 @@ export = { const target = alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 5 }); // THEN - test.throws(() => target.scaleOnUtilization({targetUtilizationPercent: 150}), /The tracked metric, LambdaProvisionedConcurrencyUtilization, must be between 10% and 90%./); - + test.throws(() => target.scaleOnUtilization({targetUtilizationPercent: 0.95}), /TargetUtilizationPercent should be between 0.1 and 0.9./); test.done(); }, From 73bdf1150a6050388b6ed7eb52a8a1328a90b2a6 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 16 Jul 2020 16:14:03 -0400 Subject: [PATCH 16/35] typo --- packages/@aws-cdk/aws-lambda/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index 47056938ffc9e..9cc127da09a7c 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -284,7 +284,7 @@ const alias = new lambda.Alias(stack, 'Alias', { // Configure autoscaling to track utilization alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 50 }).scaleOnUtilization({ - targetUtilizationPercent: 50, + targetUtilizationPercent: 0.5, }); // or you can configure schedule scaling From 35df97a7b48ba8054066e302c345705dea5b2e11 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Thu, 16 Jul 2020 16:42:42 -0400 Subject: [PATCH 17/35] last minute doc changes --- .../aws-lambda/lib/scalable-function-attribute.ts | 10 +++++++--- packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts index 5a8352ff4cfcd..3584c2bc77fdb 100644 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts @@ -6,11 +6,15 @@ import { Construct } from '@aws-cdk/core'; */ export interface IScalableFunctionAttribute { /** - * Scale out or in to keep utilization at a given level + * Scale out or in to keep utilization at a given level. The utilization is tracked by the + * LambdaProvisionedConcurrencyUtilization metric, emitted by lambda. See: + * https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html#monitoring-metrics-concurrency + * + * Allowed values: 0.1 - 0.9. */ scaleOnUtilization(props: UtilizationScalingProps): void; /** - * Add scheduled scaling for this scaling attribute + * Scale out or in based on time. */ scaleOnSchedule(id: string, actions: appscaling.ScalingSchedule): void; } @@ -20,7 +24,7 @@ export interface IScalableFunctionAttribute { */ export interface UtilizationScalingProps extends appscaling.BaseTargetTrackingProps { /** - * Target utilization percentage for the attribute + * Target utilization percent for the attribute */ readonly targetUtilizationPercent: number; } diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts index 74cd6fad30c7f..2021e28351a8d 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts @@ -40,4 +40,4 @@ scalingTarget.scaleOnSchedule('ScaleDownAtNight', { maxCapacity: 20, }); -app.synth(); \ No newline at end of file +app.synth(); From 6a5e9767a171bceefce51f365498db7e105abf49 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 20 Jul 2020 12:57:48 -0400 Subject: [PATCH 18/35] updated docs and readme --- packages/@aws-cdk/aws-lambda/README.md | 4 ++-- .../lib/scalable-function-attribute.ts | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index 9cc127da09a7c..11b74b4335956 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -272,9 +272,9 @@ const fn = new lambda.Function(this, 'MyFunction', { See [the AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/concurrent-executions.html) managing concurrency. -### Configure Provisioned Concurrency AutoScaling +### Configuring AutoScaling -You can configure provisioned concurrency autoscaling on a function alias using the `autoScaleProvisionedConcurrency()` method: +You can configure autoscaling of provisioned concurrency on a function alias using the `autoScaleProvisionedConcurrency()` method: ```ts const alias = new lambda.Alias(stack, 'Alias', { diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts index 3584c2bc77fdb..03b56a8796369 100644 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts @@ -12,9 +12,9 @@ export interface IScalableFunctionAttribute { * * Allowed values: 0.1 - 0.9. */ - scaleOnUtilization(props: UtilizationScalingProps): void; + scaleOnUtilization(props: UtilizationScalingOptions): void; /** - * Scale out or in based on time. + * Scale out or in based on schedule. */ scaleOnSchedule(id: string, actions: appscaling.ScalingSchedule): void; } @@ -22,15 +22,17 @@ export interface IScalableFunctionAttribute { /** * Properties for enabling Lambda utilization tracking */ -export interface UtilizationScalingProps extends appscaling.BaseTargetTrackingProps { +export interface UtilizationScalingOptions extends appscaling.BaseTargetTrackingProps { /** - * Target utilization percent for the attribute + * Target utilization percent for the attribute. + * + * Allowed values: 0.1 - 0.9. */ readonly targetUtilizationPercent: number; } /** - * A scalable lambda function attribute + * A scalable lambda alias attribute */ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute { constructor(scope: Construct, id: string, props: appscaling.BaseScalableAttributeProps) { @@ -44,7 +46,7 @@ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute * * Allowed values: 0.1 - 0.9. */ - public scaleOnUtilization(props: UtilizationScalingProps) { + public scaleOnUtilization(props: UtilizationScalingOptions) { if (props.targetUtilizationPercent < 0.1 || props.targetUtilizationPercent > 0.9) { throw new Error('TargetUtilizationPercent should be between 0.1 and 0.9.'); } @@ -56,7 +58,7 @@ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute } /** - * Scale out or in based on time. + * Scale out or in based on schedule. */ public scaleOnSchedule(id: string, action: appscaling.ScalingSchedule) { super.doScaleOnSchedule(id, action); From 34ae9d5825de97363ef4afcd894a9446ea133e6b Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 21 Jul 2020 16:43:57 -0400 Subject: [PATCH 19/35] changed targetUtilizationPercent to targetUtilizationValue --- .../aws-lambda/lib/scalable-function-attribute.ts | 8 ++++---- .../aws-lambda/test/integ.autoscaling.expected.json | 7 ++++++- packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts index 03b56a8796369..27db8715fc828 100644 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts @@ -24,11 +24,11 @@ export interface IScalableFunctionAttribute { */ export interface UtilizationScalingOptions extends appscaling.BaseTargetTrackingProps { /** - * Target utilization percent for the attribute. + * Target utilization value for the attribute. For example, .5 indicates that 50 percent of allocated provisioned concurrency is in use. * * Allowed values: 0.1 - 0.9. */ - readonly targetUtilizationPercent: number; + readonly targetUtilizationValue: number; } /** @@ -47,11 +47,11 @@ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute * Allowed values: 0.1 - 0.9. */ public scaleOnUtilization(props: UtilizationScalingOptions) { - if (props.targetUtilizationPercent < 0.1 || props.targetUtilizationPercent > 0.9) { + if (props.targetUtilizationValue < 0.1 || props.targetUtilizationValue > 0.9) { throw new Error('TargetUtilizationPercent should be between 0.1 and 0.9.'); } super.doScaleToTrackMetric('Tracking', { - targetValue: props.targetUtilizationPercent, + targetValue: props.targetUtilizationValue, predefinedMetric: appscaling.PredefinedMetric.LAMBDA_PROVISIONED_CONCURRENCY_UTILIZATION, ...props, }); diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json index 5c3826d035d8e..fd622e9532bd6 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json @@ -134,7 +134,12 @@ "Schedule": "cron(0 20 * * ? *)", "ScheduledActionName": "ScaleDownAtNight" } - ] + ], + "SuspendedState": { + "DynamicScalingInSuspended": false, + "DynamicScalingOutSuspended": false, + "ScheduledScalingSuspended": false + } } }, "AliasAliasScalingTargetTrackingA7718D48": { diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts index 2021e28351a8d..c5b417a394f90 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts @@ -27,7 +27,7 @@ const alias = new lambda.Alias(stack, 'Alias', { const scalingTarget = alias.autoScaleProvisionedConcurrency({ minCapacity: 3, maxCapacity: 50 }); scalingTarget.scaleOnUtilization({ - targetUtilizationPercent: 0.5, + targetUtilizationValue: 0.5, }); scalingTarget.scaleOnSchedule('ScaleUpInTheMorning', { From 3e162e48240964d8d6a8b0e993d163614e81a686 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 21 Jul 2020 16:48:15 -0400 Subject: [PATCH 20/35] readme modification --- packages/@aws-cdk/aws-lambda/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index 11b74b4335956..15be451ce5a4d 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -284,7 +284,7 @@ const alias = new lambda.Alias(stack, 'Alias', { // Configure autoscaling to track utilization alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 50 }).scaleOnUtilization({ - targetUtilizationPercent: 0.5, + targetUtilizationValue: 0.5, }); // or you can configure schedule scaling From 133c161558d25d025c9ba57ff9c9a2e8f5f4e9df Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 21 Jul 2020 16:51:22 -0400 Subject: [PATCH 21/35] missed a few changes --- packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts | 2 +- packages/@aws-cdk/aws-lambda/test/test.alias.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts index 27db8715fc828..9b86e33014a33 100644 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts @@ -48,7 +48,7 @@ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute */ public scaleOnUtilization(props: UtilizationScalingOptions) { if (props.targetUtilizationValue < 0.1 || props.targetUtilizationValue > 0.9) { - throw new Error('TargetUtilizationPercent should be between 0.1 and 0.9.'); + throw new Error('TargetUtilizationValue should be between 0.1 and 0.9.'); } super.doScaleToTrackMetric('Tracking', { targetValue: props.targetUtilizationValue, diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index 7c18aac47d4ff..086ab9fd97592 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -565,7 +565,7 @@ export = { const target = alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 5 }); // THEN - test.throws(() => target.scaleOnUtilization({targetUtilizationPercent: 0.95}), /TargetUtilizationPercent should be between 0.1 and 0.9./); + test.throws(() => target.scaleOnUtilization({targetUtilizationValue: 0.95}), /TargetUtilizationValue should be between 0.1 and 0.9./); test.done(); }, From 7244d4a3f8bea1515e7eecd749dc42c6c53c8733 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 21 Jul 2020 16:51:44 -0400 Subject: [PATCH 22/35] yet another --- packages/@aws-cdk/aws-lambda/test/test.alias.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index 086ab9fd97592..c7d316277dafd 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -545,7 +545,7 @@ export = { test.done(); }, - 'error when specifying invalid utilizationPercent when AutoScaling on utilization'(test: Test): void { + 'error when specifying invalid utilization value when AutoScaling on utilization'(test: Test): void { // GIVEN const stack = new Stack(); const fn = new lambda.Function(stack, 'MyLambda', { From b5135699d577ebef516016667f382f76b5952dbb Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 21 Jul 2020 17:46:06 -0400 Subject: [PATCH 23/35] update integ test --- .../aws-lambda/test/integ.autoscaling.expected.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json index fd622e9532bd6..5c3826d035d8e 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json @@ -134,12 +134,7 @@ "Schedule": "cron(0 20 * * ? *)", "ScheduledActionName": "ScaleDownAtNight" } - ], - "SuspendedState": { - "DynamicScalingInSuspended": false, - "DynamicScalingOutSuspended": false, - "ScheduledScalingSuspended": false - } + ] } }, "AliasAliasScalingTargetTrackingA7718D48": { From ba7d0679b8143cbef7e20c8fc6ca3dbb8637bbc8 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 22 Jul 2020 13:23:48 -0400 Subject: [PATCH 24/35] incorporated elads comments --- packages/@aws-cdk/aws-lambda/README.md | 17 ++++--- packages/@aws-cdk/aws-lambda/lib/alias.ts | 14 ++--- .../lib/scalable-function-attribute.ts | 32 ++++++------ packages/@aws-cdk/aws-lambda/package.json | 3 +- .../aws-lambda/test/integ.autoscaling.ts | 4 +- .../@aws-cdk/aws-lambda/test/test.alias.ts | 51 ++++++++++++++++--- 6 files changed, 78 insertions(+), 43 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index 15be451ce5a4d..d361d3147cee8 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -272,9 +272,9 @@ const fn = new lambda.Function(this, 'MyFunction', { See [the AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/concurrent-executions.html) managing concurrency. -### Configuring AutoScaling +### AutoScaling -You can configure autoscaling of provisioned concurrency on a function alias using the `autoScaleProvisionedConcurrency()` method: +You can use Application AutoScaling to automatically configure the provisioned concurrency for your functions. AWS Lambda supports Target Tracking and Scheduled Scaling policies. Use the `addAutoScaling()` method to configure autoscaling on a function alias: ```ts const alias = new lambda.Alias(stack, 'Alias', { @@ -282,13 +282,16 @@ const alias = new lambda.Alias(stack, 'Alias', { version, }); -// Configure autoscaling to track utilization -alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 50 }).scaleOnUtilization({ - targetUtilizationValue: 0.5, +// Create AutoScaling target +const as = alias.addAutoScaling({ maxCapacity: 50 }) + +// Configure Target Tracking +as.scaleOnUtilization({ + utilizationTarget: 0.5, }); -// or you can configure schedule scaling -alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 50}).scaleOnSchedule('ScaleUpInTheMorning', { +// Configure Scheduled Scaling +as.scaleOnSchedule('ScaleUpInTheMorning', { schedule: appscaling.Schedule.cron({ hour: '8', minute: '0'}), minCapacity: 20, }); diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index 383c9a9dc2e76..ea30bb5b69e54 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -1,12 +1,12 @@ import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; import * as iam from '@aws-cdk/aws-iam'; -import { Construct, Stack} from '@aws-cdk/core'; +import { Construct } from '@aws-cdk/core'; import { EventInvokeConfigOptions } from './event-invoke-config'; import { IFunction, QualifiedFunctionBase } from './function-base'; import { extractQualifierFromArn, IVersion } from './lambda-version'; import { CfnAlias } from './lambda.generated'; -import { EnableScalingProps, IScalableFunctionAttribute, ScalableFunctionAttribute } from './scalable-function-attribute'; +import { AutoScalingOptions, IScalableFunctionAttribute, ScalableFunctionAttribute } from './scalable-function-attribute'; export interface IAlias extends IFunction { /** @@ -155,7 +155,7 @@ export class Alias extends QualifiedFunctionBase implements IAlias { // Use a Service Linked Role // https://docs.aws.amazon.com/autoscaling/application/userguide/application-auto-scaling-service-linked-roles.html - this.scalingRole = iam.Role.fromRoleArn(this, 'ScalingRole', Stack.of(this).formatArn({ + this.scalingRole = iam.Role.fromRoleArn(this, 'ScalingRole', this.stack.formatArn({ service: 'iam', region: '', resource: 'role/aws-service-role/lambda.application-autoscaling.amazonaws.com', @@ -212,15 +212,15 @@ export class Alias extends QualifiedFunctionBase implements IAlias { * Configure provisioned concurrency autoscaling on a function alias. Returns a scalable attribute that can call * `scaleOnUtilization()` and `scaleOnSchedule()`. * - * @param props The properties for autoscaling + * @param options Autoscaling options */ - public autoScaleProvisionedConcurrency(props: EnableScalingProps): IScalableFunctionAttribute { + public addAutoScaling(options: AutoScalingOptions): IScalableFunctionAttribute { if (this.scalableAlias) { throw new Error('Autoscaling already enabled for this alias'); } return this.scalableAlias = new ScalableFunctionAttribute(this, 'AliasScaling', { - minCapacity: props.minCapacity, - maxCapacity: props.maxCapacity, + minCapacity: options.minCapacity ?? 1, + maxCapacity: options.maxCapacity, resourceId: `function:${this.functionName}`, dimension: 'lambda:function:ProvisionedConcurrency', serviceNamespace: appscaling.ServiceNamespace.LAMBDA, diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts index 9b86e33014a33..cd97f551fd545 100644 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts @@ -1,10 +1,10 @@ import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; -import { Construct } from '@aws-cdk/core'; +import { IConstruct, Token } from '@aws-cdk/core'; /** * Interface for scalable attributes */ -export interface IScalableFunctionAttribute { +export interface IScalableFunctionAttribute extends IConstruct { /** * Scale out or in to keep utilization at a given level. The utilization is tracked by the * LambdaProvisionedConcurrencyUtilization metric, emitted by lambda. See: @@ -12,7 +12,7 @@ export interface IScalableFunctionAttribute { * * Allowed values: 0.1 - 0.9. */ - scaleOnUtilization(props: UtilizationScalingOptions): void; + scaleOnUtilization(options: UtilizationScalingOptions): void; /** * Scale out or in based on schedule. */ @@ -24,21 +24,17 @@ export interface IScalableFunctionAttribute { */ export interface UtilizationScalingOptions extends appscaling.BaseTargetTrackingProps { /** - * Target utilization value for the attribute. For example, .5 indicates that 50 percent of allocated provisioned concurrency is in use. + * Utilization target for the attribute. For example, .5 indicates that 50 percent of allocated provisioned concurrency is in use. * * Allowed values: 0.1 - 0.9. */ - readonly targetUtilizationValue: number; + readonly utilizationTarget: number; } /** * A scalable lambda alias attribute */ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute { - constructor(scope: Construct, id: string, props: appscaling.BaseScalableAttributeProps) { - super(scope, id, props); - } - /** * Scale out or in to keep utilization at a given level. The utilization is tracked by the * LambdaProvisionedConcurrencyUtilization metric, emitted by lambda. See: @@ -46,14 +42,14 @@ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute * * Allowed values: 0.1 - 0.9. */ - public scaleOnUtilization(props: UtilizationScalingOptions) { - if (props.targetUtilizationValue < 0.1 || props.targetUtilizationValue > 0.9) { - throw new Error('TargetUtilizationValue should be between 0.1 and 0.9.'); + public scaleOnUtilization(options: UtilizationScalingOptions) { + if ( !Token.isUnresolved(options.utilizationTarget) && (options.utilizationTarget < 0.1 || options.utilizationTarget > 0.9)) { + throw new Error('Utilization Target should be between 0.1 and 0.9.'); } super.doScaleToTrackMetric('Tracking', { - targetValue: props.targetUtilizationValue, + targetValue: options.utilizationTarget, predefinedMetric: appscaling.PredefinedMetric.LAMBDA_PROVISIONED_CONCURRENCY_UTILIZATION, - ...props, + ...options, }); } @@ -66,13 +62,15 @@ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute } /** - * Properties for enabling Lambda capacity scaling + * Properties for enabling Lambda autoscaling */ -export interface EnableScalingProps { +export interface AutoScalingOptions { /** * Minimum capacity to scale to + * + * @default 1 */ - readonly minCapacity: number; + readonly minCapacity?: number; /** * Maximum capacity to scale to diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 6fd95cdb61ae5..bd2a5ad138fed 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -173,8 +173,7 @@ "docs-public-apis:@aws-cdk/aws-lambda.VersionAttributes", "props-physical-name:@aws-cdk/aws-lambda.EventInvokeConfigProps", "props-struct-name:@aws-cdk/aws-lambda.IScalableFunctionAttribute", - "construct-ctor-props-type:@aws-cdk/aws-lambda.ScalableFunctionAttribute", - "construct-interface-extends-iconstruct:@aws-cdk/aws-lambda.IScalableFunctionAttribute" + "construct-ctor-props-type:@aws-cdk/aws-lambda.ScalableFunctionAttribute" ] }, "stability": "stable", diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts index c5b417a394f90..4896f42c757fc 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts @@ -24,10 +24,10 @@ const alias = new lambda.Alias(stack, 'Alias', { version, }); -const scalingTarget = alias.autoScaleProvisionedConcurrency({ minCapacity: 3, maxCapacity: 50 }); +const scalingTarget = alias.addAutoScaling({ minCapacity: 3, maxCapacity: 50 }); scalingTarget.scaleOnUtilization({ - targetUtilizationValue: 0.5, + utilizationTarget: 0.5, }); scalingTarget.scaleOnSchedule('ScaleUpInTheMorning', { diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index c7d316277dafd..04e477955539b 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -1,7 +1,7 @@ import { arrayWith, beASupersetOfTemplate, expect, haveResource, haveResourceLike, objectLike } from '@aws-cdk/assert'; import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; -import { Stack } from '@aws-cdk/core'; +import { Lazy, Stack } from '@aws-cdk/core'; import { Test } from 'nodeunit'; import * as lambda from '../lib'; @@ -449,7 +449,7 @@ export = { }); // WHEN - alias.autoScaleProvisionedConcurrency({minCapacity: 1, maxCapacity: 5}); + alias.addAutoScaling({ maxCapacity: 5}); // THEN expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalableTarget', { @@ -491,7 +491,7 @@ export = { }); // WHEN - alias.autoScaleProvisionedConcurrency({minCapacity: 1, maxCapacity: 5}); + alias.addAutoScaling({ maxCapacity: 5}); // THEN expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalableTarget', { @@ -520,6 +520,41 @@ export = { test.done(); }, + 'validation for utilizationTarget does not fail when using Tokens'(test: Test) { + // GIVEN + const stack = new Stack(); + const fn = new lambda.Function(stack, 'MyLambda', { + code: new lambda.InlineCode('hello()'), + handler: 'index.hello', + runtime: lambda.Runtime.NODEJS_10_X, + }); + + const version = fn.addVersion('1', undefined, 'testing'); + + const alias = new lambda.Alias(stack, 'Alias', { + aliasName: 'prod', + version, + provisionedConcurrentExecutions: 10, + }); + + // WHEN + const target = alias.addAutoScaling({ maxCapacity: 5 }); + + target.scaleOnUtilization({utilizationTarget: Lazy.numberValue({ produce: () => 0.95 })}); + + // THEN: no exception + expect(stack).to(haveResource('AWS::ApplicationAutoScaling::ScalingPolicy', { + PolicyType: 'TargetTrackingScaling', + TargetTrackingScalingPolicyConfiguration: { + PredefinedMetricSpecification: { PredefinedMetricType: 'LambdaProvisionedConcurrencyUtilization' }, + TargetValue: 0.95, + }, + + })); + + test.done(); + }, + 'cannot enable AutoScaling twice on same property'(test: Test): void { // GIVEN const stack = new Stack(); @@ -537,10 +572,10 @@ export = { }); // WHEN - alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 5 }); + alias.addAutoScaling({ maxCapacity: 5 }); // THEN - test.throws(() => alias.autoScaleProvisionedConcurrency({ minCapacity: 3, maxCapacity: 8 }), /Autoscaling already enabled for this alias/); + test.throws(() => alias.addAutoScaling({ maxCapacity: 8 }), /Autoscaling already enabled for this alias/); test.done(); }, @@ -562,10 +597,10 @@ export = { }); // WHEN - const target = alias.autoScaleProvisionedConcurrency({ minCapacity: 1, maxCapacity: 5 }); + const target = alias.addAutoScaling({ maxCapacity: 5 }); // THEN - test.throws(() => target.scaleOnUtilization({targetUtilizationValue: 0.95}), /TargetUtilizationValue should be between 0.1 and 0.9./); + test.throws(() => target.scaleOnUtilization({utilizationTarget: 0.95}), /Utilization Target should be between 0.1 and 0.9./); test.done(); }, @@ -586,7 +621,7 @@ export = { }); // WHEN - const target = alias.autoScaleProvisionedConcurrency({minCapacity: 1, maxCapacity: 5}); + const target = alias.addAutoScaling({ maxCapacity: 5}); target.scaleOnSchedule('Scheduling', { schedule: appscaling.Schedule.cron({}), maxCapacity: 10, From 54d10dcfaa31668aec423a6f83252205829e52d9 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 22 Jul 2020 14:19:10 -0400 Subject: [PATCH 25/35] minor edit --- packages/@aws-cdk/aws-lambda/lib/alias.ts | 2 +- packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index ea30bb5b69e54..7a049d898c88c 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -216,7 +216,7 @@ export class Alias extends QualifiedFunctionBase implements IAlias { */ public addAutoScaling(options: AutoScalingOptions): IScalableFunctionAttribute { if (this.scalableAlias) { - throw new Error('Autoscaling already enabled for this alias'); + throw new Error('AutoScaling already enabled for this alias'); } return this.scalableAlias = new ScalableFunctionAttribute(this, 'AliasScaling', { minCapacity: options.minCapacity ?? 1, diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts index cd97f551fd545..f2e21cb4eb6a8 100644 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts @@ -20,7 +20,7 @@ export interface IScalableFunctionAttribute extends IConstruct { } /** - * Properties for enabling Lambda utilization tracking + * Options for enabling Lambda utilization tracking */ export interface UtilizationScalingOptions extends appscaling.BaseTargetTrackingProps { /** From ed87489082a1afabc9b60b5ca653da3541219123 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 22 Jul 2020 14:54:29 -0400 Subject: [PATCH 26/35] fixed test case --- packages/@aws-cdk/aws-lambda/test/test.alias.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index 04e477955539b..d6635acd4c092 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -575,7 +575,7 @@ export = { alias.addAutoScaling({ maxCapacity: 5 }); // THEN - test.throws(() => alias.addAutoScaling({ maxCapacity: 8 }), /Autoscaling already enabled for this alias/); + test.throws(() => alias.addAutoScaling({ maxCapacity: 8 }), /AutoScaling already enabled for this alias/); test.done(); }, From eb907d8f4b6a1eaccd3e801a67dc66e360e3ee17 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Wed, 22 Jul 2020 15:26:40 -0400 Subject: [PATCH 27/35] readme change --- packages/@aws-cdk/aws-lambda/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index d361d3147cee8..dc21256773660 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -274,7 +274,7 @@ managing concurrency. ### AutoScaling -You can use Application AutoScaling to automatically configure the provisioned concurrency for your functions. AWS Lambda supports Target Tracking and Scheduled Scaling policies. Use the `addAutoScaling()` method to configure autoscaling on a function alias: +You can use Application AutoScaling to automatically configure the provisioned concurrency for your functions. AutoScaling can be set to track utilization or be based on a schedule. To configure AutoScaling on a function alias: ```ts const alias = new lambda.Alias(stack, 'Alias', { From e73ae72b4d3c1674b5155d3115415a8ec42b7034 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Mon, 3 Aug 2020 14:03:23 -0400 Subject: [PATCH 28/35] satisfied aws-lint --- .../aws-lambda/lib/scalable-function-attribute.ts | 11 ++++++++++- packages/@aws-cdk/aws-lambda/package.json | 4 +--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts index f2e21cb4eb6a8..afc615b9a8b73 100644 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts @@ -1,5 +1,5 @@ import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; -import { IConstruct, Token } from '@aws-cdk/core'; +import { Construct, IConstruct, Token } from '@aws-cdk/core'; /** * Interface for scalable attributes @@ -35,6 +35,9 @@ export interface UtilizationScalingOptions extends appscaling.BaseTargetTracking * A scalable lambda alias attribute */ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute { + constructor(scope: Construct, id: string, props: ScalableFunctionAttributeProps){ + super(scope, id, props); + } /** * Scale out or in to keep utilization at a given level. The utilization is tracked by the * LambdaProvisionedConcurrencyUtilization metric, emitted by lambda. See: @@ -61,6 +64,12 @@ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute } } +/** + * Properties of a scalable function attribute + */ +export interface ScalableFunctionAttributeProps extends appscaling.BaseScalableAttributeProps { +} + /** * Properties for enabling Lambda autoscaling */ diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index bd2a5ad138fed..a4274700c3e5a 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -171,9 +171,7 @@ "props-default-doc:@aws-cdk/aws-lambda.Permission.sourceArn", "docs-public-apis:@aws-cdk/aws-lambda.ResourceBindOptions", "docs-public-apis:@aws-cdk/aws-lambda.VersionAttributes", - "props-physical-name:@aws-cdk/aws-lambda.EventInvokeConfigProps", - "props-struct-name:@aws-cdk/aws-lambda.IScalableFunctionAttribute", - "construct-ctor-props-type:@aws-cdk/aws-lambda.ScalableFunctionAttribute" + "props-physical-name:@aws-cdk/aws-lambda.EventInvokeConfigProps" ] }, "stability": "stable", From 7f3e40347e723bd9394474c760367edf9f977306 Mon Sep 17 00:00:00 2001 From: Neta Nir Date: Thu, 6 Aug 2020 14:16:43 -0700 Subject: [PATCH 29/35] cr changes --- packages/@aws-cdk/aws-lambda/README.md | 2 +- ...on => integ.autoscaling.lit.expected.json} | 7 +++ .../aws-lambda/test/integ.autoscaling.lit.ts | 53 +++++++++++++++++++ .../aws-lambda/test/integ.autoscaling.ts | 43 --------------- 4 files changed, 61 insertions(+), 44 deletions(-) rename packages/@aws-cdk/aws-lambda/test/{integ.autoscaling.expected.json => integ.autoscaling.lit.expected.json} (97%) create mode 100644 packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts delete mode 100644 packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts diff --git a/packages/@aws-cdk/aws-lambda/README.md b/packages/@aws-cdk/aws-lambda/README.md index a8a973464f48b..46af73e61beb5 100644 --- a/packages/@aws-cdk/aws-lambda/README.md +++ b/packages/@aws-cdk/aws-lambda/README.md @@ -297,7 +297,7 @@ as.scaleOnSchedule('ScaleUpInTheMorning', { }); ``` -[Example of Lambda AutoScaling usage](test/integ.autoscaling.ts) +[Example of Lambda AutoScaling usage](test/integ.autoscaling.lit.ts) See [the AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/invocation-scaling.html) on autoscaling lambda functions. diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.expected.json similarity index 97% rename from packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json rename to packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.expected.json index 5c3826d035d8e..57336bdb03462 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.expected.json @@ -153,5 +153,12 @@ } } } + }, + "Outputs": { + "Output": { + "Value": { + "Ref": "MyLambdaCCE802FB" + } + } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts new file mode 100644 index 0000000000000..a08b6ea076a9f --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts @@ -0,0 +1,53 @@ +import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; +import * as cdk from '@aws-cdk/core'; +import * as lambda from '../lib'; + +/** +* Stack verification steps: +* aws application-autoscaling describe-scalable-targets --service-namespace lambda --resource-ids function::prod +* has a minCapacity of 3 and maxCapacity of 50 +*/ +class TestStack extends cdk.Stack { + constructor(scope: cdk.App, id: string) { + super(scope, id); + + const fn = new lambda.Function(this, 'MyLambda', { + code: new lambda.InlineCode('exports.handler = async () => {\nconsole.log(\'hello world\');\n};'), + handler: 'index.handler', + runtime: lambda.Runtime.NODEJS_10_X, + }); + + const version = fn.addVersion('1', undefined, 'integ-test'); + + const alias = new lambda.Alias(this, 'Alias', { + aliasName: 'prod', + version, + }); + + const scalingTarget = alias.addAutoScaling({ minCapacity: 3, maxCapacity: 50 }); + + scalingTarget.scaleOnUtilization({ + utilizationTarget: 0.5, + }); + + scalingTarget.scaleOnSchedule('ScaleUpInTheMorning', { + schedule: appscaling.Schedule.cron({ hour: '8', minute: '0'}), + minCapacity: 20, + }); + + scalingTarget.scaleOnSchedule('ScaleDownAtNight', { + schedule: appscaling.Schedule.cron({ hour: '20', minute: '0'}), + maxCapacity: 20, + }); + + new cdk.CfnOutput(this, 'Output', { + value: fn.functionName, + }); + } +} + +const app = new cdk.App(); + +new TestStack(app, 'aws-lambda-autoscaling'); + +app.synth(); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts deleted file mode 100644 index 4896f42c757fc..0000000000000 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; -import * as cdk from '@aws-cdk/core'; -import * as lambda from '../lib'; - -/** - * Stack verification steps: - * aws application-autoscaling describe-scalable-targets --service-namespace lambda --resource-ids function:aws-lambda-autoscaling-:prod - * has a minCapacity of 3 and maxCapacity of 50 - */ - -const app = new cdk.App(); -const stack = new cdk.Stack(app, 'aws-lambda-autoscaling'); - -const fn = new lambda.Function(stack, 'MyLambda', { - code: new lambda.InlineCode('exports.handler = async () => {\nconsole.log(\'hello world\');\n};'), - handler: 'index.handler', - runtime: lambda.Runtime.NODEJS_10_X, -}); - -const version = fn.addVersion('1', undefined, 'integ-test'); - -const alias = new lambda.Alias(stack, 'Alias', { - aliasName: 'prod', - version, -}); - -const scalingTarget = alias.addAutoScaling({ minCapacity: 3, maxCapacity: 50 }); - -scalingTarget.scaleOnUtilization({ - utilizationTarget: 0.5, -}); - -scalingTarget.scaleOnSchedule('ScaleUpInTheMorning', { - schedule: appscaling.Schedule.cron({ hour: '8', minute: '0'}), - minCapacity: 20, -}); - -scalingTarget.scaleOnSchedule('ScaleDownAtNight', { - schedule: appscaling.Schedule.cron({ hour: '20', minute: '0'}), - maxCapacity: 20, -}); - -app.synth(); From 281a8714d945e9e49c8694c165a9402b0ffcca6c Mon Sep 17 00:00:00 2001 From: Neta Nir Date: Thu, 6 Aug 2020 15:18:27 -0700 Subject: [PATCH 30/35] spaces everywhere --- .../aws-lambda/test/integ.autoscaling.lit.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts index a08b6ea076a9f..b143ebeaff575 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts @@ -10,31 +10,31 @@ import * as lambda from '../lib'; class TestStack extends cdk.Stack { constructor(scope: cdk.App, id: string) { super(scope, id); - + const fn = new lambda.Function(this, 'MyLambda', { code: new lambda.InlineCode('exports.handler = async () => {\nconsole.log(\'hello world\');\n};'), handler: 'index.handler', runtime: lambda.Runtime.NODEJS_10_X, }); - + const version = fn.addVersion('1', undefined, 'integ-test'); - + const alias = new lambda.Alias(this, 'Alias', { aliasName: 'prod', version, }); const scalingTarget = alias.addAutoScaling({ minCapacity: 3, maxCapacity: 50 }); - + scalingTarget.scaleOnUtilization({ utilizationTarget: 0.5, }); - + scalingTarget.scaleOnSchedule('ScaleUpInTheMorning', { schedule: appscaling.Schedule.cron({ hour: '8', minute: '0'}), minCapacity: 20, }); - + scalingTarget.scaleOnSchedule('ScaleDownAtNight', { schedule: appscaling.Schedule.cron({ hour: '20', minute: '0'}), maxCapacity: 20, From 6c02e2237250d85a5f2099dc3defe67126d5e8c7 Mon Sep 17 00:00:00 2001 From: Neta Nir Date: Mon, 10 Aug 2020 16:17:52 -0700 Subject: [PATCH 31/35] temp --- .../@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts index afc615b9a8b73..2ded163524f5f 100644 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts @@ -34,7 +34,8 @@ export interface UtilizationScalingOptions extends appscaling.BaseTargetTracking /** * A scalable lambda alias attribute */ -export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute { + + export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute implements IScalableFunctionAttribute{ constructor(scope: Construct, id: string, props: ScalableFunctionAttributeProps){ super(scope, id, props); } From 57d51a6ec26b57713e62887ba66b86b87db38ad8 Mon Sep 17 00:00:00 2001 From: Neta Nir Date: Mon, 10 Aug 2020 17:42:03 -0700 Subject: [PATCH 32/35] split files to allow not exporting private types --- packages/@aws-cdk/aws-lambda/lib/index.ts | 2 +- .../private/scalable-function-attribute.ts | 43 +++++++++ .../aws-lambda/lib/scalable-attribute-api.ts | 50 +++++++++++ .../lib/scalable-function-attribute.ts | 89 ------------------- 4 files changed, 94 insertions(+), 90 deletions(-) create mode 100644 packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts create mode 100644 packages/@aws-cdk/aws-lambda/lib/scalable-attribute-api.ts delete mode 100644 packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts diff --git a/packages/@aws-cdk/aws-lambda/lib/index.ts b/packages/@aws-cdk/aws-lambda/lib/index.ts index 8e629c9d89a55..3581a40cdf535 100644 --- a/packages/@aws-cdk/aws-lambda/lib/index.ts +++ b/packages/@aws-cdk/aws-lambda/lib/index.ts @@ -13,7 +13,7 @@ export * from './event-source'; export * from './event-source-mapping'; export * from './destination'; export * from './event-invoke-config'; -export * from './scalable-function-attribute'; +export * from './scalable-attribute-api'; export * from './log-retention'; diff --git a/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts new file mode 100644 index 0000000000000..dbf5c00d25ac2 --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts @@ -0,0 +1,43 @@ +import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; +import { Construct, Token } from '@aws-cdk/core'; +import { IScalableFunctionAttribute, UtilizationScalingOptions } from '../scalable-function-attribute'; + +/** + * A scalable lambda alias attribute + */ +export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute implements IScalableFunctionAttribute{ + constructor(scope: Construct, id: string, props: ScalableFunctionAttributeProps){ + super(scope, id, props); + } + + /** + * Scale out or in to keep utilization at a given level. The utilization is tracked by the + * LambdaProvisionedConcurrencyUtilization metric, emitted by lambda. See: + * https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html#monitoring-metrics-concurrency + * + * Allowed values: 0.1 - 0.9. + */ + public scaleOnUtilization(options: UtilizationScalingOptions) { + if ( !Token.isUnresolved(options.utilizationTarget) && (options.utilizationTarget < 0.1 || options.utilizationTarget > 0.9)) { + throw new Error('Utilization Target should be between 0.1 and 0.9.'); + } + super.doScaleToTrackMetric('Tracking', { + targetValue: options.utilizationTarget, + predefinedMetric: appscaling.PredefinedMetric.LAMBDA_PROVISIONED_CONCURRENCY_UTILIZATION, + ...options, + }); + } + + /** + * Scale out or in based on schedule. + */ + public scaleOnSchedule(id: string, action: appscaling.ScalingSchedule) { + super.doScaleOnSchedule(id, action); + } +} + +/** + * Properties of a scalable function attribute + */ +export interface ScalableFunctionAttributeProps extends appscaling.BaseScalableAttributeProps { +} diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-attribute-api.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-attribute-api.ts new file mode 100644 index 0000000000000..917af171cebba --- /dev/null +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-attribute-api.ts @@ -0,0 +1,50 @@ +import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; +import { IConstruct } from '@aws-cdk/core'; + + +/** + * Interface for scalable attributes + */ +export interface IScalableFunctionAttribute extends IConstruct { + /** + * Scale out or in to keep utilization at a given level. The utilization is tracked by the + * LambdaProvisionedConcurrencyUtilization metric, emitted by lambda. See: + * https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html#monitoring-metrics-concurrency + * + * Allowed values: 0.1 - 0.9. + */ + scaleOnUtilization(options: UtilizationScalingOptions): void; + /** + * Scale out or in based on schedule. + */ + scaleOnSchedule(id: string, actions: appscaling.ScalingSchedule): void; +} + +/** + * Options for enabling Lambda utilization tracking + */ +export interface UtilizationScalingOptions extends appscaling.BaseTargetTrackingProps { + /** + * Utilization target for the attribute. For example, .5 indicates that 50 percent of allocated provisioned concurrency is in use. + * + * Allowed values: 0.1 - 0.9. + */ + readonly utilizationTarget: number; +} + +/** + * Properties for enabling Lambda autoscaling + */ +export interface AutoScalingOptions { + /** + * Minimum capacity to scale to + * + * @default 1 + */ + readonly minCapacity?: number; + + /** + * Maximum capacity to scale to + */ + readonly maxCapacity: number; +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts deleted file mode 100644 index 2ded163524f5f..0000000000000 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-function-attribute.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; -import { Construct, IConstruct, Token } from '@aws-cdk/core'; - -/** - * Interface for scalable attributes - */ -export interface IScalableFunctionAttribute extends IConstruct { - /** - * Scale out or in to keep utilization at a given level. The utilization is tracked by the - * LambdaProvisionedConcurrencyUtilization metric, emitted by lambda. See: - * https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html#monitoring-metrics-concurrency - * - * Allowed values: 0.1 - 0.9. - */ - scaleOnUtilization(options: UtilizationScalingOptions): void; - /** - * Scale out or in based on schedule. - */ - scaleOnSchedule(id: string, actions: appscaling.ScalingSchedule): void; -} - -/** - * Options for enabling Lambda utilization tracking - */ -export interface UtilizationScalingOptions extends appscaling.BaseTargetTrackingProps { - /** - * Utilization target for the attribute. For example, .5 indicates that 50 percent of allocated provisioned concurrency is in use. - * - * Allowed values: 0.1 - 0.9. - */ - readonly utilizationTarget: number; -} - -/** - * A scalable lambda alias attribute - */ - - export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute implements IScalableFunctionAttribute{ - constructor(scope: Construct, id: string, props: ScalableFunctionAttributeProps){ - super(scope, id, props); - } - /** - * Scale out or in to keep utilization at a given level. The utilization is tracked by the - * LambdaProvisionedConcurrencyUtilization metric, emitted by lambda. See: - * https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html#monitoring-metrics-concurrency - * - * Allowed values: 0.1 - 0.9. - */ - public scaleOnUtilization(options: UtilizationScalingOptions) { - if ( !Token.isUnresolved(options.utilizationTarget) && (options.utilizationTarget < 0.1 || options.utilizationTarget > 0.9)) { - throw new Error('Utilization Target should be between 0.1 and 0.9.'); - } - super.doScaleToTrackMetric('Tracking', { - targetValue: options.utilizationTarget, - predefinedMetric: appscaling.PredefinedMetric.LAMBDA_PROVISIONED_CONCURRENCY_UTILIZATION, - ...options, - }); - } - - /** - * Scale out or in based on schedule. - */ - public scaleOnSchedule(id: string, action: appscaling.ScalingSchedule) { - super.doScaleOnSchedule(id, action); - } -} - -/** - * Properties of a scalable function attribute - */ -export interface ScalableFunctionAttributeProps extends appscaling.BaseScalableAttributeProps { -} - -/** - * Properties for enabling Lambda autoscaling - */ -export interface AutoScalingOptions { - /** - * Minimum capacity to scale to - * - * @default 1 - */ - readonly minCapacity?: number; - - /** - * Maximum capacity to scale to - */ - readonly maxCapacity: number; -} \ No newline at end of file From 741b5a5347ff1e5eda2a922715db66ea3cb6988e Mon Sep 17 00:00:00 2001 From: Neta Nir Date: Mon, 10 Aug 2020 18:27:41 -0700 Subject: [PATCH 33/35] fix docs --- .../aws-lambda/lib/private/scalable-function-attribute.ts | 4 +--- packages/@aws-cdk/aws-lambda/lib/scalable-attribute-api.ts | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts index dbf5c00d25ac2..8ccf96a40b604 100644 --- a/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts @@ -1,6 +1,6 @@ import * as appscaling from '@aws-cdk/aws-applicationautoscaling'; import { Construct, Token } from '@aws-cdk/core'; -import { IScalableFunctionAttribute, UtilizationScalingOptions } from '../scalable-function-attribute'; +import { IScalableFunctionAttribute, UtilizationScalingOptions } from '../scalable-attribute-api'; /** * A scalable lambda alias attribute @@ -14,8 +14,6 @@ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute * Scale out or in to keep utilization at a given level. The utilization is tracked by the * LambdaProvisionedConcurrencyUtilization metric, emitted by lambda. See: * https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html#monitoring-metrics-concurrency - * - * Allowed values: 0.1 - 0.9. */ public scaleOnUtilization(options: UtilizationScalingOptions) { if ( !Token.isUnresolved(options.utilizationTarget) && (options.utilizationTarget < 0.1 || options.utilizationTarget > 0.9)) { diff --git a/packages/@aws-cdk/aws-lambda/lib/scalable-attribute-api.ts b/packages/@aws-cdk/aws-lambda/lib/scalable-attribute-api.ts index 917af171cebba..e64fcbd5a8ca7 100644 --- a/packages/@aws-cdk/aws-lambda/lib/scalable-attribute-api.ts +++ b/packages/@aws-cdk/aws-lambda/lib/scalable-attribute-api.ts @@ -10,8 +10,6 @@ export interface IScalableFunctionAttribute extends IConstruct { * Scale out or in to keep utilization at a given level. The utilization is tracked by the * LambdaProvisionedConcurrencyUtilization metric, emitted by lambda. See: * https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html#monitoring-metrics-concurrency - * - * Allowed values: 0.1 - 0.9. */ scaleOnUtilization(options: UtilizationScalingOptions): void; /** @@ -26,8 +24,6 @@ export interface IScalableFunctionAttribute extends IConstruct { export interface UtilizationScalingOptions extends appscaling.BaseTargetTrackingProps { /** * Utilization target for the attribute. For example, .5 indicates that 50 percent of allocated provisioned concurrency is in use. - * - * Allowed values: 0.1 - 0.9. */ readonly utilizationTarget: number; } From 74ea805c80f45f67c76e387a2f63725cfd8b4e54 Mon Sep 17 00:00:00 2001 From: Neta Nir Date: Mon, 10 Aug 2020 18:49:07 -0700 Subject: [PATCH 34/35] fix; --- packages/@aws-cdk/aws-lambda/lib/alias.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/alias.ts b/packages/@aws-cdk/aws-lambda/lib/alias.ts index 7a049d898c88c..c740b6620a061 100644 --- a/packages/@aws-cdk/aws-lambda/lib/alias.ts +++ b/packages/@aws-cdk/aws-lambda/lib/alias.ts @@ -6,7 +6,8 @@ import { EventInvokeConfigOptions } from './event-invoke-config'; import { IFunction, QualifiedFunctionBase } from './function-base'; import { extractQualifierFromArn, IVersion } from './lambda-version'; import { CfnAlias } from './lambda.generated'; -import { AutoScalingOptions, IScalableFunctionAttribute, ScalableFunctionAttribute } from './scalable-function-attribute'; +import { ScalableFunctionAttribute } from './private/scalable-function-attribute'; +import { AutoScalingOptions, IScalableFunctionAttribute } from './scalable-attribute-api'; export interface IAlias extends IFunction { /** From 9ab7fee3664ea9934e9e8b0eaf4827ad5831ea92 Mon Sep 17 00:00:00 2001 From: Kaizen Conroy Date: Tue, 11 Aug 2020 14:10:23 -0400 Subject: [PATCH 35/35] final changes --- .../aws-lambda/lib/private/scalable-function-attribute.ts | 2 +- .../aws-lambda/test/integ.autoscaling.lit.expected.json | 2 +- packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts | 2 +- packages/@aws-cdk/aws-lambda/test/test.alias.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts b/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts index 8ccf96a40b604..21b09cd2c79e2 100644 --- a/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts +++ b/packages/@aws-cdk/aws-lambda/lib/private/scalable-function-attribute.ts @@ -17,7 +17,7 @@ export class ScalableFunctionAttribute extends appscaling.BaseScalableAttribute */ public scaleOnUtilization(options: UtilizationScalingOptions) { if ( !Token.isUnresolved(options.utilizationTarget) && (options.utilizationTarget < 0.1 || options.utilizationTarget > 0.9)) { - throw new Error('Utilization Target should be between 0.1 and 0.9.'); + throw new Error(`Utilization Target should be between 0.1 and 0.9. Found ${options.utilizationTarget}.`); } super.doScaleToTrackMetric('Tracking', { targetValue: options.utilizationTarget, diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.expected.json index 57336bdb03462..39ff5f8f0e0fb 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.expected.json @@ -155,7 +155,7 @@ } }, "Outputs": { - "Output": { + "FunctionName": { "Value": { "Ref": "MyLambdaCCE802FB" } diff --git a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts index b143ebeaff575..360dda2a1b772 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts +++ b/packages/@aws-cdk/aws-lambda/test/integ.autoscaling.lit.ts @@ -40,7 +40,7 @@ class TestStack extends cdk.Stack { maxCapacity: 20, }); - new cdk.CfnOutput(this, 'Output', { + new cdk.CfnOutput(this, 'FunctionName', { value: fn.functionName, }); } diff --git a/packages/@aws-cdk/aws-lambda/test/test.alias.ts b/packages/@aws-cdk/aws-lambda/test/test.alias.ts index d6635acd4c092..af24cca911a5c 100644 --- a/packages/@aws-cdk/aws-lambda/test/test.alias.ts +++ b/packages/@aws-cdk/aws-lambda/test/test.alias.ts @@ -600,7 +600,7 @@ export = { const target = alias.addAutoScaling({ maxCapacity: 5 }); // THEN - test.throws(() => target.scaleOnUtilization({utilizationTarget: 0.95}), /Utilization Target should be between 0.1 and 0.9./); + test.throws(() => target.scaleOnUtilization({utilizationTarget: 0.95}), /Utilization Target should be between 0.1 and 0.9. Found 0.95/); test.done(); },