Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ class ListMultipleDependentStack extends Stack {
const dependentStack1 = new DependentStack1(this, 'DependentStack1');
const dependentStack2 = new DependentStack2(this, 'DependentStack2');

this.addDependency(dependentStack1);
this.addDependency(dependentStack2);
this.addStackDependency(dependentStack1);
this.addStackDependency(dependentStack2);
}
}

Expand All @@ -192,7 +192,7 @@ class ListStack extends Stack {

const dependentStack = new DependentStack(this, 'DependentStack');

this.addDependency(dependentStack);
this.addStackDependency(dependentStack);
}
}

Expand All @@ -202,7 +202,7 @@ class DependentStack extends Stack {

const innerDependentStack = new InnerDependentStack(this, 'InnerDependentStack');

this.addDependency(innerDependentStack);
this.addStackDependency(innerDependentStack);
}
}

Expand Down Expand Up @@ -1162,7 +1162,7 @@ switch (stackSet) {

// A stack that depends on the failed stack -- used to test that '-e' does not deploy the failing stack
const dependsOnFailed = new OutputsStack(app, `${stackPrefix}-depends-on-failed`);
dependsOnFailed.addDependency(failed);
dependsOnFailed.addStackDependency(failed);

if (process.env.ENABLE_VPC_TESTING) { // Gating so we don't do context fetching unless that's what we are here for
const env = { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const queue = new cdk.CfnResource(stack, 'Queue', {
});

const assertionStack = new cdk.Stack(app, 'TypeScriptAssertions');
assertionStack.addDependency(stack);
assertionStack.addStackDependency(stack);

const integ = new IntegTest(app, 'TypeScript', {
testCases: [stack],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { HotswapChange } from './common';
import { classifyChanges, nonHotswappableChange } from './common';
import { NonHotswappableReason } from '../../payloads';
import type { ResourceChange } from '../../payloads/hotswap';
import { ToolkitError } from '../../toolkit/toolkit-error';
import type { SDK } from '../aws-auth/private';
import { CfnEvaluationException, type EvaluateCloudFormationTemplate } from '../cloudformation';

Expand Down Expand Up @@ -92,11 +93,15 @@ export async function isHotswappableCloudControlChange(
return;
}

await cloudControl.updateResource({
TypeName: resourceType,
Identifier: identifier,
PatchDocument: JSON.stringify(patchOps),
});
try {
await cloudControl.updateResource({
TypeName: resourceType,
Identifier: identifier,
PatchDocument: JSON.stringify(patchOps),
});
} catch (e) {
throw ToolkitError.withCause('HotswapFailed', `Failed to update ${identifier} (${resourceType})`, e);
}
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -576,10 +576,12 @@ async function applyHotswapOperation(sdk: SDK, ioSpan: IMessageSpan<any>, hotswa
} catch (e: any) {
if (e.name === 'TimeoutError' || e.name === 'AbortError') {
const result: WaiterResult = JSON.parse(formatErrorMessage(e));
const error = new ToolkitError('HotswapWaiterFailed', formatWaiterErrorResult(result));
error.name = e.name;
const error = ToolkitError.withCause('HotswapWaiterFailed', `[${hotswapOperation.service}] ` + formatWaiterErrorResult(result), e);
throw error;
}

// Always prepend the hotswap service to errors
e.message = `[${hotswapOperation.service}] ${e.message}`;
throw e;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/aws-cdk/lib/cli/cdk-toolkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2576,8 +2576,8 @@ class WorkGraphDeploymentActions implements WorkGraphActions {
// It has to be exactly this string because an integration test tests for
// "bold(stackname) failed: ResourceNotReady: <error>"
const code = ToolkitError.isToolkitError(e) ? e.name : 'DeployStackFailed'; // Formerly 'DeployFailed'
const newMessage = [`❌ ${chalk.bold(stack.stackName)} failed:`, ...(e.name ? [`${e.name}:`] : []), e.message].join(' ');
const wrappedError = new ToolkitError(code, newMessage);
const newMessage = `❌ ${chalk.bold(stack.stackName)} failed to deploy`;
const wrappedError = ToolkitError.withCause(code, newMessage, e);

error = {
name: cdkCliErrorName(wrappedError),
Expand Down
51 changes: 38 additions & 13 deletions packages/aws-cdk/lib/cli/pretty-print-error.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,57 @@
/* eslint-disable no-console */
import chalk from 'chalk';

interface PrettyErrorPrinterOptions {
/**
* Print the error as an expected outcome, for example when a user declined a confirmation prompt.
* While thrown as exceptions, these should visually not be presented as a crash.
*/
readonly soft: boolean;
/**
* Prints as much debug output as possible.
*/
readonly debug: boolean;
}

/* c8 ignore start */
export function prettyPrintError(error: unknown, options: { soft?: boolean; debug?: boolean } = {}) {
export function prettyPrintError(error: unknown, options: PrettyErrorPrinterOptions = { soft: false, debug: false }) {
const err = ensureError(error);
const debug = options.debug ?? false;
const soft = options.soft ?? false;

// A soft error (for example a user-declined confirmation) is an expected outcome, not a crash.
// Present the message less scary.
const errorPaint = soft ? chalk.yellow : chalk.red;
const errorPaint = options.soft ? chalk.yellow : chalk.red;

console.error(errorPaint(err.message));
if (err.cause && !soft) {
const cause = ensureError(err.cause);
console.error(chalk.yellow(cause.message));
printTrace(cause, debug);
printCauses(err, options);

// Log the stack trace if we're on a developer workstation. Otherwise this will be into a minified
// file and the printed code line and stack trace are huge and useless.
if (options.debug) {
printTraces(err);
}
}

printTrace(err, debug);
/**
* Recursively print all error causes recursively.
*/
function printCauses(err: Error, options: PrettyErrorPrinterOptions) {
if (err.cause && !options.soft) {
const cause = ensureError(err.cause);
console.error(chalk.yellow(`‣ ${cause.name}: ${cause.message}`));
printCauses(cause, options);
}
}

function printTrace(err: Error, debug = false) {
// Log the stack trace if we're on a developer workstation. Otherwise this will be into a minified
// file and the printed code line and stack trace are huge and useless.
if (err.stack && debug) {
/**
* Recursively print all error traces.
*/
function printTraces(err: Error) {
if (err.stack) {
console.debug(chalk.gray(err.stack));
}
if (err.cause) {
printTraces(ensureError(err.cause));
}
}

function ensureError(value: unknown): Error {
Expand Down
8 changes: 6 additions & 2 deletions packages/aws-cdk/test/commands/deploy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ describe('deploy parameters forwarded to CloudFormation', () => {
});

describe('deploy failures', () => {
test('wraps a failed resource deployment as "<stack> failed: <error>" and rethrows', async () => {
test('wraps a failed resource deployment with cause', async () => {
// Error messages are not emitted to the IoHost, so the snapshot
// shows the deploy stopping mid-flight rather than a failure line.
const resourceFailure = Object.assign(
Expand All @@ -558,7 +558,11 @@ describe('deploy failures', () => {
}).catch((e) => e);

expect(stripAnsi(error.message)).toBe(
'❌ Test-Stack-A failed: ResourceNotReady: Resource TemplateName did not stabilize (reason: CREATE_FAILED)',
'❌ Test-Stack-A failed to deploy',
);
expect(stripAnsi(error.cause.name)).toBe('ResourceNotReady');
expect(stripAnsi(error.cause.message)).toContain(
'Resource TemplateName did not stabilize (reason: CREATE_FAILED)',
);
expect(error.name).toBe('DeployStackFailed');
});
Expand Down
Loading