diff --git a/packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/payloads/hotswap.ts b/packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/payloads/hotswap.ts index 8423d46f3..22a479fd3 100644 --- a/packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/payloads/hotswap.ts +++ b/packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/payloads/hotswap.ts @@ -1,5 +1,6 @@ import type { PropertyDifference, Resource } from '@aws-cdk/cloudformation-diff'; import type * as cxapi from '@aws-cdk/cx-api'; +import type { Duration } from './types'; import type { ResourceMetadata } from '../../resource-metadata/resource-metadata'; /** @@ -194,7 +195,7 @@ export interface HotswapDeployment { /** * The result of an attempted hotswap deployment */ -export interface HotswapResult { +export interface HotswapResult extends Duration { /** * The stack that was hotswapped */ diff --git a/packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/private/messages.ts b/packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/private/messages.ts index d4b06866f..0271eb09c 100644 --- a/packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/private/messages.ts +++ b/packages/@aws-cdk/tmp-toolkit-helpers/src/api/io/private/messages.ts @@ -5,7 +5,7 @@ import type { BootstrapEnvironmentProgress } from '../payloads/bootstrap-environ import type { MissingContext, UpdatedContext } from '../payloads/context'; import type { BuildAsset, DeployConfirmationRequest, PublishAsset, StackDeployProgress, SuccessfulDeployStackResult } from '../payloads/deploy'; import type { StackDestroy, StackDestroyProgress } from '../payloads/destroy'; -import type { HotswapDeployment } from '../payloads/hotswap'; +import type { AffectedResource, HotswapDeployment, HotswappableChange, HotswapResult, NonHotswappableChange } from '../payloads/hotswap'; import type { StackDetailsPayload } from '../payloads/list'; import type { CloudWatchLogEvent, CloudWatchLogMonitorControlEvent } from '../payloads/logs-monitor'; import type { StackRollbackProgress } from '../payloads/rollback'; @@ -202,10 +202,35 @@ export const IO = { description: 'Starting a hotswap deployment', interface: 'HotswapDeployment', }), - CDK_TOOLKIT_I5410: make.info({ + CDK_TOOLKIT_I5401: make.trace({ + code: 'CDK_TOOLKIT_I5401', + description: 'A hotswappable change is processed as part of a hotswap deployment', + interface: 'HotswappableChange', + }), + CDK_TOOLKIT_I5402: make.trace({ + code: 'CDK_TOOLKIT_I5402', + description: 'The hotswappable change has completed', + interface: 'HotswappableChange', + }), + CDK_TOOLKIT_I5403: make.info({ + code: 'CDK_TOOLKIT_I5403', + description: 'Resource affected by the current hotswap operation', + interface: 'AffectedResource', + }), + CDK_TOOLKIT_I5404: make.info({ + code: 'CDK_TOOLKIT_I5404', + description: 'Resource affected by the current hotswap operation has finished changing', + interface: 'AffectedResource', + }), + CDK_TOOLKIT_I5405: make.info({ + code: 'CDK_TOOLKIT_I5405', + description: 'Non hotswappable resource that are ignored by the hotswap deployment', + interface: 'NonHotswappableChange[]', + }), + CDK_TOOLKIT_I5410: make.info({ code: 'CDK_TOOLKIT_I5410', description: 'Hotswap deployment has ended, a full deployment might still follow if needed', - interface: 'Duration', + interface: 'HotswapResult', }), // Stack Monitor (55xx) diff --git a/packages/@aws-cdk/toolkit-lib/docs/message-registry.md b/packages/@aws-cdk/toolkit-lib/docs/message-registry.md index fbcaa004d..714a6cf13 100644 --- a/packages/@aws-cdk/toolkit-lib/docs/message-registry.md +++ b/packages/@aws-cdk/toolkit-lib/docs/message-registry.md @@ -41,7 +41,12 @@ group: Documents | `CDK_TOOLKIT_I5314` | Initial watch deployment started | `info` | n/a | | `CDK_TOOLKIT_I5315` | Queued watch deployment started | `info` | n/a | | `CDK_TOOLKIT_I5400` | Starting a hotswap deployment | `trace` | {@link HotswapDeployment} | -| `CDK_TOOLKIT_I5410` | Hotswap deployment has ended, a full deployment might still follow if needed | `info` | {@link Duration} | +| `CDK_TOOLKIT_I5401` | A hotswappable change is processed as part of a hotswap deployment | `trace` | {@link HotswappableChange} | +| `CDK_TOOLKIT_I5402` | The hotswappable change has completed | `trace` | {@link HotswappableChange} | +| `CDK_TOOLKIT_I5403` | Resource affected by the current hotswap operation | `info` | {@link AffectedResource} | +| `CDK_TOOLKIT_I5404` | Resource affected by the current hotswap operation has finished changing | `info` | {@link AffectedResource} | +| `CDK_TOOLKIT_I5405` | Non hotswappable resource that are ignored by the hotswap deployment | `info` | {@link NonHotswappableChange[]} | +| `CDK_TOOLKIT_I5410` | Hotswap deployment has ended, a full deployment might still follow if needed | `info` | {@link HotswapResult} | | `CDK_TOOLKIT_I5501` | Stack Monitoring: Start monitoring of a single stack | `info` | {@link StackMonitoringControlEvent} | | `CDK_TOOLKIT_I5502` | Stack Monitoring: Activity event for a single stack | `info` | {@link StackActivity} | | `CDK_TOOLKIT_I5503` | Stack Monitoring: Finished monitoring of a single stack | `info` | {@link StackMonitoringControlEvent} | diff --git a/packages/aws-cdk/lib/api/deployments/hotswap-deployments.ts b/packages/aws-cdk/lib/api/deployments/hotswap-deployments.ts index 00c6d2054..6eebc446d 100644 --- a/packages/aws-cdk/lib/api/deployments/hotswap-deployments.ts +++ b/packages/aws-cdk/lib/api/deployments/hotswap-deployments.ts @@ -110,7 +110,7 @@ export async function tryHotswapDeployment( hotswapPropertyOverrides, ); - await hotswapSpan.end(); + await hotswapSpan.end(result); if (result?.hotswapped === true) { return { @@ -135,7 +135,7 @@ async function hotswapDeployment( stack: cxapi.CloudFormationStackArtifact, hotswapMode: HotswapMode, hotswapPropertyOverrides: HotswapPropertyOverrides, -): Promise { +): Promise> { // resolve the environment, so we can substitute things like AWS::Region in CFN expressions const resolvedEnv = await sdkProvider.resolveEnvironment(stack.environment); // create a new SDK using the CLI credentials, because the default one will not work for new-style synthesis - @@ -162,7 +162,7 @@ async function hotswapDeployment( currentTemplate.nestedStacks, hotswapPropertyOverrides, ); - await logNonHotswappableChanges(ioSpan, nonHotswappable, hotswapMode); + await logRejectedChanges(ioSpan, nonHotswappable, hotswapMode); const hotswappableChanges = hotswappable.map(o => o.change); const nonHotswappableChanges = nonHotswappable.map(n => n.change); @@ -181,7 +181,7 @@ async function hotswapDeployment( } // apply the short-circuitable changes - await applyAllHotswappableChanges(sdk, ioSpan, hotswappable); + await applyAllHotswapOperations(sdk, ioSpan, hotswappable); return { stack, @@ -489,26 +489,30 @@ function isCandidateForHotswapping( }; } -async function applyAllHotswappableChanges(sdk: SDK, ioSpan: IMessageSpan, hotswappableChanges: HotswapOperation[]): Promise { +async function applyAllHotswapOperations(sdk: SDK, ioSpan: IMessageSpan, hotswappableChanges: HotswapOperation[]): Promise { if (hotswappableChanges.length > 0) { await ioSpan.notify(IO.DEFAULT_TOOLKIT_INFO.msg(`\n${ICON} hotswapping resources:`)); } const limit = pLimit(10); // eslint-disable-next-line @cdklabs/promiseall-no-unbounded-parallelism return Promise.all(hotswappableChanges.map(hotswapOperation => limit(() => { - return applyHotswappableChange(sdk, ioSpan, hotswapOperation); + return applyHotswapOperation(sdk, ioSpan, hotswapOperation); }))); } -async function applyHotswappableChange(sdk: SDK, ioSpan: IMessageSpan, hotswapOperation: HotswapOperation): Promise { +async function applyHotswapOperation(sdk: SDK, ioSpan: IMessageSpan, hotswapOperation: HotswapOperation): Promise { // note the type of service that was successfully hotswapped in the User-Agent const customUserAgent = `cdk-hotswap/success-${hotswapOperation.service}`; sdk.appendCustomUserAgent(customUserAgent); - const resourceText = (r: AffectedResource) => r.description ?? `${r.resourceType} '${r.physicalName ?? r.logicalId}'`; + await ioSpan.notify(IO.CDK_TOOLKIT_I5401.msg( + `Hotswapping ${hotswapOperation.change.cause.logicalId} (${hotswapOperation.change.cause.newValue.Type})...`, + hotswapOperation.change, + )); + for (const resource of hotswapOperation.change.resources) { - await ioSpan.notify(IO.DEFAULT_TOOLKIT_INFO.msg(format(` ${ICON} %s`, chalk.bold(resourceText(resource))))); + await ioSpan.notify(IO.CDK_TOOLKIT_I5403.msg(format(` ${ICON} %s`, chalk.bold(resourceText(resource))), resource)); } // if the SDK call fails, an error will be thrown by the SDK @@ -526,9 +530,14 @@ async function applyHotswappableChange(sdk: SDK, ioSpan: IMessageSpan, hots } for (const resource of hotswapOperation.change.resources) { - await ioSpan.notify(IO.DEFAULT_TOOLKIT_INFO.msg(format(`${ICON} %s %s`, chalk.bold(resourceText(resource)), chalk.green('hotswapped!')))); + await ioSpan.notify(IO.CDK_TOOLKIT_I5404.msg(format(`${ICON} %s %s`, chalk.bold(resourceText(resource)), chalk.green('hotswapped!')), resource)); } + await ioSpan.notify(IO.CDK_TOOLKIT_I5402.msg( + `Hotswapping ${hotswapOperation.change.cause.logicalId} (${hotswapOperation.change.cause.newValue.Type})... Done`, + hotswapOperation.change, + )); + sdk.removeCustomUserAgent(customUserAgent); } @@ -550,12 +559,12 @@ function formatWaiterErrorResult(result: WaiterResult) { return main; } -async function logNonHotswappableChanges( +async function logRejectedChanges( ioSpan: IMessageSpan, - nonHotswappableChanges: RejectedChange[], + rejectedChanges: RejectedChange[], hotswapMode: HotswapMode, ): Promise { - if (nonHotswappableChanges.length === 0) { + if (rejectedChanges.length === 0) { return; } /** @@ -566,9 +575,9 @@ async function logNonHotswappableChanges( * This logic prevents us from logging that change as non-hotswappable when we hotswap it. */ if (hotswapMode === 'hotswap-only') { - nonHotswappableChanges = nonHotswappableChanges.filter((change) => change.hotswapOnlyVisible === true); + rejectedChanges = rejectedChanges.filter((change) => change.hotswapOnlyVisible === true); - if (nonHotswappableChanges.length === 0) { + if (rejectedChanges.length === 0) { return; } } @@ -581,12 +590,14 @@ async function logNonHotswappableChanges( messages.push(format('%s %s', chalk.red('⚠️'), chalk.red('The following non-hotswappable changes were found:'))); } - for (const rejection of nonHotswappableChanges) { - messages.push(' ' + nonHotswappableChangeMessage(rejection.change)); + const nonHotswappableChanges = rejectedChanges.map(r => r.change); + + for (const change of nonHotswappableChanges) { + messages.push(' ' + nonHotswappableChangeMessage(change)); } messages.push(''); // newline - await ioSpan.notify(IO.DEFAULT_TOOLKIT_INFO.msg(messages.join('\n'))); + await ioSpan.notify(IO.CDK_TOOLKIT_I5405.msg(messages.join('\n'), nonHotswappableChanges)); } /**