From 1800eabcd55b88987a82f7f1865d0b98138ff512 Mon Sep 17 00:00:00 2001 From: Matthew Hawkins Date: Thu, 14 Mar 2024 18:16:51 -0700 Subject: [PATCH 1/5] feat(appconfig): Constrain environments to a single deployment at a time [Issue #29345] ------------------------------------------------------------------------ [Reason for this change] ------------------------------------------------------------------------ The current L2 AppConfig constructs do not have any guardrails that prevent simultaneous Deployments to a single Environment. This is not allowed, and will result in Cfn deploy-time conflicts. [Description of changes] ------------------------------------------------------------------------ This commit adds a pair of new public methods to IEnvironment that enable the addition of a new Deployment for a given IConfiguration. It then updates the creation of new Deployments in ConfigurationBase to utilize these new methods instead of the current resource creation. These new methods interact with an internal queue. This queue creates a chain of Cfn dependencies between Deployments in order to enforce that only a single Deployment can be in progress for the Environment at any given time. [Description of how you validated changes] ------------------------------------------------------------------------ Added new unit test coverage. --- .../aws-appconfig/lib/configuration.ts | 13 +- .../aws-appconfig/lib/environment.ts | 45 +++- .../aws-appconfig/test/environment.test.ts | 212 +++++++++++++++++- 3 files changed, 257 insertions(+), 13 deletions(-) diff --git a/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts b/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts index a974aae0c4203..c0b91fcd51cc4 100644 --- a/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts +++ b/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts @@ -2,12 +2,11 @@ import * as fs from 'fs'; import * as path from 'path'; import { Construct, IConstruct } from 'constructs'; -import { CfnConfigurationProfile, CfnDeployment, CfnHostedConfigurationVersion } from './appconfig.generated'; +import { CfnConfigurationProfile, CfnHostedConfigurationVersion } from './appconfig.generated'; import { IApplication } from './application'; import { DeploymentStrategy, IDeploymentStrategy, RolloutStrategy } from './deployment-strategy'; import { IEnvironment } from './environment'; import { ActionPoint, IEventDestination, ExtensionOptions, IExtension, IExtensible, ExtensibleBase } from './extension'; -import { getHash } from './private/hash'; import * as cp from '../../aws-codepipeline'; import * as iam from '../../aws-iam'; import * as kms from '../../aws-kms'; @@ -319,15 +318,7 @@ abstract class ConfigurationBase extends Construct implements IConfiguration, IE if ((this.deployTo && !this.deployTo.includes(environment))) { return; } - new CfnDeployment(this, `Deployment${getHash(environment.name!)}`, { - applicationId: this.application.applicationId, - configurationProfileId: this.configurationProfileId, - deploymentStrategyId: this.deploymentStrategy!.deploymentStrategyId, - environmentId: environment.environmentId, - configurationVersion: this.versionNumber!, - description: this.description, - kmsKeyIdentifier: this.deploymentKey?.keyArn, - }); + environment.addDeployment(this); }); } } diff --git a/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts b/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts index 437f267c1b366..57b22be765cf0 100644 --- a/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts +++ b/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts @@ -1,6 +1,7 @@ import { Construct } from 'constructs'; -import { CfnEnvironment } from './appconfig.generated'; +import { CfnDeployment, CfnEnvironment } from './appconfig.generated'; import { IApplication } from './application'; +import { IConfiguration } from './configuration'; import { ActionPoint, IEventDestination, ExtensionOptions, IExtension, IExtensible, ExtensibleBase } from './extension'; import { getHash } from './private/hash'; import * as cloudwatch from '../../aws-cloudwatch'; @@ -47,7 +48,31 @@ abstract class EnvironmentBase extends Resource implements IEnvironment, IExtens public abstract applicationId: string; public abstract environmentId: string; public abstract environmentArn: string; + public abstract name?: string | undefined; protected extensible!: ExtensibleBase; + protected deploymentQueue: Array = []; + + public addDeployment(configuration: IConfiguration): void { + const queueSize = this.deploymentQueue.push( + new CfnDeployment(configuration, `Deployment${getHash(this.name!)}`, { + applicationId: configuration.application.applicationId, + configurationProfileId: configuration.configurationProfileId, + deploymentStrategyId: configuration.deploymentStrategy!.deploymentStrategyId, + environmentId: this.environmentId, + configurationVersion: configuration.versionNumber!, + description: configuration.description, + kmsKeyIdentifier: configuration.deploymentKey?.keyArn, + }), + ); + + if (queueSize > 1) { + this.deploymentQueue[queueSize - 1].addDependency(this.deploymentQueue[queueSize - 2]); + } + } + + public addDeployments(...configurations: IConfiguration[]): void { + configurations.forEach((config) => this.addDeployment(config)); + } public on(actionPoint: ActionPoint, eventDestination: IEventDestination, options?: ExtensionOptions) { this.extensible.on(actionPoint, eventDestination, options); @@ -154,6 +179,7 @@ export class Environment extends EnvironmentBase { public readonly applicationId = applicationId; public readonly environmentId = environmentId; public readonly environmentArn = environmentArn; + public readonly name?: string | undefined; } return new Import(scope, id, { @@ -413,6 +439,23 @@ export interface IEnvironment extends IResource { */ readonly environmentArn: string; + /** + * Creates a deployment of the supplied configuration to this environment. + * Note that you can only deploy one configuration at a time to an environment. + * However, you can deploy one configuration each to different environments at the same time. + * If more than one deployment is requested for this environment, they will occur in the same order they were provided. + * + * @param configuration The configuration that will be deployed to this environment. + */ + addDeployment(configuration: IConfiguration): void; + + /** + * Creates a deployment for each of the supplied configurations to this environment. + * + * @param configurations The configurations that will be deployed to this environment. + */ + addDeployments(...configurations: Array): void; + /** * Adds an extension defined by the action point and event destination and also * creates an extension association to the environment. diff --git a/packages/aws-cdk-lib/aws-appconfig/test/environment.test.ts b/packages/aws-cdk-lib/aws-appconfig/test/environment.test.ts index dd2e5e278482f..2cbe0fa159c6d 100644 --- a/packages/aws-cdk-lib/aws-appconfig/test/environment.test.ts +++ b/packages/aws-cdk-lib/aws-appconfig/test/environment.test.ts @@ -2,7 +2,7 @@ import { Template } from '../../assertions'; import { Alarm, CompositeAlarm, Metric } from '../../aws-cloudwatch'; import * as iam from '../../aws-iam'; import * as cdk from '../../core'; -import { Application, Environment, Monitor } from '../lib'; +import { Application, ConfigurationContent, Environment, HostedConfiguration, Monitor } from '../lib'; describe('environment', () => { test('default environment', () => { @@ -54,6 +54,216 @@ describe('environment', () => { }); }); + test('environment with single deployment', () => { + const stack = new cdk.Stack(); + const application = new Application(stack, 'MyAppConfig'); + const env = new Environment(stack, 'MyEnvironment', { + application, + }); + + const firstConfig = new HostedConfiguration(stack, 'FirstConfig', { + application, + content: ConfigurationContent.fromInlineText('This is my content 1'), + }); + env.addDeployment(firstConfig); + + const actual = Template.fromStack(stack); + + actual.hasResourceProperties('AWS::AppConfig::Environment', { + Name: 'MyEnvironment', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + }); + + actual.hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'FirstConfig', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: 'hosted', + }); + actual.hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'FirstConfigConfigurationProfileDEF37C63', + }, + Content: 'This is my content 1', + ContentType: 'text/plain', + }); + actual.hasResource('AWS::AppConfig::Deployment', { + Properties: { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + EnvironmentId: { + Ref: 'MyEnvironment465E4DEA', + }, + ConfigurationVersion: { + Ref: 'FirstConfigC35E996C', + }, + ConfigurationProfileId: { + Ref: 'FirstConfigConfigurationProfileDEF37C63', + }, + DeploymentStrategyId: { + Ref: 'FirstConfigDeploymentStrategy863BBA9A', + }, + }, + }); + + actual.resourceCountIs('AWS::AppConfig::Deployment', 1); + }); + + test('environment with multiple deployments', () => { + const stack = new cdk.Stack(); + const application = new Application(stack, 'MyAppConfig'); + const env = new Environment(stack, 'MyEnvironment', { + application, + }); + + const firstConfig = new HostedConfiguration(stack, 'FirstConfig', { + application, + content: ConfigurationContent.fromInlineText('This is my content 1'), + }); + const secondConfig = new HostedConfiguration(stack, 'SecondConfig', { + application, + content: ConfigurationContent.fromInlineText('This is my content 2'), + }); + const thirdConfig = new HostedConfiguration(stack, 'ThirdConfig', { + application, + content: ConfigurationContent.fromInlineText('This is my content 3'), + }); + + env.addDeployments(firstConfig, secondConfig); + env.addDeployment(thirdConfig); + + const actual = Template.fromStack(stack); + + actual.hasResourceProperties('AWS::AppConfig::Environment', { + Name: 'MyEnvironment', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + }); + + actual.hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'FirstConfig', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: 'hosted', + }); + actual.hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'FirstConfigConfigurationProfileDEF37C63', + }, + Content: 'This is my content 1', + ContentType: 'text/plain', + }); + actual.hasResource('AWS::AppConfig::Deployment', { + Properties: { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + EnvironmentId: { + Ref: 'MyEnvironment465E4DEA', + }, + ConfigurationVersion: { + Ref: 'FirstConfigC35E996C', + }, + ConfigurationProfileId: { + Ref: 'FirstConfigConfigurationProfileDEF37C63', + }, + DeploymentStrategyId: { + Ref: 'FirstConfigDeploymentStrategy863BBA9A', + }, + }, + }); + + actual.hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'SecondConfig', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: 'hosted', + }); + actual.hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'SecondConfigConfigurationProfileE64FE7B4', + }, + Content: 'This is my content 2', + ContentType: 'text/plain', + }); + actual.hasResource('AWS::AppConfig::Deployment', { + Properties: { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + EnvironmentId: { + Ref: 'MyEnvironment465E4DEA', + }, + ConfigurationVersion: { + Ref: 'SecondConfig22E40AAE', + }, + ConfigurationProfileId: { + Ref: 'SecondConfigConfigurationProfileE64FE7B4', + }, + DeploymentStrategyId: { + Ref: 'SecondConfigDeploymentStrategy9929738B', + }, + }, + DependsOn: ['FirstConfigDeployment52928BE68587B'], + }); + + actual.hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'ThirdConfig', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: 'hosted', + }); + actual.hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'ThirdConfigConfigurationProfile4945C970', + }, + Content: 'This is my content 3', + ContentType: 'text/plain', + }); + actual.hasResource('AWS::AppConfig::Deployment', { + Properties: { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + EnvironmentId: { + Ref: 'MyEnvironment465E4DEA', + }, + ConfigurationVersion: { + Ref: 'ThirdConfig498595D6', + }, + ConfigurationProfileId: { + Ref: 'ThirdConfigConfigurationProfile4945C970', + }, + DeploymentStrategyId: { + Ref: 'ThirdConfigDeploymentStrategy246FBD1A', + }, + }, + DependsOn: ['SecondConfigDeployment5292843F35B55'], + }); + + actual.resourceCountIs('AWS::AppConfig::Deployment', 3); + }); + test('environment with monitors with alarm and alarmRole', () => { const stack = new cdk.Stack(); const app = new Application(stack, 'MyAppConfig'); From 005841308e207348616686d2b9ca655a88eb8541 Mon Sep 17 00:00:00 2001 From: Matthew Hawkins Date: Thu, 14 Mar 2024 20:10:11 -0700 Subject: [PATCH 2/5] Added README content --- packages/aws-cdk-lib/aws-appconfig/README.md | 84 ++++++++++++++++++- .../aws-appconfig/lib/configuration.ts | 4 +- 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/packages/aws-cdk-lib/aws-appconfig/README.md b/packages/aws-cdk-lib/aws-appconfig/README.md index 40aa40bf39035..6c6d35650a7ef 100644 --- a/packages/aws-cdk-lib/aws-appconfig/README.md +++ b/packages/aws-cdk-lib/aws-appconfig/README.md @@ -252,8 +252,7 @@ new appconfig.HostedConfiguration(this, 'MyHostedConfiguration', { }); ``` -The `deployTo` parameter is used to specify which environments to deploy the configuration to. If this parameter is not -specified, there will not be a deployment. +The `deployTo` parameter is used to specify which environments to deploy the configuration to. A hosted configuration with `deployTo`: @@ -268,6 +267,87 @@ new appconfig.HostedConfiguration(this, 'MyHostedConfiguration', { }); ``` +When more than one configuration is set to deploy to the same environment, the +deployments will occur one at a time. This is done to satisfy +[AppConfig's constraint:](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-deploying.html) +> [!NOTE] +> You can only deploy one configuration at a time to an environment. +> However, you can deploy one configuration each to different environments at the same time. + +The deployment order matches the order in which the configurations are declared. + +```ts +const app = new appconfig.Application(this, 'MyApp'); +const env = new appconfig.Environment(this, 'MyEnv', { + application: app, +}); + +new appconfig.HostedConfiguration(this, 'MyFirstHostedConfig', { + application: app, + deployTo: [env], + content: appconfig.ConfigurationContent.fromInlineText('This is my first configuration content.'), +}); + +new appconfig.HostedConfiguration(this, 'MySecondHostedConfig', { + application: app, + deployTo: [env], + content: appconfig.ConfigurationContent.fromInlineText('This is my second configuration content.'), +}); +``` + +If an application would benefit from a deployment order that differs from the +declared order, you can defer the decision by using `IEnvironment.addDeployment` +rather than the `deployTo` property. + +```ts +const app = new appconfig.Application(this, 'MyApp'); +const env = new appconfig.Environment(this, 'MyEnv', { + application: app, +}); + +const secondConfig = new appconfig.HostedConfiguration(this, 'MySecondHostedConfig', { + application: app, + content: appconfig.ConfigurationContent.fromInlineText('This is my second configuration content.'), +}); + +const firstConfig = new appconfig.HostedConfiguration(this, 'MyFirstHostedConfig', { + application: app, + deployTo: [env], + content: appconfig.ConfigurationContent.fromInlineText('This is my first configuration content.'), +}); + +env.addDeployment(secondConfig); +``` + +Alternatively, you can defer multiple deployments in favor of +`IEnvironment.addDeployments`, which allows you to declare multiple +configurations in the order they will be deployed. + +```ts +const app = new appconfig.Application(this, 'MyApp'); +const env = new appconfig.Environment(this, 'MyEnv', { + application: app, +}); + +const secondConfig = new appconfig.HostedConfiguration(this, 'MySecondHostedConfig', { + application: app, + content: appconfig.ConfigurationContent.fromInlineText('This is my second configuration content.'), +}); + +const firstConfig = new appconfig.HostedConfiguration(this, 'MyFirstHostedConfig', { + application: app, + content: appconfig.ConfigurationContent.fromInlineText('This is my first configuration content.'), +}); + +env.addDeployments(firstConfig, secondConfig); +``` + +Any mix of `deployTo`, `addDeployment`, and `addDeployments` is permitted. +The declaration order will be respected regardless of the approach used. + +> [!IMPORTANT] +> If none of these options are utilized, there will not be any deployments. + ### SourcedConfiguration A sourced configuration represents configuration stored in any of the following: diff --git a/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts b/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts index c0b91fcd51cc4..521557b526bdc 100644 --- a/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts +++ b/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts @@ -61,7 +61,9 @@ export interface ConfigurationOptions { * The list of environments to deploy the configuration to. * * If this parameter is not specified, then there will be no - * deployment. + * deployment created alongside this configuration. + * + * A deployment can be added later via IEnvironment. * * @default - None. */ From e72cd81825bc879b6add5d6ea659c0a4147a7b95 Mon Sep 17 00:00:00 2001 From: Matthew Hawkins Date: Thu, 14 Mar 2024 21:43:14 -0700 Subject: [PATCH 3/5] Added new integ test --- ...efaultTestDeployAssert1621E45D.assets.json | 19 + ...aultTestDeployAssert1621E45D.template.json | 36 ++ ...aws-appconfig-multi-config-env.assets.json | 19 + ...s-appconfig-multi-config-env.template.json | 156 ++++++++ .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 161 +++++++++ .../tree.json | 334 ++++++++++++++++++ .../test/integ.multi-config-env.ts | 44 +++ 9 files changed, 782 insertions(+) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/appconfigmulticonfigenvDefaultTestDeployAssert1621E45D.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/appconfigmulticonfigenvDefaultTestDeployAssert1621E45D.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/aws-appconfig-multi-config-env.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/aws-appconfig-multi-config-env.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/appconfigmulticonfigenvDefaultTestDeployAssert1621E45D.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/appconfigmulticonfigenvDefaultTestDeployAssert1621E45D.assets.json new file mode 100644 index 0000000000000..bcfd14bc7d20a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/appconfigmulticonfigenvDefaultTestDeployAssert1621E45D.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "appconfigmulticonfigenvDefaultTestDeployAssert1621E45D.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-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/appconfigmulticonfigenvDefaultTestDeployAssert1621E45D.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/appconfigmulticonfigenvDefaultTestDeployAssert1621E45D.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/appconfigmulticonfigenvDefaultTestDeployAssert1621E45D.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-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/aws-appconfig-multi-config-env.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/aws-appconfig-multi-config-env.assets.json new file mode 100644 index 0000000000000..6b8622f47adc9 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/aws-appconfig-multi-config-env.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "ebc42c894086c85ac3a2ea694b62aa368f6bc0556f3fe3f0a2edb2b93d5d8d74": { + "source": { + "path": "aws-appconfig-multi-config-env.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "ebc42c894086c85ac3a2ea694b62aa368f6bc0556f3fe3f0a2edb2b93d5d8d74.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-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/aws-appconfig-multi-config-env.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/aws-appconfig-multi-config-env.template.json new file mode 100644 index 0000000000000..410942d6012ec --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/aws-appconfig-multi-config-env.template.json @@ -0,0 +1,156 @@ +{ + "Resources": { + "MyApplicationForEnv1F597ED9": { + "Type": "AWS::AppConfig::Application", + "Properties": { + "Name": "awsappconfigmulticonfigenv-MyApplicationForEnv-1EE3EA95" + } + }, + "MultiConfigEnvironment5F41B747": { + "Type": "AWS::AppConfig::Environment", + "Properties": { + "ApplicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "Name": "awsappconfigmulticonfigenv-MultiConfigEnvironment-59525230" + } + }, + "QuickDeploymentStrategy980252EC": { + "Type": "AWS::AppConfig::DeploymentStrategy", + "Properties": { + "DeploymentDurationInMinutes": 1, + "GrowthFactor": 50, + "GrowthType": "LINEAR", + "Name": "awsappconfigmulticonfigenv-QuickDeploymentStrategy-CAB72574", + "ReplicateTo": "NONE" + } + }, + "MyFirstConfigConfigurationProfileAB11F87A": { + "Type": "AWS::AppConfig::ConfigurationProfile", + "Properties": { + "ApplicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "LocationUri": "hosted", + "Name": "awsappconfigmulticonfigenv-MyFirstConfig-2FF7CAAB" + } + }, + "MyFirstConfig117AFBAC": { + "Type": "AWS::AppConfig::HostedConfigurationVersion", + "Properties": { + "ApplicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "ConfigurationProfileId": { + "Ref": "MyFirstConfigConfigurationProfileAB11F87A" + }, + "Content": "first config content", + "ContentType": "application/octet-stream" + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "MyFirstConfigDeployment5AEBAD52B8EE4": { + "Type": "AWS::AppConfig::Deployment", + "Properties": { + "ApplicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "ConfigurationProfileId": { + "Ref": "MyFirstConfigConfigurationProfileAB11F87A" + }, + "ConfigurationVersion": { + "Ref": "MyFirstConfig117AFBAC" + }, + "DeploymentStrategyId": { + "Ref": "QuickDeploymentStrategy980252EC" + }, + "EnvironmentId": { + "Ref": "MultiConfigEnvironment5F41B747" + } + } + }, + "MySecondConfigConfigurationProfileD0CC1BAA": { + "Type": "AWS::AppConfig::ConfigurationProfile", + "Properties": { + "ApplicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "LocationUri": "hosted", + "Name": "awsappconfigmulticonfigenv-MySecondConfig-4F837809" + } + }, + "MySecondConfig28DEBAC4": { + "Type": "AWS::AppConfig::HostedConfigurationVersion", + "Properties": { + "ApplicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "ConfigurationProfileId": { + "Ref": "MySecondConfigConfigurationProfileD0CC1BAA" + }, + "Content": "second config content", + "ContentType": "application/octet-stream" + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "MySecondConfigDeployment5AEBAD1470BE4": { + "Type": "AWS::AppConfig::Deployment", + "Properties": { + "ApplicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "ConfigurationProfileId": { + "Ref": "MySecondConfigConfigurationProfileD0CC1BAA" + }, + "ConfigurationVersion": { + "Ref": "MySecondConfig28DEBAC4" + }, + "DeploymentStrategyId": { + "Ref": "QuickDeploymentStrategy980252EC" + }, + "EnvironmentId": { + "Ref": "MultiConfigEnvironment5F41B747" + } + }, + "DependsOn": [ + "MyFirstConfigDeployment5AEBAD52B8EE4" + ] + } + }, + "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-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/integ.json new file mode 100644 index 0000000000000..aa013d97752d6 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "appconfig-multi-config-env/DefaultTest": { + "stacks": [ + "aws-appconfig-multi-config-env" + ], + "assertionStack": "appconfig-multi-config-env/DefaultTest/DeployAssert", + "assertionStackName": "appconfigmulticonfigenvDefaultTestDeployAssert1621E45D" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/manifest.json new file mode 100644 index 0000000000000..22a3fbb12bb74 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/manifest.json @@ -0,0 +1,161 @@ +{ + "version": "36.0.0", + "artifacts": { + "aws-appconfig-multi-config-env.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-appconfig-multi-config-env.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-appconfig-multi-config-env": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-appconfig-multi-config-env.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/ebc42c894086c85ac3a2ea694b62aa368f6bc0556f3fe3f0a2edb2b93d5d8d74.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-appconfig-multi-config-env.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-appconfig-multi-config-env.assets" + ], + "metadata": { + "/aws-appconfig-multi-config-env/MyApplicationForEnv/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyApplicationForEnv1F597ED9" + } + ], + "/aws-appconfig-multi-config-env/MultiConfigEnvironment/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MultiConfigEnvironment5F41B747" + } + ], + "/aws-appconfig-multi-config-env/QuickDeploymentStrategy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "QuickDeploymentStrategy980252EC" + } + ], + "/aws-appconfig-multi-config-env/MyFirstConfig/ConfigurationProfile": [ + { + "type": "aws:cdk:logicalId", + "data": "MyFirstConfigConfigurationProfileAB11F87A" + } + ], + "/aws-appconfig-multi-config-env/MyFirstConfig/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyFirstConfig117AFBAC" + } + ], + "/aws-appconfig-multi-config-env/MyFirstConfig/Deployment5AEBA": [ + { + "type": "aws:cdk:logicalId", + "data": "MyFirstConfigDeployment5AEBAD52B8EE4" + } + ], + "/aws-appconfig-multi-config-env/MySecondConfig/ConfigurationProfile": [ + { + "type": "aws:cdk:logicalId", + "data": "MySecondConfigConfigurationProfileD0CC1BAA" + } + ], + "/aws-appconfig-multi-config-env/MySecondConfig/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MySecondConfig28DEBAC4" + } + ], + "/aws-appconfig-multi-config-env/MySecondConfig/Deployment5AEBA": [ + { + "type": "aws:cdk:logicalId", + "data": "MySecondConfigDeployment5AEBAD1470BE4" + } + ], + "/aws-appconfig-multi-config-env/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-appconfig-multi-config-env/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-appconfig-multi-config-env" + }, + "appconfigmulticonfigenvDefaultTestDeployAssert1621E45D.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "appconfigmulticonfigenvDefaultTestDeployAssert1621E45D.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "appconfigmulticonfigenvDefaultTestDeployAssert1621E45D": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "appconfigmulticonfigenvDefaultTestDeployAssert1621E45D.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "appconfigmulticonfigenvDefaultTestDeployAssert1621E45D.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": [ + "appconfigmulticonfigenvDefaultTestDeployAssert1621E45D.assets" + ], + "metadata": { + "/appconfig-multi-config-env/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/appconfig-multi-config-env/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "appconfig-multi-config-env/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/tree.json new file mode 100644 index 0000000000000..93e0497afe1a4 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.js.snapshot/tree.json @@ -0,0 +1,334 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-appconfig-multi-config-env": { + "id": "aws-appconfig-multi-config-env", + "path": "aws-appconfig-multi-config-env", + "children": { + "MyApplicationForEnv": { + "id": "MyApplicationForEnv", + "path": "aws-appconfig-multi-config-env/MyApplicationForEnv", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-multi-config-env/MyApplicationForEnv/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Application", + "aws:cdk:cloudformation:props": { + "name": "awsappconfigmulticonfigenv-MyApplicationForEnv-1EE3EA95" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "MultiConfigEnvironment": { + "id": "MultiConfigEnvironment", + "path": "aws-appconfig-multi-config-env/MultiConfigEnvironment", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-multi-config-env/MultiConfigEnvironment/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Environment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "name": "awsappconfigmulticonfigenv-MultiConfigEnvironment-59525230" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "QuickDeploymentStrategy": { + "id": "QuickDeploymentStrategy", + "path": "aws-appconfig-multi-config-env/QuickDeploymentStrategy", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-appconfig-multi-config-env/QuickDeploymentStrategy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::DeploymentStrategy", + "aws:cdk:cloudformation:props": { + "deploymentDurationInMinutes": 1, + "growthFactor": 50, + "growthType": "LINEAR", + "name": "awsappconfigmulticonfigenv-QuickDeploymentStrategy-CAB72574", + "replicateTo": "NONE" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "MyFirstConfig": { + "id": "MyFirstConfig", + "path": "aws-appconfig-multi-config-env/MyFirstConfig", + "children": { + "ConfigurationProfile": { + "id": "ConfigurationProfile", + "path": "aws-appconfig-multi-config-env/MyFirstConfig/ConfigurationProfile", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ConfigurationProfile", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "locationUri": "hosted", + "name": "awsappconfigmulticonfigenv-MyFirstConfig-2FF7CAAB" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-multi-config-env/MyFirstConfig/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::HostedConfigurationVersion", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "configurationProfileId": { + "Ref": "MyFirstConfigConfigurationProfileAB11F87A" + }, + "content": "first config content", + "contentType": "application/octet-stream" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Deployment5AEBA": { + "id": "Deployment5AEBA", + "path": "aws-appconfig-multi-config-env/MyFirstConfig/Deployment5AEBA", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Deployment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "configurationProfileId": { + "Ref": "MyFirstConfigConfigurationProfileAB11F87A" + }, + "configurationVersion": { + "Ref": "MyFirstConfig117AFBAC" + }, + "deploymentStrategyId": { + "Ref": "QuickDeploymentStrategy980252EC" + }, + "environmentId": { + "Ref": "MultiConfigEnvironment5F41B747" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "MySecondConfig": { + "id": "MySecondConfig", + "path": "aws-appconfig-multi-config-env/MySecondConfig", + "children": { + "ConfigurationProfile": { + "id": "ConfigurationProfile", + "path": "aws-appconfig-multi-config-env/MySecondConfig/ConfigurationProfile", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::ConfigurationProfile", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "locationUri": "hosted", + "name": "awsappconfigmulticonfigenv-MySecondConfig-4F837809" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-appconfig-multi-config-env/MySecondConfig/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::HostedConfigurationVersion", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "configurationProfileId": { + "Ref": "MySecondConfigConfigurationProfileD0CC1BAA" + }, + "content": "second config content", + "contentType": "application/octet-stream" + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Deployment5AEBA": { + "id": "Deployment5AEBA", + "path": "aws-appconfig-multi-config-env/MySecondConfig/Deployment5AEBA", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::AppConfig::Deployment", + "aws:cdk:cloudformation:props": { + "applicationId": { + "Ref": "MyApplicationForEnv1F597ED9" + }, + "configurationProfileId": { + "Ref": "MySecondConfigConfigurationProfileD0CC1BAA" + }, + "configurationVersion": { + "Ref": "MySecondConfig28DEBAC4" + }, + "deploymentStrategyId": { + "Ref": "QuickDeploymentStrategy980252EC" + }, + "environmentId": { + "Ref": "MultiConfigEnvironment5F41B747" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-appconfig-multi-config-env/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-appconfig-multi-config-env/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "appconfig-multi-config-env": { + "id": "appconfig-multi-config-env", + "path": "appconfig-multi-config-env", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "appconfig-multi-config-env/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "appconfig-multi-config-env/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "appconfig-multi-config-env/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "appconfig-multi-config-env/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "appconfig-multi-config-env/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.ts new file mode 100644 index 0000000000000..bcacbd18d326e --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-appconfig/test/integ.multi-config-env.ts @@ -0,0 +1,44 @@ +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; +import { App, Duration, Stack } from 'aws-cdk-lib'; +import { Application, ConfigurationContent, DeploymentStrategy, Environment, HostedConfiguration, RolloutStrategy } from 'aws-cdk-lib/aws-appconfig'; + +/** + * Test case: + * - Single Environment + * - Two Configurations + * - Both have non-zero deployment duration + * + * If this was done via the L1 constructs alone, Cfn would fail due to + * violating AppConfig's simultaneous deployment rule. + */ + +const app = new App(); +const stack = new Stack(app, 'aws-appconfig-multi-config-env'); +const application = new Application(stack, 'MyApplicationForEnv'); +const env = new Environment(stack, 'MultiConfigEnvironment', { + application, +}); +const deploymentStrategy = new DeploymentStrategy(stack, 'QuickDeploymentStrategy', + { + rolloutStrategy: RolloutStrategy.linear({ + deploymentDuration: Duration.minutes(1), + growthFactor: 50, + }), + }); + +new HostedConfiguration(stack, 'MyFirstConfig', { + application, + content: ConfigurationContent.fromInline('first config content'), + deploymentStrategy, + deployTo: [env], +}); +new HostedConfiguration(stack, 'MySecondConfig', { + application, + content: ConfigurationContent.fromInline('second config content'), + deploymentStrategy, + deployTo: [env], +}); + +new IntegTest(app, 'appconfig-multi-config-env', { + testCases: [stack], +}); From 15a057bbfe295308a01e4123f9549b2afc69209c Mon Sep 17 00:00:00 2001 From: Matthew Hawkins Date: Sat, 16 Mar 2024 12:14:13 -0700 Subject: [PATCH 4/5] Addressed suggestions - imported env coverage --- packages/aws-cdk-lib/aws-appconfig/README.md | 2 +- .../aws-appconfig/lib/configuration.ts | 3 +- .../aws-appconfig/lib/environment.ts | 10 ++- .../aws-appconfig/test/environment.test.ts | 83 +++++++++++++++++++ 4 files changed, 93 insertions(+), 5 deletions(-) diff --git a/packages/aws-cdk-lib/aws-appconfig/README.md b/packages/aws-cdk-lib/aws-appconfig/README.md index 6c6d35650a7ef..3ee4cf800d728 100644 --- a/packages/aws-cdk-lib/aws-appconfig/README.md +++ b/packages/aws-cdk-lib/aws-appconfig/README.md @@ -269,7 +269,7 @@ new appconfig.HostedConfiguration(this, 'MyHostedConfiguration', { When more than one configuration is set to deploy to the same environment, the deployments will occur one at a time. This is done to satisfy -[AppConfig's constraint:](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-deploying.html) +[AppConfig's constraint](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-deploying.html): > [!NOTE] > You can only deploy one configuration at a time to an environment. > However, you can deploy one configuration each to different environments at the same time. diff --git a/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts b/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts index 521557b526bdc..1e2197a411a56 100644 --- a/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts +++ b/packages/aws-cdk-lib/aws-appconfig/lib/configuration.ts @@ -63,7 +63,8 @@ export interface ConfigurationOptions { * If this parameter is not specified, then there will be no * deployment created alongside this configuration. * - * A deployment can be added later via IEnvironment. + * Deployments can be added later using the `IEnvironment.addDeployment` or + * `IEnvironment.addDeployments` methods. * * @default - None. */ diff --git a/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts b/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts index 57b22be765cf0..d188647e1bd64 100644 --- a/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts +++ b/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts @@ -48,13 +48,17 @@ abstract class EnvironmentBase extends Resource implements IEnvironment, IExtens public abstract applicationId: string; public abstract environmentId: string; public abstract environmentArn: string; - public abstract name?: string | undefined; + public abstract name?: string; protected extensible!: ExtensibleBase; protected deploymentQueue: Array = []; public addDeployment(configuration: IConfiguration): void { + if (this.name === undefined) { + throw new Error('Environment name must be known to add a Deployment'); + } + const queueSize = this.deploymentQueue.push( - new CfnDeployment(configuration, `Deployment${getHash(this.name!)}`, { + new CfnDeployment(configuration, `Deployment${getHash(this.name)}`, { applicationId: configuration.application.applicationId, configurationProfileId: configuration.configurationProfileId, deploymentStrategyId: configuration.deploymentStrategy!.deploymentStrategyId, @@ -179,7 +183,7 @@ export class Environment extends EnvironmentBase { public readonly applicationId = applicationId; public readonly environmentId = environmentId; public readonly environmentArn = environmentArn; - public readonly name?: string | undefined; + public readonly name? = undefined; } return new Import(scope, id, { diff --git a/packages/aws-cdk-lib/aws-appconfig/test/environment.test.ts b/packages/aws-cdk-lib/aws-appconfig/test/environment.test.ts index 2cbe0fa159c6d..eb59aa6742333 100644 --- a/packages/aws-cdk-lib/aws-appconfig/test/environment.test.ts +++ b/packages/aws-cdk-lib/aws-appconfig/test/environment.test.ts @@ -695,6 +695,20 @@ describe('environment', () => { expect(env.env.region).toEqual('us-west-2'); }); + test('from environment arn; cannot add new deployment', () => { + const stack = new cdk.Stack(); + const application = new Application(stack, 'MyAppConfig'); + const env = Environment.fromEnvironmentArn(stack, 'MyEnvironment', + 'arn:aws:appconfig:us-west-2:123456789012:application/abc123/environment/def456'); + + expect(() => { + env.addDeployment(new HostedConfiguration(stack, 'FirstConfig', { + application, + content: ConfigurationContent.fromInlineText('This is my content 1'), + })); + }).toThrow('Environment name must be known to add a Deployment'); + }); + test('from environment arn with no resource name', () => { const stack = new cdk.Stack(); expect(() => { @@ -746,4 +760,73 @@ describe('environment', () => { expect(env.env.account).toEqual('123456789012'); expect(env.env.region).toEqual('us-west-2'); }); + + test('from environment attributes; cannot add new deployment without name', () => { + const stack = new cdk.Stack(); + const application = new Application(stack, 'MyAppConfig'); + const env = Environment.fromEnvironmentAttributes(stack, 'MyEnvironment', { + application, + environmentId: 'def456', + }); + + expect(() => { + env.addDeployment(new HostedConfiguration(stack, 'FirstConfig', { + application, + content: ConfigurationContent.fromInlineText('This is my content 1'), + })); + }).toThrow('Environment name must be known to add a Deployment'); + }); + + test('from environment attributes with name; can add new deployment', () => { + const stack = new cdk.Stack(); + const application = new Application(stack, 'MyAppConfig'); + const env = Environment.fromEnvironmentAttributes(stack, 'MyEnvironment', { + application, + environmentId: 'def456', + name: 'NamedEnv', + }); + env.addDeployment(new HostedConfiguration(stack, 'FirstConfig', { + application, + content: ConfigurationContent.fromInlineText('This is my content 1'), + })); + + const actual = Template.fromStack(stack); + + actual.hasResourceProperties('AWS::AppConfig::ConfigurationProfile', { + Name: 'FirstConfig', + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + LocationUri: 'hosted', + }); + actual.hasResourceProperties('AWS::AppConfig::HostedConfigurationVersion', { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + ConfigurationProfileId: { + Ref: 'FirstConfigConfigurationProfileDEF37C63', + }, + Content: 'This is my content 1', + ContentType: 'text/plain', + }); + actual.hasResource('AWS::AppConfig::Deployment', { + Properties: { + ApplicationId: { + Ref: 'MyAppConfigB4B63E75', + }, + EnvironmentId: 'def456', + ConfigurationVersion: { + Ref: 'FirstConfigC35E996C', + }, + ConfigurationProfileId: { + Ref: 'FirstConfigConfigurationProfileDEF37C63', + }, + DeploymentStrategyId: { + Ref: 'FirstConfigDeploymentStrategy863BBA9A', + }, + }, + }); + + actual.resourceCountIs('AWS::AppConfig::Deployment', 1); + }); }); From a1aae971060b862cd93cca9fc3a842744c423cea Mon Sep 17 00:00:00 2001 From: Matthew Hawkins Date: Wed, 17 Apr 2024 17:53:28 -0700 Subject: [PATCH 5/5] Addressed suggestions - readme and comment additions --- packages/aws-cdk-lib/aws-appconfig/README.md | 10 +++++++++- packages/aws-cdk-lib/aws-appconfig/lib/environment.ts | 6 +++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/aws-cdk-lib/aws-appconfig/README.md b/packages/aws-cdk-lib/aws-appconfig/README.md index 3ee4cf800d728..ee5f4902b7902 100644 --- a/packages/aws-cdk-lib/aws-appconfig/README.md +++ b/packages/aws-cdk-lib/aws-appconfig/README.md @@ -298,6 +298,7 @@ new appconfig.HostedConfiguration(this, 'MySecondHostedConfig', { If an application would benefit from a deployment order that differs from the declared order, you can defer the decision by using `IEnvironment.addDeployment` rather than the `deployTo` property. +In this example, `firstConfig` will be deployed before `secondConfig`. ```ts const app = new appconfig.Application(this, 'MyApp'); @@ -322,6 +323,8 @@ env.addDeployment(secondConfig); Alternatively, you can defer multiple deployments in favor of `IEnvironment.addDeployments`, which allows you to declare multiple configurations in the order they will be deployed. +In this example the deployment order will be +`firstConfig`, then `secondConfig`, and finally `thirdConfig`. ```ts const app = new appconfig.Application(this, 'MyApp'); @@ -334,12 +337,17 @@ const secondConfig = new appconfig.HostedConfiguration(this, 'MySecondHostedConf content: appconfig.ConfigurationContent.fromInlineText('This is my second configuration content.'), }); +const thirdConfig = new appconfig.HostedConfiguration(this, 'MyThirdHostedConfig', { + application: app, + content: appconfig.ConfigurationContent.fromInlineText('This is my third configuration content.'), +}); + const firstConfig = new appconfig.HostedConfiguration(this, 'MyFirstHostedConfig', { application: app, content: appconfig.ConfigurationContent.fromInlineText('This is my first configuration content.'), }); -env.addDeployments(firstConfig, secondConfig); +env.addDeployments(firstConfig, secondConfig, thirdConfig); ``` Any mix of `deployTo`, `addDeployment`, and `addDeployments` is permitted. diff --git a/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts b/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts index d188647e1bd64..be0965edcbe42 100644 --- a/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts +++ b/packages/aws-cdk-lib/aws-appconfig/lib/environment.ts @@ -69,6 +69,9 @@ abstract class EnvironmentBase extends Resource implements IEnvironment, IExtens }), ); + // This internal member is used to keep track of configuration deployments + // as they are requested. Each element in this queue will depend on its + // predecessor, ensuring that the deployments occur sequentially in Cfn. if (queueSize > 1) { this.deploymentQueue[queueSize - 1].addDependency(this.deploymentQueue[queueSize - 2]); } @@ -183,7 +186,7 @@ export class Environment extends EnvironmentBase { public readonly applicationId = applicationId; public readonly environmentId = environmentId; public readonly environmentArn = environmentArn; - public readonly name? = undefined; + public readonly name?: string; } return new Import(scope, id, { @@ -455,6 +458,7 @@ export interface IEnvironment extends IResource { /** * Creates a deployment for each of the supplied configurations to this environment. + * These configurations will be deployed in the same order as the input array. * * @param configurations The configurations that will be deployed to this environment. */