From 279065ab93e068da300b688475af00952dd440e9 Mon Sep 17 00:00:00 2001 From: mazyu36 Date: Fri, 21 Jun 2024 10:08:28 +0900 Subject: [PATCH] feat(apprunner): add AutoScalingConfiguration for AppRunner Service (#30358) ### Issue # (if applicable) Closes #30353 . ### Reason for this change At the moment, L2 Construct does not support a custom auto scaling configuration for the AppRunner Service. ### Description of changes * Add `AutoScalingConfiguration` Class * Add `autoScalingConfiguration` property to the `Service` Class ### Description of how you validated changes Add unit tests and integ tests. ### 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* --- .../@aws-cdk/aws-apprunner-alpha/README.md | 21 ++ .../lib/auto-scaling-configuration.ts | 202 +++++++++++++++ .../@aws-cdk/aws-apprunner-alpha/lib/index.ts | 1 + .../aws-apprunner-alpha/lib/service.ts | 14 ++ .../test/auto-scaling-configuration.test.ts | 110 +++++++++ ...efaultTestDeployAssert4AFB3BF1.assets.json | 19 ++ ...aultTestDeployAssert4AFB3BF1.template.json | 36 +++ .../cdk.out | 1 + ...ner-auto-scaling-configuration.assets.json | 19 ++ ...r-auto-scaling-configuration.template.json | 117 +++++++++ .../integ.json | 12 + .../manifest.json | 176 +++++++++++++ .../tree.json | 232 ++++++++++++++++++ ...nteg.service-auto-scaling-configuration.ts | 33 +++ .../aws-apprunner-alpha/test/service.test.ts | 27 ++ 15 files changed, 1020 insertions(+) create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/lib/auto-scaling-configuration.ts create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/auto-scaling-configuration.test.ts create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1.assets.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1.template.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/integ-apprunner-auto-scaling-configuration.assets.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/integ-apprunner-auto-scaling-configuration.template.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.ts diff --git a/packages/@aws-cdk/aws-apprunner-alpha/README.md b/packages/@aws-cdk/aws-apprunner-alpha/README.md index 845a1eb34c447..4f282c2f50396 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/README.md +++ b/packages/@aws-cdk/aws-apprunner-alpha/README.md @@ -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. diff --git a/packages/@aws-cdk/aws-apprunner-alpha/lib/auto-scaling-configuration.ts b/packages/@aws-cdk/aws-apprunner-alpha/lib/auto-scaling-configuration.ts new file mode 100644 index 0000000000000..1d3c1ce943fca --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/lib/auto-scaling-configuration.ts @@ -0,0 +1,202 @@ +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 { + /** + * 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); + } + + /** + * 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; + + public constructor(scope: Construct, id: string, props: AutoScalingConfigurationProps = {}) { + super(scope, id, { + physicalName: props.autoScalingConfigurationName, + }); + + this.validateAutoScalingConfiguration(props); + + 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; + } + + private validateAutoScalingConfiguration(props: AutoScalingConfigurationProps) { + 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 = 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}`); + } + } + +} diff --git a/packages/@aws-cdk/aws-apprunner-alpha/lib/index.ts b/packages/@aws-cdk/aws-apprunner-alpha/lib/index.ts index e061d5b7ecf80..1908a112b1ae7 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/lib/index.ts +++ b/packages/@aws-cdk/aws-apprunner-alpha/lib/index.ts @@ -1,3 +1,4 @@ // AWS::AppRunner CloudFormation Resources: +export * from './auto-scaling-configuration'; export * from './service'; export * from './vpc-connector'; diff --git a/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts b/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts index 412372e18bb90..58babba734037 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts +++ b/packages/@aws-cdk/aws-apprunner-alpha/lib/service.ts @@ -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 @@ -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; + /** * The number of CPU units reserved for each instance of your App Runner service. * @@ -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', diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/auto-scaling-configuration.test.ts b/packages/@aws-cdk/aws-apprunner-alpha/test/auto-scaling-configuration.test.ts new file mode 100644 index 0000000000000..c563e4a5c7745 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/auto-scaling-configuration.test.ts @@ -0,0 +1,110 @@ +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([-1, 0, 26])('invalid minSize', (minSize: number) => { + 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}`); +}); + +test('create an Auto scaling Configuration with tags', () => { + // WHEN + const autoScalingConfiguration = new AutoScalingConfiguration(stack, 'AutoScalingConfiguration', { + autoScalingConfigurationName: 'my-autoscaling-config', + maxConcurrency: 150, + maxSize: 20, + minSize: 5, + }); + + cdk.Tags.of(autoScalingConfiguration).add('Environment', 'production'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::AutoScalingConfiguration', { + Tags: [ + { + Key: 'Environment', + Value: 'production', + }, + ], + }); +}); diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1.assets.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1.assets.json new file mode 100644 index 0000000000000..aceb9fdaf269b --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1.template.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/cdk.out b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/integ-apprunner-auto-scaling-configuration.assets.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/integ-apprunner-auto-scaling-configuration.assets.json new file mode 100644 index 0000000000000..020d27008b0a8 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/integ-apprunner-auto-scaling-configuration.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "363fcfee21919c2d1b81b2bce2345af9e946429f13cfaa0093b8342d674b6ae0": { + "source": { + "path": "integ-apprunner-auto-scaling-configuration.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "363fcfee21919c2d1b81b2bce2345af9e946429f13cfaa0093b8342d674b6ae0.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/integ-apprunner-auto-scaling-configuration.template.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/integ-apprunner-auto-scaling-configuration.template.json new file mode 100644 index 0000000000000..de4478446df05 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/integ-apprunner-auto-scaling-configuration.template.json @@ -0,0 +1,117 @@ +{ + "Resources": { + "AutoScalingConfigurationB226C248": { + "Type": "AWS::AppRunner::AutoScalingConfiguration", + "Properties": { + "MaxConcurrency": 150, + "MaxSize": 20, + "MinSize": 5 + } + }, + "ServiceInstanceRoleDFA90CEC": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "tasks.apprunner.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "ServiceDBC79909": { + "Type": "AWS::AppRunner::Service", + "Properties": { + "AutoScalingConfigurationArn": { + "Fn::GetAtt": [ + "AutoScalingConfigurationB226C248", + "AutoScalingConfigurationArn" + ] + }, + "InstanceConfiguration": { + "InstanceRoleArn": { + "Fn::GetAtt": [ + "ServiceInstanceRoleDFA90CEC", + "Arn" + ] + } + }, + "NetworkConfiguration": { + "EgressConfiguration": { + "EgressType": "DEFAULT" + } + }, + "ServiceName": "service", + "SourceConfiguration": { + "AuthenticationConfiguration": {}, + "AutoDeploymentsEnabled": false, + "ImageRepository": { + "ImageConfiguration": { + "Port": "8000" + }, + "ImageIdentifier": "public.ecr.aws/aws-containers/hello-app-runner:latest", + "ImageRepositoryType": "ECR_PUBLIC" + } + } + } + } + }, + "Outputs": { + "URL": { + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { + "Fn::GetAtt": [ + "ServiceDBC79909", + "ServiceUrl" + ] + } + ] + ] + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/integ.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/integ.json new file mode 100644 index 0000000000000..8652a35fecd22 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "AppRunnerAutoScalingConfiguration/DefaultTest": { + "stacks": [ + "integ-apprunner-auto-scaling-configuration" + ], + "assertionStack": "AppRunnerAutoScalingConfiguration/DefaultTest/DeployAssert", + "assertionStackName": "AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/manifest.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/manifest.json new file mode 100644 index 0000000000000..d701cc4701158 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/manifest.json @@ -0,0 +1,176 @@ +{ + "version": "36.0.0", + "artifacts": { + "integ-apprunner-auto-scaling-configuration.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "integ-apprunner-auto-scaling-configuration.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "integ-apprunner-auto-scaling-configuration": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "integ-apprunner-auto-scaling-configuration.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/363fcfee21919c2d1b81b2bce2345af9e946429f13cfaa0093b8342d674b6ae0.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "integ-apprunner-auto-scaling-configuration.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "integ-apprunner-auto-scaling-configuration.assets" + ], + "metadata": { + "/integ-apprunner-auto-scaling-configuration/AutoScalingConfiguration/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "AutoScalingConfigurationB226C248" + } + ], + "/integ-apprunner-auto-scaling-configuration/Service/InstanceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ServiceInstanceRoleDFA90CEC" + } + ], + "/integ-apprunner-auto-scaling-configuration/Service/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ServiceDBC79909" + } + ], + "/integ-apprunner-auto-scaling-configuration/URL": [ + { + "type": "aws:cdk:logicalId", + "data": "URL" + } + ], + "/integ-apprunner-auto-scaling-configuration/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/integ-apprunner-auto-scaling-configuration/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ], + "ASG1CF037CF4": [ + { + "type": "aws:cdk:logicalId", + "data": "ASG1CF037CF4", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ], + "ASG2409F2238": [ + { + "type": "aws:cdk:logicalId", + "data": "ASG2409F2238", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ], + "ASG3D4584027": [ + { + "type": "aws:cdk:logicalId", + "data": "ASG3D4584027", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ], + "ASG4C27FF86D": [ + { + "type": "aws:cdk:logicalId", + "data": "ASG4C27FF86D", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ], + "ASG5B28C3BE5": [ + { + "type": "aws:cdk:logicalId", + "data": "ASG5B28C3BE5", + "trace": [ + "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" + ] + } + ] + }, + "displayName": "integ-apprunner-auto-scaling-configuration" + }, + "AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "AppRunnerAutoScalingConfigurationDefaultTestDeployAssert4AFB3BF1.assets" + ], + "metadata": { + "/AppRunnerAutoScalingConfiguration/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/AppRunnerAutoScalingConfiguration/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "AppRunnerAutoScalingConfiguration/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/tree.json b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/tree.json new file mode 100644 index 0000000000000..c2d8604d2275d --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.js.snapshot/tree.json @@ -0,0 +1,232 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "integ-apprunner-auto-scaling-configuration": { + "id": "integ-apprunner-auto-scaling-configuration", + "path": "integ-apprunner-auto-scaling-configuration", + "children": { + "AutoScalingConfiguration": { + "id": "AutoScalingConfiguration", + "path": "integ-apprunner-auto-scaling-configuration/AutoScalingConfiguration", + "children": { + "Resource": { + "id": "Resource", + "path": "integ-apprunner-auto-scaling-configuration/AutoScalingConfiguration/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppRunner::AutoScalingConfiguration", + "aws:cdk:cloudformation:props": { + "maxConcurrency": 150, + "maxSize": 20, + "minSize": 5 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apprunner.CfnAutoScalingConfiguration", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-apprunner-alpha.AutoScalingConfiguration", + "version": "0.0.0" + } + }, + "Service": { + "id": "Service", + "path": "integ-apprunner-auto-scaling-configuration/Service", + "children": { + "InstanceRole": { + "id": "InstanceRole", + "path": "integ-apprunner-auto-scaling-configuration/Service/InstanceRole", + "children": { + "ImportInstanceRole": { + "id": "ImportInstanceRole", + "path": "integ-apprunner-auto-scaling-configuration/Service/InstanceRole/ImportInstanceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-apprunner-auto-scaling-configuration/Service/InstanceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "tasks.apprunner.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "integ-apprunner-auto-scaling-configuration/Service/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppRunner::Service", + "aws:cdk:cloudformation:props": { + "autoScalingConfigurationArn": { + "Fn::GetAtt": [ + "AutoScalingConfigurationB226C248", + "AutoScalingConfigurationArn" + ] + }, + "instanceConfiguration": { + "instanceRoleArn": { + "Fn::GetAtt": [ + "ServiceInstanceRoleDFA90CEC", + "Arn" + ] + } + }, + "networkConfiguration": { + "egressConfiguration": { + "egressType": "DEFAULT" + } + }, + "serviceName": "service", + "sourceConfiguration": { + "authenticationConfiguration": {}, + "autoDeploymentsEnabled": false, + "imageRepository": { + "imageConfiguration": { + "port": "8000" + }, + "imageIdentifier": "public.ecr.aws/aws-containers/hello-app-runner:latest", + "imageRepositoryType": "ECR_PUBLIC" + } + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_apprunner.CfnService", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-apprunner-alpha.Service", + "version": "0.0.0" + } + }, + "URL": { + "id": "URL", + "path": "integ-apprunner-auto-scaling-configuration/URL", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnOutput", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "integ-apprunner-auto-scaling-configuration/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "integ-apprunner-auto-scaling-configuration/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "AppRunnerAutoScalingConfiguration": { + "id": "AppRunnerAutoScalingConfiguration", + "path": "AppRunnerAutoScalingConfiguration", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "AppRunnerAutoScalingConfiguration/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "AppRunnerAutoScalingConfiguration/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "AppRunnerAutoScalingConfiguration/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "AppRunnerAutoScalingConfiguration/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "AppRunnerAutoScalingConfiguration/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.ts b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.ts new file mode 100644 index 0000000000000..7d89aea11dfc6 --- /dev/null +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/integ.service-auto-scaling-configuration.ts @@ -0,0 +1,33 @@ +import * as cdk from 'aws-cdk-lib'; +import { Service, Source, AutoScalingConfiguration } from '../lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'integ-apprunner-auto-scaling-configuration'); + +const autoScalingConfiguration = new AutoScalingConfiguration(stack, 'AutoScalingConfiguration', { + maxConcurrency: 150, + maxSize: 20, + minSize: 5, +}); + +const service = new Service(stack, 'Service', { + serviceName: 'service', + source: Source.fromEcrPublic({ + imageConfiguration: { + port: 8000, + }, + imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest', + }), + autoDeploymentsEnabled: false, + autoScalingConfiguration, +}); + +new cdk.CfnOutput(stack, 'URL', { value: `https://${service.serviceUrl}` }); + +new integ.IntegTest(app, 'AppRunnerAutoScalingConfiguration', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts b/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts index 6843cd13a3bc5..8a27342b2c987 100644 --- a/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts +++ b/packages/@aws-cdk/aws-apprunner-alpha/test/service.test.ts @@ -1627,3 +1627,30 @@ test.each([apprunner.IpAddressType.IPV4, apprunner.IpAddressType.DUAL_STACK])('i }, }); }); + +test('create a service with an AutoScalingConfiguration', () => { + // GIVEN + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'demo-stack'); + const autoScalingConfiguration = new apprunner.AutoScalingConfiguration(stack, 'AutoScalingConfiguration', { + autoScalingConfigurationName: 'MyAutoScalingConfiguration', + }); + + // WHEN + new apprunner.Service(stack, 'DemoService', { + source: apprunner.Source.fromEcrPublic({ + imageConfiguration: { port: 8000 }, + imageIdentifier: 'public.ecr.aws/aws-containers/hello-app-runner:latest', + }), + autoScalingConfiguration, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::AutoScalingConfiguration', { + AutoScalingConfigurationName: 'MyAutoScalingConfiguration', + }); + + Template.fromStack(stack).hasResourceProperties('AWS::AppRunner::Service', { + AutoScalingConfigurationArn: stack.resolve(autoScalingConfiguration.autoScalingConfigurationArn), + }); +});