From fc9178b3cc47a73b2f049e30aa4f9c0d09fdd24d Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Tue, 13 Sep 2022 12:14:20 -0700 Subject: [PATCH 1/8] feat(codedeploy): Add CodeDeploy deployment config constructs for Lambda and ECS --- packages/@aws-cdk/aws-codedeploy/README.md | 143 +++++++++++++----- .../lib/ecs/deployment-config.ts | 95 ++++++++++-- packages/@aws-cdk/aws-codedeploy/lib/index.ts | 1 + .../lib/lambda/deployment-config.ts | 105 +++++++++++-- .../lib/traffic-routing-config.ts | 117 ++++++++++++++ packages/@aws-cdk/aws-codedeploy/package.json | 13 ++ .../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 | 141 +++++++++++++++++ .../test/ecs/integ.deployment-config.ts | 19 +++ .../test/lambda/application.test.ts | 9 ++ ...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 | 141 +++++++++++++++++ .../test/lambda/integ.deployment-config.ts | 19 +++ 28 files changed, 1440 insertions(+), 68 deletions(-) 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..852874c404915 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,107 @@ const deploymentGroup = codedeploy.LambdaDeploymentGroup.fromLambdaDeploymentGro deploymentGroupName: 'MyExistingDeploymentGroup', }); ``` + +## Lambda Deployment Configurations + +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 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 built-in configurations for traffic shifting. +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/ecs/deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts index f9f41e12202a7..edec6e8c87a11 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts @@ -1,46 +1,109 @@ +import { ArnFormat, Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { arnForDeploymentConfig } from '../utils'; +import { CfnDeploymentConfig } from '../codedeploy.generated'; +import { AllAtOnceTrafficRoutingConfig, ITrafficRoutingConfig } from '../traffic-routing-config'; +import { arnForDeploymentConfig, validateName } from '../utils'; /** * 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 { + /** @attribute */ readonly deploymentConfigName: string; + + /** @attribute */ readonly deploymentConfigArn: string; } +/** + * Construction properties of {@link EcsDeploymentConfig}. + */ +export interface EcsDeploymentConfigProps { + /** + * The physical, human-readable name of the Deployment Configuration. + * @default - automatically generated name + */ + readonly deploymentConfigName?: string; + + /** + * The configuration that specifies how traffic is shifted from the 'blue' + * target group to the 'green' target group during a deployment. + * @default AllAtOnce + */ + readonly trafficRoutingConfig?: ITrafficRoutingConfig; +} + /** * 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 { +export class EcsDeploymentConfig extends Resource implements IEcsDeploymentConfig { public static readonly ALL_AT_ONCE = deploymentConfig('CodeDeployDefault.ECSAllAtOnce'); + public static readonly LINEAR_10PERCENT_EVERY_1MINUTES = deploymentConfig('CodeDeployDefault.ECSLinear10PercentEvery1Minutes'); + public static readonly LINEAR_10PERCENT_EVERY_3MINUTES = deploymentConfig('CodeDeployDefault.ECSLinear10PercentEvery3Minutes'); + public static readonly CANARY_10PERCENT_5MINUTES = deploymentConfig('CodeDeployDefault.ECSCanary10Percent5Minutes'); + public static readonly CANARY_10PERCENT_15MINUTES = 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 { + public static fromEcsDeploymentConfigName(scope: Construct, id: string, ecsDeploymentConfigName: string): IEcsDeploymentConfig { + ignore(scope); + ignore(id); return deploymentConfig(ecsDeploymentConfigName); } - private constructor() { - // nothing to do until CFN supports custom ECS deployment configurations + /** + * 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?: EcsDeploymentConfigProps) { + super(scope, id, { + physicalName: props?.deploymentConfigName, + }); + + // Construct the traffic routing configuration for the deployment group + const routingConfig = props && props.trafficRoutingConfig ? props.trafficRoutingConfig : new AllAtOnceTrafficRoutingConfig(); + + const resource = new CfnDeploymentConfig(this, 'Resource', { + deploymentConfigName: this.physicalName, + computePlatform: 'ECS', + trafficRoutingConfig: routingConfig.renderTrafficRoutingConfig(), + }); + + 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) }); } } @@ -50,3 +113,5 @@ function deploymentConfig(name: string): IEcsDeploymentConfig { deploymentConfigArn: arnForDeploymentConfig(name), }; } + +function ignore(_x: any) { return; } diff --git a/packages/@aws-cdk/aws-codedeploy/lib/index.ts b/packages/@aws-cdk/aws-codedeploy/lib/index.ts index 147f27580b874..8c9e8fa9f70d9 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/index.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/index.ts @@ -1,4 +1,5 @@ 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/deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts index 594d994942f46..ebb7aa9d8b3ed 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts @@ -1,17 +1,27 @@ +import { ArnFormat, Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { arnForDeploymentConfig } from '../utils'; +import { CfnDeploymentConfig } from '../codedeploy.generated'; +import { AllAtOnceTrafficRoutingConfig, ITrafficRoutingConfig } from '../traffic-routing-config'; +import { arnForDeploymentConfig, validateName } from '../utils'; /** * 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 { + /** @attribute */ readonly deploymentConfigName: string; + + /** @attribute */ readonly deploymentConfigArn: string; } @@ -28,16 +38,29 @@ export interface LambdaDeploymentConfigImportProps { readonly deploymentConfigName: string; } +/** + * Construction properties of {@link LambdaDeploymentConfig}. + */ +export interface LambdaDeploymentConfigProps { + /** + * The physical, human-readable name of the Deployment Configuration. + * @default - automatically generated name + */ + readonly deploymentConfigName?: string; + + /** + * The configuration that specifies how traffic is shifted from the 'blue' + * target group to the 'green' target group during a deployment. + * @default AllAtOnce + */ + readonly trafficRoutingConfig?: ITrafficRoutingConfig; +} + /** * 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 { +export class LambdaDeploymentConfig extends Resource implements ILambdaDeploymentConfig { 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'); @@ -49,19 +72,67 @@ export class LambdaDeploymentConfig { public static readonly LINEAR_10PERCENT_EVERY_3MINUTES = deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery3Minutes'); /** - * 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 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 { + ignore(scope); + ignore(id); + return deploymentConfig(lambdaDeploymentConfigName); + } + + /** + * 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 + /** + * 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?: LambdaDeploymentConfigProps) { + super(scope, id, { + physicalName: props?.deploymentConfigName, + }); + + // Construct the traffic routing configuration for the deployment group + const routingConfig = props && props.trafficRoutingConfig ? props.trafficRoutingConfig : new AllAtOnceTrafficRoutingConfig(); + + const resource = new CfnDeploymentConfig(this, 'Resource', { + deploymentConfigName: this.physicalName, + computePlatform: 'Lambda', + trafficRoutingConfig: routingConfig.renderTrafficRoutingConfig(), + }); + + 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) }); } } @@ -71,3 +142,5 @@ function deploymentConfig(name: string): ILambdaDeploymentConfig { 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..66dc1142f8a4c --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts @@ -0,0 +1,117 @@ +import { Duration } from '@aws-cdk/core'; +import { CfnDeploymentConfig } from './codedeploy.generated'; + +/** + * Interface for traffic routing configs. + */ +export interface ITrafficRoutingConfig { + /** + * Abstract method on interface for subclasses to implement to render themselves as a TrafficRoutingConfigProperty. + */ + renderTrafficRoutingConfig(): CfnDeploymentConfig.TrafficRoutingConfigProperty +} + +/** + * 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 AllAtOnceTrafficRoutingConfig implements ITrafficRoutingConfig { + constructor() {} + + /** + * Render a TrafficRoutingConfigProperty of type `AllAtOnce`. + */ + renderTrafficRoutingConfig(): CfnDeploymentConfig.TrafficRoutingConfigProperty { + return { + type: 'AllAtOnce', + }; + } +} + +/** + * Construction properties for {@link TimeBasedCanaryTrafficRoutingConfig}. + */ +export interface TimeBasedCanaryTrafficRoutingConfigProps extends BaseTrafficShiftingConfigProps {} + +/** + * Define a traffic routing config of type 'TimeBasedCanary'. + */ +export class TimeBasedCanaryTrafficRoutingConfig implements ITrafficRoutingConfig { + /** + * 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: TimeBasedCanaryTrafficRoutingConfigProps) { + this.interval = props.interval; + this.percentage = props.percentage; + } + + /** + * Render a TrafficRoutingConfigProperty of type `TimeBasedCanary`. + */ + renderTrafficRoutingConfig(): CfnDeploymentConfig.TrafficRoutingConfigProperty { + return { + type: 'TimeBasedCanary', + timeBasedCanary: { + canaryInterval: this.interval.toMinutes(), + canaryPercentage: this.percentage, + }, + }; + } +} + +/** + * Construction properties for {@link TimeBasedLinearTrafficRoutingConfig}. + */ +export interface TimeBasedLinearTrafficRoutingConfigProps extends BaseTrafficShiftingConfigProps {} + +/** + * Define a traffic routing config of type 'TimeBasedLinear'. + */ +export class TimeBasedLinearTrafficRoutingConfig implements ITrafficRoutingConfig { + /** + * 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: TimeBasedLinearTrafficRoutingConfigProps) { + this.interval = props.interval; + this.percentage = props.percentage; + } + + /** + * Render a TrafficRoutingConfigProperty of type `TimeBasedLinear`. + */ + renderTrafficRoutingConfig(): CfnDeploymentConfig.TrafficRoutingConfigProperty { + 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..5d89a4387c01e 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,7 +125,12 @@ "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", + "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.ServerDeploymentConfig.deploymentConfigArn", "docs-public-apis:@aws-cdk/aws-codedeploy.IServerDeploymentGroup.role", "docs-public-apis:@aws-cdk/aws-codedeploy.InstanceTagSet.instanceTagGroups", @@ -139,6 +145,9 @@ "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.LambdaDeploymentConfig.deploymentConfigName", + "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.LambdaDeploymentGroup", "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentGroup.role", "docs-public-apis:@aws-cdk/aws-codedeploy.LoadBalancer.generation", @@ -172,6 +181,10 @@ "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.EcsDeploymentConfig.CANARY_10PERCENT_5MINUTES", + "docs-public-apis:@aws-cdk/aws-codedeploy.EcsDeploymentConfig.CANARY_10PERCENT_15MINUTES", + "docs-public-apis:@aws-cdk/aws-codedeploy.EcsDeploymentConfig.LINEAR_10PERCENT_EVERY_1MINUTES", + "docs-public-apis:@aws-cdk/aws-codedeploy.EcsDeploymentConfig.LINEAR_10PERCENT_EVERY_3MINUTES", "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", 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..4c2aaa508a917 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.test.ts @@ -0,0 +1,141 @@ +import { Template } from '@aws-cdk/assertions'; +import * as cdk from '@aws-cdk/core'; +import * as codedeploy 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', { + trafficRoutingConfig: new codedeploy.AllAtOnceTrafficRoutingConfig(), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'ECS', + 'TrafficRoutingConfig': { + 'Type': 'AllAtOnce', + }, + }); +}); + +test('can create linear config', () => { + // WHEN + new codedeploy.EcsDeploymentConfig(stack, 'MyConfig', { + trafficRoutingConfig: new codedeploy.TimeBasedLinearTrafficRoutingConfig({ + 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', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + 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', + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + 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', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + 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', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + 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..a61976308c15b --- /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', { + trafficRoutingConfig: new codedeploy.TimeBasedLinearTrafficRoutingConfig({ + 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/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..d1b5455a6a45a --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.test.ts @@ -0,0 +1,141 @@ +import { Template } from '@aws-cdk/assertions'; +import * as cdk from '@aws-cdk/core'; +import * as codedeploy 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', { + trafficRoutingConfig: new codedeploy.AllAtOnceTrafficRoutingConfig(), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::CodeDeploy::DeploymentConfig', { + 'ComputePlatform': 'Lambda', + 'TrafficRoutingConfig': { + 'Type': 'AllAtOnce', + }, + }); +}); + +test('can create linear config', () => { + // WHEN + new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { + trafficRoutingConfig: new codedeploy.TimeBasedLinearTrafficRoutingConfig({ + 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', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + 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', + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + 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', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + 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', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + 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/integ.deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-config.ts new file mode 100644 index 0000000000000..6a115312caf7c --- /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', { + trafficRoutingConfig: new codedeploy.TimeBasedLinearTrafficRoutingConfig({ + interval: cdk.Duration.minutes(1), + percentage: 5, + }), +}); + +new integ.IntegTest(app, 'LambdaDeploymentConfigTest', { + testCases: [stack], +}); + +app.synth(); From 7b99479e5090d0da5c1433c2cc7a9d3dd4f7bafd Mon Sep 17 00:00:00 2001 From: Casey Lee Date: Tue, 20 Sep 2022 22:43:00 -0700 Subject: [PATCH 2/8] chore: deprecate codedeploy.CustomLambdaDeploymentConfig in favor of codedeploy.LambdaDeploymentConfig --- .../lib/lambda/custom-deployment-config.ts | 11 ++++++++ .../lambda/custom-deployment-config.test.ts | 15 +++++----- .../test/lambda/deployment-group.test.ts | 28 +++++++++++++++++++ 3 files changed, 47 insertions(+), 7 deletions(-) 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/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-group.test.ts b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-group.test.ts index 9fc6049e9a2c5..c73c5f8d1867c 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 @@ -629,3 +629,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', { + trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + 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), + ], + }); +}); From 381ab47fe48975b30777329f0ce996c7c9249063 Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Mon, 26 Sep 2022 10:13:31 -0700 Subject: [PATCH 3/8] Update traffic routing config to use an abstract class pattern --- .../lib/ecs/deployment-config.ts | 8 +- .../lib/lambda/deployment-config.ts | 8 +- .../lib/traffic-routing-config.ts | 100 +++++++++++++----- .../test/ecs/deployment-config.test.ts | 13 +-- .../test/ecs/integ.deployment-config.ts | 2 +- .../test/lambda/deployment-config.test.ts | 13 +-- .../test/lambda/deployment-group.test.ts | 3 +- .../test/lambda/integ.deployment-config.ts | 2 +- 8 files changed, 97 insertions(+), 52 deletions(-) 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 edec6e8c87a11..0fb40ebc3d737 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts @@ -1,7 +1,7 @@ import { ArnFormat, Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnDeploymentConfig } from '../codedeploy.generated'; -import { AllAtOnceTrafficRoutingConfig, ITrafficRoutingConfig } from '../traffic-routing-config'; +import { TrafficRouting } from '../traffic-routing-config'; import { arnForDeploymentConfig, validateName } from '../utils'; /** @@ -40,7 +40,7 @@ export interface EcsDeploymentConfigProps { * target group to the 'green' target group during a deployment. * @default AllAtOnce */ - readonly trafficRoutingConfig?: ITrafficRoutingConfig; + readonly trafficRouting?: TrafficRouting; } /** @@ -87,12 +87,12 @@ export class EcsDeploymentConfig extends Resource implements IEcsDeploymentConfi }); // Construct the traffic routing configuration for the deployment group - const routingConfig = props && props.trafficRoutingConfig ? props.trafficRoutingConfig : new AllAtOnceTrafficRoutingConfig(); + const routingConfig = props?.trafficRouting ?? TrafficRouting.allAtOnce(); const resource = new CfnDeploymentConfig(this, 'Resource', { deploymentConfigName: this.physicalName, computePlatform: 'ECS', - trafficRoutingConfig: routingConfig.renderTrafficRoutingConfig(), + trafficRoutingConfig: routingConfig.bind(this).config, }); this.deploymentConfigName = this.getResourceNameAttribute(resource.ref); 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 ebb7aa9d8b3ed..82423df05fe1c 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts @@ -1,7 +1,7 @@ import { ArnFormat, Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnDeploymentConfig } from '../codedeploy.generated'; -import { AllAtOnceTrafficRoutingConfig, ITrafficRoutingConfig } from '../traffic-routing-config'; +import { TrafficRouting } from '../traffic-routing-config'; import { arnForDeploymentConfig, validateName } from '../utils'; /** @@ -53,7 +53,7 @@ export interface LambdaDeploymentConfigProps { * target group to the 'green' target group during a deployment. * @default AllAtOnce */ - readonly trafficRoutingConfig?: ITrafficRoutingConfig; + readonly trafficRouting?: TrafficRouting; } /** @@ -116,12 +116,12 @@ export class LambdaDeploymentConfig extends Resource implements ILambdaDeploymen }); // Construct the traffic routing configuration for the deployment group - const routingConfig = props && props.trafficRoutingConfig ? props.trafficRoutingConfig : new AllAtOnceTrafficRoutingConfig(); + const routingConfig = props?.trafficRouting ?? TrafficRouting.allAtOnce(); const resource = new CfnDeploymentConfig(this, 'Resource', { deploymentConfigName: this.physicalName, computePlatform: 'Lambda', - trafficRoutingConfig: routingConfig.renderTrafficRoutingConfig(), + trafficRoutingConfig: routingConfig.bind(this).config, }); this.deploymentConfigName = this.getResourceNameAttribute(resource.ref); diff --git a/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts index 66dc1142f8a4c..a15b56598c92d 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts @@ -1,14 +1,46 @@ import { Duration } from '@aws-cdk/core'; +import { Construct } from 'constructs'; import { CfnDeploymentConfig } from './codedeploy.generated'; /** - * Interface for traffic routing configs. + * Represents the structure to pass into the underlying CfnDeploymentConfig class. */ -export interface ITrafficRoutingConfig { +export interface TrafficRoutingConfig { /** - * Abstract method on interface for subclasses to implement to render themselves as a TrafficRoutingConfigProperty. + * represents the underlying traffic routing config structure */ - renderTrafficRoutingConfig(): CfnDeploymentConfig.TrafficRoutingConfigProperty + readonly config: CfnDeploymentConfig.TrafficRoutingConfigProperty; +} + +/** + * Represents how traffic is shifted during a CodeDeploy deployment. + */ +export abstract class TrafficRouting { + /** + * Shifts 100% of traffic in a single shift. + */ + public static allAtOnce(): AllAtOnceTrafficRouting { + 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; } /** @@ -29,28 +61,32 @@ export interface BaseTrafficShiftingConfigProps { /** * Define a traffic routing config of type 'AllAtOnce'. */ -export class AllAtOnceTrafficRoutingConfig implements ITrafficRoutingConfig { - constructor() {} +export class AllAtOnceTrafficRouting extends TrafficRouting { + constructor() { + super(); + } /** - * Render a TrafficRoutingConfigProperty of type `AllAtOnce`. + * Return a TrafficRoutingConfig of type `AllAtOnce`. */ - renderTrafficRoutingConfig(): CfnDeploymentConfig.TrafficRoutingConfigProperty { + bind(_scope: Construct): TrafficRoutingConfig { return { - type: 'AllAtOnce', + config: { + type: 'AllAtOnce', + }, }; } } /** - * Construction properties for {@link TimeBasedCanaryTrafficRoutingConfig}. + * Construction properties for {@link TimeBasedCanaryTrafficRouting}. */ -export interface TimeBasedCanaryTrafficRoutingConfigProps extends BaseTrafficShiftingConfigProps {} +export interface TimeBasedCanaryTrafficRoutingProps extends BaseTrafficShiftingConfigProps {} /** * Define a traffic routing config of type 'TimeBasedCanary'. */ -export class TimeBasedCanaryTrafficRoutingConfig implements ITrafficRoutingConfig { +export class TimeBasedCanaryTrafficRouting extends TrafficRouting { /** * The amount of time between additional traffic shifts. */ @@ -60,34 +96,37 @@ export class TimeBasedCanaryTrafficRoutingConfig implements ITrafficRoutingConfi */ readonly percentage: number; - constructor(props: TimeBasedCanaryTrafficRoutingConfigProps) { + constructor(props: TimeBasedCanaryTrafficRoutingProps) { + super(); this.interval = props.interval; this.percentage = props.percentage; } /** - * Render a TrafficRoutingConfigProperty of type `TimeBasedCanary`. + * Return a TrafficRoutingConfig of type `TimeBasedCanary`. */ - renderTrafficRoutingConfig(): CfnDeploymentConfig.TrafficRoutingConfigProperty { + bind(_scope: Construct): TrafficRoutingConfig { return { - type: 'TimeBasedCanary', - timeBasedCanary: { - canaryInterval: this.interval.toMinutes(), - canaryPercentage: this.percentage, + config: { + type: 'TimeBasedCanary', + timeBasedCanary: { + canaryInterval: this.interval.toMinutes(), + canaryPercentage: this.percentage, + }, }, }; } } /** - * Construction properties for {@link TimeBasedLinearTrafficRoutingConfig}. + * Construction properties for {@link TimeBasedLinearTrafficRouting}. */ -export interface TimeBasedLinearTrafficRoutingConfigProps extends BaseTrafficShiftingConfigProps {} +export interface TimeBasedLinearTrafficRoutingProps extends BaseTrafficShiftingConfigProps {} /** * Define a traffic routing config of type 'TimeBasedLinear'. */ -export class TimeBasedLinearTrafficRoutingConfig implements ITrafficRoutingConfig { +export class TimeBasedLinearTrafficRouting extends TrafficRouting { /** * The amount of time between additional traffic shifts. */ @@ -97,20 +136,23 @@ export class TimeBasedLinearTrafficRoutingConfig implements ITrafficRoutingConfi */ readonly percentage: number; - constructor(props: TimeBasedLinearTrafficRoutingConfigProps) { + constructor(props: TimeBasedLinearTrafficRoutingProps) { + super(); this.interval = props.interval; this.percentage = props.percentage; } /** - * Render a TrafficRoutingConfigProperty of type `TimeBasedLinear`. + * Return a TrafficRoutingConfig of type `TimeBasedLinear`. */ - renderTrafficRoutingConfig(): CfnDeploymentConfig.TrafficRoutingConfigProperty { + bind(_scope: Construct): TrafficRoutingConfig { return { - type: 'TimeBasedLinear', - timeBasedLinear: { - linearInterval: this.interval.toMinutes(), - linearPercentage: this.percentage, + config: { + type: 'TimeBasedLinear', + timeBasedLinear: { + linearInterval: this.interval.toMinutes(), + linearPercentage: this.percentage, + }, }, }; } 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 index 4c2aaa508a917..3dc660e10bb08 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/deployment-config.test.ts @@ -1,6 +1,7 @@ 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 */ @@ -26,7 +27,7 @@ test('can create default config', () => { test('can create all-at-once config', () => { // WHEN new codedeploy.EcsDeploymentConfig(stack, 'MyConfig', { - trafficRoutingConfig: new codedeploy.AllAtOnceTrafficRoutingConfig(), + trafficRouting: TrafficRouting.allAtOnce(), }); // THEN @@ -41,7 +42,7 @@ test('can create all-at-once config', () => { test('can create linear config', () => { // WHEN new codedeploy.EcsDeploymentConfig(stack, 'MyConfig', { - trafficRoutingConfig: new codedeploy.TimeBasedLinearTrafficRoutingConfig({ + trafficRouting: TrafficRouting.timeBasedLinear({ interval: cdk.Duration.minutes(1), percentage: 5, }), @@ -63,7 +64,7 @@ test('can create linear config', () => { test('can create canary config', () => { // WHEN new codedeploy.EcsDeploymentConfig(stack, 'MyConfig', { - trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + trafficRouting: TrafficRouting.timeBasedCanary({ interval: cdk.Duration.minutes(1), percentage: 5, }), @@ -86,7 +87,7 @@ test('can create a config with a specific name', () => { // WHEN new codedeploy.EcsDeploymentConfig(stack, 'MyConfig', { deploymentConfigName: 'MyCanaryConfig', - trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + trafficRouting: TrafficRouting.timeBasedCanary({ interval: cdk.Duration.minutes(1), percentage: 5, }), @@ -116,7 +117,7 @@ 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', { - trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + trafficRouting: TrafficRouting.timeBasedCanary({ interval: cdk.Duration.minutes(1), percentage: 5, }), @@ -130,7 +131,7 @@ test('fail with unallowed characters in name', () => { const app = new cdk.App(); const stackWithApp = new cdk.Stack(app); new codedeploy.EcsDeploymentConfig(stackWithApp, 'MyConfig', { - trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + trafficRouting: TrafficRouting.timeBasedCanary({ interval: cdk.Duration.minutes(1), percentage: 5, }), 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 index a61976308c15b..651ed7efa0962 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/ecs/integ.deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/ecs/integ.deployment-config.ts @@ -6,7 +6,7 @@ const app = new cdk.App(); const stack = new cdk.Stack(app, 'aws-cdk-codedeploy-ecs-config'); new codedeploy.EcsDeploymentConfig(stack, 'LinearConfig', { - trafficRoutingConfig: new codedeploy.TimeBasedLinearTrafficRoutingConfig({ + trafficRouting: codedeploy.TrafficRouting.timeBasedLinear({ interval: cdk.Duration.minutes(1), percentage: 5, }), 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 index d1b5455a6a45a..b50e897bf366d 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.test.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/deployment-config.test.ts @@ -1,6 +1,7 @@ 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 */ @@ -26,7 +27,7 @@ test('can create default config', () => { test('can create all-at-once config', () => { // WHEN new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { - trafficRoutingConfig: new codedeploy.AllAtOnceTrafficRoutingConfig(), + trafficRouting: TrafficRouting.allAtOnce(), }); // THEN @@ -41,7 +42,7 @@ test('can create all-at-once config', () => { test('can create linear config', () => { // WHEN new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { - trafficRoutingConfig: new codedeploy.TimeBasedLinearTrafficRoutingConfig({ + trafficRouting: TrafficRouting.timeBasedLinear({ interval: cdk.Duration.minutes(1), percentage: 5, }), @@ -63,7 +64,7 @@ test('can create linear config', () => { test('can create canary config', () => { // WHEN new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { - trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + trafficRouting: TrafficRouting.timeBasedCanary({ interval: cdk.Duration.minutes(1), percentage: 5, }), @@ -86,7 +87,7 @@ test('can create a config with a specific name', () => { // WHEN new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { deploymentConfigName: 'MyCanaryConfig', - trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + trafficRouting: TrafficRouting.timeBasedCanary({ interval: cdk.Duration.minutes(1), percentage: 5, }), @@ -116,7 +117,7 @@ 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', { - trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + trafficRouting: TrafficRouting.timeBasedCanary({ interval: cdk.Duration.minutes(1), percentage: 5, }), @@ -130,7 +131,7 @@ test('fail with unallowed characters in name', () => { const app = new cdk.App(); const stackWithApp = new cdk.Stack(app); new codedeploy.LambdaDeploymentConfig(stackWithApp, 'MyConfig', { - trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + trafficRouting: TrafficRouting.timeBasedCanary({ interval: cdk.Duration.minutes(1), percentage: 5, }), 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 c73c5f8d1867c..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, { @@ -636,7 +637,7 @@ test('dependency on the config exists to ensure ordering', () => { const application = new codedeploy.LambdaApplication(stack, 'MyApp'); const alias = mockAlias(stack); const config = new codedeploy.LambdaDeploymentConfig(stack, 'MyConfig', { - trafficRoutingConfig: new codedeploy.TimeBasedCanaryTrafficRoutingConfig({ + trafficRouting: TrafficRouting.timeBasedCanary({ interval: cdk.Duration.minutes(1), percentage: 5, }), 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 index 6a115312caf7c..de578cb7eb648 100644 --- a/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/test/lambda/integ.deployment-config.ts @@ -6,7 +6,7 @@ const app = new cdk.App(); const stack = new cdk.Stack(app, 'aws-cdk-codedeploy-lambda-config'); new codedeploy.LambdaDeploymentConfig(stack, 'LinearConfig', { - trafficRoutingConfig: new codedeploy.TimeBasedLinearTrafficRoutingConfig({ + trafficRouting: codedeploy.TrafficRouting.timeBasedLinear({ interval: cdk.Duration.minutes(1), percentage: 5, }), From a91098cadfc4f307eab2fa49e470d11dfd94baa0 Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Mon, 26 Sep 2022 10:43:21 -0700 Subject: [PATCH 4/8] Add docstrings --- packages/@aws-cdk/aws-codedeploy/README.md | 24 +++++++++++++++++-- .../lib/ecs/deployment-config.ts | 15 ++++++++++-- .../lib/lambda/deployment-config.ts | 19 +++++++++++++-- packages/@aws-cdk/aws-codedeploy/package.json | 21 ---------------- 4 files changed, 52 insertions(+), 27 deletions(-) diff --git a/packages/@aws-cdk/aws-codedeploy/README.md b/packages/@aws-cdk/aws-codedeploy/README.md index 852874c404915..ec5449698294a 100644 --- a/packages/@aws-cdk/aws-codedeploy/README.md +++ b/packages/@aws-cdk/aws-codedeploy/README.md @@ -295,7 +295,21 @@ const deploymentGroup = codedeploy.LambdaDeploymentGroup.fromLambdaDeploymentGro ## Lambda Deployment Configurations -CodeDeploy for Lambda comes with built-in configurations for traffic shifting. +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. @@ -361,7 +375,13 @@ const application = codedeploy.EcsApplication.fromEcsApplicationName( ## ECS Deployment Configurations -CodeDeploy for ECS comes with built-in configurations for traffic shifting. +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. 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 0fb40ebc3d737..942353a6538c8 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts @@ -18,10 +18,16 @@ import { arnForDeploymentConfig, validateName } from '../utils'; * (for example, `EcsDeploymentConfig.AllAtOnce`). */ export interface IEcsDeploymentConfig { - /** @attribute */ + /** + * The physical, human-readable name of the Deployment Configuration. + * @attribute + */ readonly deploymentConfigName: string; - /** @attribute */ + /** + * The ARN of the Deployment Configuration. + * @attribute + */ readonly deploymentConfigArn: string; } @@ -49,10 +55,15 @@ export interface EcsDeploymentConfigProps { * @resource AWS::CodeDeploy::DeploymentConfig */ export class EcsDeploymentConfig extends Resource implements IEcsDeploymentConfig { + /** CodeDeploy predefined deployment configuration that shifts all traffic to the updated ECS task set at once. */ public static readonly ALL_AT_ONCE = 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 = 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 = 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 = 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 = deploymentConfig('CodeDeployDefault.ECSCanary10Percent15Minutes'); /** 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 82423df05fe1c..174d75d9117ba 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts @@ -18,10 +18,16 @@ import { arnForDeploymentConfig, validateName } from '../utils'; * (`LambdaDeploymentConfig.AllAtOnce`, `LambdaDeploymentConfig.Canary10Percent30Minutes`, etc.). */ export interface ILambdaDeploymentConfig { - /** @attribute */ + /** + * The physical, human-readable name of the Deployment Configuration. + * @attribute + */ readonly deploymentConfigName: string; - /** @attribute */ + /** + * The ARN of the Deployment Configuration. + * @attribute + */ readonly deploymentConfigArn: string; } @@ -61,14 +67,23 @@ export interface LambdaDeploymentConfigProps { * @resource AWS::CodeDeploy::DeploymentConfig */ export class LambdaDeploymentConfig extends Resource implements ILambdaDeploymentConfig { + /** CodeDeploy predefined deployment configuration that shifts all traffic to the updated Lambda function at once. */ public static readonly ALL_AT_ONCE = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery3Minutes'); /** diff --git a/packages/@aws-cdk/aws-codedeploy/package.json b/packages/@aws-cdk/aws-codedeploy/package.json index 5d89a4387c01e..652db365074b5 100644 --- a/packages/@aws-cdk/aws-codedeploy/package.json +++ b/packages/@aws-cdk/aws-codedeploy/package.json @@ -136,18 +136,6 @@ "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.LambdaDeploymentConfig.deploymentConfigName", - "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.LambdaDeploymentGroup", "docs-public-apis:@aws-cdk/aws-codedeploy.LambdaDeploymentGroup.role", "docs-public-apis:@aws-cdk/aws-codedeploy.LoadBalancer.generation", @@ -167,8 +155,6 @@ "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", @@ -180,15 +166,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.EcsDeploymentConfig.CANARY_10PERCENT_5MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.EcsDeploymentConfig.CANARY_10PERCENT_15MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.EcsDeploymentConfig.LINEAR_10PERCENT_EVERY_1MINUTES", - "docs-public-apis:@aws-cdk/aws-codedeploy.EcsDeploymentConfig.LINEAR_10PERCENT_EVERY_3MINUTES", "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" ] }, From a0405ce1822f8bcb77c93fea9b4c15fa3fe7f301 Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Tue, 27 Sep 2022 09:09:16 -0700 Subject: [PATCH 5/8] Fold in PR comments --- .../lib/ecs/deployment-config.ts | 16 +++- .../lib/lambda/deployment-config.ts | 16 +++- .../lib/traffic-routing-config.ts | 73 ++++++++++++++----- 3 files changed, 78 insertions(+), 27 deletions(-) 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 942353a6538c8..c581a395d57e2 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/ecs/deployment-config.ts @@ -1,4 +1,4 @@ -import { ArnFormat, Resource } from '@aws-cdk/core'; +import { ArnFormat, Resource, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnDeploymentConfig } from '../codedeploy.generated'; import { TrafficRouting } from '../traffic-routing-config'; @@ -75,9 +75,17 @@ export class EcsDeploymentConfig extends Resource implements IEcsDeploymentConfi * @returns a Construct representing a reference to an existing custom Deployment Configuration */ public static fromEcsDeploymentConfigName(scope: Construct, id: string, ecsDeploymentConfigName: string): IEcsDeploymentConfig { - ignore(scope); ignore(id); - return deploymentConfig(ecsDeploymentConfigName); + const arn = Stack.of(scope).formatArn({ + service: 'codedeploy', + resource: 'deploymentconfig', + resourceName: ecsDeploymentConfigName, + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + }); + return { + deploymentConfigName: ecsDeploymentConfigName, + deploymentConfigArn: arn, + }; } /** @@ -103,7 +111,7 @@ export class EcsDeploymentConfig extends Resource implements IEcsDeploymentConfi const resource = new CfnDeploymentConfig(this, 'Resource', { deploymentConfigName: this.physicalName, computePlatform: 'ECS', - trafficRoutingConfig: routingConfig.bind(this).config, + trafficRoutingConfig: routingConfig.bind(this), }); this.deploymentConfigName = this.getResourceNameAttribute(resource.ref); 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 174d75d9117ba..69c0668bd5b54 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/lambda/deployment-config.ts @@ -1,4 +1,4 @@ -import { ArnFormat, Resource } from '@aws-cdk/core'; +import { ArnFormat, Resource, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnDeploymentConfig } from '../codedeploy.generated'; import { TrafficRouting } from '../traffic-routing-config'; @@ -95,9 +95,17 @@ export class LambdaDeploymentConfig extends Resource implements ILambdaDeploymen * @returns a Construct representing a reference to an existing Lambda Deployment Configuration */ public static fromLambdaDeploymentConfigName(scope: Construct, id: string, lambdaDeploymentConfigName: string): ILambdaDeploymentConfig { - ignore(scope); ignore(id); - return deploymentConfig(lambdaDeploymentConfigName); + const arn = Stack.of(scope).formatArn({ + service: 'codedeploy', + resource: 'deploymentconfig', + resourceName: lambdaDeploymentConfigName, + arnFormat: ArnFormat.COLON_RESOURCE_NAME, + }); + return { + deploymentConfigName: lambdaDeploymentConfigName, + deploymentConfigArn: arn, + }; } /** @@ -136,7 +144,7 @@ export class LambdaDeploymentConfig extends Resource implements ILambdaDeploymen const resource = new CfnDeploymentConfig(this, 'Resource', { deploymentConfigName: this.physicalName, computePlatform: 'Lambda', - trafficRoutingConfig: routingConfig.bind(this).config, + trafficRoutingConfig: routingConfig.bind(this), }); this.deploymentConfigName = this.getResourceNameAttribute(resource.ref); diff --git a/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts index a15b56598c92d..2aa86c8e1f40f 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/traffic-routing-config.ts @@ -1,15 +1,56 @@ import { Duration } from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { CfnDeploymentConfig } from './codedeploy.generated'; /** * Represents the structure to pass into the underlying CfnDeploymentConfig class. */ export interface TrafficRoutingConfig { /** - * represents the underlying traffic routing config structure + * The type of traffic shifting ( `TimeBasedCanary` or `TimeBasedLinear` ) used by a deployment configuration. */ - readonly config: CfnDeploymentConfig.TrafficRoutingConfigProperty; + 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; } /** @@ -19,7 +60,7 @@ export abstract class TrafficRouting { /** * Shifts 100% of traffic in a single shift. */ - public static allAtOnce(): AllAtOnceTrafficRouting { + public static allAtOnce(): TrafficRouting { return new AllAtOnceTrafficRouting(); } @@ -71,9 +112,7 @@ export class AllAtOnceTrafficRouting extends TrafficRouting { */ bind(_scope: Construct): TrafficRoutingConfig { return { - config: { - type: 'AllAtOnce', - }, + type: 'AllAtOnce', }; } } @@ -107,12 +146,10 @@ export class TimeBasedCanaryTrafficRouting extends TrafficRouting { */ bind(_scope: Construct): TrafficRoutingConfig { return { - config: { - type: 'TimeBasedCanary', - timeBasedCanary: { - canaryInterval: this.interval.toMinutes(), - canaryPercentage: this.percentage, - }, + type: 'TimeBasedCanary', + timeBasedCanary: { + canaryInterval: this.interval.toMinutes(), + canaryPercentage: this.percentage, }, }; } @@ -147,12 +184,10 @@ export class TimeBasedLinearTrafficRouting extends TrafficRouting { */ bind(_scope: Construct): TrafficRoutingConfig { return { - config: { - type: 'TimeBasedLinear', - timeBasedLinear: { - linearInterval: this.interval.toMinutes(), - linearPercentage: this.percentage, - }, + type: 'TimeBasedLinear', + timeBasedLinear: { + linearInterval: this.interval.toMinutes(), + linearPercentage: this.percentage, }, }; } From af4252a3b52363b7f4f2b3dd16c9e0c8a2bf47d2 Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Wed, 28 Sep 2022 11:03:29 -0700 Subject: [PATCH 6/8] Move most deployment config logic into base class --- .../lib/base-deployment-config.ts | 159 ++++++++++++++++++ .../lib/ecs/deployment-config.ts | 93 ++-------- packages/@aws-cdk/aws-codedeploy/lib/index.ts | 1 + .../lib/lambda/deployment-config.ts | 101 ++--------- .../lib/server/deployment-config.ts | 83 ++++----- packages/@aws-cdk/aws-codedeploy/package.json | 7 - 6 files changed, 220 insertions(+), 224 deletions(-) create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts 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..8a017a25f3eb3 --- /dev/null +++ b/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts @@ -0,0 +1,159 @@ +import { ArnFormat, Resource, Stack } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { CfnDeploymentConfig } from './codedeploy.generated'; +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; +} + +/** + * 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, additionalProps?: any) { + 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'); + } + + const resource = new CfnDeploymentConfig(this, 'Resource', { + deploymentConfigName: this.physicalName, + computePlatform: props?.computePlatform, + trafficRoutingConfig: props?.trafficRouting?.bind(this), + ...additionalProps, + }); + + 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 c581a395d57e2..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,8 +1,6 @@ -import { ArnFormat, Resource, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { CfnDeploymentConfig } from '../codedeploy.generated'; +import { BaseDeploymentConfig, BaseDeploymentConfigOptions, ComputePlatform, IBaseDeploymentConfig } from '../base-deployment-config'; import { TrafficRouting } from '../traffic-routing-config'; -import { arnForDeploymentConfig, validateName } from '../utils'; /** * The Deployment Configuration of an ECS Deployment Group. @@ -17,30 +15,13 @@ import { arnForDeploymentConfig, validateName } from '../utils'; * The default, pre-defined Configurations are available as constants on the {@link EcsDeploymentConfig} class * (for example, `EcsDeploymentConfig.AllAtOnce`). */ -export interface IEcsDeploymentConfig { - /** - * The physical, human-readable name of the Deployment Configuration. - * @attribute - */ - readonly deploymentConfigName: string; - - /** - * The ARN of the Deployment Configuration. - * @attribute - */ - readonly deploymentConfigArn: string; +export interface IEcsDeploymentConfig extends IBaseDeploymentConfig { } /** * Construction properties of {@link EcsDeploymentConfig}. */ -export interface EcsDeploymentConfigProps { - /** - * The physical, human-readable name of the Deployment Configuration. - * @default - automatically generated name - */ - readonly deploymentConfigName?: string; - +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. @@ -54,17 +35,17 @@ export interface EcsDeploymentConfigProps { * * @resource AWS::CodeDeploy::DeploymentConfig */ -export class EcsDeploymentConfig extends Resource implements IEcsDeploymentConfig { +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 = deploymentConfig('CodeDeployDefault.ECSAllAtOnce'); + 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 = deploymentConfig('CodeDeployDefault.ECSLinear10PercentEvery1Minutes'); + 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 = deploymentConfig('CodeDeployDefault.ECSLinear10PercentEvery3Minutes'); + 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 = deploymentConfig('CodeDeployDefault.ECSCanary10Percent5Minutes'); + 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 = deploymentConfig('CodeDeployDefault.ECSCanary10Percent15Minutes'); + public static readonly CANARY_10PERCENT_15MINUTES = EcsDeploymentConfig.deploymentConfig('CodeDeployDefault.ECSCanary10Percent15Minutes'); /** * Import a custom Deployment Configuration for an ECS Deployment Group defined outside the CDK. @@ -75,62 +56,14 @@ export class EcsDeploymentConfig extends Resource implements IEcsDeploymentConfi * @returns a Construct representing a reference to an existing custom Deployment Configuration */ public static fromEcsDeploymentConfigName(scope: Construct, id: string, ecsDeploymentConfigName: string): IEcsDeploymentConfig { - ignore(id); - const arn = Stack.of(scope).formatArn({ - service: 'codedeploy', - resource: 'deploymentconfig', - resourceName: ecsDeploymentConfigName, - arnFormat: ArnFormat.COLON_RESOURCE_NAME, - }); - return { - deploymentConfigName: ecsDeploymentConfigName, - deploymentConfigArn: arn, - }; + return this.fromDeploymentConfigName(scope, id, ecsDeploymentConfigName); } - /** - * 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?: EcsDeploymentConfigProps) { super(scope, id, { - physicalName: props?.deploymentConfigName, - }); - - // Construct the traffic routing configuration for the deployment group - const routingConfig = props?.trafficRouting ?? TrafficRouting.allAtOnce(); - - const resource = new CfnDeploymentConfig(this, 'Resource', { - deploymentConfigName: this.physicalName, - computePlatform: 'ECS', - trafficRoutingConfig: routingConfig.bind(this), + ...props, + computePlatform: ComputePlatform.ECS, + trafficRouting: props?.trafficRouting ?? TrafficRouting.allAtOnce(), }); - - 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 deploymentConfig(name: string): IEcsDeploymentConfig { - return { - deploymentConfigName: name, - deploymentConfigArn: arnForDeploymentConfig(name), - }; -} - -function ignore(_x: any) { return; } diff --git a/packages/@aws-cdk/aws-codedeploy/lib/index.ts b/packages/@aws-cdk/aws-codedeploy/lib/index.ts index 8c9e8fa9f70d9..54e3ff0676f64 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/index.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/index.ts @@ -1,3 +1,4 @@ +export * from './base-deployment-config'; export * from './rollback-config'; export * from './traffic-routing-config'; export * from './ecs'; 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 69c0668bd5b54..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,8 +1,6 @@ -import { ArnFormat, Resource, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; -import { CfnDeploymentConfig } from '../codedeploy.generated'; +import { BaseDeploymentConfig, BaseDeploymentConfigOptions, ComputePlatform, IBaseDeploymentConfig } from '../base-deployment-config'; import { TrafficRouting } from '../traffic-routing-config'; -import { arnForDeploymentConfig, validateName } from '../utils'; /** * The Deployment Configuration of a Lambda Deployment Group. @@ -17,18 +15,7 @@ import { arnForDeploymentConfig, validateName } from '../utils'; * The default, pre-defined Configurations are available as constants on the {@link LambdaDeploymentConfig} class * (`LambdaDeploymentConfig.AllAtOnce`, `LambdaDeploymentConfig.Canary10Percent30Minutes`, etc.). */ -export interface ILambdaDeploymentConfig { - /** - * The physical, human-readable name of the Deployment Configuration. - * @attribute - */ - readonly deploymentConfigName: string; - - /** - * The ARN of the Deployment Configuration. - * @attribute - */ - readonly deploymentConfigArn: string; +export interface ILambdaDeploymentConfig extends IBaseDeploymentConfig { } /** @@ -47,13 +34,7 @@ export interface LambdaDeploymentConfigImportProps { /** * Construction properties of {@link LambdaDeploymentConfig}. */ -export interface LambdaDeploymentConfigProps { - /** - * The physical, human-readable name of the Deployment Configuration. - * @default - automatically generated name - */ - readonly deploymentConfigName?: string; - +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. @@ -66,25 +47,25 @@ export interface LambdaDeploymentConfigProps { * A custom Deployment Configuration for a Lambda Deployment Group. * @resource AWS::CodeDeploy::DeploymentConfig */ -export class LambdaDeploymentConfig extends Resource implements ILambdaDeploymentConfig { +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 = deploymentConfig('CodeDeployDefault.LambdaAllAtOnce'); + 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 = deploymentConfig('CodeDeployDefault.LambdaCanary10Percent30Minutes'); + 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 = deploymentConfig('CodeDeployDefault.LambdaCanary10Percent5Minutes'); + 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 = deploymentConfig('CodeDeployDefault.LambdaCanary10Percent10Minutes'); + 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 = deploymentConfig('CodeDeployDefault.LambdaCanary10Percent15Minutes'); + 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 = deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery10Minutes'); + 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 = deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery1Minute'); + 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 = deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery2Minutes'); + 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 = deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery3Minutes'); + public static readonly LINEAR_10PERCENT_EVERY_3MINUTES = LambdaDeploymentConfig.deploymentConfig('CodeDeployDefault.LambdaLinear10PercentEvery3Minutes'); /** * Import a Deployment Configuration for a Lambda Deployment Group defined outside the CDK. @@ -95,17 +76,7 @@ export class LambdaDeploymentConfig extends Resource implements ILambdaDeploymen * @returns a Construct representing a reference to an existing Lambda Deployment Configuration */ public static fromLambdaDeploymentConfigName(scope: Construct, id: string, lambdaDeploymentConfigName: string): ILambdaDeploymentConfig { - ignore(id); - const arn = Stack.of(scope).formatArn({ - service: 'codedeploy', - resource: 'deploymentconfig', - resourceName: lambdaDeploymentConfigName, - arnFormat: ArnFormat.COLON_RESOURCE_NAME, - }); - return { - deploymentConfigName: lambdaDeploymentConfigName, - deploymentConfigArn: arn, - }; + return this.fromDeploymentConfigName(scope, id, lambdaDeploymentConfigName); } /** @@ -121,49 +92,11 @@ export class LambdaDeploymentConfig extends Resource implements ILambdaDeploymen return this.fromLambdaDeploymentConfigName(_scope, _id, props.deploymentConfigName); } - /** - * 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?: LambdaDeploymentConfigProps) { super(scope, id, { - physicalName: props?.deploymentConfigName, - }); - - // Construct the traffic routing configuration for the deployment group - const routingConfig = props?.trafficRouting ?? TrafficRouting.allAtOnce(); - - const resource = new CfnDeploymentConfig(this, 'Resource', { - deploymentConfigName: this.physicalName, - computePlatform: 'Lambda', - trafficRoutingConfig: routingConfig.bind(this), + ...props, + computePlatform: ComputePlatform.LAMBDA, + trafficRouting: props?.trafficRouting ?? TrafficRouting.allAtOnce(), }); - - 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 deploymentConfig(name: string): ILambdaDeploymentConfig { - return { - deploymentConfigName: name, - deploymentConfigArn: arnForDeploymentConfig(name), - }; -} - -function ignore(_x: any) { return; } 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..0c5f67a528109 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 { BaseDeploymentConfig, BaseDeploymentConfigOptions, IBaseDeploymentConfig } from '../base-deployment-config'; import { CfnDeploymentConfig } from '../codedeploy.generated'; -import { arnForDeploymentConfig, validateName } from '../utils'; /** * The Deployment Configuration of an EC2/on-premise Deployment Group. @@ -10,16 +9,7 @@ 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; +export interface IServerDeploymentConfig extends IBaseDeploymentConfig { } /** @@ -60,14 +50,7 @@ export class MinimumHealthyHosts { /** * 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 +62,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 +95,16 @@ 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, + { + minimumHealthyHosts: props.minimumHealthyHosts._json, + }); } } - -function deploymentConfig(name: string): IServerDeploymentConfig { - return { - deploymentConfigName: name, - deploymentConfigArn: arnForDeploymentConfig(name), - }; -} - -function ignore(_x: any) { return; } diff --git a/packages/@aws-cdk/aws-codedeploy/package.json b/packages/@aws-cdk/aws-codedeploy/package.json index 652db365074b5..4cb2301eb5303 100644 --- a/packages/@aws-cdk/aws-codedeploy/package.json +++ b/packages/@aws-cdk/aws-codedeploy/package.json @@ -131,7 +131,6 @@ "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.ServerDeploymentConfig.deploymentConfigArn", "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", @@ -142,11 +141,7 @@ "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", @@ -157,8 +152,6 @@ "docs-public-apis:@aws-cdk/aws-codedeploy.ILambdaApplication.applicationName", "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", From 313ecd3e739ac393ea660a392c34d69e42ce9cbf Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Wed, 28 Sep 2022 13:00:24 -0700 Subject: [PATCH 7/8] Move healthy hosts config to base class --- .../lib/base-deployment-config.ts | 18 ++++++-- .../aws-codedeploy/lib/host-health-config.ts | 36 +++++++++++++++ packages/@aws-cdk/aws-codedeploy/lib/index.ts | 1 + .../lib/server/deployment-config.ts | 45 +------------------ 4 files changed, 54 insertions(+), 46 deletions(-) create mode 100644 packages/@aws-cdk/aws-codedeploy/lib/host-health-config.ts diff --git a/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts index 8a017a25f3eb3..0f66ebbd8fe98 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts @@ -1,6 +1,7 @@ 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'; @@ -71,6 +72,12 @@ export interface BaseDeploymentConfigProps extends BaseDeploymentConfigOptions { * @default None */ readonly trafficRouting?: TrafficRouting; + + /** + * Minimum number of healthy hosts. + * @default None + */ + readonly minimumHealthyHosts?: MinimumHealthyHosts; } /** @@ -127,21 +134,26 @@ export abstract class BaseDeploymentConfig extends Resource implements IBaseDepl */ public readonly deploymentConfigArn: string; - public constructor(scope: Construct, id: string, props?: BaseDeploymentConfigProps, additionalProps?: any) { + 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)) { + 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), - ...additionalProps, + minimumHealthyHosts: props?.minimumHealthyHosts?._json, }); this.deploymentConfigName = this.getResourceNameAttribute(resource.ref); 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 54e3ff0676f64..47dd10cc39190 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/index.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/index.ts @@ -1,4 +1,5 @@ export * from './base-deployment-config'; +export * from './host-health-config'; export * from './rollback-config'; export * from './traffic-routing-config'; export * from './ecs'; 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 0c5f67a528109..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,6 +1,6 @@ import { Construct } from 'constructs'; import { BaseDeploymentConfig, BaseDeploymentConfigOptions, IBaseDeploymentConfig } from '../base-deployment-config'; -import { CfnDeploymentConfig } from '../codedeploy.generated'; +import { MinimumHealthyHosts } from '../host-health-config'; /** * The Deployment Configuration of an EC2/on-premise Deployment Group. @@ -12,41 +12,6 @@ import { CfnDeploymentConfig } from '../codedeploy.generated'; export interface IServerDeploymentConfig extends IBaseDeploymentConfig { } -/** - * 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; - } -} - /** * Construction properties of {@link ServerDeploymentConfig}. */ @@ -99,12 +64,6 @@ export class ServerDeploymentConfig extends BaseDeploymentConfig implements ISer } constructor(scope: Construct, id: string, props: ServerDeploymentConfigProps) { - super( - scope, - id, - props, - { - minimumHealthyHosts: props.minimumHealthyHosts._json, - }); + super(scope, id, props); } } From 62a24966024c0692d77c16e57fd40c5c884794c9 Mon Sep 17 00:00:00 2001 From: Clare Liguori Date: Thu, 29 Sep 2022 06:50:27 -0700 Subject: [PATCH 8/8] Fix comparison --- packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts b/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts index 0f66ebbd8fe98..0cd04ae3628c5 100644 --- a/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts +++ b/packages/@aws-cdk/aws-codedeploy/lib/base-deployment-config.ts @@ -145,7 +145,7 @@ export abstract class BaseDeploymentConfig extends Resource implements IBaseDepl } // Minimum healthy hosts is only applicable to Server-based deployment configs - if (props?.minimumHealthyHosts && props?.computePlatform && props?.computePlatform != ComputePlatform.SERVER) { + 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'); }