Skip to content

Commit d3f3309

Browse files
authored
feat(autoscaling): throw ValidationError instead of untyped Errors (#33388)
### Issue Relates to #32569 ### Description of changes `ValidationErrors` everywhere ### Describe any new or updated permissions being added n/a ### Description of how you validated changes Existing tests. Exemptions granted as this is a refactor of existing code. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 48f2bf7 commit d3f3309

11 files changed

+90
-85
lines changed

packages/aws-cdk-lib/.eslintrc.js

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const enableNoThrowDefaultErrorIn = [
2828
'aws-applicationautoscaling',
2929
'aws-appsync',
3030
'aws-appmesh',
31+
'aws-autoscaling',
32+
'aws-autoscaling-common',
3133
'aws-batch',
3234
'aws-cognito',
3335
'aws-elasticloadbalancing',

packages/aws-cdk-lib/aws-autoscaling-common/lib/interval-utils.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ScalingInterval } from './types';
2+
import { UnscopedValidationError } from '../../core';
23

34
export interface CompleteScalingInterval {
45
readonly lower: number;
@@ -30,12 +31,12 @@ export function normalizeIntervals(intervals: ScalingInterval[], changesAreAbsol
3031
*/
3132
function orderAndCompleteIntervals(intervals: ScalingInterval[]): CompleteScalingInterval[] {
3233
if (intervals.length < 2) {
33-
throw new Error('Require at least 2 intervals');
34+
throw new UnscopedValidationError('Require at least 2 intervals');
3435
}
3536

3637
for (const interval of intervals) {
3738
if (interval.lower === undefined && interval.upper === undefined) {
38-
throw new Error(`Must supply at least one of 'upper' or 'lower', got: ${JSON.stringify(interval)}`);
39+
throw new UnscopedValidationError(`Must supply at least one of 'upper' or 'lower', got: ${JSON.stringify(interval)}`);
3940
}
4041
}
4142

@@ -55,7 +56,7 @@ function orderAndCompleteIntervals(intervals: ScalingInterval[]): CompleteScalin
5556
if (intervals[lastIndex].upper === undefined) { intervals[lastIndex] = { ...intervals[lastIndex], upper: Infinity }; }
5657
for (const interval of intervals) {
5758
if (interval.lower === undefined || interval.upper === undefined) {
58-
throw new Error(`Could not determine the lower and upper bounds for ${JSON.stringify(interval)}`);
59+
throw new UnscopedValidationError(`Could not determine the lower and upper bounds for ${JSON.stringify(interval)}`);
5960
}
6061
}
6162

@@ -64,7 +65,7 @@ function orderAndCompleteIntervals(intervals: ScalingInterval[]): CompleteScalin
6465
// Validate that we have nonoverlapping intervals now.
6566
for (let i = 0; i < completeIntervals.length - 1; i++) {
6667
if (overlap(completeIntervals[i], completeIntervals[i + 1])) {
67-
throw new Error(`Two intervals overlap: ${JSON.stringify(completeIntervals[i])} and ${JSON.stringify(completeIntervals[i + 1])}`);
68+
throw new UnscopedValidationError(`Two intervals overlap: ${JSON.stringify(completeIntervals[i])} and ${JSON.stringify(completeIntervals[i + 1])}`);
6869
}
6970
}
7071

@@ -148,7 +149,7 @@ function combineUndefineds(intervals: CompleteScalingInterval[]) {
148149
function validateAtMostOneUndefined(intervals: CompleteScalingInterval[]) {
149150
const undef = intervals.filter(x => x.change === undefined);
150151
if (undef.length > 1) {
151-
throw new Error(`Can have at most one no-change interval, got ${JSON.stringify(undef)}`);
152+
throw new UnscopedValidationError(`Can have at most one no-change interval, got ${JSON.stringify(undef)}`);
152153
}
153154
}
154155

packages/aws-cdk-lib/aws-autoscaling/lib/auto-scaling-group.ts

+54-54
Large diffs are not rendered by default.

packages/aws-cdk-lib/aws-autoscaling/lib/lifecycle-hook.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { IAutoScalingGroup } from './auto-scaling-group';
33
import { CfnLifecycleHook } from './autoscaling.generated';
44
import { ILifecycleHookTarget } from './lifecycle-hook-target';
55
import * as iam from '../../aws-iam';
6-
import { Duration, IResource, Resource } from '../../core';
6+
import { Duration, IResource, Resource, ValidationError } from '../../core';
77
import { addConstructMetadata } from '../../core/lib/metadata-resource';
88

99
/**
@@ -97,7 +97,7 @@ export class LifecycleHook extends Resource implements ILifecycleHook {
9797
*/
9898
public get role() {
9999
if (!this._role) {
100-
throw new Error('\'role\' is undefined. Please specify a \'role\' or specify a \'notificationTarget\' to have a role provided for you.');
100+
throw new ValidationError('\'role\' is undefined. Please specify a \'role\' or specify a \'notificationTarget\' to have a role provided for you.', this);
101101
}
102102

103103
return this._role;
@@ -122,7 +122,7 @@ export class LifecycleHook extends Resource implements ILifecycleHook {
122122
this._role = props.role;
123123

124124
if (!props.notificationTarget) {
125-
throw new Error("'notificationTarget' parameter required when 'role' parameter is specified");
125+
throw new ValidationError("'notificationTarget' parameter required when 'role' parameter is specified", this);
126126
}
127127
} else {
128128
this._role = targetProps ? targetProps.createdRole : undefined;

packages/aws-cdk-lib/aws-autoscaling/lib/schedule.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Construct } from 'constructs';
2-
import { Annotations } from '../../core';
2+
import { Annotations, UnscopedValidationError } from '../../core';
33

44
/**
55
* Schedule for scheduled scaling actions
@@ -20,7 +20,7 @@ export abstract class Schedule {
2020
*/
2121
public static cron(options: CronOptions): Schedule {
2222
if (options.weekDay !== undefined && options.day !== undefined) {
23-
throw new Error('Cannot supply both \'day\' and \'weekDay\', use at most one');
23+
throw new UnscopedValidationError('Cannot supply both \'day\' and \'weekDay\', use at most one');
2424
}
2525

2626
const minute = fallback(options.minute, '*');

packages/aws-cdk-lib/aws-autoscaling/lib/scheduled-action.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Construct } from 'constructs';
22
import { IAutoScalingGroup } from './auto-scaling-group';
33
import { CfnScheduledAction } from './autoscaling.generated';
44
import { Schedule } from './schedule';
5-
import { Resource } from '../../core';
5+
import { Resource, ValidationError } from '../../core';
66
import { addConstructMetadata } from '../../core/lib/metadata-resource';
77

88
/**
@@ -104,7 +104,7 @@ export class ScheduledAction extends Resource {
104104
addConstructMetadata(this, props);
105105

106106
if (props.minCapacity === undefined && props.maxCapacity === undefined && props.desiredCapacity === undefined) {
107-
throw new Error('At least one of minCapacity, maxCapacity, or desiredCapacity is required');
107+
throw new ValidationError('At least one of minCapacity, maxCapacity, or desiredCapacity is required', this);
108108
}
109109

110110
// add a warning on synth when minute is not defined in a cron schedule

packages/aws-cdk-lib/aws-autoscaling/lib/step-scaling-action.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Construct } from 'constructs';
22
import { IAutoScalingGroup } from './auto-scaling-group';
33
import { CfnScalingPolicy } from './autoscaling.generated';
4-
import { Annotations, Duration, Lazy } from '../../core';
4+
import { Annotations, Duration, Lazy, ValidationError } from '../../core';
55

66
/**
77
* Properties for a scaling policy
@@ -97,7 +97,7 @@ export class StepScalingAction extends Construct {
9797
*/
9898
public addAdjustment(adjustment: AdjustmentTier) {
9999
if (adjustment.lowerBound === undefined && adjustment.upperBound === undefined) {
100-
throw new Error('At least one of lowerBound or upperBound is required');
100+
throw new ValidationError('At least one of lowerBound or upperBound is required', this);
101101
}
102102
this.adjustments.push({
103103
metricIntervalLowerBound: adjustment.lowerBound,

packages/aws-cdk-lib/aws-autoscaling/lib/step-scaling-policy.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { IAutoScalingGroup } from './auto-scaling-group';
33
import { AdjustmentType, MetricAggregationType, StepScalingAction } from './step-scaling-action';
44
import { findAlarmThresholds, normalizeIntervals } from '../../aws-autoscaling-common';
55
import * as cloudwatch from '../../aws-cloudwatch';
6-
import { Duration, Token } from '../../core';
6+
import { Duration, Token, ValidationError } from '../../core';
77

88
export interface BasicStepScalingPolicyProps {
99
/**
@@ -112,28 +112,28 @@ export class StepScalingPolicy extends Construct {
112112
super(scope, id);
113113

114114
if (props.scalingSteps.length < 2) {
115-
throw new Error('You must supply at least 2 intervals for autoscaling');
115+
throw new ValidationError('You must supply at least 2 intervals for autoscaling', this);
116116
}
117117

118118
if (props.scalingSteps.length > 40) {
119-
throw new Error(`'scalingSteps' can have at most 40 steps, got ${props.scalingSteps.length}`);
119+
throw new ValidationError(`'scalingSteps' can have at most 40 steps, got ${props.scalingSteps.length}`, this);
120120
}
121121

122122
if (props.evaluationPeriods !== undefined && !Token.isUnresolved(props.evaluationPeriods) && props.evaluationPeriods < 1) {
123-
throw new Error(`evaluationPeriods cannot be less than 1, got: ${props.evaluationPeriods}`);
123+
throw new ValidationError(`evaluationPeriods cannot be less than 1, got: ${props.evaluationPeriods}`, this);
124124
}
125125
if (props.datapointsToAlarm !== undefined) {
126126
if (props.evaluationPeriods === undefined) {
127-
throw new Error('evaluationPeriods must be set if datapointsToAlarm is set');
127+
throw new ValidationError('evaluationPeriods must be set if datapointsToAlarm is set', this);
128128
}
129129
if (!Token.isUnresolved(props.datapointsToAlarm) && props.datapointsToAlarm < 1) {
130-
throw new Error(`datapointsToAlarm cannot be less than 1, got: ${props.datapointsToAlarm}`);
130+
throw new ValidationError(`datapointsToAlarm cannot be less than 1, got: ${props.datapointsToAlarm}`, this);
131131
}
132132
if (!Token.isUnresolved(props.datapointsToAlarm)
133133
&& !Token.isUnresolved(props.evaluationPeriods)
134134
&& props.evaluationPeriods < props.datapointsToAlarm
135135
) {
136-
throw new Error(`datapointsToAlarm must be less than or equal to evaluationPeriods, got datapointsToAlarm: ${props.datapointsToAlarm}, evaluationPeriods: ${props.evaluationPeriods}`);
136+
throw new ValidationError(`datapointsToAlarm must be less than or equal to evaluationPeriods, got datapointsToAlarm: ${props.datapointsToAlarm}, evaluationPeriods: ${props.evaluationPeriods}`, this);
137137
}
138138
}
139139

packages/aws-cdk-lib/aws-autoscaling/lib/target-tracking-scaling-policy.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Construct } from 'constructs';
22
import { IAutoScalingGroup } from './auto-scaling-group';
33
import { CfnScalingPolicy } from './autoscaling.generated';
44
import * as cloudwatch from '../../aws-cloudwatch';
5-
import { Duration } from '../../core';
5+
import { Duration, ValidationError } from '../../core';
66

77
/**
88
* Base interface for target tracking props
@@ -110,20 +110,20 @@ export class TargetTrackingScalingPolicy extends Construct {
110110
private resource: CfnScalingPolicy;
111111

112112
constructor(scope: Construct, id: string, props: TargetTrackingScalingPolicyProps) {
113+
super(scope, id);
114+
113115
if ((props.customMetric === undefined) === (props.predefinedMetric === undefined)) {
114-
throw new Error('Exactly one of \'customMetric\' or \'predefinedMetric\' must be specified.');
116+
throw new ValidationError('Exactly one of \'customMetric\' or \'predefinedMetric\' must be specified.', this);
115117
}
116118

117119
if (props.predefinedMetric === PredefinedMetric.ALB_REQUEST_COUNT_PER_TARGET && !props.resourceLabel) {
118-
throw new Error('When tracking the ALBRequestCountPerTarget metric, the ALB identifier must be supplied in resourceLabel');
120+
throw new ValidationError('When tracking the ALBRequestCountPerTarget metric, the ALB identifier must be supplied in resourceLabel', this);
119121
}
120122

121123
if (props.customMetric && !props.customMetric.toMetricConfig().metricStat) {
122-
throw new Error('Only direct metrics are supported for Target Tracking. Use Step Scaling or supply a Metric object.');
124+
throw new ValidationError('Only direct metrics are supported for Target Tracking. Use Step Scaling or supply a Metric object.', this);
123125
}
124126

125-
super(scope, id);
126-
127127
this.resource = new CfnScalingPolicy(this, 'Resource', {
128128
policyType: 'TargetTrackingScaling',
129129
autoScalingGroupName: props.autoScalingGroup.autoScalingGroupName,

packages/aws-cdk-lib/aws-autoscaling/lib/volume.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// existed first in the "autoscaling" module before it existed in the "ec2"
33
// module so we couldn't standardize the structs in the right way.
44

5+
import { UnscopedValidationError } from '../../core';
6+
57
/**
68
* Block device
79
*/
@@ -154,7 +156,7 @@ export class BlockDeviceVolume {
154156
*/
155157
public static ephemeral(volumeIndex: number) {
156158
if (volumeIndex < 0) {
157-
throw new Error(`volumeIndex must be a number starting from 0, got "${volumeIndex}"`);
159+
throw new UnscopedValidationError(`volumeIndex must be a number starting from 0, got "${volumeIndex}"`);
158160
}
159161

160162
return new this(undefined, `ephemeral${volumeIndex}`);

packages/aws-cdk-lib/aws-autoscaling/lib/warm-pool.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Construct } from 'constructs';
22
import { IAutoScalingGroup } from './auto-scaling-group';
33
import { CfnWarmPool } from './autoscaling.generated';
4-
import { Lazy, Names, Resource } from '../../core';
4+
import { Lazy, Names, Resource, ValidationError } from '../../core';
55
import { addConstructMetadata } from '../../core/lib/metadata-resource';
66

77
/**
@@ -64,11 +64,11 @@ export class WarmPool extends Resource {
6464
addConstructMetadata(this, props);
6565

6666
if (props.maxGroupPreparedCapacity && props.maxGroupPreparedCapacity < -1) {
67-
throw new Error('\'maxGroupPreparedCapacity\' parameter should be greater than or equal to -1');
67+
throw new ValidationError('\'maxGroupPreparedCapacity\' parameter should be greater than or equal to -1', this);
6868
}
6969

7070
if (props.minSize && props.minSize < 0) {
71-
throw new Error('\'minSize\' parameter should be greater than or equal to 0');
71+
throw new ValidationError('\'minSize\' parameter should be greater than or equal to 0', this);
7272
}
7373

7474
new CfnWarmPool(this, 'Resource', {

0 commit comments

Comments
 (0)