Skip to content

Commit c5fbd97

Browse files
authored
fix(lambda): add token resolution validation to capacity providers (#36275)
### Issue # (if applicable) N/a ### Reason for this change Current validation of fields for recent capacity provider additions in `aws-lambda` does not take into account unresolved tokens. ### Description of changes Added `Token.isUnresolved()` checks before all new validations. This ensures that validation only occurs when concrete values are provided, while still allowing tokens to pass through for CloudFormation resolution. ### Describe any new or updated permissions being added N/a ### Description of how you validated changes Changes should be only internal, validated that unit and integration tests continued to work. ### 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 4a3fe1e commit c5fbd97

File tree

4 files changed

+76
-13
lines changed

4 files changed

+76
-13
lines changed

packages/aws-cdk-lib/aws-lambda/lib/capacity-provider.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { CfnCapacityProvider, CfnFunction } from './lambda.generated';
55
import * as ec2 from '../../aws-ec2';
66
import * as iam from '../../aws-iam';
77
import * as kms from '../../aws-kms';
8-
import { Annotations, Arn, ArnFormat, IResource, Resource, Stack, ValidationError } from '../../core';
8+
import { Annotations, Arn, ArnFormat, IResource, Resource, Stack, Token, ValidationError } from '../../core';
99
import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource';
1010
import { propertyInjectable } from '../../core/lib/prop-injectable';
1111

@@ -444,15 +444,15 @@ export class CapacityProvider extends CapacityProviderBase {
444444
private validateCapacityProviderProps(props: CapacityProviderProps) {
445445
const validationErrorCPName = props.capacityProviderName || 'your capacity provider';
446446

447-
if (props.maxVCpuCount !== undefined && (props.maxVCpuCount < 12 || props.maxVCpuCount > 15000)) {
447+
if (props.maxVCpuCount !== undefined && !Token.isUnresolved(props.maxVCpuCount) && (props.maxVCpuCount < 12 || props.maxVCpuCount > 15000)) {
448448
throw new ValidationError(`maxVCpuCount must be between 12 and 15000, but ${validationErrorCPName} has ${props.maxVCpuCount}.`, this);
449449
}
450450

451-
if (props.subnets && (props.subnets.length < 1 || props.subnets.length > 16)) {
451+
if (!Token.isUnresolved(props.subnets) && (props.subnets.length < 1 || props.subnets.length > 16)) {
452452
throw new ValidationError(`subnets must contain between 1 and 16 items but ${validationErrorCPName} has ${props.subnets.length} items.`, this);
453453
}
454454

455-
if (props.securityGroups.length < 1 || props.securityGroups.length > 5) {
455+
if (!Token.isUnresolved(props.securityGroups) && (props.securityGroups.length < 1 || props.securityGroups.length > 5)) {
456456
throw new ValidationError(`securityGroups must contain between 1 and 5 items but ${validationErrorCPName} has ${props.securityGroups.length} items.`, this);
457457
}
458458

@@ -470,6 +470,9 @@ export class CapacityProvider extends CapacityProviderBase {
470470
}
471471

472472
private validateCapacityProviderName(name: string) {
473+
if (Token.isUnresolved(name)) {
474+
return;
475+
}
473476
if (!/^([a-zA-Z0-9-_]+|arn:aws[a-zA-Z-]*:lambda:capacity-provider:[a-zA-Z0-9-_]+)$/.test(name)) {
474477
throw new ValidationError(`capacityProviderName must be an arn or have only alphanumeric characters, but did not: ${name}`, this);
475478
}
@@ -479,12 +482,12 @@ export class CapacityProvider extends CapacityProviderBase {
479482
}
480483

481484
private validateScalingPolicies(scalingOptions: ScalingOptions, validationErrorCPName: string) {
482-
if (scalingOptions?.scalingPolicies) {
483-
if (scalingOptions?.scalingPolicies.length < 1) {
485+
if (scalingOptions?.scalingPolicies && !Token.isUnresolved(scalingOptions.scalingPolicies)) {
486+
if (scalingOptions.scalingPolicies.length < 1) {
484487
throw new ValidationError(`scalingOptions must have at least one policy when scalingMode is 'Manual', but ${validationErrorCPName} has ${scalingOptions.scalingPolicies.length} items.`, this);
485488
}
486489

487-
if (scalingOptions?.scalingPolicies.length > 10) {
490+
if (scalingOptions.scalingPolicies.length > 10) {
488491
throw new ValidationError(`scalingOptions can have at most ten policies when scalingMode is 'Manual', but ${validationErrorCPName} has ${scalingOptions.scalingPolicies.length} items.`, this);
489492
}
490493
}
@@ -537,15 +540,18 @@ export class CapacityProvider extends CapacityProviderBase {
537540
}
538541

539542
private validateFunctionScalingConfig(minExecutionEnvironments?: number, maxExecutionEnvironments?: number) {
540-
if (minExecutionEnvironments !== undefined && (minExecutionEnvironments < 0 || minExecutionEnvironments > 15000)) {
543+
const minDefined = minExecutionEnvironments !== undefined && !Token.isUnresolved(minExecutionEnvironments);
544+
const maxDefined = maxExecutionEnvironments !== undefined && !Token.isUnresolved(maxExecutionEnvironments);
545+
546+
if (minDefined && (minExecutionEnvironments < 0 || minExecutionEnvironments > 15000)) {
541547
throw new ValidationError(`minExecutionEnvironments must be between 0 and 15000, but was ${minExecutionEnvironments}.`, this);
542548
}
543549

544-
if (maxExecutionEnvironments !== undefined && (maxExecutionEnvironments < 0 || maxExecutionEnvironments > 15000)) {
550+
if (maxDefined && (maxExecutionEnvironments < 0 || maxExecutionEnvironments > 15000)) {
545551
throw new ValidationError(`maxExecutionEnvironments must be between 0 and 15000, but was ${maxExecutionEnvironments}.`, this);
546552
}
547553

548-
if (minExecutionEnvironments !== undefined && maxExecutionEnvironments !== undefined && minExecutionEnvironments > maxExecutionEnvironments) {
554+
if (minDefined && maxDefined && minExecutionEnvironments > maxExecutionEnvironments) {
549555
throw new ValidationError('minExecutionEnvironments must be less than or equal to maxExecutionEnvironments.', this);
550556
}
551557
}

packages/aws-cdk-lib/aws-lambda/lib/lambda-version.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -355,13 +355,16 @@ export class Version extends QualifiedFunctionBase implements IVersion {
355355
return undefined;
356356
}
357357

358-
if (minExecutionEnvironments && minExecutionEnvironments < 0) {
358+
const minDefined = minExecutionEnvironments !== undefined && !Token.isUnresolved(minExecutionEnvironments);
359+
const maxDefined = maxExecutionEnvironments !== undefined && !Token.isUnresolved(maxExecutionEnvironments);
360+
361+
if (minDefined && minExecutionEnvironments < 0) {
359362
throw new ValidationError('minExecutionEnvironments must be a non-negative integer.', this);
360363
}
361-
if (maxExecutionEnvironments && maxExecutionEnvironments < 0) {
364+
if (maxDefined && maxExecutionEnvironments < 0) {
362365
throw new ValidationError('maxExecutionEnvironments must be a non-negative integer.', this);
363366
}
364-
if (minExecutionEnvironments && maxExecutionEnvironments && minExecutionEnvironments > maxExecutionEnvironments) {
367+
if (minDefined && maxDefined && minExecutionEnvironments > maxExecutionEnvironments) {
365368
throw new ValidationError('minExecutionEnvironments must be less than or equal to maxExecutionEnvironments', this);
366369
}
367370

packages/aws-cdk-lib/aws-lambda/test/capacity-provider.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,34 @@ describe('capacity provider', () => {
387387
});
388388
}).toThrow();
389389
});
390+
391+
test('accepts tokens for all validated fields', () => {
392+
// GIVEN
393+
const tokenName = cdk.Fn.ref('CapacityProviderNameParam');
394+
const tokenMaxVCpu = cdk.Fn.ref('MaxVCpuParam');
395+
const tokenSubnets = cdk.Fn.split(',', cdk.Fn.ref('SubnetIdsParam'));
396+
const tokenSecurityGroups = cdk.Fn.split(',', cdk.Fn.ref('SecurityGroupIdsParam'));
397+
398+
// WHEN - should not throw
399+
new lambda.CapacityProvider(stack, 'MyCapacityProvider', {
400+
capacityProviderName: tokenName,
401+
maxVCpuCount: cdk.Token.asNumber(tokenMaxVCpu),
402+
subnets: tokenSubnets.map((id, i) => ec2.Subnet.fromSubnetId(stack, `TokenSubnet${i}`, id)),
403+
securityGroups: tokenSecurityGroups.map((id, i) => ec2.SecurityGroup.fromSecurityGroupId(stack, `TokenSG${i}`, id)),
404+
});
405+
406+
// THEN
407+
Template.fromStack(stack).hasResourceProperties('AWS::Lambda::CapacityProvider', {
408+
CapacityProviderName: { Ref: 'CapacityProviderNameParam' },
409+
VpcConfig: {
410+
SubnetIds: { 'Fn::Split': [',', { Ref: 'SubnetIdsParam' }] },
411+
SecurityGroupIds: { 'Fn::Split': [',', { Ref: 'SecurityGroupIdsParam' }] },
412+
},
413+
CapacityProviderScalingConfig: {
414+
MaxVCpuCount: { Ref: 'MaxVCpuParam' },
415+
},
416+
});
417+
});
390418
});
391419

392420
describe('static methods', () => {

packages/aws-cdk-lib/aws-lambda/test/lambda-version.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,5 +376,31 @@ describe('lambda version', () => {
376376
});
377377
}).toThrow(/minExecutionEnvironments must be less than or equal to maxExecutionEnvironments/);
378378
});
379+
380+
test('accepts tokens for execution environment scaling config', () => {
381+
const stack = new cdk.Stack();
382+
const fn = new lambda.Function(stack, 'Fn', {
383+
code: new lambda.InlineCode('foo'),
384+
handler: 'index.handler',
385+
runtime: lambda.Runtime.NODEJS_LATEST,
386+
});
387+
const tokenMin = cdk.Token.asNumber(cdk.Fn.ref('MinEnvParam'));
388+
const tokenMax = cdk.Token.asNumber(cdk.Fn.ref('MaxEnvParam'));
389+
390+
// WHEN - should not throw
391+
new lambda.Version(stack, 'Version', {
392+
lambda: fn,
393+
minExecutionEnvironments: tokenMin,
394+
maxExecutionEnvironments: tokenMax,
395+
});
396+
397+
// THEN
398+
Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Version', {
399+
FunctionScalingConfig: {
400+
MinExecutionEnvironments: { Ref: 'MinEnvParam' },
401+
MaxExecutionEnvironments: { Ref: 'MaxEnvParam' },
402+
},
403+
});
404+
});
379405
});
380406
});

0 commit comments

Comments
 (0)