Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export interface BasicStepScalingPolicyProps {
* The intervals for scaling.
*
* Maps a range of metric values to a particular scaling behavior.
*
* Must be between 2 and 40 steps.
*/
readonly scalingSteps: ScalingInterval[];

Expand Down Expand Up @@ -111,6 +113,10 @@ export class StepScalingPolicy extends Construct {
throw new Error('You must supply at least 2 intervals for autoscaling');
}

if (props.scalingSteps.length > 40) {
throw new Error(`'scalingSteps' can have at most 40 steps, got ${props.scalingSteps.length}`);
}

if (props.datapointsToAlarm !== undefined && props.datapointsToAlarm < 1) {
throw new RangeError(`datapointsToAlarm cannot be less than 1, got: ${props.datapointsToAlarm}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,47 @@ describe('step scaling policy', () => {
});
}).toThrow('datapointsToAlarm cannot be less than 1, got: 0');
});

test('scalingSteps must have at least 2 steps', () => {
// GIVEN
const stack = new cdk.Stack();
const target = createScalableTarget(stack);

expect(() => {
target.scaleOnMetric('Tracking', {
metric: new cloudwatch.Metric({ namespace: 'Test', metricName: 'Metric' }),
scalingSteps: [
{ lower: 0, upper: 2, change: +1 },
],
});
}).toThrow(/must supply at least 2/);
});

test('scalingSteps has a maximum of 40 steps', () => {
// GIVEN
const stack = new cdk.Stack();
const target = createScalableTarget(stack);

const numSteps = 41;
const messagesPerTask = 20;
let steps: appscaling.ScalingInterval[] = [];

for (let i = 0; i < numSteps; ++i) {
const step: appscaling.ScalingInterval = {
lower: i * messagesPerTask,
upper: i * (messagesPerTask + 1) - 1,
change: i + 1,
};
steps.push(step);
}

expect(() => {
target.scaleOnMetric('Tracking', {
metric: new cloudwatch.Metric({ namespace: 'Test', metricName: 'Metric' }),
scalingSteps: steps,
});
}).toThrow('\'scalingSteps\' can have at most 40 steps, got 41');
});
});

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export interface BasicStepScalingPolicyProps {
* The intervals for scaling.
*
* Maps a range of metric values to a particular scaling behavior.
*
* Must be between 2 and 40 steps.
*/
readonly scalingSteps: ScalingInterval[];

Expand Down Expand Up @@ -96,6 +98,10 @@ export class StepScalingPolicy extends Construct {
throw new Error('You must supply at least 2 intervals for autoscaling');
}

if (props.scalingSteps.length > 40) {
throw new Error(`'scalingSteps' can have at most 40 steps, got ${props.scalingSteps.length}`);
}

const adjustmentType = props.adjustmentType || AdjustmentType.CHANGE_IN_CAPACITY;
const changesAreAbsolute = adjustmentType === AdjustmentType.EXACT_CAPACITY;

Expand Down
54 changes: 54 additions & 0 deletions packages/aws-cdk-lib/aws-autoscaling/test/scaling.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,60 @@ test('step scaling with evaluation period configured', () => {
});
});

describe('step-scaling-policy scalingSteps length validation checks', () => {
test('scalingSteps must have at least 2 steps', () => {
// GIVEN
const stack = new cdk.Stack();
const fixture = new ASGFixture(stack, 'Fixture');

expect(() => {
fixture.asg.scaleOnMetric('Metric', {
metric: new cloudwatch.Metric({
metricName: 'Legs',
namespace: 'Henk',
dimensionsMap: { Mustache: 'Bushy' },
}),
estimatedInstanceWarmup: cdk.Duration.seconds(150),
// only one scaling step throws an error.
scalingSteps: [
{ lower: 0, upper: 2, change: +1 },
],
});
}).toThrow(/must supply at least 2/);
});

test('scalingSteps has a maximum of 40 steps', () => {
// GIVEN
const stack = new cdk.Stack();
const fixture = new ASGFixture(stack, 'Fixture');

const numSteps = 41;
const messagesPerTask = 20;
let steps: autoscaling.ScalingInterval[] = [];

for (let i = 0; i < numSteps; ++i) {
const step: autoscaling.ScalingInterval = {
lower: i * messagesPerTask,
upper: i * (messagesPerTask + 1) - 1,
change: i + 1,
};
steps.push(step);
}

expect(() => {
fixture.asg.scaleOnMetric('Metric', {
metric: new cloudwatch.Metric({
metricName: 'Legs',
namespace: 'Henk',
dimensionsMap: { Mustache: 'Bushy' },
}),
estimatedInstanceWarmup: cdk.Duration.seconds(150),
scalingSteps: steps,
});
}).toThrow('\'scalingSteps\' can have at most 40 steps, got 41');
});
});

class ASGFixture extends Construct {
public readonly vpc: ec2.Vpc;
public readonly asg: autoscaling.AutoScalingGroup;
Expand Down