Skip to content

Commit c83ca82

Browse files
authored
feat(batch): throw ValidationError instead of untyped Errors (#33389)
### 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 b4efb1e commit c83ca82

6 files changed

+36
-34
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const enableNoThrowDefaultErrorIn = [
2727
'aws-applicationautoscaling',
2828
'aws-appsync',
2929
'aws-appmesh',
30+
'aws-batch',
3031
'aws-cognito',
3132
'aws-elasticloadbalancing',
3233
'aws-elasticloadbalancingv2',

packages/aws-cdk-lib/aws-batch/lib/ecs-container-definition.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as iam from '../../aws-iam';
77
import { LogGroup } from '../../aws-logs';
88
import * as secretsmanager from '../../aws-secretsmanager';
99
import * as ssm from '../../aws-ssm';
10-
import { Lazy, PhysicalName, Size } from '../../core';
10+
import { Lazy, PhysicalName, Size, ValidationError } from '../../core';
1111

1212
const EFS_VOLUME_SYMBOL = Symbol.for('aws-cdk-lib/aws-batch/lib/container-definition.EfsVolume');
1313
const HOST_VOLUME_SYMBOL = Symbol.for('aws-cdk-lib/aws-batch/lib/container-definition.HostVolume');
@@ -695,7 +695,7 @@ abstract class EcsContainerDefinitionBase extends Construct implements IEcsConta
695695
};
696696
}
697697

698-
throw new Error('unsupported Volume encountered');
698+
throw new ValidationError('unsupported Volume encountered', this);
699699
});
700700
},
701701
}),
@@ -1059,15 +1059,15 @@ export class EcsFargateContainerDefinition extends EcsContainerDefinitionBase im
10591059

10601060
if (this.fargateOperatingSystemFamily?.isWindows() && this.readonlyRootFilesystem) {
10611061
// see https://kubernetes.io/docs/concepts/windows/intro/
1062-
throw new Error('Readonly root filesystem is not possible on Windows; write access is required for registry & system processes to run inside the container');
1062+
throw new ValidationError('Readonly root filesystem is not possible on Windows; write access is required for registry & system processes to run inside the container', this);
10631063
}
10641064

10651065
// validates ephemeralStorageSize is within limits
10661066
if (props.ephemeralStorageSize) {
10671067
if (props.ephemeralStorageSize.toGibibytes() > 200) {
1068-
throw new Error(`ECS Fargate container '${id}' specifies 'ephemeralStorageSize' at ${props.ephemeralStorageSize.toGibibytes()} > 200 GB`);
1068+
throw new ValidationError(`ECS Fargate container '${id}' specifies 'ephemeralStorageSize' at ${props.ephemeralStorageSize.toGibibytes()} > 200 GB`, this);
10691069
} else if (props.ephemeralStorageSize.toGibibytes() < 21) {
1070-
throw new Error(`ECS Fargate container '${id}' specifies 'ephemeralStorageSize' at ${props.ephemeralStorageSize.toGibibytes()} < 21 GB`);
1070+
throw new ValidationError(`ECS Fargate container '${id}' specifies 'ephemeralStorageSize' at ${props.ephemeralStorageSize.toGibibytes()} < 21 GB`, this);
10711071
}
10721072
}
10731073
}

packages/aws-cdk-lib/aws-batch/lib/eks-job-definition.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Construct } from 'constructs';
22
import { CfnJobDefinition } from './batch.generated';
33
import { EksContainerDefinition, EmptyDirVolume, HostPathVolume, SecretPathVolume } from './eks-container-definition';
44
import { baseJobDefinitionProperties, IJobDefinition, JobDefinitionBase, JobDefinitionProps } from './job-definition-base';
5-
import { ArnFormat, Lazy, Stack } from '../../core';
5+
import { ArnFormat, Lazy, Stack, ValidationError } from '../../core';
66
import { addConstructMetadata } from '../../core/lib/metadata-resource';
77

88
/**
@@ -204,7 +204,7 @@ export class EksJobDefinition extends JobDefinitionBase implements IEksJobDefini
204204
};
205205
}
206206

207-
throw new Error('unknown volume type');
207+
throw new ValidationError('unknown volume type', this);
208208
});
209209
},
210210
}),

packages/aws-cdk-lib/aws-batch/lib/job-queue.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Construct } from 'constructs';
22
import { CfnJobQueue } from './batch.generated';
33
import { IComputeEnvironment } from './compute-environment-base';
44
import { ISchedulingPolicy } from './scheduling-policy';
5-
import { ArnFormat, Duration, IResource, Lazy, Resource, Stack } from '../../core';
5+
import { ArnFormat, Duration, IResource, Lazy, Resource, Stack, ValidationError } from '../../core';
66
import { addConstructMetadata } from '../../core/lib/metadata-resource';
77

88
/**
@@ -241,7 +241,7 @@ export class JobQueue extends Resource implements IJobQueue {
241241
public readonly jobQueueName = stack.splitArn(jobQueueArn, ArnFormat.SLASH_RESOURCE_NAME).resourceName!;
242242

243243
public addComputeEnvironment(_computeEnvironment: IComputeEnvironment, _order: number): void {
244-
throw new Error(`cannot add ComputeEnvironments to imported JobQueue '${id}'`);
244+
throw new ValidationError(`cannot add ComputeEnvironments to imported JobQueue '${id}'`, this);
245245
}
246246
}
247247

@@ -308,16 +308,17 @@ export class JobQueue extends Resource implements IJobQueue {
308308
return;
309309
}
310310

311-
return jobStateTimeLimitActions.map((action, index) => renderJobStateTimeLimitAction(action, index));
311+
return jobStateTimeLimitActions.map((action, index) => renderJobStateTimeLimitAction(this, action, index));
312312

313313
function renderJobStateTimeLimitAction(
314+
scope: Construct,
314315
jobStateTimeLimitAction: JobStateTimeLimitAction,
315316
index: number,
316317
): CfnJobQueue.JobStateTimeLimitActionProperty {
317318
const maxTimeSeconds = jobStateTimeLimitAction.maxTime.toSeconds();
318319

319320
if (maxTimeSeconds < 600 || maxTimeSeconds > 86400) {
320-
throw new Error(`maxTime must be between 600 and 86400 seconds, got ${maxTimeSeconds} seconds at jobStateTimeLimitActions[${index}]`);
321+
throw new ValidationError(`maxTime must be between 600 and 86400 seconds, got ${maxTimeSeconds} seconds at jobStateTimeLimitActions[${index}]`, scope);
321322
}
322323

323324
return {

packages/aws-cdk-lib/aws-batch/lib/linux-parameters.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export class LinuxParameters extends Construct {
101101
props.swappiness !== undefined &&
102102
(!Number.isInteger(props.swappiness) || props.swappiness < 0 || props.swappiness > 100)
103103
) {
104-
throw new Error(`swappiness: Must be an integer between 0 and 100; received ${props.swappiness}.`);
104+
throw new cdk.ValidationError(`swappiness: Must be an integer between 0 and 100; received ${props.swappiness}.`, this);
105105
}
106106
}
107107

packages/aws-cdk-lib/aws-batch/lib/managed-compute-environment.ts

+22-22
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as ec2 from '../../aws-ec2';
55
import * as eks from '../../aws-eks';
66
import * as iam from '../../aws-iam';
77
import { IRole } from '../../aws-iam';
8-
import { ArnFormat, Duration, ITaggable, Lazy, Resource, Stack, TagManager, TagType, Token } from '../../core';
8+
import { ArnFormat, Duration, ITaggable, Lazy, Resource, Stack, TagManager, TagType, Token, ValidationError } from '../../core';
99
import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource';
1010

1111
/**
@@ -619,10 +619,10 @@ export class ManagedEc2EcsComputeEnvironment extends ManagedComputeEnvironmentBa
619619
public readonly tags: TagManager = new TagManager(TagType.MAP, 'AWS::Batch::ComputeEnvironment');
620620

621621
public addInstanceClass(_instanceClass: ec2.InstanceClass): void {
622-
throw new Error(`cannot add instance class to imported ComputeEnvironment '${id}'`);
622+
throw new ValidationError(`cannot add instance class to imported ComputeEnvironment '${id}'`, this);
623623
}
624624
public addInstanceType(_instanceType: ec2.InstanceType): void {
625-
throw new Error(`cannot add instance type to imported ComputeEnvironment '${id}'`);
625+
throw new ValidationError(`cannot add instance type to imported ComputeEnvironment '${id}'`, this);
626626
}
627627
}
628628

@@ -650,7 +650,7 @@ export class ManagedEc2EcsComputeEnvironment extends ManagedComputeEnvironmentBa
650650
addConstructMetadata(this, props);
651651

652652
this.images = props.images;
653-
this.allocationStrategy = determineAllocationStrategy(id, props.allocationStrategy, this.spot);
653+
this.allocationStrategy = determineAllocationStrategy(this, props.allocationStrategy, this.spot);
654654
this.spotBidPercentage = props.spotBidPercentage;
655655

656656
this.spotFleetRole = props.spotFleetRole ?? (
@@ -665,7 +665,7 @@ export class ManagedEc2EcsComputeEnvironment extends ManagedComputeEnvironmentBa
665665
(this.instanceClasses.includes(ec2.InstanceClass.A1) ||
666666
this.instanceTypes.find(instanceType => instanceType.sameInstanceClassAs(ec2.InstanceType.of(ec2.InstanceClass.A1, ec2.InstanceSize.LARGE))))
667667
) {
668-
throw new Error('Amazon Linux 2023 does not support A1 instances.');
668+
throw new ValidationError('Amazon Linux 2023 does not support A1 instances.', this);
669669
}
670670

671671
const { instanceRole, instanceProfile } = createInstanceRoleAndProfile(this, props.instanceRole);
@@ -676,8 +676,8 @@ export class ManagedEc2EcsComputeEnvironment extends ManagedComputeEnvironmentBa
676676
this.minvCpus = props.minvCpus ?? DEFAULT_MIN_VCPUS;
677677
this.placementGroup = props.placementGroup;
678678

679-
validateVCpus(id, this.minvCpus, this.maxvCpus);
680-
validateSpotConfig(id, this.spot, this.spotBidPercentage, this.spotFleetRole);
679+
validateVCpus(this, this.minvCpus, this.maxvCpus);
680+
validateSpotConfig(this, this.spot, this.spotBidPercentage, this.spotFleetRole);
681681

682682
const { subnetIds } = props.vpc.selectSubnets(props.vpcSubnets);
683683
const resource = new CfnComputeEnvironment(this, 'Resource', {
@@ -1010,9 +1010,9 @@ export class ManagedEc2EksComputeEnvironment extends ManagedComputeEnvironmentBa
10101010
this.eksCluster = props.eksCluster;
10111011

10121012
this.images = props.images;
1013-
this.allocationStrategy = determineAllocationStrategy(id, props.allocationStrategy, this.spot);
1013+
this.allocationStrategy = determineAllocationStrategy(this, props.allocationStrategy, this.spot);
10141014
if (this.allocationStrategy === AllocationStrategy.BEST_FIT) {
1015-
throw new Error(`ManagedEc2EksComputeEnvironment '${id}' uses invalid allocation strategy 'AllocationStrategy.BEST_FIT'`);
1015+
throw new ValidationError(`ManagedEc2EksComputeEnvironment '${id}' uses invalid allocation strategy 'AllocationStrategy.BEST_FIT'`, this);
10161016
}
10171017
this.spotBidPercentage = props.spotBidPercentage;
10181018
this.instanceTypes = props.instanceTypes ?? [];
@@ -1026,8 +1026,8 @@ export class ManagedEc2EksComputeEnvironment extends ManagedComputeEnvironmentBa
10261026
this.minvCpus = props.minvCpus ?? DEFAULT_MIN_VCPUS;
10271027
this.placementGroup = props.placementGroup;
10281028

1029-
validateVCpus(id, this.minvCpus, this.maxvCpus);
1030-
validateSpotConfig(id, this.spot, this.spotBidPercentage);
1029+
validateVCpus(this, this.minvCpus, this.maxvCpus);
1030+
validateSpotConfig(this, this.spot, this.spotBidPercentage);
10311031

10321032
const { subnetIds } = props.vpc.selectSubnets(props.vpcSubnets);
10331033
const resource = new CfnComputeEnvironment(this, 'Resource', {
@@ -1179,14 +1179,14 @@ function createSpotFleetRole(scope: Construct): IRole {
11791179
});
11801180
}
11811181

1182-
function determineAllocationStrategy(id: string, allocationStrategy?: AllocationStrategy, spot?: boolean): AllocationStrategy | undefined {
1182+
function determineAllocationStrategy(scope: Construct, allocationStrategy?: AllocationStrategy, spot?: boolean): AllocationStrategy | undefined {
11831183
let result = allocationStrategy;
11841184
if (!allocationStrategy) {
11851185
result = spot ? AllocationStrategy.SPOT_PRICE_CAPACITY_OPTIMIZED : AllocationStrategy.BEST_FIT_PROGRESSIVE;
11861186
} else if (allocationStrategy === AllocationStrategy.SPOT_PRICE_CAPACITY_OPTIMIZED && !spot) {
1187-
throw new Error(`Managed ComputeEnvironment '${id}' specifies 'AllocationStrategy.SPOT_PRICE_CAPACITY_OPTIMIZED' without using spot instances`);
1187+
throw new ValidationError(`Managed ComputeEnvironment '${scope.node.id}' specifies 'AllocationStrategy.SPOT_PRICE_CAPACITY_OPTIMIZED' without using spot instances`, scope);
11881188
} else if (allocationStrategy === AllocationStrategy.SPOT_CAPACITY_OPTIMIZED && !spot) {
1189-
throw new Error(`Managed ComputeEnvironment '${id}' specifies 'AllocationStrategy.SPOT_CAPACITY_OPTIMIZED' without using spot instances`);
1189+
throw new ValidationError(`Managed ComputeEnvironment '${scope.node.id}' specifies 'AllocationStrategy.SPOT_CAPACITY_OPTIMIZED' without using spot instances`, scope);
11901190
}
11911191

11921192
return result;
@@ -1200,34 +1200,34 @@ function validateInstances(types?: ec2.InstanceType[], classes?: ec2.InstanceCla
12001200
return [];
12011201
}
12021202

1203-
function validateSpotConfig(id: string, spot?: boolean, spotBidPercentage?: number, spotFleetRole?: iam.IRole): void {
1203+
function validateSpotConfig(scope: Construct, spot?: boolean, spotBidPercentage?: number, spotFleetRole?: iam.IRole): void {
12041204
if (spotBidPercentage) {
12051205
if (!spot) {
1206-
throw new Error(`Managed ComputeEnvironment '${id}' specifies 'spotBidPercentage' without specifying 'spot'`);
1206+
throw new ValidationError(`Managed ComputeEnvironment '${scope.node.id}' specifies 'spotBidPercentage' without specifying 'spot'`, scope);
12071207
}
12081208

12091209
if (!Token.isUnresolved(spotBidPercentage)) {
12101210
if (spotBidPercentage > 100) {
1211-
throw new Error(`Managed ComputeEnvironment '${id}' specifies 'spotBidPercentage' > 100`);
1211+
throw new ValidationError(`Managed ComputeEnvironment '${scope.node.id}' specifies 'spotBidPercentage' > 100`, scope);
12121212
} else if (spotBidPercentage < 0) {
1213-
throw new Error(`Managed ComputeEnvironment '${id}' specifies 'spotBidPercentage' < 0`);
1213+
throw new ValidationError(`Managed ComputeEnvironment '${scope.node.id}' specifies 'spotBidPercentage' < 0`, scope);
12141214
}
12151215
}
12161216
}
12171217

12181218
if (spotFleetRole) {
12191219
if (!spot) {
1220-
throw new Error(`Managed ComputeEnvironment '${id}' specifies 'spotFleetRole' without specifying 'spot'`);
1220+
throw new ValidationError(`Managed ComputeEnvironment '${scope.node.id}' specifies 'spotFleetRole' without specifying 'spot'`, scope);
12211221
}
12221222
}
12231223
}
12241224

1225-
function validateVCpus(id: string, minvCpus: number, maxvCpus: number): void {
1225+
function validateVCpus(scope: Construct, minvCpus: number, maxvCpus: number): void {
12261226
if (!Token.isUnresolved(minvCpus) && minvCpus < 0) {
1227-
throw new Error(`Managed ComputeEnvironment '${id}' has 'minvCpus' = ${minvCpus} < 0; 'minvCpus' cannot be less than zero`);
1227+
throw new ValidationError(`Managed ComputeEnvironment '${scope.node.id}' has 'minvCpus' = ${minvCpus} < 0; 'minvCpus' cannot be less than zero`, scope);
12281228
}
12291229
if (!Token.isUnresolved(minvCpus) && !Token.isUnresolved(maxvCpus) && minvCpus > maxvCpus) {
1230-
throw new Error(`Managed ComputeEnvironment '${id}' has 'minvCpus' = ${minvCpus} > 'maxvCpus' = ${maxvCpus}; 'minvCpus' cannot be greater than 'maxvCpus'`);
1230+
throw new ValidationError(`Managed ComputeEnvironment '${scope.node.id}' has 'minvCpus' = ${minvCpus} > 'maxvCpus' = ${maxvCpus}; 'minvCpus' cannot be greater than 'maxvCpus'`, scope);
12311231
}
12321232
}
12331233

0 commit comments

Comments
 (0)