Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(apprunner): add AutoScalingConfiguration for AppRunner Service #30358

Merged
merged 24 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0a5114b
feat: add autoscaling for apprunner
mazyu36 May 28, 2024
bdb85cb
fix: add @default
mazyu36 May 28, 2024
4ada3f6
feat: add fromArn method
mazyu36 May 28, 2024
7e2c80f
Merge branch 'main' into apprunner-auto-scaling-30353
mazyu36 May 28, 2024
7ed5abc
Merge branch 'main' into apprunner-auto-scaling-30353
mazyu36 May 30, 2024
61a1b00
Update packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts
mazyu36 Jun 2, 2024
84a3cce
Update packages/@aws-cdk/aws-apprunner-alpha/lib/auto-scaling-configu…
mazyu36 Jun 2, 2024
55501a7
Update packages/@aws-cdk/aws-apprunner-alpha/lib/auto-scaling-configu…
mazyu36 Jun 2, 2024
e999f83
Update packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts
mazyu36 Jun 2, 2024
59a6f32
Update packages/@aws-cdk/aws-apprunner-alpha/test/auto-scaling-config…
mazyu36 Jun 2, 2024
215e5a9
Update packages/@aws-cdk/aws-apprunner-alpha/lib/auto-scaling-configu…
mazyu36 Jun 2, 2024
b161ed7
fix: incorporate review comments
mazyu36 Jun 2, 2024
50fc0d3
Update packages/@aws-cdk/aws-apprunner-alpha/lib/auto-scaling-configu…
mazyu36 Jun 2, 2024
0cac270
Update packages/@aws-cdk/aws-apprunner-alpha/lib/auto-scaling-configu…
mazyu36 Jun 2, 2024
f46a7c7
fix: unit tests
mazyu36 Jun 2, 2024
9bb0dcb
Update packages/@aws-cdk/aws-apprunner-alpha/lib/auto-scaling-configu…
mazyu36 Jun 3, 2024
3ca5979
Merge branch 'main' into apprunner-auto-scaling-30353
mazyu36 Jun 11, 2024
2c74142
Update packages/@aws-cdk/aws-apprunner-alpha/lib/auto-scaling-configu…
mazyu36 Jun 19, 2024
0aef49e
fix: incorporate review comments
mazyu36 Jun 19, 2024
361ab63
Update packages/@aws-cdk/aws-apprunner-alpha/test/auto-scaling-config…
mazyu36 Jun 19, 2024
b8c7d61
Update packages/@aws-cdk/aws-apprunner-alpha/test/auto-scaling-config…
mazyu36 Jun 19, 2024
2633a0d
fix: incorporate review comments
mazyu36 Jun 19, 2024
2c5e154
Merge branch 'main' into apprunner-auto-scaling-30353
mergify[bot] Jun 20, 2024
5a88d1d
Merge branch 'main' into apprunner-auto-scaling-30353
mergify[bot] Jun 21, 2024
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
21 changes: 21 additions & 0 deletions packages/@aws-cdk/aws-apprunner-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,27 @@ when required.

See [App Runner IAM Roles](https://docs.aws.amazon.com/apprunner/latest/dg/security_iam_service-with-iam.html#security_iam_service-with-iam-roles) for more details.

## Auto Scaling Configuration

To associate an App Runner service with a custom Auto Scaling Configuration, define `autoScalingConfiguration` for the service.

```ts
const autoScalingConfiguration = new apprunner.AutoScalingConfiguration(this, 'AutoScalingConfiguration', {
autoScalingConfigurationName: 'MyAutoScalingConfiguration',
maxConcurrency: 150,
maxSize: 20,
minSize: 5,
});

new apprunner.Service(this, 'DemoService', {
source: apprunner.Source.fromEcrPublic({
imageConfiguration: { port: 8000 },
imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest',
}),
autoScalingConfiguration,
});
```

## VPC Connector

To associate an App Runner service with a custom VPC, define `vpcConnector` for the service.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import * as cdk from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
import { CfnAutoScalingConfiguration } from 'aws-cdk-lib/aws-apprunner';

/**
* Properties of the App Runner Auto Scaling Configuration.
*/
export interface AutoScalingConfigurationProps {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we allow to specify tags as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the other L2 Constructs, it seemed like there were few resources adding tags as properties, so I didn't add them.
Since tags can also be added through aspects, I thought they might be unnecessary like other resources. What do you think?
I would appreciate your opinion on the criteria for adding tags, as I'm not sure about it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense 👍 Thanks for clarifying!

/**
* The name for the Auto Scaling Configuration.
*
* @default - a name generated by CloudFormation
*/
readonly autoScalingConfigurationName?: string;

/**
* The maximum number of concurrent requests that an instance processes.
* If the number of concurrent requests exceeds this limit, App Runner scales the service up.
*
* Must be between 1 and 200.
*
* @default 100
*/
readonly maxConcurrency?: number;

/**
* The maximum number of instances that a service scales up to.
* At most maxSize instances actively serve traffic for your service.
*
* Must be between 1 and 25.
*
* @default 25
*/
readonly maxSize?: number;

/**
* The minimum number of instances that App Runner provisions for a service.
* The service always has at least minSize provisioned instances.
*
*
* Must be between 1 and 25.
*
* @default 1
*/
readonly minSize?: number;
}

/**
* Attributes for the App Runner Auto Scaling Configuration.
*/
export interface AutoScalingConfigurationAttributes {
/**
* The name of the Auto Scaling Configuration.
*/
readonly autoScalingConfigurationName: string;

/**
* The revision of the Auto Scaling Configuration.
*/
readonly autoScalingConfigurationRevision: number;
}

/**
* Represents the App Runner Auto Scaling Configuration.
*/
export interface IAutoScalingConfiguration extends cdk.IResource {
/**
* The ARN of the Auto Scaling Configuration.
* @attribute
*/
readonly autoScalingConfigurationArn: string;

/**
* The Name of the Auto Scaling Configuration.
* @attribute
*/
readonly autoScalingConfigurationName: string;

/**
* The revision of the Auto Scaling Configuration.
* @attribute
*/
readonly autoScalingConfigurationRevision: number;
}

/**
* The App Runner Auto Scaling Configuration.
*
* @resource AWS::AppRunner::AutoScalingConfiguration
*/
export class AutoScalingConfiguration extends cdk.Resource implements IAutoScalingConfiguration {
/**
* Imports an App Runner Auto Scaling Configuration from attributes
*/
public static fromAutoScalingConfigurationAttributes(scope: Construct, id: string,
attrs: AutoScalingConfigurationAttributes): IAutoScalingConfiguration {
const autoScalingConfigurationName = attrs.autoScalingConfigurationName;
const autoScalingConfigurationRevision = attrs.autoScalingConfigurationRevision;

class Import extends cdk.Resource implements IAutoScalingConfiguration {
public readonly autoScalingConfigurationName = autoScalingConfigurationName;
public readonly autoScalingConfigurationRevision = autoScalingConfigurationRevision;
public readonly autoScalingConfigurationArn = cdk.Stack.of(this).formatArn({
resource: 'autoscalingconfiguration',
service: 'apprunner',
resourceName: `${attrs.autoScalingConfigurationName}/${attrs.autoScalingConfigurationRevision}`,
});
}

return new Import(scope, id);
}

Copy link
Contributor

@pahud pahud Jun 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we just know the Name and Revision, we can formate the Arn string.

How about just have Name and Revision in the interface:

export interface AutoScalingConfigurationAttributes {
  /**
   * The name of the Auto Scaling Configuration.
   */
  readonly autoScalingConfigurationName: string;

  /**
   * The revision of the Auto Scaling Configuration.
   */
  readonly autoScalingConfigurationRevision: number;
}

And in the fromXxx method:

public static fromAutoScalingConfigurationAttributes(scope: Construct, id: string,
    attrs: AutoScalingConfigurationAttributes): IAutoScalingConfiguration {

    class Import extends cdk.Resource implements IAutoScalingConfiguration {
       public readonly autoScalingConfigurationName = attrs.autoScalingConfigurationName;
       public readonly autoScalingConfigurationRevision = attrs.autoScalingConfigurationRevision;
       public readonly autoScalingConfigurationArn = Stack.of(this).formatArn({
           resource: 'autoscalingconfiguration',
           service: 'apprunner',
           resourceName: `${attrs.autoScalingConfigurationName}/${attrs.autoScalingConfigurationRevision}`,
      })
    }
    
    return new Import(scope, id);
};

If we can generate the Arn from given Name and Revision then Arn would not be required in the Attributes?

Think about it, if user already knows the Arn, why should they bother to use fromXxxAttributes instead of just fromArn?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. You are certainly right. I have removed arn from the attributes and made the correction.

/**
* Imports an App Runner Auto Scaling Configuration from its ARN
*/
public static fromArn(scope: Construct, id: string, autoScalingConfigurationArn: string): IAutoScalingConfiguration {
const resourceParts = cdk.Fn.split('/', autoScalingConfigurationArn);

if (!resourceParts || resourceParts.length < 3) {
throw new Error(`Unexpected ARN format: ${autoScalingConfigurationArn}`);
}

const autoScalingConfigurationName = cdk.Fn.select(0, resourceParts);
const autoScalingConfigurationRevision = Number(cdk.Fn.select(1, resourceParts));

class Import extends cdk.Resource implements IAutoScalingConfiguration {
public readonly autoScalingConfigurationName = autoScalingConfigurationName;
public readonly autoScalingConfigurationRevision = autoScalingConfigurationRevision;
public readonly autoScalingConfigurationArn = autoScalingConfigurationArn;
}

return new Import(scope, id);
}

/**
* The ARN of the Auto Scaling Configuration.
* @attribute
*/
readonly autoScalingConfigurationArn: string;

/**
* The name of the Auto Scaling Configuration.
* @attribute
*/
readonly autoScalingConfigurationName: string;

/**
* The revision of the Auto Scaling Configuration.
* @attribute
*/
readonly autoScalingConfigurationRevision: number;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we probably should make it string. Just like ecs task definition.

public readonly revision: string;

Also in the doc

AutoScalingConfigurationRevision
The revision of this auto scaling configuration. It's unique among all the active configurations that share the same AutoScalingConfigurationName.

It's unclear to me if it would be number. I am afraid some day it might support revision like latest just like ecs task revision and this would be a breaking change in CDK.

With that being said, I don't see the benefits using number over string. wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Personally, I'm in favor of making the revision a string.
However, I have some concerns.

The revision in the Cfn (L1 construct) is a number, but would it be appropriate to change it to a string in the L2 construct? I'm not sure if there's a possibility of it changing, so if you know anything about it, please let me know.

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apprunner.CfnAutoScalingConfiguration.html#attrautoscalingconfigurationrevision

Additionally, since the already implemented VpcConnector treats the revision as a number, I'm also concerned about having a different approach from that.

https://docs.aws.amazon.com/cdk/api/v2/docs/@aws-cdk_aws-apprunner-alpha.VpcConnector.html#vpcconnectorrevisionspan-classapi-icon-api-icon-experimental-titlethis-api-element-is-experimental-it-may-change-without-noticespan

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the callout.

I checked the cfnspec and yes the type is Integer, which is number in TS.

"Attributes": {
        "AutoScalingConfigurationRevision": {
          "PrimitiveType": "Integer"
        },

I am fine having it as number for consistency here but I kind of feel if apprunner supports latest revision someday, which would be a breaking change to CFN then CDK would have breaking change as well.

@GavinZZ @paulhcsun what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed with team internally. I think we don't need to over-optimize the type here. CFN schema type changes are not supposed to happen especially after it's already in CloudFormation resource schema registry.


public constructor(scope: Construct, id: string, props: AutoScalingConfigurationProps = {}) {
super(scope, id, {
physicalName: props.autoScalingConfigurationName,
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add validation for the autoScalingConfigurationName property (docs)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I added validation and unit tests.

if (
props.autoScalingConfigurationName !== undefined &&
!cdk.Token.isUnresolved(props.autoScalingConfigurationName) &&
!/^[A-Za-z0-9][A-Za-z0-9\-_]{3,31}$/.test(props.autoScalingConfigurationName)
) {
throw new Error(`autoScalingConfigurationName must match the ^[A-Za-z0-9][A-Za-z0-9\-_]{3,31}$ pattern, got ${props.autoScalingConfigurationName}`);
}

const isMinSizeDefined = props.minSize !== undefined && !cdk.Token.isUnresolved(props.minSize);
const isMaxSizeDefined = props.maxSize !== undefined && !cdk.Token.isUnresolved(props.maxSize);
if (isMinSizeDefined && (props.minSize < 1 || props.minSize > 25)) {
throw new Error(`minSize must be between 1 and 25, got ${props.minSize}`);
}
if (isMaxSizeDefined && (props.maxSize < 1 || props.maxSize > 25)) {
throw new Error(`maxSize must be between 1 and 25, got ${props.maxSize}`);
}
if (isMinSizeDefined && isMaxSizeDefined && !(props.minSize < props.maxSize)) {
throw new Error('maxSize must be greater than minSize');
}
if (
props.maxConcurrency !== undefined &&
!cdk.Token.isUnresolved(props.maxConcurrency) &&
(props.maxConcurrency < 1 || props.maxConcurrency > 200)
) {
throw new Error(`maxConcurrency must be between 1 and 200, got ${props.maxConcurrency}`);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider the code like this

function validateAutoScalingConfiguration(props: {
  minSize?: number;
  maxSize?: number;
  maxConcurrency?: number;
}) {
  const isMinSizeDefined = typeof props.minSize === 'number';
  const isMaxSizeDefined = typeof props.maxSize === 'number';
  const isMaxConcurrencyDefined = typeof props.maxConcurrency === 'number';

  if (isMinSizeDefined && (props.minSize < 1 || props.minSize > 25)) {
    throw new Error(`minSize must be between 1 and 25, got ${props.minSize}`);
  }

  if (isMaxSizeDefined && (props.maxSize < 1 || props.maxSize > 25)) {
    throw new Error(`maxSize must be between 1 and 25, got ${props.maxSize}`);
  }

  if (isMinSizeDefined && isMaxSizeDefined && !(props.minSize < props.maxSize)) {
    throw new Error('maxSize must be greater than minSize');
  }

  if (isMaxConcurrencyDefined && (props.maxConcurrency < 1 || props.maxConcurrency > 200)) {
    throw new Error(`maxConcurrency must be between 1 and 200, got ${props.maxConcurrency}`);
  }
}

The main changes are:

  1. Extracted the validation logic into a separate function validateAutoScalingConfiguration to make the code more modular and easier to test.

  2. Used typeof to check if the properties are defined and a number as well as !cdk.Token.isUnresolved because if it is unresolved it would not be number? I am not 100% sure here maybe you still need

typeof props.minSize === 'number' && !cdk.Token.isUnresolved(props.minSize);
  1. Moved the isMaxConcurrencyDefined check into a separate condition to make the code more readable.

There are a few benefits to using typeof to check if the properties are defined and a number, instead of using
undefined and cdk.Token.isUnresolved:

  1. Simplicity and Readability: The typeof checks are more straightforward and easier to understand at a glance. They directly check the type of the value, which makes the code more self-documenting and easier to maintain.

  2. Handling Unresolved Tokens: The cdk.Token.isUnresolved check is specifically for handling unresolved tokens in the AWS CDK. However, in this case, we're not just checking for unresolved tokens, but also ensuring the property is defined and a number. Using typeof handles all of these cases in a single, simple check. [1]

  3. Robustness: The typeof checks are more robust because they handle a wider range of cases. For example, if the property is set to null, the typeof check will still correctly identify it as not a number, whereas the
    undefined check would not catch this case.

  4. Performance: The typeof checks are generally faster than using undefined and cdk.Token.isUnresolved
    , as they are simpler operations. This can be especially beneficial in performance-critical code.

  5. Consistency: Using typeof to check the type of a value is a common and widely-accepted pattern in JavaScript/TypeScript development. It aligns with the language's standard practices and makes the code more familiar to other developers.

In summary, the typeof checks provide a more straightforward, robust, and performant way to validate the properties, while also making the code more readable and consistent with standard JavaScript/TypeScript practices. This can lead to better maintainability and easier understanding of the validation logic.

This optimized version of the code is more concise, easier to read, and easier to maintain. It also follows best practices for input validation, such as using clear error messages and keeping the validation logic separate from the main application logic.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider to make it a construct private method and use something like this.validateAutoScalingConfiguration() for all the validations, it's very opinionated and not mandatory though.

Copy link
Contributor Author

@mazyu36 mazyu36 Jun 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I like your suggested your approach.
I've created a private method including autoScalingConfigurationName validation.

const resource = new CfnAutoScalingConfiguration(this, 'Resource', {
autoScalingConfigurationName: props.autoScalingConfigurationName,
maxConcurrency: props.maxConcurrency,
maxSize: props.maxSize,
minSize: props.minSize,
});

this.autoScalingConfigurationArn = resource.attrAutoScalingConfigurationArn;
this.autoScalingConfigurationRevision = resource.attrAutoScalingConfigurationRevision;
this.autoScalingConfigurationName = resource.ref;
}
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-apprunner-alpha/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// AWS::AppRunner CloudFormation Resources:
export * from './auto-scaling-configuration';
export * from './service';
export * from './vpc-connector';
14 changes: 14 additions & 0 deletions packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Lazy } from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
import { CfnService } from 'aws-cdk-lib/aws-apprunner';
import { IVpcConnector } from './vpc-connector';
import { IAutoScalingConfiguration } from './auto-scaling-configuration';

/**
* The image repository types
Expand Down Expand Up @@ -656,6 +657,18 @@ export interface ServiceProps {
*/
readonly autoDeploymentsEnabled?: boolean;

/**
* Specifies an App Runner Auto Scaling Configuration.
*
* A default configuration is either the AWS recommended configuration,
* or the configuration you set as the default.
*
* @see https://docs.aws.amazon.com/apprunner/latest/dg/manage-autoscaling.html
*
* @default - the latest revision of a default auto scaling configuration is used.
*/
readonly autoScalingConfiguration?: IAutoScalingConfiguration;
Copy link
Contributor

@pahud pahud Jun 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer just autoScaling?: IAutoScalingConfiguration; but also OK with autoScalingConfiguration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. I will keep it as is, in the form of aligning with the class names or property names of the L1 construct.


/**
* The number of CPU units reserved for each instance of your App Runner service.
*
Expand Down Expand Up @@ -1272,6 +1285,7 @@ export class Service extends cdk.Resource implements iam.IGrantable {
encryptionConfiguration: this.props.kmsKey ? {
kmsKey: this.props.kmsKey.keyArn,
} : undefined,
autoScalingConfigurationArn: this.props.autoScalingConfiguration?.autoScalingConfigurationArn,
networkConfiguration: {
egressConfiguration: {
egressType: this.props.vpcConnector ? 'VPC' : 'DEFAULT',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { Match, Template } from 'aws-cdk-lib/assertions';
import * as cdk from 'aws-cdk-lib';
import { AutoScalingConfiguration } from '../lib';

let stack: cdk.Stack;
beforeEach(() => {
stack = new cdk.Stack();
});

test.each([
['MyAutoScalingConfiguration'],
['my-autoscaling-configuration_1'],
])('create an Auto scaling Configuration with all properties (name: %s)', (autoScalingConfigurationName: string) => {
// WHEN
new AutoScalingConfiguration(stack, 'AutoScalingConfiguration', {
autoScalingConfigurationName,
maxConcurrency: 150,
maxSize: 20,
minSize: 5,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::AutoScalingConfiguration', {
AutoScalingConfigurationName: autoScalingConfigurationName,
MaxConcurrency: 150,
MaxSize: 20,
MinSize: 5,
});
});

test('create an Auto scaling Configuration without all properties', () => {
// WHEN
new AutoScalingConfiguration(stack, 'AutoScalingConfiguration');

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::AutoScalingConfiguration', {
AutoScalingConfigurationName: Match.absent(),
MaxConcurrency: Match.absent(),
MaxSize: Match.absent(),
MinSize: Match.absent(),
});
});

test.each([0, 26])('invalid minSize', (minSize: number) => {
mazyu36 marked this conversation as resolved.
Show resolved Hide resolved
expect(() => {
new AutoScalingConfiguration(stack, 'AutoScalingConfiguration', {
minSize,
});
}).toThrow(`minSize must be between 1 and 25, got ${minSize}`);
});

test.each([0, 26])('invalid maxSize', (maxSize: number) => {
expect(() => {
new AutoScalingConfiguration(stack, 'AutoScalingConfiguration', {
maxSize,
});
}).toThrow(`maxSize must be between 1 and 25, got ${maxSize}`);
});

test('minSize greater than maxSize', () => {
expect(() => {
new AutoScalingConfiguration(stack, 'AutoScalingConfiguration', {
minSize: 5,
maxSize: 3,
});
}).toThrow('maxSize must be greater than minSize');
});

test.each([0, 201])('invalid maxConcurrency', (maxConcurrency: number) => {
expect(() => {
new AutoScalingConfiguration(stack, 'AutoScalingConfiguration', {
maxConcurrency,
});
}).toThrow(`maxConcurrency must be between 1 and 200, got ${maxConcurrency}`);
});

test.each([
['tes'],
['test-autoscaling-configuration-name-over-limitation'],
['-test'],
['test-?'],
])('invalid autoScalingConfigurationName (name: %s)', (autoScalingConfigurationName: string) => {
expect(() => {
new AutoScalingConfiguration(stack, 'AutoScalingConfiguration', {
autoScalingConfigurationName,
});
}).toThrow(`autoScalingConfigurationName must match the ^[A-Za-z0-9][A-Za-z0-9\-_]{3,31}$ pattern, got ${autoScalingConfigurationName}`);
});
mazyu36 marked this conversation as resolved.
Show resolved Hide resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading