Skip to content

Commit

Permalink
Merge pull request #613 from magieno/add-change-sets-cloudformation
Browse files Browse the repository at this point in the history
- Updated the logic.
  • Loading branch information
etiennenoel authored Nov 27, 2023
2 parents 64889ff + 96f94de commit 0cbbfa9
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 16 deletions.
60 changes: 45 additions & 15 deletions packages/aws/src/clients/cloudformation.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,42 @@ import {LogHandlerInterface} from "@pristine-ts/logging";
import {moduleScoped, tag} from "@pristine-ts/common";
import {AwsModuleKeyname} from "../aws.module.keyname";
import {
Capability,
CloudFormationClient as AWSCloudformationClient,
CloudFormationClientConfig,
CreateStackCommand,
CreateStackCommandInput,
CreateStackCommandOutput, DeleteStackCommand, DeleteStackCommandInput, DeleteStackCommandOutput,
CreateChangeSetCommand,
CreateChangeSetCommandInput,
CreateChangeSetCommandOutput,
CreateStackCommand,
CreateStackCommandInput,
CreateStackCommandOutput,
DeleteChangeSetCommand,
DeleteChangeSetCommandInput,
DeleteChangeSetCommandOutput,
DeleteStackCommand,
DeleteStackCommandInput,
DeleteStackCommandOutput,
DescribeChangeSetCommand,
DescribeChangeSetCommandInput,
DescribeChangeSetCommandOutput,
DescribeStacksCommand,
DescribeStacksCommandOutput,
ExecuteChangeSetCommand,
ExecuteChangeSetCommandInput,
ExecuteChangeSetCommandOutput,
ListChangeSetsCommand,
ListChangeSetsCommandInput,
ListChangeSetsCommandOutput,
DescribeStacksCommand,
DescribeStacksCommandOutput,
StackStatus, ChangeSetStatus,
Parameter,
Capability,
Stack, UpdateStackCommand, UpdateStackCommandInput, UpdateStackCommandOutput
Stack,
UpdateStackCommand,
UpdateStackCommandInput,
UpdateStackCommandOutput,
ChangeSetType
} from "@aws-sdk/client-cloudformation";
import {CloudformationClientInterface} from "../interfaces/cloudformation-client.interface";
import {v4 as uuid} from "uuid";
import {CloudformationDeploymentStatusEnum} from "../enums/cloudformation-deployment-status.enum";

/**
* The client to use to interact with AWS Cloudformation. It is a wrapper around the CloudformationClient of @aws-sdk/client-cloudformation.
Expand Down Expand Up @@ -255,7 +262,7 @@ export class CloudformationClient implements CloudformationClientInterface {
* @param capabilities
* @param statusCallback
*/
async deployStack(stackName: string, cloudformationTemplateS3Url: string, stackParameters: {[key in string]:string}, capabilities: Capability[], statusCallback?: (status: ChangeSetStatus, changeSetName: string) => void): Promise<ChangeSetStatus | "NO_CHANGES_TO_PERFORM"> {
async deployStack(stackName: string, cloudformationTemplateS3Url: string, stackParameters: {[key in string]:string}, capabilities: Capability[], statusCallback?: (status: CloudformationDeploymentStatusEnum, changeSetName: string) => void): Promise<CloudformationDeploymentStatusEnum> {
const parameters: Parameter[] = [];

for(const key in stackParameters) {
Expand All @@ -270,24 +277,42 @@ export class CloudformationClient implements CloudformationClientInterface {

const changeSetName = `p-${uuid()}`;

// Check if the stack exists or not first.
let changeSetType: ChangeSetType = ChangeSetType.UPDATE;

const stack = await this.getStackDescription(stackName);

if(stack === undefined) {
changeSetType = ChangeSetType.CREATE;
}

await this.createChangeSet(
{
StackName: stackName,
TemplateURL: cloudformationTemplateS3Url,
Parameters: parameters,
Capabilities: capabilities,
ChangeSetName: changeSetName,
ChangeSetType: changeSetType,
}
);

// Check if there are actual changes in the ChangeSet.
const describeChangeSetCommandOutput = await this.describeChangeSet({
await this.describeChangeSet({
StackName: stackName,
ChangeSetName: changeSetName,
})

if(describeChangeSetCommandOutput?.Changes?.length === 0) {
return "NO_CHANGES_TO_PERFORM";
const status = await this.monitorStack(stackName, changeSetName);

switch (status) {
case CloudformationDeploymentStatusEnum.Failed:
case CloudformationDeploymentStatusEnum.NoChangesToPerform:
return status;
case CloudformationDeploymentStatusEnum.InProgress:
this.logHandler.error("The returned status of 'monitorStack' should never be 'InProgress'.", {}, AwsModuleKeyname)
default:
break;
}

// Execute the changes and start monitoring
Expand All @@ -296,6 +321,10 @@ export class CloudformationClient implements CloudformationClientInterface {
ChangeSetName: changeSetName,
})

return this.monitorStack(stackName, changeSetName, statusCallback);
}

private async monitorStack(stackName: string, changeSetName: string, statusCallback?: (status: CloudformationDeploymentStatusEnum, changeSetName: string) => void): Promise<CloudformationDeploymentStatusEnum> {
while(true) {
const response = await this.describeChangeSet({
StackName: stackName,
Expand All @@ -315,15 +344,16 @@ export class CloudformationClient implements CloudformationClientInterface {
case "DELETE_IN_PROGRESS":
case "DELETE_PENDING":
if(statusCallback) {
statusCallback(status, changeSetName);
}
statusCallback(CloudformationDeploymentStatusEnum.InProgress, changeSetName);
} response.ExecutionStatus
continue;

case "CREATE_COMPLETE":
case "DELETE_COMPLETE":
return CloudformationDeploymentStatusEnum.Completed;
case "DELETE_FAILED":
case "FAILED":
return response.Status;
return CloudformationDeploymentStatusEnum.Failed;
}

await new Promise(resolve => setTimeout(resolve, 5000));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export enum CloudformationDeploymentStatusEnum {
NoChangesToPerform = "NO_CHANGES_TO_PERFORM",
Completed = "COMPLETED",
Failed = "FAILED",
InProgress = "IN_PROGRESS",
}
1 change: 1 addition & 0 deletions packages/aws/src/enums/enums.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./cloudformation-deployment-status.enum";
export * from "./dynamodb-event-type.enum";
export * from "./dynamodb-sort-order.enum";
export * from "./event-bridge-event-type.enum";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
} from "@aws-sdk/client-cloudformation";
import {CloudFormationClientConfig} from "@aws-sdk/client-cloudformation/dist-types/ts3.4";
import {AwsModuleKeyname} from "../aws.module.keyname";
import {CloudformationDeploymentStatusEnum} from "../enums/cloudformation-deployment-status.enum";

/**
* The CloudformationClient Interface defines the methods that a Cloudformation client must implement.
Expand Down Expand Up @@ -114,5 +115,5 @@ export interface CloudformationClientInterface {
* @param capabilities
* @param statusCallback
*/
deployStack(stackName: string, cloudformationTemplateS3Url: string, stackParameters: {[key in string]:string}, capabilities: Capability[], statusCallback?: (status: ChangeSetStatus, changeSetName: string) => void): Promise<ChangeSetStatus | "NO_CHANGES_TO_PERFORM">;
deployStack(stackName: string, cloudformationTemplateS3Url: string, stackParameters: {[key in string]:string}, capabilities: Capability[], statusCallback?: (status: CloudformationDeploymentStatusEnum, changeSetName: string) => void): Promise<CloudformationDeploymentStatusEnum>;
}

0 comments on commit 0cbbfa9

Please sign in to comment.