8000 appconfig: Constrain environments to a single deployment at a time · Issue #29345 · aws/aws-cdk · GitHub
[go: up one dir, main page]

Skip to content
appconfig: Constrain environments to a single deployment at a time #29345
@M-Hawkins

Description

@M-Hawkins

Describe the feature

Summary

The current L2 constructs make it easy to create Cfn deploy-time conflicts. They do not expose any mechanisms to resolve concurrent deployment without leveraging escape hatches or ignoring the L2 methods in favor of manual L1 deployments.

Context

AppConfig requires that its resources are deployed in a particular order:

  • Create an application
  • Create an environment
  • Create a configuration profile
  • Create a deployment strategy
  • Deploy the configuration

The last of these is modeled as CfnDeployment, and consists of:

  • The Environment where the deployment will occur
  • The Configuration that will be deployed
  • The Strategy that will be utilized for this deployment

The Problem

With the newly added L2 constructs, the creation of deployments is owned by the Configuration (both Hosted and Shared use the same base class implementation).

This creates a problem, as no individual Configuration is aware of another.
If more than one Configuration intends to deploy to the same Environment, they will attempt to do so at the same time, resulting in a Cfn failure.
Per the AppConfig docs:

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.

In other words, this extension of the basic use case will fail to deploy:

const app = new appconfig.Application(this, 'MyApp');
const env = new appconfig.Environment(this, 'MyEnv', {
  application: app,
});

new appconfig.HostedConfiguration(this, 'MyHostedConfig', {
  application: app,
  deployTo: [env],
  content: appconfig.ConfigurationContent.fromInlineText('This is my configuration content.'),
});

new appconfig.HostedConfiguration(this, 'MyHostedConfig2', {
  application: app,
  deployTo: [env],
  content: appconfig.ConfigurationContent.fromInlineText('This is my second configuration content.'),
});

Use Case

It is common and expected for an AppConfig Environment to host more than one Configuration.
The current behavior makes it impossible to rely solely on the L2 constructs and impacts our adoption of them.

Proposed Solution

Migrate the ownership and creation of CfnDeployment from Configuration to Environment.
Then, Cfn dependencies can be created whenever a new CfnDeployment is added, such that each deployment depends on the previous one.
This will ensure that only one CfnDeployment is in progress at any given point in time, avoiding deploy-time conflicts.

Prototype solution

In environment.ts, first expose new functionality for IEnvironment.

export interface IEnvironment extends IResource {
 
 ...

  /**
   * 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<IConfiguration>): void;

}

Then, implement this new functionality as a part of EnvironmentBase.

abstract class EnvironmentBase extends Resource implements IEnvironment, IExtensible {
  public abstract applicationId: string;
  public abstract environmentId: string;
  public abstract environmentArn: string;
  protected extensible!: ExtensibleBase;
  protected deploymentQueue: Array<CfnDeployment> = [];

  public addDeployment(configuration: IConfiguration): void {
    const queueSize = this.deploymentQueue.push(new CfnDeployment(this, `Deployment${getHash(this.environmentArn)}`, {
      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(this.addDeployment);
  }

  ...

}

Then, change the existing internal helper in configuration.ts to utilize this function instead.

  protected deployConfigToEnvironments() {
    if (!this.deployTo || !this.versionNumber) {
      return;
    }

    this.application.environments().forEach((environment) => {
      if ((this.deployTo && !this.deployTo.includes(environment))) {
        return;
      }
      environment.addDeployment(this);
    });
  }

Other Information

No response

Acknowledgements

  • I may be able to implement this feature request
  • This feature might incur a breaking change

CDK version used

2.130.0

Environment details (OS name and version, etc.)

Various Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0