From 128d55d020ce0526d071bfb91280ff52988af079 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 22 Aug 2018 10:02:54 +0200 Subject: [PATCH] feat(aws-cdk): display status reason on failure (#609) Make the toolkit show the StackStatusReason if deploying or destroying failed. Fixes #604. --- packages/aws-cdk/lib/api/deploy-stack.ts | 4 +++- packages/aws-cdk/lib/api/util/cloudformation.ts | 8 ++++---- .../lib/api/util/cloudformation/stack-status.ts | 12 +++++++++++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/aws-cdk/lib/api/deploy-stack.ts b/packages/aws-cdk/lib/api/deploy-stack.ts index f744ac05d8384..f4cb93c38f865 100644 --- a/packages/aws-cdk/lib/api/deploy-stack.ts +++ b/packages/aws-cdk/lib/api/deploy-stack.ts @@ -9,6 +9,7 @@ import { Mode } from './aws-auth/credentials'; import { ToolkitInfo } from './toolkit-info'; import { describeStack, stackExists, waitForChangeSet, waitForStack } from './util/cloudformation'; import { StackActivityMonitor } from './util/cloudformation/stack-activity-monitor'; +import { StackStatus } from './util/cloudformation/stack-status'; import { SDK } from './util/sdk'; type TemplateBodyParameter = { @@ -149,7 +150,8 @@ export async function destroyStack(stack: cxapi.StackInfo, sdk: SDK, deployName? const destroyedStack = await waitForStack(cfn, deployName, false); if (monitor) { monitor.stop(); } if (destroyedStack && destroyedStack.StackStatus !== 'DELETE_COMPLETE') { - throw new Error(`Failed to destroy ${deployName} (current state: ${destroyedStack.StackStatus})!`); + const status = StackStatus.fromStackDescription(destroyedStack); + throw new Error(`Failed to destroy ${deployName}: ${status}`); } return; } diff --git a/packages/aws-cdk/lib/api/util/cloudformation.ts b/packages/aws-cdk/lib/api/util/cloudformation.ts index 716425328bb1f..600b4829d73e2 100644 --- a/packages/aws-cdk/lib/api/util/cloudformation.ts +++ b/packages/aws-cdk/lib/api/util/cloudformation.ts @@ -119,15 +119,15 @@ export async function waitForStack(cfn: CloudFormation, debug('Stack %s does not exist', stackName); return null; } - const status = new StackStatus(description.StackStatus); + const status = StackStatus.fromStackDescription(description); if (!status.isStable) { - debug('Stack %s is still not stable (%s)', stackName, status.name); + debug('Stack %s is still not stable (%s)', stackName, status); return undefined; } if (status.isCreationFailure) { - throw new Error(`The stack named ${stackName} failed creation, it may need to be manually deleted from the AWS console.`); + throw new Error(`The stack named ${stackName} failed creation, it may need to be manually deleted from the AWS console: ${status}`); } else if (!status.isSuccess) { - throw new Error(`The stack named ${stackName} is in a failed state: ${status.name}`); + throw new Error(`The stack named ${stackName} is in a failed state: ${status}`); } else if (status.isDeleted) { if (failOnDeletedStack) { throw new Error(`The stack named ${stackName} was deleted`); } return undefined; diff --git a/packages/aws-cdk/lib/api/util/cloudformation/stack-status.ts b/packages/aws-cdk/lib/api/util/cloudformation/stack-status.ts index 9c2f6d8a14076..aa64dd61d0922 100644 --- a/packages/aws-cdk/lib/api/util/cloudformation/stack-status.ts +++ b/packages/aws-cdk/lib/api/util/cloudformation/stack-status.ts @@ -1,10 +1,16 @@ +import AWS = require('aws-sdk'); + /** * A utility class to inspect CloudFormation stack statuses. * * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html */ export class StackStatus { - constructor(readonly name: string) {} + public static fromStackDescription(description: AWS.CloudFormation.Stack) { + return new StackStatus(description.StackStatus, description.StackStatusReason); + } + + constructor(public readonly name: string, public readonly reason?: string) {} get isCreationFailure(): boolean { return this.name === 'ROLLBACK_COMPLETE' @@ -30,4 +36,8 @@ export class StackStatus { get isSuccess(): boolean { return !this.isRollback && !this.isFailure; } + + public toString(): string { + return this.name + (this.reason ? ` (${this.reason})` : ''); + } } \ No newline at end of file