From 6319c2530b83298b6da2c997da3f3d63994f44c1 Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Thu, 29 Sep 2022 08:32:38 -0700 Subject: [PATCH] feat(codedeploy): CodeDeploy deployment config constructs for Lambda and ECS (#22159) CloudFormation now supports Lambda and ECS in the `AWS::CodeDeploy::DeploymentConfig` resource type. This PR adds L2 constructs specific to ECS and Lambda for that resource type. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-codedeploy/README.md | 163 +++++++++++---- .../lib/base-deployment-config.ts | 171 +++++++++++++++ .../lib/ecs/deployment-config.ts | 71 ++++--- .../aws-codedeploy/lib/host-health-config.ts | 36 ++++ packages/@aws-cdk/aws-codedeploy/lib/index.ts | 3 + .../lib/lambda/custom-deployment-config.ts | 11 + .../lib/lambda/deployment-config.ts | 99 +++++---- .../lib/server/deployment-config.ts | 114 +++------- .../lib/traffic-routing-config.ts | 194 ++++++++++++++++++ packages/@aws-cdk/aws-codedeploy/package.json | 27 +-- .../test/ecs/application.test.ts | 9 + ...efaultTestDeployAssertA6573788.assets.json | 19 ++ ...aultTestDeployAssertA6573788.template.json | 36 ++++ .../aws-cdk-codedeploy-ecs-config.assets.json | 19 ++ ...ws-cdk-codedeploy-ecs-config.template.json | 51 +++++ .../deployment-config.integ.snapshot/cdk.out | 1 + .../integ.json | 12 ++ .../manifest.json | 111 ++++++++++ .../tree.json | 99 +++++++++ .../test/ecs/deployment-config.test.ts | 142 +++++++++++++ .../test/ecs/integ.deployment-config.ts | 19 ++ .../test/lambda/application.test.ts | 9 + .../lambda/custom-deployment-config.test.ts | 15 +- ...efaultTestDeployAssert161B09F6.assets.json | 19 ++ ...aultTestDeployAssert161B09F6.template.json | 36 ++++ ...s-cdk-codedeploy-lambda-config.assets.json | 19 ++ ...cdk-codedeploy-lambda-config.template.json | 51 +++++ .../deployment-config.integ.snapshot/cdk.out | 1 + .../integ.json | 12 ++ .../manifest.json | 111 ++++++++++ .../tree.json | 99 +++++++++ .../test/lambda/deployment-config.test.ts | 142 +++++++++++++ .../test/lambda/deployment-group.test.ts | 29 +++ .../test/lambda/integ.deployment-config.ts | 19 ++ 34 files changed, 1753 insertions(+), 216 deletions(-) create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/host-health-config.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.template.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.assets.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.template.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.test.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/test/ecs/integ.deployment-config.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.template.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.assets.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.template.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.test.ts create mode 100644 packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-config.ts diff --git a/packages/@aws-cdk/aws-codedeploy/README.md b/packages/@aws-cdk/aws-codedeploy/README.md index 2608c706022ea..ec5449698294a 100644 --- a/packages/@aws-cdk/aws-codedeploy/README.md +++ b/packages/@aws-cdk/aws-codedeploy/README.md @@ -15,7 +15,7 @@ AWS CodeDeploy is a deployment service that automates application deployments to Amazon EC2 instances, on-premises instances, serverless Lambda functions, or Amazon ECS services. -The CDK currently supports Amazon EC2, on-premise and AWS Lambda applications. +The CDK currently supports Amazon EC2, on-premise, AWS Lambda, and Amazon ECS applications. ## EC2/on-premise Applications @@ -143,7 +143,7 @@ const deploymentGroup = new codedeploy.ServerDeploymentGroup(this, 'DeploymentGr }); ``` -## Deployment Configurations +## EC2/on-premise Deployment Configurations You can also pass a Deployment Configuration when creating the Deployment Group: @@ -226,41 +226,6 @@ In order to deploy a new version of this function: 2. Re-deploy the stack (this will trigger a deployment). 3. Monitor the CodeDeploy deployment as traffic shifts between the versions. - -### Create a custom Deployment Config - -CodeDeploy for Lambda comes with built-in configurations for traffic shifting. -If you want to specify your own strategy, -you can do so with the CustomLambdaDeploymentConfig construct, -letting you specify precisely how fast a new function version is deployed. - -```ts -const config = new codedeploy.CustomLambdaDeploymentConfig(this, 'CustomConfig', { - type: codedeploy.CustomLambdaDeploymentConfigType.CANARY, - interval: Duration.minutes(1), - percentage: 5, -}); - -declare const application: codedeploy.LambdaApplication; -declare const alias: lambda.Alias; -const deploymentGroup = new codedeploy.LambdaDeploymentGroup(this, 'BlueGreenDeployment', { - application, - alias, - deploymentConfig: config, -}); -``` - -You can specify a custom name for your deployment config, but if you do you will not be able to update the interval/percentage through CDK. - -```ts -const config = new codedeploy.CustomLambdaDeploymentConfig(this, 'CustomConfig', { - type: codedeploy.CustomLambdaDeploymentConfigType.CANARY, - interval: Duration.minutes(1), - percentage: 5, - deploymentConfigName: 'MyDeploymentConfig', -}); -``` - ### Rollbacks and Alarms CodeDeploy will roll back if the deployment fails. You can optionally trigger a rollback when one or more alarms are in a failed state: @@ -327,3 +292,127 @@ const deploymentGroup = codedeploy.LambdaDeploymentGroup.fromLambdaDeploymentGro deploymentGroupName: 'MyExistingDeploymentGroup', }); ``` + +## Lambda Deployment Configurations + +CodeDeploy for Lambda comes with predefined configurations for traffic shifting. +The predefined configurations are available as LambdaDeploymentConfig constants. + +```ts +const config = codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_30MINUTES; + +declare const application: codedeploy.LambdaApplication; +declare const alias: lambda.Alias; +const deploymentGroup = new codedeploy.LambdaDeploymentGroup(this, 'BlueGreenDeployment', { + application, + alias, + deploymentConfig: config, +}); +``` + +If you want to specify your own strategy, +you can do so with the LambdaDeploymentConfig construct, +letting you specify precisely how fast a new function version is deployed. + +```ts +const config = new codedeploy.LambdaDeploymentConfig(this, 'CustomConfig', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + interval: cdk.Duration.minutes(15), + percentage: 5, + }), +}); + +declare const application: codedeploy.LambdaApplication; +declare const alias: lambda.Alias; +const deploymentGroup = new codedeploy.LambdaDeploymentGroup(this, 'BlueGreenDeployment', { + application, + alias, + deploymentConfig: config, +}); +``` + +You can specify a custom name for your deployment config, but if you do you will not be able to update the interval/percentage through CDK. + +```ts +const config = new codedeploy.LambdaDeploymentConfig(this, 'CustomConfig', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + interval: cdk.Duration.minutes(15), + percentage: 5, + }), + deploymentConfigName: 'MyDeploymentConfig', +}); +``` + +To import an already existing Deployment Config: + +```ts +const deploymentConfig = codedeploy.LambdaDeploymentConfig.fromLambdaDeploymentConfigName( + this, + 'ExistingDeploymentConfiguration', + 'MyExistingDeploymentConfiguration', +); +``` + +## ECS Applications + +To create a new CodeDeploy Application that deploys an ECS service: + +```ts +const application = new codedeploy.EcsApplication(this, 'CodeDeployApplication', { + applicationName: 'MyApplication', // optional property +}); +``` + +To import an already existing Application: + +```ts +const application = codedeploy.EcsApplication.fromEcsApplicationName( + this, + 'ExistingCodeDeployApplication', + 'MyExistingApplication', +); +``` + +## ECS Deployment Configurations + +CodeDeploy for ECS comes with predefined configurations for traffic shifting. +The predefined configurations are available as LambdaDeploymentConfig constants. + +```ts +const config = codedeploy.EcsDeploymentConfig.CANARY_10PERCENT_5MINUTES; +``` + +If you want to specify your own strategy, +you can do so with the EcsDeploymentConfig construct, +letting you specify precisely how fast an ECS service is deployed. + +```ts +new codedeploy.EcsDeploymentConfig(this, 'CustomConfig', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + interval: cdk.Duration.minutes(15), + percentage: 5, + }), +}); +``` + +You can specify a custom name for your deployment config, but if you do you will not be able to update the interval/percentage through CDK. + +```ts +const config = new codedeploy.EcsDeploymentConfig(this, 'CustomConfig', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + interval: cdk.Duration.minutes(15), + percentage: 5, + }), + deploymentConfigName: 'MyDeploymentConfig', +}); +``` + +Or import an existing one: + +```ts +const deploymentConfig = codedeploy.EcsDeploymentConfig.fromEcsDeploymentConfigName( + this, + 'ExistingDeploymentConfiguration', + 'MyExistingDeploymentConfiguration', +); +``` diff --git a/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts new file mode 100644 index 0000000000000..0cd04ae3628c5 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts @@ -0,0 +1,171 @@ +import { ArnFormat, Resource, Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { CfnDeploymentConfig } from './codedeploy.generated'; +import { MinimumHealthyHosts } from './host-health-config'; +import { TrafficRouting } from './traffic-routing-config'; +import { arnForDeploymentConfig, validateName } from './utils'; + +/** + * The base class for ServerDeploymentConfig, EcsDeploymentConfig, + * and LambdaDeploymentConfig deployment configurations. + */ +export interface IBaseDeploymentConfig { + /** + * The physical, human-readable name of the Deployment Configuration. + * @attribute + */ + readonly deploymentConfigName: string; + + /** + * The ARN of the Deployment Configuration. + * @attribute + */ + readonly deploymentConfigArn: string; +} + +/** + * Construction properties of {@link BaseDeploymentConfig}. + */ +export interface BaseDeploymentConfigOptions { + /** + * The physical, human-readable name of the Deployment Configuration. + * @default - automatically generated name + */ + readonly deploymentConfigName?: string; +} + +/** + * The compute platform of a deployment configuration + */ +export enum ComputePlatform { + /** + * The deployment will target EC2 instances or on-premise servers + */ + SERVER = 'Server', + + /** + * The deployment will target a Lambda function + */ + LAMBDA = 'Lambda', + + /** + * The deployment will target an ECS server + */ + ECS = 'ECS' +} + +/** + * Complete base deployment config properties that are required to be supplied by the implementation + * of the BaseDeploymentConfig class. + */ +export interface BaseDeploymentConfigProps extends BaseDeploymentConfigOptions { + /** + * The destination compute platform for the deployment. + * + * @default ComputePlatform.Server + */ + readonly computePlatform?: ComputePlatform; + + /** + * The configuration that specifies how traffic is shifted during a deployment. + * Only applicable to ECS and Lambda deployments, and must not be specified for Server deployments. + * @default None + */ + readonly trafficRouting?: TrafficRouting; + + /** + * Minimum number of healthy hosts. + * @default None + */ + readonly minimumHealthyHosts?: MinimumHealthyHosts; +} + +/** + * The base class for ServerDeploymentConfig, EcsDeploymentConfig, + * and LambdaDeploymentConfig deployment configurations. + * + * @resource AWS::CodeDeploy::DeploymentConfig + */ +export abstract class BaseDeploymentConfig extends Resource implements IBaseDeploymentConfig { + /** + * Import a custom Deployment Configuration for a Deployment Group defined outside the CDK. + * + * @param scope the parent Construct for this new Construct + * @param id the logical ID of this new Construct + * @param deploymentConfigName the name of the referenced custom Deployment Configuration + * @returns a Construct representing a reference to an existing custom Deployment Configuration + */ + protected static fromDeploymentConfigName(scope: Construct, id: string, deploymentConfigName: string): IBaseDeploymentConfig { + ignore(id); + const arn = Stack.of(scope).formatArn({ + service: 'codedeploy', + resource: 'deploymentconfig', + resourceName: deploymentConfigName, + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + }); + return { + deploymentConfigName: deploymentConfigName, + deploymentConfigArn: arn, + }; + } + + /** + * This method should be used only for static references to predefined deployment configurations, + * like EcsDeploymentConfig.ALL_AT_ONCE + * @param name the name of the referenced custom Deployment Configuration + * @returns a reference to an existing custom Deployment Configuration + */ + protected static deploymentConfig(name: string): IBaseDeploymentConfig { + return { + deploymentConfigName: name, + deploymentConfigArn: arnForDeploymentConfig(name), + }; + } + + /** + * The name of the deployment config + * @attribute + */ + public readonly deploymentConfigName: string; + + /** + * The arn of the deployment config + * @attribute + */ + public readonly deploymentConfigArn: string; + + public constructor(scope: Construct, id: string, props?: BaseDeploymentConfigProps) { + super(scope, id, { + physicalName: props?.deploymentConfigName, + }); + + // Traffic routing is not applicable to Server-based deployment configs + if (props?.trafficRouting && (props?.computePlatform === undefined || props?.computePlatform === ComputePlatform.SERVER)) { + throw new Error('Traffic routing config must not be specified for a Server-base deployment configuration'); + } + + // Minimum healthy hosts is only applicable to Server-based deployment configs + if (props?.minimumHealthyHosts && props?.computePlatform && props?.computePlatform !== ComputePlatform.SERVER) { + throw new Error('Minimum healthy hosts config must only be specified for a Server-base deployment configuration'); + } + + const resource = new CfnDeploymentConfig(this, 'Resource', { + deploymentConfigName: this.physicalName, + computePlatform: props?.computePlatform, + trafficRoutingConfig: props?.trafficRouting?.bind(this), + minimumHealthyHosts: props?.minimumHealthyHosts?._json, + }); + + this.deploymentConfigName = this.getResourceNameAttribute(resource.ref); + this.deploymentConfigArn = this.getResourceArnAttribute(arnForDeploymentConfig(resource.ref), { + service: 'codedeploy', + resource: 'deploymentconfig', + resourceName: this.physicalName, + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + }); + + this.node.addValidation({ validate: () => validateName('Deployment config', this.physicalName) }); + } +} + +function ignore(_x: any) { return; } diff --git a/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts index f9f41e12202a7..cd1f96aa50945 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts @@ -1,52 +1,69 @@ import { Construct } from 'constructs'; -import { arnForDeploymentConfig } from '../utils'; +import { BaseDeploymentConfig, BaseDeploymentConfigOptions, ComputePlatform, IBaseDeploymentConfig } from '../base-deployment-config'; +import { TrafficRouting } from '../traffic-routing-config'; /** * The Deployment Configuration of an ECS Deployment Group. + * + * If you're managing the Deployment Configuration alongside the rest of your CDK resources, + * use the {@link EcsDeploymentConfig} class. + * + * If you want to reference an already existing deployment configuration, + * or one defined in a different CDK Stack, + * use the {@link EcsDeploymentConfig#fromEcsDeploymentConfigName} method. + * * The default, pre-defined Configurations are available as constants on the {@link EcsDeploymentConfig} class * (for example, `EcsDeploymentConfig.AllAtOnce`). - * - * Note: CloudFormation does not currently support creating custom ECS configs outside - * of using a custom resource. You can import custom deployment config created outside the - * CDK or via a custom resource with {@link EcsDeploymentConfig#fromEcsDeploymentConfigName}. */ -export interface IEcsDeploymentConfig { - readonly deploymentConfigName: string; - readonly deploymentConfigArn: string; +export interface IEcsDeploymentConfig extends IBaseDeploymentConfig { +} + +/** + * Construction properties of {@link EcsDeploymentConfig}. + */ +export interface EcsDeploymentConfigProps extends BaseDeploymentConfigOptions { + /** + * The configuration that specifies how traffic is shifted from the 'blue' + * target group to the 'green' target group during a deployment. + * @default AllAtOnce + */ + readonly trafficRouting?: TrafficRouting; } /** * A custom Deployment Configuration for an ECS Deployment Group. * - * Note: This class currently stands as namespaced container of the default configurations - * until CloudFormation supports custom ECS Deployment Configs. Until then it is closed - * (private constructor) and does not extend {@link Construct} - * * @resource AWS::CodeDeploy::DeploymentConfig */ -export class EcsDeploymentConfig { - public static readonly ALL_AT_ONCE = deploymentConfig('CodeDeployDefault.ECSAllAtOnce'); +export class EcsDeploymentConfig extends BaseDeploymentConfig implements IEcsDeploymentConfig { + /** CodeDeploy predefined deployment configuration that shifts all traffic to the updated ECS task set at once. */ + public static readonly ALL_AT_ONCE = EcsDeploymentConfig.deploymentConfig('CodeDeployDefault.ECSAllAtOnce'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic every minute until all traffic is shifted. */ + public static readonly LINEAR_10PERCENT_EVERY_1MINUTES = EcsDeploymentConfig.deploymentConfig('CodeDeployDefault.ECSLinear10PercentEvery1Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic every three minutes until all traffic is shifted. */ + public static readonly LINEAR_10PERCENT_EVERY_3MINUTES = EcsDeploymentConfig.deploymentConfig('CodeDeployDefault.ECSLinear10PercentEvery3Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed five minutes later. */ + public static readonly CANARY_10PERCENT_5MINUTES = EcsDeploymentConfig.deploymentConfig('CodeDeployDefault.ECSCanary10Percent5Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed 15 minutes later. */ + public static readonly CANARY_10PERCENT_15MINUTES = EcsDeploymentConfig.deploymentConfig('CodeDeployDefault.ECSCanary10Percent15Minutes'); /** * Import a custom Deployment Configuration for an ECS Deployment Group defined outside the CDK. * - * @param _scope the parent Construct for this new Construct - * @param _id the logical ID of this new Construct + * @param scope the parent Construct for this new Construct + * @param id the logical ID of this new Construct * @param ecsDeploymentConfigName the name of the referenced custom Deployment Configuration * @returns a Construct representing a reference to an existing custom Deployment Configuration */ - public static fromEcsDeploymentConfigName(_scope: Construct, _id: string, ecsDeploymentConfigName: string): IEcsDeploymentConfig { - return deploymentConfig(ecsDeploymentConfigName); + public static fromEcsDeploymentConfigName(scope: Construct, id: string, ecsDeploymentConfigName: string): IEcsDeploymentConfig { + return this.fromDeploymentConfigName(scope, id, ecsDeploymentConfigName); } - private constructor() { - // nothing to do until CFN supports custom ECS deployment configurations + public constructor(scope: Construct, id: string, props?: EcsDeploymentConfigProps) { + super(scope, id, { + ...props, + computePlatform: ComputePlatform.ECS, + trafficRouting: props?.trafficRouting ?? TrafficRouting.allAtOnce(), + }); } } - -function deploymentConfig(name: string): IEcsDeploymentConfig { - return { - deploymentConfigName: name, - deploymentConfigArn: arnForDeploymentConfig(name), - }; -} diff --git a/packages/@aws-cdk/aws-codedeploy/lib/host-health-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/host-health-config.ts new file mode 100644 index 0000000000000..26117518ed090 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/host-health-config.ts @@ -0,0 +1,36 @@ +import { CfnDeploymentConfig } from './codedeploy.generated'; + +/** + * Minimum number of healthy hosts for a server deployment. + */ +export class MinimumHealthyHosts { + + /** + * The minimum healhty hosts threshold expressed as an absolute number. + */ + public static count(value: number): MinimumHealthyHosts { + return new MinimumHealthyHosts({ + type: 'HOST_COUNT', + value, + }); + } + + /** + * The minmum healhty hosts threshold expressed as a percentage of the fleet. + */ + public static percentage(value: number): MinimumHealthyHosts { + return new MinimumHealthyHosts({ + type: 'FLEET_PERCENT', + value, + }); + } + + private constructor(private readonly json: CfnDeploymentConfig.MinimumHealthyHostsProperty) { } + + /** + * @internal + */ + public get _json() { + return this.json; + } +} diff --git a/packages/@aws-cdk/aws-codedeploy/lib/index.ts b/packages/@aws-cdk/aws-codedeploy/lib/index.ts index 147f27580b874..47dd10cc39190 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/index.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/index.ts @@ -1,4 +1,7 @@ +export * from './base-deployment-config'; +export * from './host-health-config'; export * from './rollback-config'; +export * from './traffic-routing-config'; export * from './ecs'; export * from './lambda'; export * from './server'; diff --git a/packages/@aws-cdk/aws-codedeploy/lib/lambda/custom-deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/lambda/custom-deployment-config.ts index 726a7781c2dd3..fe4ceeb249af2 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/lambda/custom-deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/custom-deployment-config.ts @@ -6,26 +6,31 @@ import { ILambdaDeploymentConfig } from './deployment-config'; /** * Lambda Deployment config type + * @deprecated Use `LambdaDeploymentConfig` */ export enum CustomLambdaDeploymentConfigType { /** * Canary deployment type + * @deprecated Use `LambdaDeploymentConfig` */ CANARY = 'Canary', /** * Linear deployment type + * @deprecated Use `LambdaDeploymentConfig` */ LINEAR = 'Linear' } /** * Properties of a reference to a CodeDeploy Lambda Deployment Configuration. + * @deprecated Use `LambdaDeploymentConfig` */ export interface CustomLambdaDeploymentConfigProps { /** * The type of deployment config, either CANARY or LINEAR + * @deprecated Use `LambdaDeploymentConfig` */ readonly type: CustomLambdaDeploymentConfigType; @@ -33,6 +38,7 @@ export interface CustomLambdaDeploymentConfigProps { * The integer percentage of traffic to shift: * - For LINEAR, the percentage to shift every interval * - For CANARY, the percentage to shift until the interval passes, before the full deployment + * @deprecated Use `LambdaDeploymentConfig` */ readonly percentage: number; @@ -40,6 +46,7 @@ export interface CustomLambdaDeploymentConfigProps { * The interval, in number of minutes: * - For LINEAR, how frequently additional traffic is shifted * - For CANARY, how long to shift traffic before the full deployment + * @deprecated Use `LambdaDeploymentConfig` */ readonly interval: Duration; @@ -47,6 +54,7 @@ export interface CustomLambdaDeploymentConfigProps { * The verbatim name of the deployment config. Must be unique per account/region. * Other parameters cannot be updated if this name is provided. * @default - automatically generated name + * @deprecated Use `LambdaDeploymentConfig` */ readonly deploymentConfigName?: string; } @@ -54,18 +62,21 @@ export interface CustomLambdaDeploymentConfigProps { /** * A custom Deployment Configuration for a Lambda Deployment Group. * @resource AWS::CodeDeploy::DeploymentGroup + * @deprecated CloudFormation now supports Lambda deployment configurations without custom resources. Use {@link LambdaDeploymentConfig}. */ export class CustomLambdaDeploymentConfig extends Resource implements ILambdaDeploymentConfig { /** * The name of the deployment config * @attribute + * @deprecated Use `LambdaDeploymentConfig` */ public readonly deploymentConfigName: string; /** * The arn of the deployment config * @attribute + * @deprecated Use `LambdaDeploymentConfig` */ public readonly deploymentConfigArn: string; diff --git a/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts index 594d994942f46..18690f239d820 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts @@ -1,18 +1,21 @@ import { Construct } from 'constructs'; -import { arnForDeploymentConfig } from '../utils'; +import { BaseDeploymentConfig, BaseDeploymentConfigOptions, ComputePlatform, IBaseDeploymentConfig } from '../base-deployment-config'; +import { TrafficRouting } from '../traffic-routing-config'; /** * The Deployment Configuration of a Lambda Deployment Group. + * + * If you're managing the Deployment Configuration alongside the rest of your CDK resources, + * use the {@link LambdaDeploymentConfig} class. + * + * If you want to reference an already existing deployment configuration, + * or one defined in a different CDK Stack, + * use the {@link LambdaDeploymentConfig#fromLambdaDeploymentConfigName} method. + * * The default, pre-defined Configurations are available as constants on the {@link LambdaDeploymentConfig} class * (`LambdaDeploymentConfig.AllAtOnce`, `LambdaDeploymentConfig.Canary10Percent30Minutes`, etc.). - * - * Note: CloudFormation does not currently support creating custom lambda configs outside - * of using a custom resource. You can import custom deployment config created outside the - * CDK or via a custom resource with {@link LambdaDeploymentConfig#import}. */ -export interface ILambdaDeploymentConfig { - readonly deploymentConfigName: string; - readonly deploymentConfigArn: string; +export interface ILambdaDeploymentConfig extends IBaseDeploymentConfig { } /** @@ -28,46 +31,72 @@ export interface LambdaDeploymentConfigImportProps { readonly deploymentConfigName: string; } +/** + * Construction properties of {@link LambdaDeploymentConfig}. + */ +export interface LambdaDeploymentConfigProps extends BaseDeploymentConfigOptions { + /** + * The configuration that specifies how traffic is shifted from the 'blue' + * target group to the 'green' target group during a deployment. + * @default AllAtOnce + */ + readonly trafficRouting?: TrafficRouting; +} + /** * A custom Deployment Configuration for a Lambda Deployment Group. - * - * Note: This class currently stands as namespaced container of the default configurations - * until CloudFormation supports custom Lambda Deployment Configs. Until then it is closed - * (private constructor) and does not extend {@link Construct} - * * @resource AWS::CodeDeploy::DeploymentConfig */ -export class LambdaDeploymentConfig { - public static readonly ALL_AT_ONCE = deploymentConfig('CodeDeployDefault.LambdaAllAtOnce'); - public static readonly CANARY_10PERCENT_30MINUTES = deploymentConfig('CodeDeployDefault.LambdaCanary10Percent30Minutes'); - public static readonly CANARY_10PERCENT_5MINUTES = deploymentConfig('CodeDeployDefault.LambdaCanary10Percent5Minutes'); - public static readonly CANARY_10PERCENT_10MINUTES = deploymentConfig('CodeDeployDefault.LambdaCanary10Percent10Minutes'); - public static readonly CANARY_10PERCENT_15MINUTES = deploymentConfig('CodeDeployDefault.LambdaCanary10Percent15Minutes'); - public static readonly LINEAR_10PERCENT_EVERY_10MINUTES = deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery10Minutes'); - public static readonly LINEAR_10PERCENT_EVERY_1MINUTE = deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery1Minute'); - public static readonly LINEAR_10PERCENT_EVERY_2MINUTES = deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery2Minutes'); - public static readonly LINEAR_10PERCENT_EVERY_3MINUTES = deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery3Minutes'); +export class LambdaDeploymentConfig extends BaseDeploymentConfig implements ILambdaDeploymentConfig { + /** CodeDeploy predefined deployment configuration that shifts all traffic to the updated Lambda function at once. */ + public static readonly ALL_AT_ONCE = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaAllAtOnce'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed 30 minutes later. */ + public static readonly CANARY_10PERCENT_30MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaCanary10Percent30Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed five minutes later. */ + public static readonly CANARY_10PERCENT_5MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaCanary10Percent5Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed 10 minutes later. */ + public static readonly CANARY_10PERCENT_10MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaCanary10Percent10Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic in the first increment. The remaining 90 percent is deployed 15 minutes later. */ + public static readonly CANARY_10PERCENT_15MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaCanary10Percent15Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic every 10 minutes until all traffic is shifted. */ + public static readonly LINEAR_10PERCENT_EVERY_10MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery10Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic every minute until all traffic is shifted. */ + public static readonly LINEAR_10PERCENT_EVERY_1MINUTE = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery1Minute'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic every two minutes until all traffic is shifted. */ + public static readonly LINEAR_10PERCENT_EVERY_2MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery2Minutes'); + /** CodeDeploy predefined deployment configuration that shifts 10 percent of traffic every three minutes until all traffic is shifted. */ + public static readonly LINEAR_10PERCENT_EVERY_3MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery3Minutes'); + + /** + * Import a Deployment Configuration for a Lambda Deployment Group defined outside the CDK. + * + * @param scope the parent Construct for this new Construct + * @param id the logical ID of this new Construct + * @param lambdaDeploymentConfigName the name of the Lambda Deployment Configuration to import + * @returns a Construct representing a reference to an existing Lambda Deployment Configuration + */ + public static fromLambdaDeploymentConfigName(scope: Construct, id: string, lambdaDeploymentConfigName: string): ILambdaDeploymentConfig { + return this.fromDeploymentConfigName(scope, id, lambdaDeploymentConfigName); + } /** - * Import a custom Deployment Configuration for a Lambda Deployment Group defined outside the CDK. + * Import a Deployment Configuration for a Lambda Deployment Group defined outside the CDK. * * @param _scope the parent Construct for this new Construct * @param _id the logical ID of this new Construct * @param props the properties of the referenced custom Deployment Configuration * @returns a Construct representing a reference to an existing custom Deployment Configuration + * @deprecated use `fromLambdaDeploymentConfigName` */ - public static import(_scope:Construct, _id: string, props: LambdaDeploymentConfigImportProps): ILambdaDeploymentConfig { - return deploymentConfig(props.deploymentConfigName); + public static import(_scope: Construct, _id: string, props: LambdaDeploymentConfigImportProps): ILambdaDeploymentConfig { + return this.fromLambdaDeploymentConfigName(_scope, _id, props.deploymentConfigName); } - private constructor() { - // nothing to do until CFN supports custom lambda deployment configurations + public constructor(scope: Construct, id: string, props?: LambdaDeploymentConfigProps) { + super(scope, id, { + ...props, + computePlatform: ComputePlatform.LAMBDA, + trafficRouting: props?.trafficRouting ?? TrafficRouting.allAtOnce(), + }); } } - -function deploymentConfig(name: string): ILambdaDeploymentConfig { - return { - deploymentConfigName: name, - deploymentConfigArn: arnForDeploymentConfig(name), - }; -} diff --git a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts index 7ddb0ec1d37d9..97a9357cbe0bc 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/server/deployment-config.ts @@ -1,7 +1,6 @@ -import * as cdk from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { CfnDeploymentConfig } from '../codedeploy.generated'; -import { arnForDeploymentConfig, validateName } from '../utils'; +import { BaseDeploymentConfig, BaseDeploymentConfigOptions, IBaseDeploymentConfig } from '../base-deployment-config'; +import { MinimumHealthyHosts } from '../host-health-config'; /** * The Deployment Configuration of an EC2/on-premise Deployment Group. @@ -10,64 +9,13 @@ import { arnForDeploymentConfig, validateName } from '../utils'; * To create a custom Deployment Configuration, * instantiate the {@link ServerDeploymentConfig} Construct. */ -export interface IServerDeploymentConfig { - /** - * @attribute - */ - readonly deploymentConfigName: string; - - /** - * @attribute - */ - readonly deploymentConfigArn: string; -} - -/** - * Minimum number of healthy hosts for a server deployment. - */ -export class MinimumHealthyHosts { - - /** - * The minimum healhty hosts threshold expressed as an absolute number. - */ - public static count(value: number): MinimumHealthyHosts { - return new MinimumHealthyHosts({ - type: 'HOST_COUNT', - value, - }); - } - - /** - * The minmum healhty hosts threshold expressed as a percentage of the fleet. - */ - public static percentage(value: number): MinimumHealthyHosts { - return new MinimumHealthyHosts({ - type: 'FLEET_PERCENT', - value, - }); - } - - private constructor(private readonly json: CfnDeploymentConfig.MinimumHealthyHostsProperty) { } - - /** - * @internal - */ - public get _json() { - return this.json; - } +export interface IServerDeploymentConfig extends IBaseDeploymentConfig { } /** * Construction properties of {@link ServerDeploymentConfig}. */ -export interface ServerDeploymentConfigProps { - /** - * The physical, human-readable name of the Deployment Configuration. - * - * @default a name will be auto-generated - */ - readonly deploymentConfigName?: string; - +export interface ServerDeploymentConfigProps extends BaseDeploymentConfigOptions { /** * Minimum number of healthy hosts. */ @@ -79,10 +27,25 @@ export interface ServerDeploymentConfigProps { * * @resource AWS::CodeDeploy::DeploymentConfig */ -export class ServerDeploymentConfig extends cdk.Resource implements IServerDeploymentConfig { - public static readonly ONE_AT_A_TIME = deploymentConfig('CodeDeployDefault.OneAtATime'); - public static readonly HALF_AT_A_TIME = deploymentConfig('CodeDeployDefault.HalfAtATime'); - public static readonly ALL_AT_ONCE = deploymentConfig('CodeDeployDefault.AllAtOnce'); +export class ServerDeploymentConfig extends BaseDeploymentConfig implements IServerDeploymentConfig { + /** + * The CodeDeployDefault.OneAtATime predefined deployment configuration for EC2/on-premises compute platform + * + * @see https://docs.aws.amazon.com/codedeploy/latest/userguide/deployment-configurations.html#deployment-configuration-server + */ + public static readonly ONE_AT_A_TIME = ServerDeploymentConfig.deploymentConfig('CodeDeployDefault.OneAtATime'); + /** + * The CodeDeployDefault.HalfAtATime predefined deployment configuration for EC2/on-premises compute platform + * + * @see https://docs.aws.amazon.com/codedeploy/latest/userguide/deployment-configurations.html#deployment-configuration-server + */ + public static readonly HALF_AT_A_TIME = ServerDeploymentConfig.deploymentConfig('CodeDeployDefault.HalfAtATime'); + /** + * The CodeDeployDefault.AllAtOnce predefined deployment configuration for EC2/on-premises compute platform + * + * @see https://docs.aws.amazon.com/codedeploy/latest/userguide/deployment-configurations.html#deployment-configuration-server + */ + public static readonly ALL_AT_ONCE = ServerDeploymentConfig.deploymentConfig('CodeDeployDefault.AllAtOnce'); /** * Import a custom Deployment Configuration for an EC2/on-premise Deployment Group defined either outside the CDK app, @@ -97,37 +60,10 @@ export class ServerDeploymentConfig extends cdk.Resource implements IServerDeplo scope: Construct, id: string, serverDeploymentConfigName: string): IServerDeploymentConfig { - - ignore(scope); - ignore(id); - return deploymentConfig(serverDeploymentConfigName); + return this.fromDeploymentConfigName(scope, id, serverDeploymentConfigName); } - public readonly deploymentConfigName: string; - public readonly deploymentConfigArn: string; - constructor(scope: Construct, id: string, props: ServerDeploymentConfigProps) { - super(scope, id, { - physicalName: props.deploymentConfigName, - }); - - const resource = new CfnDeploymentConfig(this, 'Resource', { - deploymentConfigName: this.physicalName, - minimumHealthyHosts: props.minimumHealthyHosts._json, - }); - - this.deploymentConfigName = resource.ref; - this.deploymentConfigArn = arnForDeploymentConfig(this.deploymentConfigName); - - this.node.addValidation({ validate: () => validateName('Deployment config', this.physicalName) }); + super(scope, id, props); } } - -function deploymentConfig(name: string): IServerDeploymentConfig { - return { - deploymentConfigName: name, - deploymentConfigArn: arnForDeploymentConfig(name), - }; -} - -function ignore(_x: any) { return; } diff --git a/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts new file mode 100644 index 0000000000000..2aa86c8e1f40f --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts @@ -0,0 +1,194 @@ +import { Duration } from '@aws-cdk/core'; +import { Construct } from 'constructs'; + +/** + * Represents the structure to pass into the underlying CfnDeploymentConfig class. + */ +export interface TrafficRoutingConfig { + /** + * The type of traffic shifting ( `TimeBasedCanary` or `TimeBasedLinear` ) used by a deployment configuration. + */ + readonly type: string; + + /** + * A configuration that shifts traffic from one version of a Lambda function or ECS task set to another in two increments. + * @default none + */ + readonly timeBasedCanary?: CanaryTrafficRoutingConfig; + + /** + * A configuration that shifts traffic from one version of a Lambda function or Amazon ECS task set to another in equal increments, with an equal number of minutes between each increment. + * @default none + */ + readonly timeBasedLinear?: LinearTrafficRoutingConfig; +} + +/** + * Represents the configuration specific to canary traffic shifting. + */ +export interface CanaryTrafficRoutingConfig { + /** + * The number of minutes between the first and second traffic shifts of a `TimeBasedCanary` deployment. + */ + readonly canaryInterval: number; + + /** + * The percentage of traffic to shift in the first increment of a `TimeBasedCanary` deployment. + */ + readonly canaryPercentage: number; +} + +/** + * Represents the configuration specific to linear traffic shifting. + */ +export interface LinearTrafficRoutingConfig { + /** + * The number of minutes between each incremental traffic shift of a `TimeBasedLinear` deployment. + */ + readonly linearInterval: number; + + /** + * The percentage of traffic that is shifted at the start of each increment of a `TimeBasedLinear` deployment. + */ + readonly linearPercentage: number; +} + +/** + * Represents how traffic is shifted during a CodeDeploy deployment. + */ +export abstract class TrafficRouting { + /** + * Shifts 100% of traffic in a single shift. + */ + public static allAtOnce(): TrafficRouting { + return new AllAtOnceTrafficRouting(); + } + + /** + * Shifts a specified percentage of traffic, waits for a specified amount of time, then shifts the rest of traffic. + */ + public static timeBasedCanary(props: TimeBasedCanaryTrafficRoutingProps): TrafficRouting { + return new TimeBasedCanaryTrafficRouting(props); + } + + /** + * Keeps shifting a specified percentage of traffic until reaching 100%, waiting for a specified amount of time in between each traffic shift. + */ + public static timeBasedLinear(props: TimeBasedLinearTrafficRoutingProps): TrafficRouting { + return new TimeBasedLinearTrafficRouting(props); + } + + /** + * Returns the traffic routing configuration. + */ + public abstract bind(scope: Construct): TrafficRoutingConfig; +} + +/** + * Common properties of traffic shifting routing configurations + */ +export interface BaseTrafficShiftingConfigProps { + /** + * The amount of time between traffic shifts. + */ + readonly interval: Duration; + + /** + * The percentage to increase traffic on each traffic shift. + */ + readonly percentage: number; +} + +/** + * Define a traffic routing config of type 'AllAtOnce'. + */ +export class AllAtOnceTrafficRouting extends TrafficRouting { + constructor() { + super(); + } + + /** + * Return a TrafficRoutingConfig of type `AllAtOnce`. + */ + bind(_scope: Construct): TrafficRoutingConfig { + return { + type: 'AllAtOnce', + }; + } +} + +/** + * Construction properties for {@link TimeBasedCanaryTrafficRouting}. + */ +export interface TimeBasedCanaryTrafficRoutingProps extends BaseTrafficShiftingConfigProps {} + +/** + * Define a traffic routing config of type 'TimeBasedCanary'. + */ +export class TimeBasedCanaryTrafficRouting extends TrafficRouting { + /** + * The amount of time between additional traffic shifts. + */ + readonly interval: Duration; + /** + * The percentage to increase traffic on each traffic shift. + */ + readonly percentage: number; + + constructor(props: TimeBasedCanaryTrafficRoutingProps) { + super(); + this.interval = props.interval; + this.percentage = props.percentage; + } + + /** + * Return a TrafficRoutingConfig of type `TimeBasedCanary`. + */ + bind(_scope: Construct): TrafficRoutingConfig { + return { + type: 'TimeBasedCanary', + timeBasedCanary: { + canaryInterval: this.interval.toMinutes(), + canaryPercentage: this.percentage, + }, + }; + } +} + +/** + * Construction properties for {@link TimeBasedLinearTrafficRouting}. + */ +export interface TimeBasedLinearTrafficRoutingProps extends BaseTrafficShiftingConfigProps {} + +/** + * Define a traffic routing config of type 'TimeBasedLinear'. + */ +export class TimeBasedLinearTrafficRouting extends TrafficRouting { + /** + * The amount of time between additional traffic shifts. + */ + readonly interval: Duration; + /** + * The percentage to increase traffic on each traffic shift. + */ + readonly percentage: number; + + constructor(props: TimeBasedLinearTrafficRoutingProps) { + super(); + this.interval = props.interval; + this.percentage = props.percentage; + } + + /** + * Return a TrafficRoutingConfig of type `TimeBasedLinear`. + */ + bind(_scope: Construct): TrafficRoutingConfig { + return { + type: 'TimeBasedLinear', + timeBasedLinear: { + linearInterval: this.interval.toMinutes(), + linearPercentage: this.percentage, + }, + }; + } +} diff --git a/packages/@aws-cdk/aws-codedeploy/package.json b/packages/@aws-cdk/aws-codedeploy/package.json index 9077c1ba954d2..4cb2301eb5303 100644 --- a/packages/@aws-cdk/aws-codedeploy/package.json +++ b/packages/@aws-cdk/aws-codedeploy/package.json @@ -86,6 +86,7 @@ "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/integ-runner": "0.0.0", + "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", @@ -124,32 +125,23 @@ "awslint": { "exclude": [ "construct-interface-extends-iconstruct:@aws-cdk/aws-codedeploy.IServerDeploymentConfig", + "construct-interface-extends-iconstruct:@aws-cdk/aws-codedeploy.ILambdaDeploymentConfig", + "construct-interface-extends-iconstruct:@aws-cdk/aws-codedeploy.IEcsDeploymentConfig", "resource-interface-extends-resource:@aws-cdk/aws-codedeploy.IServerDeploymentConfig", - "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentConfig.deploymentConfigArn", + "resource-interface-extends-resource:@aws-cdk/aws-codedeploy.ILambdaDeploymentConfig", + "resource-interface-extends-resource:@aws-cdk/aws-codedeploy.IEcsDeploymentConfig", + "no-static-import:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.import", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentGroup.role", "docs-public-apis:@aws-cdk/aws-codedeploy.InstanceTagSet.instanceTagGroups", "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaApplication.applicationArn", "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaApplication.applicationName", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.ALL_AT_ONCE", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_10MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_15MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_30MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.LINEAR_10PERCENT_EVERY_10MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.LINEAR_10PERCENT_EVERY_1MINUTE", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.LINEAR_10PERCENT_EVERY_2MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentConfig.LINEAR_10PERCENT_EVERY_3MINUTES", "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentGroup", "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentGroup.role", "docs-public-apis:@aws-cdk/aws-codedeploy.LoadBalancer.generation", "docs-public-apis:@aws-cdk/aws-codedeploy.LoadBalancer.name", "docs-public-apis:@aws-cdk/aws-codedeploy.ServerApplication.applicationArn", "docs-public-apis:@aws-cdk/aws-codedeploy.ServerApplication.applicationName", - "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentConfig.ALL_AT_ONCE", - "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentConfig.HALF_AT_A_TIME", - "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentConfig.ONE_AT_A_TIME", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentGroup.autoScalingGroups", - "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentConfig.deploymentConfigName", "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentGroup.application", "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentGroup.deploymentConfig", "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentGroup.deploymentGroupArn", @@ -158,12 +150,8 @@ "docs-public-apis:@aws-cdk/aws-codedeploy.ServerDeploymentGroup.role", "docs-public-apis:@aws-cdk/aws-codedeploy.ILambdaApplication.applicationArn", "docs-public-apis:@aws-cdk/aws-codedeploy.ILambdaApplication.applicationName", - "docs-public-apis:@aws-cdk/aws-codedeploy.ILambdaDeploymentConfig.deploymentConfigArn", - "docs-public-apis:@aws-cdk/aws-codedeploy.ILambdaDeploymentConfig.deploymentConfigName", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerApplication.applicationArn", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerApplication.applicationName", - "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentConfig.deploymentConfigArn", - "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentConfig.deploymentConfigName", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentGroup", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentGroup.application", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentGroup.deploymentConfig", @@ -171,11 +159,8 @@ "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentGroup.deploymentGroupName", "docs-public-apis:@aws-cdk/aws-codedeploy.EcsApplication.applicationArn", "docs-public-apis:@aws-cdk/aws-codedeploy.EcsApplication.applicationName", - "docs-public-apis:@aws-cdk/aws-codedeploy.EcsDeploymentConfig.ALL_AT_ONCE", "docs-public-apis:@aws-cdk/aws-codedeploy.IEcsApplication.applicationArn", "docs-public-apis:@aws-cdk/aws-codedeploy.IEcsApplication.applicationName", - "docs-public-apis:@aws-cdk/aws-codedeploy.IEcsDeploymentConfig.deploymentConfigArn", - "docs-public-apis:@aws-cdk/aws-codedeploy.IEcsDeploymentConfig.deploymentConfigName", "props-physical-name:@aws-cdk/aws-codedeploy.CustomLambdaDeploymentConfigProps" ] }, diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/application.test.ts b/packages/@aws-cdk/aws-codedeploy/test/ecs/application.test.ts index a5661c3538f14..c907bb4c38740 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/ecs/application.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/application.test.ts @@ -43,4 +43,13 @@ describe('CodeDeploy ECS Application', () => { expect(() => app.synth()).toThrow('Application name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).'); }); + + test('can be imported', () => { + const stack = new cdk.Stack(); + + const application = codedeploy.EcsApplication.fromEcsApplicationName(stack, 'MyApp', 'MyApp'); + + expect(application).not.toEqual(undefined); + expect(application.applicationName).toEqual('MyApp'); + }); }); diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets.json b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets.json new file mode 100644 index 0000000000000..fa8c0147c3faa --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.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-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.template.json b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.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-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.assets.json b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.assets.json new file mode 100644 index 0000000000000..1ba080dd4d5d9 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "d38ba84a2fd21dc7efb6bafa53ff6671f4f79544905c0de9f396847a2d782a1d": { + "source": { + "path": "aws-cdk-codedeploy-ecs-config.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "d38ba84a2fd21dc7efb6bafa53ff6671f4f79544905c0de9f396847a2d782a1d.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-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.template.json b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.template.json new file mode 100644 index 0000000000000..5d1696e08036f --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/aws-cdk-codedeploy-ecs-config.template.json @@ -0,0 +1,51 @@ +{ + "Resources": { + "LinearConfig531CF4AA": { + "Type": "AWS::CodeDeploy::DeploymentConfig", + "Properties": { + "ComputePlatform": "ECS", + "TrafficRoutingConfig": { + "TimeBasedLinear": { + "LinearInterval": 1, + "LinearPercentage": 5 + }, + "Type": "TimeBasedLinear" + } + } + } + }, + "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." + } + ] + } + } +} diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/integ.json b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/integ.json new file mode 100644 index 0000000000000..8c91633c88ae6 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "21.0.0", + "testCases": { + "EcsDeploymentConfigTest/DefaultTest": { + "stacks": [ + "aws-cdk-codedeploy-ecs-config" + ], + "assertionStack": "EcsDeploymentConfigTest/DefaultTest/DeployAssert", + "assertionStackName": "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..c6dd83b5144dd --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/manifest.json @@ -0,0 +1,111 @@ +{ + "version": "21.0.0", + "artifacts": { + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "aws-cdk-codedeploy-ecs-config.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-codedeploy-ecs-config.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-codedeploy-ecs-config": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-codedeploy-ecs-config.template.json", + "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}/d38ba84a2fd21dc7efb6bafa53ff6671f4f79544905c0de9f396847a2d782a1d.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-codedeploy-ecs-config.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": [ + "aws-cdk-codedeploy-ecs-config.assets" + ], + "metadata": { + "/aws-cdk-codedeploy-ecs-config/LinearConfig/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "LinearConfig531CF4AA" + } + ], + "/aws-cdk-codedeploy-ecs-config/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-codedeploy-ecs-config/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-codedeploy-ecs-config" + }, + "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.template.json", + "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": [ + "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.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": [ + "EcsDeploymentConfigTestDefaultTestDeployAssertA6573788.assets" + ], + "metadata": { + "/EcsDeploymentConfigTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/EcsDeploymentConfigTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "EcsDeploymentConfigTest/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/tree.json b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/tree.json new file mode 100644 index 0000000000000..58546bcc2f842 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.integ.snapshot/tree.json @@ -0,0 +1,99 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.95" + } + }, + "aws-cdk-codedeploy-ecs-config": { + "id": "aws-cdk-codedeploy-ecs-config", + "path": "aws-cdk-codedeploy-ecs-config", + "children": { + "LinearConfig": { + "id": "LinearConfig", + "path": "aws-cdk-codedeploy-ecs-config/LinearConfig", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-codedeploy-ecs-config/LinearConfig/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CodeDeploy::DeploymentConfig", + "aws:cdk:cloudformation:props": { + "computePlatform": "ECS", + "deploymentConfigName": "awscdkcodedeployecsconfigLinearConfig12E7AC8B.EcsLinear5PercentEvery1Minutes", + "trafficRoutingConfig": { + "type": "TimeBasedLinear", + "timeBasedLinear": { + "linearInterval": 1, + "linearPercentage": 5 + } + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codedeploy.CfnDeploymentConfig", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codedeploy.EcsDeploymentConfig", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "EcsDeploymentConfigTest": { + "id": "EcsDeploymentConfigTest", + "path": "EcsDeploymentConfigTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "EcsDeploymentConfigTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "EcsDeploymentConfigTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.95" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "EcsDeploymentConfigTest/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.test.ts b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.test.ts new file mode 100644 index 0000000000000..3dc660e10bb08 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.test.ts @@ -0,0 +1,142 @@ +import { Template } from '@aws-cdk/assertions'; +import * as cdk from '@aws-cdk/core'; +import * as codedeploy from '../../lib'; +import { TrafficRouting } from '../../lib'; + +/* eslint-disable quote-props */ + +let stack: cdk.Stack; + +beforeEach(() => { + stack = new cdk.Stack(); +}); + +test('can create default config', () => { + // WHEN + new codedeploy.EcsDeploymentConfig(stack, 'MyConfig'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'ECS', + 'TrafficRoutingConfig': { + 'Type': 'AllAtOnce', + }, + }); +}); + +test('can create all-at-once config', () => { + // WHEN + new codedeploy.EcsDeploymentConfig(stack, 'MyConfig', { + trafficRouting: TrafficRouting.allAtOnce(), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'ECS', + 'TrafficRoutingConfig': { + 'Type': 'AllAtOnce', + }, + }); +}); + +test('can create linear config', () => { + // WHEN + new codedeploy.EcsDeploymentConfig(stack, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedLinear({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'ECS', + 'TrafficRoutingConfig': { + 'TimeBasedLinear': { + 'LinearInterval': 1, + 'LinearPercentage': 5, + }, + 'Type': 'TimeBasedLinear', + }, + }); +}); + +test('can create canary config', () => { + // WHEN + new codedeploy.EcsDeploymentConfig(stack, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'ECS', + 'TrafficRoutingConfig': { + 'TimeBasedCanary': { + 'CanaryInterval': 1, + 'CanaryPercentage': 5, + }, + 'Type': 'TimeBasedCanary', + }, + }); +}); + +test('can create a config with a specific name', () => { + // WHEN + new codedeploy.EcsDeploymentConfig(stack, 'MyConfig', { + deploymentConfigName: 'MyCanaryConfig', + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'ECS', + 'DeploymentConfigName': 'MyCanaryConfig', + 'TrafficRoutingConfig': { + 'TimeBasedCanary': { + 'CanaryInterval': 1, + 'CanaryPercentage': 5, + }, + 'Type': 'TimeBasedCanary', + }, + }); +}); + +test('can be imported', () => { + const deploymentConfig = codedeploy.EcsDeploymentConfig.fromEcsDeploymentConfigName(stack, 'MyDC', 'MyDC'); + + expect(deploymentConfig).not.toEqual(undefined); +}); + +test('fail with more than 100 characters in name', () => { + const app = new cdk.App(); + const stackWithApp = new cdk.Stack(app); + new codedeploy.EcsDeploymentConfig(stackWithApp, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + deploymentConfigName: 'a'.repeat(101), + }); + + expect(() => app.synth()).toThrow(`Deployment config name: "${'a'.repeat(101)}" can be a max of 100 characters.`); +}); + +test('fail with unallowed characters in name', () => { + const app = new cdk.App(); + const stackWithApp = new cdk.Stack(app); + new codedeploy.EcsDeploymentConfig(stackWithApp, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + deploymentConfigName: 'my name', + }); + + expect(() => app.synth()).toThrow('Deployment config name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).'); +}); diff --git a/packages/@aws-cdk/aws-codedeploy/test/ecs/integ.deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/test/ecs/integ.deployment-config.ts new file mode 100644 index 0000000000000..651ed7efa0962 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/integ.deployment-config.ts @@ -0,0 +1,19 @@ +import * as cdk from '@aws-cdk/core'; +import * as integ from '@aws-cdk/integ-tests'; +import * as codedeploy from '../../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-codedeploy-ecs-config'); + +new codedeploy.EcsDeploymentConfig(stack, 'LinearConfig', { + trafficRouting: codedeploy.TrafficRouting.timeBasedLinear({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), +}); + +new integ.IntegTest(app, 'EcsDeploymentConfigTest', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/application.test.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/application.test.ts index 4b870c53c0e1d..7832ef202c949 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/application.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/application.test.ts @@ -41,4 +41,13 @@ describe('CodeDeploy Lambda Application', () => { expect(() => app.synth()).toThrow('Application name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).'); }); + + test('can be imported', () => { + const stack = new cdk.Stack(); + + const application = codedeploy.LambdaApplication.fromLambdaApplicationName(stack, 'MyApp', 'MyApp'); + + expect(application).not.toEqual(undefined); + expect(application.applicationName).toEqual('MyApp'); + }); }); diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/custom-deployment-config.test.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/custom-deployment-config.test.ts index 4498ed8522f73..20af6c7147751 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/custom-deployment-config.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/custom-deployment-config.test.ts @@ -1,5 +1,6 @@ import { Template } from '@aws-cdk/assertions'; import * as lambda from '@aws-cdk/aws-lambda'; +import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import * as cdk from '@aws-cdk/core'; import * as codedeploy from '../../lib'; @@ -30,7 +31,7 @@ beforeEach(() => { }); -test('custom resource created', () => { +testDeprecated('custom resource created', () => { // WHEN const config = new codedeploy.CustomLambdaDeploymentConfig(stack, 'CustomConfig', { type: codedeploy.CustomLambdaDeploymentConfigType.CANARY, @@ -75,7 +76,7 @@ test('custom resource created', () => { }); }); -test('custom resource created with specific name', () => { +testDeprecated('custom resource created with specific name', () => { // WHEN const config = new codedeploy.CustomLambdaDeploymentConfig(stack, 'CustomConfig', { type: codedeploy.CustomLambdaDeploymentConfigType.CANARY, @@ -97,7 +98,7 @@ test('custom resource created with specific name', () => { }); }); -test('fail with more than 100 characters in name', () => { +testDeprecated('fail with more than 100 characters in name', () => { const app = new cdk.App(); const stackWithApp = new cdk.Stack(app); new codedeploy.CustomLambdaDeploymentConfig(stackWithApp, 'CustomConfig', { @@ -110,7 +111,7 @@ test('fail with more than 100 characters in name', () => { expect(() => app.synth()).toThrow(`Deployment config name: "${'a'.repeat(101)}" can be a max of 100 characters.`); }); -test('fail with unallowed characters in name', () => { +testDeprecated('fail with unallowed characters in name', () => { const app = new cdk.App(); const stackWithApp = new cdk.Stack(app); new codedeploy.CustomLambdaDeploymentConfig(stackWithApp, 'CustomConfig', { @@ -123,7 +124,7 @@ test('fail with unallowed characters in name', () => { expect(() => app.synth()).toThrow('Deployment config name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).'); }); -test('can create linear custom config', () => { +testDeprecated('can create linear custom config', () => { // WHEN const config = new codedeploy.CustomLambdaDeploymentConfig(stack, 'CustomConfig', { type: codedeploy.CustomLambdaDeploymentConfigType.LINEAR, @@ -142,7 +143,7 @@ test('can create linear custom config', () => { }); }); -test('can create canary custom config', () => { +testDeprecated('can create canary custom config', () => { // WHEN const config = new codedeploy.CustomLambdaDeploymentConfig(stack, 'CustomConfig', { type: codedeploy.CustomLambdaDeploymentConfigType.CANARY, @@ -161,7 +162,7 @@ test('can create canary custom config', () => { }); }); -test('dependency on the config exists to ensure ordering', () => { +testDeprecated('dependency on the config exists to ensure ordering', () => { // WHEN const config = new codedeploy.CustomLambdaDeploymentConfig(stack, 'CustomConfig', { type: codedeploy.CustomLambdaDeploymentConfigType.CANARY, diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets.json new file mode 100644 index 0000000000000..bd67c83d14603 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.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-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.template.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.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-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.assets.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.assets.json new file mode 100644 index 0000000000000..d861f05e2c9fb --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.assets.json @@ -0,0 +1,19 @@ +{ + "version": "21.0.0", + "files": { + "681328df6a987c681a4d5cafad61efbf97e5934c41842c8fc92c113ee39a6b28": { + "source": { + "path": "aws-cdk-codedeploy-lambda-config.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "681328df6a987c681a4d5cafad61efbf97e5934c41842c8fc92c113ee39a6b28.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-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.template.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.template.json new file mode 100644 index 0000000000000..2c5dddc618a4a --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/aws-cdk-codedeploy-lambda-config.template.json @@ -0,0 +1,51 @@ +{ + "Resources": { + "LinearConfig531CF4AA": { + "Type": "AWS::CodeDeploy::DeploymentConfig", + "Properties": { + "ComputePlatform": "Lambda", + "TrafficRoutingConfig": { + "TimeBasedLinear": { + "LinearInterval": 1, + "LinearPercentage": 5 + }, + "Type": "TimeBasedLinear" + } + } + } + }, + "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-codedeploy/test/lambda/deployment-config.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..8ecc185e9dbee --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"21.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/integ.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/integ.json new file mode 100644 index 0000000000000..094127896a86e --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "21.0.0", + "testCases": { + "LambdaDeploymentConfigTest/DefaultTest": { + "stacks": [ + "aws-cdk-codedeploy-lambda-config" + ], + "assertionStack": "LambdaDeploymentConfigTest/DefaultTest/DeployAssert", + "assertionStackName": "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..c6fc338e7e046 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/manifest.json @@ -0,0 +1,111 @@ +{ + "version": "21.0.0", + "artifacts": { + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "aws-cdk-codedeploy-lambda-config.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-cdk-codedeploy-lambda-config.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-cdk-codedeploy-lambda-config": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-cdk-codedeploy-lambda-config.template.json", + "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}/681328df6a987c681a4d5cafad61efbf97e5934c41842c8fc92c113ee39a6b28.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-cdk-codedeploy-lambda-config.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": [ + "aws-cdk-codedeploy-lambda-config.assets" + ], + "metadata": { + "/aws-cdk-codedeploy-lambda-config/LinearConfig/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "LinearConfig531CF4AA" + } + ], + "/aws-cdk-codedeploy-lambda-config/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-codedeploy-lambda-config/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-cdk-codedeploy-lambda-config" + }, + "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.template.json", + "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": [ + "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.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": [ + "LambdaDeploymentConfigTestDefaultTestDeployAssert161B09F6.assets" + ], + "metadata": { + "/LambdaDeploymentConfigTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/LambdaDeploymentConfigTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "LambdaDeploymentConfigTest/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/tree.json b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/tree.json new file mode 100644 index 0000000000000..9f9a449ffe63b --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.integ.snapshot/tree.json @@ -0,0 +1,99 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.95" + } + }, + "aws-cdk-codedeploy-lambda-config": { + "id": "aws-cdk-codedeploy-lambda-config", + "path": "aws-cdk-codedeploy-lambda-config", + "children": { + "LinearConfig": { + "id": "LinearConfig", + "path": "aws-cdk-codedeploy-lambda-config/LinearConfig", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-cdk-codedeploy-lambda-config/LinearConfig/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CodeDeploy::DeploymentConfig", + "aws:cdk:cloudformation:props": { + "computePlatform": "Lambda", + "deploymentConfigName": "awscdkcodedeploylambdaconfigLinearConfig655064A4.LambdaLinear5PercentEvery1Minutes", + "trafficRoutingConfig": { + "type": "TimeBasedLinear", + "timeBasedLinear": { + "linearInterval": 1, + "linearPercentage": 5 + } + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codedeploy.CfnDeploymentConfig", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-codedeploy.LambdaDeploymentConfig", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "LambdaDeploymentConfigTest": { + "id": "LambdaDeploymentConfigTest", + "path": "LambdaDeploymentConfigTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "LambdaDeploymentConfigTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "LambdaDeploymentConfigTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.95" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "LambdaDeploymentConfigTest/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.test.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.test.ts new file mode 100644 index 0000000000000..b50e897bf366d --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.test.ts @@ -0,0 +1,142 @@ +import { Template } from '@aws-cdk/assertions'; +import * as cdk from '@aws-cdk/core'; +import * as codedeploy from '../../lib'; +import { TrafficRouting } from '../../lib'; + +/* eslint-disable quote-props */ + +let stack: cdk.Stack; + +beforeEach(() => { + stack = new cdk.Stack(); +}); + +test('can create default config', () => { + // WHEN + new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig'); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'Lambda', + 'TrafficRoutingConfig': { + 'Type': 'AllAtOnce', + }, + }); +}); + +test('can create all-at-once config', () => { + // WHEN + new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { + trafficRouting: TrafficRouting.allAtOnce(), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'Lambda', + 'TrafficRoutingConfig': { + 'Type': 'AllAtOnce', + }, + }); +}); + +test('can create linear config', () => { + // WHEN + new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedLinear({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'Lambda', + 'TrafficRoutingConfig': { + 'TimeBasedLinear': { + 'LinearInterval': 1, + 'LinearPercentage': 5, + }, + 'Type': 'TimeBasedLinear', + }, + }); +}); + +test('can create canary config', () => { + // WHEN + new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'Lambda', + 'TrafficRoutingConfig': { + 'TimeBasedCanary': { + 'CanaryInterval': 1, + 'CanaryPercentage': 5, + }, + 'Type': 'TimeBasedCanary', + }, + }); +}); + +test('can create a config with a specific name', () => { + // WHEN + new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { + deploymentConfigName: 'MyCanaryConfig', + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'Lambda', + 'DeploymentConfigName': 'MyCanaryConfig', + 'TrafficRoutingConfig': { + 'TimeBasedCanary': { + 'CanaryInterval': 1, + 'CanaryPercentage': 5, + }, + 'Type': 'TimeBasedCanary', + }, + }); +}); + +test('can be imported', () => { + const deploymentConfig = codedeploy.LambdaDeploymentConfig.fromLambdaDeploymentConfigName(stack, 'MyDC', 'MyDC'); + + expect(deploymentConfig).not.toEqual(undefined); +}); + +test('fail with more than 100 characters in name', () => { + const app = new cdk.App(); + const stackWithApp = new cdk.Stack(app); + new codedeploy.LambdaDeploymentConfig(stackWithApp, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + deploymentConfigName: 'a'.repeat(101), + }); + + expect(() => app.synth()).toThrow(`Deployment config name: "${'a'.repeat(101)}" can be a max of 100 characters.`); +}); + +test('fail with unallowed characters in name', () => { + const app = new cdk.App(); + const stackWithApp = new cdk.Stack(app); + new codedeploy.LambdaDeploymentConfig(stackWithApp, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + deploymentConfigName: 'my name', + }); + + expect(() => app.synth()).toThrow('Deployment config name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).'); +}); diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts index 9fc6049e9a2c5..959c98bbce139 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts @@ -4,6 +4,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; import * as codedeploy from '../../lib'; +import { TrafficRouting } from '../../lib'; function mockFunction(stack: cdk.Stack, id: string) { return new lambda.Function(stack, id, { @@ -629,3 +630,31 @@ describe('imported with fromLambdaDeploymentGroupAttributes', () => { expect(importedGroup.deploymentConfig).toEqual(codedeploy.LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTES); }); }); + +test('dependency on the config exists to ensure ordering', () => { + // WHEN + const stack = new cdk.Stack(); + const application = new codedeploy.LambdaApplication(stack, 'MyApp'); + const alias = mockAlias(stack); + const config = new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { + trafficRouting: TrafficRouting.timeBasedCanary({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), + }); + new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', { + application, + alias, + deploymentConfig: config, + }); + + // THEN + Template.fromStack(stack).hasResource('AWS::CodeDeploy::DeploymentGroup', { + Properties: { + DeploymentConfigName: stack.resolve(config.deploymentConfigName), + }, + DependsOn: [ + stack.getLogicalId(config.node.defaultChild as codedeploy.CfnDeploymentConfig), + ], + }); +}); diff --git a/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-config.ts new file mode 100644 index 0000000000000..de578cb7eb648 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-config.ts @@ -0,0 +1,19 @@ +import * as cdk from '@aws-cdk/core'; +import * as integ from '@aws-cdk/integ-tests'; +import * as codedeploy from '../../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'aws-cdk-codedeploy-lambda-config'); + +new codedeploy.LambdaDeploymentConfig(stack, 'LinearConfig', { + trafficRouting: codedeploy.TrafficRouting.timeBasedLinear({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), +}); + +new integ.IntegTest(app, 'LambdaDeploymentConfigTest', { + testCases: [stack], +}); + +app.synth();