@go-to-k/cdkd 0.284.60 → 0.284.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{asg-provider-D9nlNFCU.js → asg-provider-BfX7AwFi.js} +2 -2
- package/dist/{asg-provider-D9nlNFCU.js.map → asg-provider-BfX7AwFi.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-Cc6OYj-d.js → deploy-engine-D8AIgkwy.js} +36 -10
- package/dist/{deploy-engine-Cc6OYj-d.js.map → deploy-engine-D8AIgkwy.js.map} +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-BYmBfcxJ.js → program-XLMSMzWe.js} +315 -35
- package/dist/{program-BYmBfcxJ.js.map → program-XLMSMzWe.js.map} +1 -1
- package/dist/{version-Dqo5kOZi.js → version-DoW7XkO0.js} +2 -2
- package/dist/{version-Dqo5kOZi.js.map → version-DoW7XkO0.js.map} +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ar as isCdkdError, Dr as StateError, Jt as TemplateParser, Mn as Synthesizer, Or as SynthesisError, Sr as ProvisioningError, Ut as DiffCalculator, W as IAMRoleProvider, Yt as LockManager, Zt as S3StateBackend, ar as getAwsClients, cr as AssetError, er as AssemblyReader, fr as DependencyError, ir as AwsClients, jr as normalizeAwsError, kr as formatError, lr as CdkdError, nr as clearBucketRegionCache, or as resetAwsClients, q as ProviderRegistry, qt as DagBuilder, r as DeployEngine, rn as AssetPublisher, rr as resolveBucketRegion, rt as CloudControlProvider, sr as setAwsClients, ur as ConfigError, vr as LockError } from "./deploy-engine-
|
|
1
|
+
import { Ar as isCdkdError, Dr as StateError, Jt as TemplateParser, Mn as Synthesizer, Or as SynthesisError, Sr as ProvisioningError, Ut as DiffCalculator, W as IAMRoleProvider, Yt as LockManager, Zt as S3StateBackend, ar as getAwsClients, cr as AssetError, er as AssemblyReader, fr as DependencyError, ir as AwsClients, jr as normalizeAwsError, kr as formatError, lr as CdkdError, nr as clearBucketRegionCache, or as resetAwsClients, q as ProviderRegistry, qt as DagBuilder, r as DeployEngine, rn as AssetPublisher, rr as resolveBucketRegion, rt as CloudControlProvider, sr as setAwsClients, ur as ConfigError, vr as LockError } from "./deploy-engine-D8AIgkwy.js";
|
|
2
2
|
import { n as getLogger, r as setLogger, t as ConsoleLogger } from "./logger-zRrlbaQt.js";
|
|
3
3
|
|
|
4
4
|
export { AssemblyReader, AssetError, AssetPublisher, AwsClients, CdkdError, CloudControlProvider, ConfigError, ConsoleLogger, DagBuilder, DependencyError, DeployEngine, DiffCalculator, IAMRoleProvider, LockError, LockManager, ProviderRegistry, ProvisioningError, S3StateBackend, StateError, SynthesisError, Synthesizer, TemplateParser, clearBucketRegionCache, formatError, getAwsClients, getLogger, isCdkdError, normalizeAwsError, resetAwsClients, resolveBucketRegion, setAwsClients, setLogger };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { $ as endCommandInterruptScope, $n as derivePartitionAndUrlSuffix, $t as exportNamesCarriedFrom, A as renderStatefulReason, An as AssetManifestLoader, At as isSingleDynamicReferenceToken, B as exportAliasCollisionScrubWarning, Bn as resolveStateBucketWithDefault, Bt as s3BucketRegionalDomainName, C as isFinalSnapshotError, Cn as buildDockerImage, Cr as ResourceTimeoutError, Ct as requireConfigString, D as extractDeploymentEventError, Dn as partitionSensitiveEnv, Dt as createSecretMasker, E as makeCanonicalizePropertiesFn, En as getDockerCmd, Er as StackTerminationProtectionError, Et as TEMPLATE_SOURCED_RULES, F as green, Fn as getLegacyStateBucketName, Fr as isThrottlingError, Ft as classifyReplaySecretRegion, G as collectInlinePolicyNamesManagedBySiblings, Gn as CFN_TEMPLATE_BODY_LIMIT, Gt as describeTypeWithThrottleRetry, H as secretBearingStateKeyWarning, Hn as resolveUseCdkBootstrapAssets, Ht as applyRoleArnIfSet, I as red, In as resolveApp, Ir as markNonRetryable, It as producerRegionsFromState, J as findActionableSilentDrops, Jn as findLargeInlineResources, Jt as TemplateParser, K as clearOnUpdateRemoval, Kn as CFN_TEMPLATE_URL_LIMIT, Kt as withRetry, L as yellow, Ln as resolveAutoAssetStorage, Lt as s3BucketArn, M as bold, Mn as Synthesizer, Mr as withErrorHandling, Mt as maskSecretsInText, N as cyan, Nn as synthesisStatusMessage, Nr as isMarkedNonRetryable, Nt as redactSecretsForState, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as runDockerForeground, Ot as dynamicReferenceTokens, P as gray, Pn as getDefaultStateBucketName, Pr as isRetryableTransientError, Pt as scrubResourceRecord, Q as beginCommandInterruptScope, Qn as canonicalizeRegion, Qt as rebuildClientForBucketRegion, R as collectDeclaredOutputNames, Rn as resolveCaptureObservedState, Rt as s3BucketDomainName, S as createPreDeleteFinalSnapshot, Sn as buildDenyExternalAccessPolicy, Sr as ProvisioningError, St as requireConfigObject, T as unsupportedFinalSnapshotError, Tn as formatDockerLoginError, Tr as StackHasActiveImportsError, Tt as STATE_SOURCED_READBACK_RULES, U as stateKeySecretExposure, Un as stateBucketExistenceConfirmed, Ut as DiffCalculator, V as isExportAliasCollision, Vn as resolveStateBucketWithDefaultAndSource, Vt as s3BucketWebsiteUrl, W as IAMRoleProvider, Wn as warnDeprecatedNoPrefixCliFlag, Wt as INTRINSIC_KEYS, X as CUSTOM_RESOURCE_RESPONSE_PREFIX, Xn as expectedOwnerParam, Xt as displaySafe, Y as findSilentDropProperties, Yn as uploadCfnTemplate, Yt as LockManager, Z as DEFAULT_STATE_PREFIX, Zn as PARTITION_TABLE, Zt as S3StateBackend, _ as computeImplicitDeleteEdges, _n as isCrossRegionRedirect, _r as LocalStartServiceError, _t as configBooleanRefusal, a as DeploymentEventsStore, an as WorkGraph, ar as getAwsClients, at as disableInstanceApiTermination, b as buildFinalSnapshotIdentifier, bn as validateAssetBucketName, br as NestedStackChildDirectDestroyError, bt as replayWarn, c as replayFailedOperations, cn as loadPublishableAssetManifest, ct as carriesDynamicReference, d as updatePartialReason, dn as stripControlChars, dr as CrossAccountSecretRefusalError, dt as refStateLookupFromResource, en as importableOutputKeys, er as AssemblyReader, et as interruptWatchListenerCount, f as UNSPECIFIED_SKIP_REASON, fn as AssetModeResolver, ft as WAFv2WebACLProvider, g as IMPLICIT_DELETE_DEPENDENCIES, gn as getBootstrapMarkerKey, gr as LocalMigrateError, gt as coerceCfnBoolean, h as maskingRetryLogger, hn as ensureAssetStorage, hr as LocalInvokeBuildError$1, ht as assertRegionMatch, i as DeploymentEventsReader, in as stringifyValue, ir as AwsClients, it as slowCcOperationTimeoutMs, j as formatResourceLine, jn as getDockerImageBySourceHash, jr as normalizeAwsError, jt as maskSecretsInError, k as isStatefulRecreateTargetSync, kn as runDockerStreaming, kt as errorCauseChain, l as replayRollback, ln as rewriteTemplateAssetReferences, lr as CdkdError, lt as cfnRefValueFromPhysicalId, m as withResourceDeadline, mn as assertAssetBucketRegion, mr as DynamicReferenceRegionAmbiguousError, mt as resolveExplicitPhysicalId, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as shouldRetainResource, nt as startInterruptWatch, o as planFailedOps, on as buildAssetRedirectMap, or as resetAwsClients, ot as isTerminationProtectionPropagationError, p as deleteSkipReason, pn as BOOTSTRAP_MARKER_PREFIX, pr as DeployCancelledError, pt as normalizeAwsTagsToCfn, q as ProviderRegistry, qn as MIGRATE_TMP_PREFIX, qt as DagBuilder, r as DeployEngine, rn as AssetPublisher, rr as resolveBucketRegion, rt as CloudControlProvider, s as planRollback, sn as createAssetRedirectResolver, sr as setAwsClients, st as IntrinsicFunctionResolver, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as importableOutputs, tr as processStackMessages, tt as isInterruptedWaitError, u as updatePartialMessage, un as escapeRegExp, ut as getAccountInfo, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as parseBootstrapMarker, vt as configStringRefusal, w as refusesFinalSnapshot, wn as dockerSpawnEnvWithSensitive, wr as ResourceUpdateNotSupportedError, wt as STATE_SOURCED_CROSS_GENERATION_RULES, x as ccRoutedFinalSnapshotError, xn as validateContainerRepoName, xr as PartialFailureError, xt as requireConfigArray, y as PRE_DELETE_SNAPSHOT_TYPES, yn as readBootstrapMarkerBody, yr as MissingCdkCliError, yt as readConfigString, z as collectPublishedOutputNames, zn as resolveSkipPrefix, zt as s3BucketDualStackDomainName } from "./deploy-engine-
|
|
1
|
+
import { $ as endCommandInterruptScope, $n as derivePartitionAndUrlSuffix, $t as exportNamesCarriedFrom, A as renderStatefulReason, An as AssetManifestLoader, At as isSingleDynamicReferenceToken, B as exportAliasCollisionScrubWarning, Bn as resolveStateBucketWithDefault, Bt as s3BucketRegionalDomainName, C as isFinalSnapshotError, Cn as buildDockerImage, Cr as ResourceTimeoutError, Ct as requireConfigString, D as extractDeploymentEventError, Dn as partitionSensitiveEnv, Dt as createSecretMasker, E as makeCanonicalizePropertiesFn, En as getDockerCmd, Er as StackTerminationProtectionError, Et as TEMPLATE_SOURCED_RULES, F as green, Fn as getLegacyStateBucketName, Fr as isThrottlingError, Ft as classifyReplaySecretRegion, G as collectInlinePolicyNamesManagedBySiblings, Gn as CFN_TEMPLATE_BODY_LIMIT, Gt as describeTypeWithThrottleRetry, H as secretBearingStateKeyWarning, Hn as resolveUseCdkBootstrapAssets, Ht as applyRoleArnIfSet, I as red, In as resolveApp, Ir as markNonRetryable, It as producerRegionsFromState, J as findActionableSilentDrops, Jn as findLargeInlineResources, Jt as TemplateParser, K as clearOnUpdateRemoval, Kn as CFN_TEMPLATE_URL_LIMIT, Kt as withRetry, L as yellow, Ln as resolveAutoAssetStorage, Lt as s3BucketArn, M as bold, Mn as Synthesizer, Mr as withErrorHandling, Mt as maskSecretsInText, N as cyan, Nn as synthesisStatusMessage, Nr as isMarkedNonRetryable, Nt as redactSecretsForState, O as MULTI_REGION_RECREATE_BLOCKED_TYPES, On as runDockerForeground, Ot as dynamicReferenceTokens, P as gray, Pn as getDefaultStateBucketName, Pr as isRetryableTransientError, Pt as scrubResourceRecord, Q as beginCommandInterruptScope, Qn as canonicalizeRegion, Qt as rebuildClientForBucketRegion, R as collectDeclaredOutputNames, Rn as resolveCaptureObservedState, Rt as s3BucketDomainName, S as createPreDeleteFinalSnapshot, Sn as buildDenyExternalAccessPolicy, Sr as ProvisioningError, St as requireConfigObject, T as unsupportedFinalSnapshotError, Tn as formatDockerLoginError, Tr as StackHasActiveImportsError, Tt as STATE_SOURCED_READBACK_RULES, U as stateKeySecretExposure, Un as stateBucketExistenceConfirmed, Ut as DiffCalculator, V as isExportAliasCollision, Vn as resolveStateBucketWithDefaultAndSource, Vt as s3BucketWebsiteUrl, W as IAMRoleProvider, Wn as warnDeprecatedNoPrefixCliFlag, Wt as INTRINSIC_KEYS, X as CUSTOM_RESOURCE_RESPONSE_PREFIX, Xn as expectedOwnerParam, Xt as displaySafe, Y as findSilentDropProperties, Yn as uploadCfnTemplate, Yt as LockManager, Z as DEFAULT_STATE_PREFIX, Zn as PARTITION_TABLE, Zt as S3StateBackend, _ as computeImplicitDeleteEdges, _n as isCrossRegionRedirect, _r as LocalStartServiceError, _t as configBooleanRefusal, a as DeploymentEventsStore, an as WorkGraph, ar as getAwsClients, at as disableInstanceApiTermination, b as buildFinalSnapshotIdentifier, bn as validateAssetBucketName, br as NestedStackChildDirectDestroyError, bt as replayWarn, c as replayFailedOperations, cn as loadPublishableAssetManifest, ct as carriesDynamicReference, d as updatePartialReason, dn as stripControlChars, dr as CrossAccountSecretRefusalError, dt as refStateLookupFromResource, en as importableOutputKeys, er as AssemblyReader, et as interruptWatchListenerCount, f as UNSPECIFIED_SKIP_REASON, fn as AssetModeResolver, ft as WAFv2WebACLProvider, g as IMPLICIT_DELETE_DEPENDENCIES, gn as getBootstrapMarkerKey, gr as LocalMigrateError, gt as coerceCfnBoolean, h as maskingRetryLogger, hn as ensureAssetStorage, hr as LocalInvokeBuildError$1, ht as assertRegionMatch, i as DeploymentEventsReader, in as stringifyValue, ir as AwsClients, it as slowCcOperationTimeoutMs, j as formatResourceLine, jn as getDockerImageBySourceHash, jr as normalizeAwsError, jt as maskSecretsInError, k as isStatefulRecreateTargetSync, kn as runDockerStreaming, kt as errorCauseChain, l as replayRollback, ln as rewriteTemplateAssetReferences, lr as CdkdError, lt as cfnRefValueFromPhysicalId, m as withResourceDeadline, mn as assertAssetBucketRegion, mr as DynamicReferenceRegionAmbiguousError, mt as resolveExplicitPhysicalId, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as shouldRetainResource, nt as startInterruptWatch, o as planFailedOps, on as buildAssetRedirectMap, or as resetAwsClients, ot as isTerminationProtectionPropagationError, p as deleteSkipReason, pn as BOOTSTRAP_MARKER_PREFIX, pr as DeployCancelledError, pt as normalizeAwsTagsToCfn, q as ProviderRegistry, qn as MIGRATE_TMP_PREFIX, qt as DagBuilder, r as DeployEngine, rn as AssetPublisher, rr as resolveBucketRegion, rt as CloudControlProvider, s as planRollback, sn as createAssetRedirectResolver, sr as setAwsClients, st as IntrinsicFunctionResolver, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as importableOutputs, tr as processStackMessages, tt as isInterruptedWaitError, u as updatePartialMessage, un as escapeRegExp, ut as getAccountInfo, v as ATOMIC_FINAL_SNAPSHOT_TYPES, vn as parseBootstrapMarker, vt as configStringRefusal, w as refusesFinalSnapshot, wn as dockerSpawnEnvWithSensitive, wr as ResourceUpdateNotSupportedError, wt as STATE_SOURCED_CROSS_GENERATION_RULES, x as ccRoutedFinalSnapshotError, xn as validateContainerRepoName, xr as PartialFailureError, xt as requireConfigArray, y as PRE_DELETE_SNAPSHOT_TYPES, yn as readBootstrapMarkerBody, yr as MissingCdkCliError, yt as readConfigString, z as collectPublishedOutputNames, zn as resolveSkipPrefix, zt as s3BucketDualStackDomainName } from "./deploy-engine-D8AIgkwy.js";
|
|
2
2
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as getCurrentStackName, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-DoW7XkO0.js";
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-BfX7AwFi.js";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
7
7
|
import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -2032,7 +2032,9 @@ function createListCommand() {
|
|
|
2032
2032
|
* cancellation, and GitLab CI / `docker stop` / Kubernetes send SIGTERM
|
|
2033
2033
|
* directly. cdkd's graceful-interrupt handling (the top-level command
|
|
2034
2034
|
* handlers, the deploy engine's partial-state save, and the per-provider
|
|
2035
|
-
* poll-abort listeners in CustomResource /
|
|
2035
|
+
* poll-abort listeners in CustomResource / CloudFront / ACM — `grep -rn
|
|
2036
|
+
* "process.on('SIGINT'" src/provisioning/providers/` regenerates that closed
|
|
2037
|
+
* set, and Route53 is NOT in it) is
|
|
2036
2038
|
* registered on SIGINT only, so an unhandled SIGTERM killed the process
|
|
2037
2039
|
* mid-run, skipped every `finally`, and stranded the stack lock for its
|
|
2038
2040
|
* full TTL.
|
|
@@ -2082,7 +2084,7 @@ function installSigtermForwarder() {
|
|
|
2082
2084
|
* `cdkd deploy` has had one of these since issue #1348 (`deploy.ts`'s
|
|
2083
2085
|
* `topLevelSigintHandler`). The destroy side had none, so the ONLY channel
|
|
2084
2086
|
* carrying "the user interrupted" from `runDestroyForStack` back to the
|
|
2085
|
-
* `--all` loop was `
|
|
2087
|
+
* `--all` loop was `DestroyRunnerResult.interrupted` — a boolean assigned ONCE,
|
|
2086
2088
|
* inside the runner's `try`, after its level loop. Every instant where the
|
|
2087
2089
|
* runner's `draining` flag flips AFTER that assignment is an instant where
|
|
2088
2090
|
* `cdkd destroy --all` went on to delete the NEXT stack after the user asked
|
|
@@ -2106,10 +2108,38 @@ function installSigtermForwarder() {
|
|
|
2106
2108
|
*
|
|
2107
2109
|
* A handler that lives for the whole command closes both, because it is armed
|
|
2108
2110
|
* in every window rather than in the ones the runner happens to span. The two
|
|
2109
|
-
* flags answer different questions and both are kept
|
|
2110
|
-
*
|
|
2111
|
-
*
|
|
2112
|
-
*
|
|
2111
|
+
* flags answer different questions and both are kept, one owner each:
|
|
2112
|
+
*
|
|
2113
|
+
* - PER-STACK, "did THIS stack finish, or is there work left in it?" —
|
|
2114
|
+
* `DestroyRunnerResult.interrupted`, owned by `destroy-runner.ts`. It has FOUR
|
|
2115
|
+
* consumers: that stack's state preservation, its summary line,
|
|
2116
|
+
* `destroy.ts`'s `--purge-events` skip, and — the one earlier revisions of
|
|
2117
|
+
* this list omitted — `NestedStackProvider.delete`, which reads the CHILD's
|
|
2118
|
+
* result to decide whether the child row is `{ outcome: 'skipped' }` on the
|
|
2119
|
+
* parent and whether its failure is `markNonRetryable`.
|
|
2120
|
+
*
|
|
2121
|
+
* That fourth consumer is why the `&& statePreserved` gate below is a
|
|
2122
|
+
* behavior change beyond the exit code: a fully-destroyed child that takes
|
|
2123
|
+
* a tail signal no longer reports `interrupted`, so the parent no longer
|
|
2124
|
+
* marks its row skipped. That is the CORRECT reading — `skipped` means
|
|
2125
|
+
* "cdkd could not address this, the resources may still exist in AWS",
|
|
2126
|
+
* which is false of a child whose every resource was deleted and whose
|
|
2127
|
+
* state record is gone; it would preserve the parent's row for a child that
|
|
2128
|
+
* no longer exists and exit 2 over a completed teardown. Stopping the
|
|
2129
|
+
* surrounding `--all` run is a DIFFERENT question, still answered by this
|
|
2130
|
+
* watch, which the parent command holds for its whole run.
|
|
2131
|
+
* - PER-RUN, "has the user asked this COMMAND to stop?" — this watch. Its
|
|
2132
|
+
* consumers are the loops' `break`s and the terminal exit-code verdict
|
|
2133
|
+
* (which pairs it with `stoppedEarly`, so a signal in a completed run's
|
|
2134
|
+
* tail does not report unfinished work there is none of).
|
|
2135
|
+
*
|
|
2136
|
+
* `--purge-events` moved onto the per-stack answer when the runner's outer
|
|
2137
|
+
* re-sync was gated on `statePreserved`. Before that the per-stack flag could
|
|
2138
|
+
* be true over a stack whose state was already DELETED, so the purge had to
|
|
2139
|
+
* read the command-level one to stay conservative — at the cost of the
|
|
2140
|
+
* opposite error: a tail signal on the LAST stack skipped the purge while the
|
|
2141
|
+
* command exited 0, reporting full success with the events left behind. With
|
|
2142
|
+
* the per-stack flag accurate, one read serves both directions.
|
|
2113
2143
|
*
|
|
2114
2144
|
* Note on the counterpart contract: `deploy-engine.ts` removes its own SIGINT
|
|
2115
2145
|
* handler before releasing its lock, which is safe only because `deploy.ts`'s
|
|
@@ -2123,6 +2153,11 @@ function installSigtermForwarder() {
|
|
|
2123
2153
|
*/
|
|
2124
2154
|
function watchCommandInterrupt(opts) {
|
|
2125
2155
|
let interrupted = false;
|
|
2156
|
+
const promptAbort = new AbortController();
|
|
2157
|
+
function markInterrupted() {
|
|
2158
|
+
interrupted = true;
|
|
2159
|
+
promptAbort.abort();
|
|
2160
|
+
}
|
|
2126
2161
|
let stacksInFlight = 0;
|
|
2127
2162
|
/**
|
|
2128
2163
|
* Is some OTHER listener going to handle this signal gracefully?
|
|
@@ -2145,27 +2180,30 @@ function watchCommandInterrupt(opts) {
|
|
|
2145
2180
|
* - `interrupt-watch.ts`'s shared handler, which is a LATCH rather than a
|
|
2146
2181
|
* graceful owner — deferring to it is exactly the swallow above.
|
|
2147
2182
|
*
|
|
2148
|
-
* A provider's transient wait listener
|
|
2149
|
-
*
|
|
2150
|
-
*
|
|
2151
|
-
*
|
|
2152
|
-
*
|
|
2183
|
+
* A provider's transient wait listener does count, and that is correct
|
|
2184
|
+
* rather than a false positive: three providers install one — grep
|
|
2185
|
+
* `process.on('SIGINT'` under `src/provisioning/providers/**` for the
|
|
2186
|
+
* closed set, today `acm-certificate-provider.ts`,
|
|
2187
|
+
* `cloudfront-distribution-provider.ts` and `custom-resource-provider.ts`,
|
|
2188
|
+
* NOT Route53, whose provider registers none — and each exists only
|
|
2189
|
+
* DURING a provider wait, which is strictly inside the runner handler's own
|
|
2190
|
+
* lifetime, so one cannot be present while the runner's is absent.
|
|
2153
2191
|
*/
|
|
2154
2192
|
function gracefulOwnerArmed() {
|
|
2155
2193
|
return process.listeners("SIGINT").filter((l) => l !== handler).length - interruptWatchListenerCount() > 0;
|
|
2156
2194
|
}
|
|
2157
2195
|
const handler = () => {
|
|
2158
2196
|
if (stacksInFlight > 0 && gracefulOwnerArmed()) {
|
|
2159
|
-
|
|
2197
|
+
markInterrupted();
|
|
2160
2198
|
return;
|
|
2161
2199
|
}
|
|
2162
2200
|
if (interrupted) {
|
|
2163
|
-
process.stderr.write("\nForce-quit.\n");
|
|
2201
|
+
process.stderr.write("\nForce-quit.\nIf the next run reports a stack lock, release it with: cdkd force-unlock <stack-name>\n");
|
|
2164
2202
|
process.exit(130);
|
|
2165
2203
|
}
|
|
2166
|
-
|
|
2204
|
+
markInterrupted();
|
|
2167
2205
|
if (stacksInFlight > 0) {
|
|
2168
|
-
process.stderr.write(`\nInterrupted before ${opts.command} armed its per-stack teardown — quitting now.
|
|
2206
|
+
process.stderr.write(`\nInterrupted before ${opts.command} armed its per-stack teardown — quitting now. No delete was issued for this stack; on an --all run, stacks processed earlier are already destroyed.\nIf the next run reports a stack lock, release it with: cdkd force-unlock <stack-name>\n`);
|
|
2169
2207
|
process.exit(130);
|
|
2170
2208
|
}
|
|
2171
2209
|
process.stderr.write(`\nInterrupted — ${opts.command} will stop before starting another stack (press Ctrl-C again to force-quit)...\n`);
|
|
@@ -2173,6 +2211,7 @@ function watchCommandInterrupt(opts) {
|
|
|
2173
2211
|
process.on("SIGINT", handler);
|
|
2174
2212
|
return {
|
|
2175
2213
|
interrupted: () => interrupted,
|
|
2214
|
+
signal: promptAbort.signal,
|
|
2176
2215
|
async runStack(fn) {
|
|
2177
2216
|
stacksInFlight += 1;
|
|
2178
2217
|
try {
|
|
@@ -2186,6 +2225,22 @@ function watchCommandInterrupt(opts) {
|
|
|
2186
2225
|
}
|
|
2187
2226
|
};
|
|
2188
2227
|
}
|
|
2228
|
+
/**
|
|
2229
|
+
* Did this rejection come from a prompt aborted through
|
|
2230
|
+
* {@link CommandInterruptWatch.signal}?
|
|
2231
|
+
*
|
|
2232
|
+
* Deliberately duck-typed rather than `instanceof`: what `readline/promises`
|
|
2233
|
+
* rejects with is an internal Node error class in some versions and a
|
|
2234
|
+
* `DOMException` in others, and neither is importable. Both spellings carry
|
|
2235
|
+
* `name === 'AbortError'` / `code === 'ABORT_ERR'`, and the caller pairs this
|
|
2236
|
+
* with its own `watch.interrupted()` check, so a same-named error from an
|
|
2237
|
+
* unrelated abort cannot be mistaken for the user's Ctrl-C.
|
|
2238
|
+
*/
|
|
2239
|
+
function isPromptAbortError(error) {
|
|
2240
|
+
if (typeof error !== "object" || error === null) return false;
|
|
2241
|
+
const { name, code } = error;
|
|
2242
|
+
return name === "AbortError" || code === "ABORT_ERR";
|
|
2243
|
+
}
|
|
2189
2244
|
|
|
2190
2245
|
//#endregion
|
|
2191
2246
|
//#region src/deployment/recreate-targets.ts
|
|
@@ -34030,6 +34085,86 @@ const DEPLOYMENT_CONFIG_PRESERVE_KEYS_CAMEL = /* @__PURE__ */ new Set(["hookDeta
|
|
|
34030
34085
|
*/
|
|
34031
34086
|
const SERVICE_CONNECT_PRESERVE_KEYS = /* @__PURE__ */ new Set(["Options"]);
|
|
34032
34087
|
/**
|
|
34088
|
+
* SHARED RATIONALE for both `CIRCUIT_BREAKER_*_DEFAULT` constants below —
|
|
34089
|
+
* WHERE THE VALUES COME FROM, and when they may be sent (issue #1861).
|
|
34090
|
+
*
|
|
34091
|
+
* They are the values CloudFormation was MEASURED to produce when a template
|
|
34092
|
+
* that previously DECLARED the member stops declaring it (us-east-1,
|
|
34093
|
+
* 2026-08-13, same service, `Rollback` flipped in the same call so the update
|
|
34094
|
+
* demonstrably applied and service identity captured on both sides so a
|
|
34095
|
+
* replacement could not be mistaken for a reset): `resetOnHealthyTask` false
|
|
34096
|
+
* -> `true`, `thresholdConfiguration` {COUNT, 7} -> {BOUNDED_PERCENT, 50}.
|
|
34097
|
+
* They match the SDK's documented defaults for both members, and the SDK
|
|
34098
|
+
* documents those unconditionally — they do not vary by scheduling strategy or
|
|
34099
|
+
* deployment type (see the strategy note in `updateService`'s comment block).
|
|
34100
|
+
*
|
|
34101
|
+
* They are used ONLY as `clearOnUpdateRemoval`'s clear value, i.e. only on a
|
|
34102
|
+
* previous-present / current-absent REMOVAL. They must NEVER be sent for a
|
|
34103
|
+
* member the template never declared — a third measurement in the same
|
|
34104
|
+
* session set both members OUT OF BAND on a service whose template never
|
|
34105
|
+
* declared them, then ran a CloudFormation update that flipped `Rollback`
|
|
34106
|
+
* INSIDE the block: the out-of-band `false` / {COUNT, 9} BOTH survived. That
|
|
34107
|
+
* arm is what excludes the competing reading "CFn re-serializes the struct
|
|
34108
|
+
* whenever its declared content changed" and makes this a genuine removal.
|
|
34109
|
+
*/
|
|
34110
|
+
/**
|
|
34111
|
+
* `DeploymentCircuitBreaker.ResetOnHealthyTask`'s AWS default. A primitive, so
|
|
34112
|
+
* it needs no copy-on-use treatment — unlike its object-valued sibling below.
|
|
34113
|
+
* Provenance and the send-only-on-removal rule: see the shared note above.
|
|
34114
|
+
*/
|
|
34115
|
+
const CIRCUIT_BREAKER_RESET_ON_HEALTHY_TASK_DEFAULT = true;
|
|
34116
|
+
/**
|
|
34117
|
+
* `DeploymentCircuitBreaker.ThresholdConfiguration`'s AWS default. Provenance
|
|
34118
|
+
* and the send-only-on-removal rule: see the shared note above.
|
|
34119
|
+
*
|
|
34120
|
+
* FROZEN, and spread into a fresh object at the use site rather than handed
|
|
34121
|
+
* over by reference. `ECSProvider` is a singleton serving concurrent
|
|
34122
|
+
* resources, so a module-level object reaching the SDK input directly would be
|
|
34123
|
+
* one shared mutable value across every in-flight `UpdateService` — a
|
|
34124
|
+
* middleware or a future normalization step mutating it would corrupt every
|
|
34125
|
+
* other caller. The two halves guard different failures and BOTH are pinned by
|
|
34126
|
+
* unit arms: the copy means no caller can ever reach this object, and the
|
|
34127
|
+
* freeze means a caller that somehow does fails LOUDLY instead of silently
|
|
34128
|
+
* poisoning every later update.
|
|
34129
|
+
*
|
|
34130
|
+
* @internal EXPORTED for tests only (issue #1861). Every production consumer
|
|
34131
|
+
* is in this file. The export exists because the freeze is otherwise
|
|
34132
|
+
* UNOBSERVABLE from a test: the resolver hands out a copy by design, so the
|
|
34133
|
+
* payload a test can reach is never this object, and dropping `Object.freeze`
|
|
34134
|
+
* left the whole suite green. Asserting it through a source grep was the
|
|
34135
|
+
* alternative and is the weaker fence this repo has retired elsewhere.
|
|
34136
|
+
*/
|
|
34137
|
+
const CIRCUIT_BREAKER_THRESHOLD_CONFIGURATION_DEFAULT = Object.freeze({
|
|
34138
|
+
type: "BOUNDED_PERCENT",
|
|
34139
|
+
value: 50
|
|
34140
|
+
});
|
|
34141
|
+
/**
|
|
34142
|
+
* A plain object — something a CFn config block can legitimately be. Arrays
|
|
34143
|
+
* and non-objects are excluded on purpose: `typeof [] === 'object'` and every
|
|
34144
|
+
* non-empty string is truthy, so a truthiness check would wave a malformed
|
|
34145
|
+
* template through into a spread.
|
|
34146
|
+
*
|
|
34147
|
+
* Deliberately a module-local copy rather than an export added to
|
|
34148
|
+
* `src/provisioning/config-shape.ts`, whose private `isPlainObject` is
|
|
34149
|
+
* byte-identical: this lane may not widen a shared module's API.
|
|
34150
|
+
*
|
|
34151
|
+
* No "promote it if a third copy appears" trigger, because that already fired
|
|
34152
|
+
* long before this PR and adding it here would read as a new one. Grepped for
|
|
34153
|
+
* a standalone helper whose body is exactly this predicate: NINE across nine
|
|
34154
|
+
* files under six different names — `config-shape.ts`, `wafv2-provider.ts`,
|
|
34155
|
+
* `s3-bucket-provider.ts` (`isPlainObject`), `emr-configuration.ts`,
|
|
34156
|
+
* `lambda-microvm-image-provider.ts` (`isRecord`),
|
|
34157
|
+
* `apigateway-provider.ts` (`isPlainObjectBlock`), `drift.ts`
|
|
34158
|
+
* (`isPlainRecord`), `lambda-vpc-deps.ts` (`isObject`), and this one — plus
|
|
34159
|
+
* further INLINE copies (`route53-provider.ts` x2,
|
|
34160
|
+
* `custom-resource-provider.ts`, `diff-recursive.ts`,
|
|
34161
|
+
* `intrinsic-function-resolver.ts`). Consolidating them touches files across
|
|
34162
|
+
* several lanes and is a separate lane's job, not this PR's.
|
|
34163
|
+
*/
|
|
34164
|
+
function isPlainCfnObject(value) {
|
|
34165
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34166
|
+
}
|
|
34167
|
+
/**
|
|
34033
34168
|
* Derive the cluster name from a long-format ECS Service ARN
|
|
34034
34169
|
* (`arn:<partition>:ecs:<region>:<account>:service/<clusterName>/<serviceName>`)
|
|
34035
34170
|
* so `DescribeServices` can be scoped to the right cluster (issue #1170).
|
|
@@ -34467,6 +34602,7 @@ var ECSProvider = class {
|
|
|
34467
34602
|
const vpcLatticeConfigurationsInput = JSON.stringify(previousProperties["VpcLatticeConfigurations"] ?? null) !== JSON.stringify(properties["VpcLatticeConfigurations"] ?? null) ? clearOnUpdateRemoval(this.convertVpcLatticeConfigurations(properties["VpcLatticeConfigurations"]), previousProperties["VpcLatticeConfigurations"], []) : void 0;
|
|
34468
34603
|
const monitoringInput = JSON.stringify(previousProperties["Monitoring"] ?? null) !== JSON.stringify(properties["Monitoring"] ?? null) && properties["Monitoring"] != null ? this.convertMonitoringConfiguration(properties["Monitoring"]) : void 0;
|
|
34469
34604
|
const forceNewDeploymentInput = this.resolveForceNewDeployment(properties, previousProperties);
|
|
34605
|
+
const deploymentConfigurationInput = this.resolveDeploymentConfiguration(properties, previousProperties);
|
|
34470
34606
|
try {
|
|
34471
34607
|
const service = (await client.send(new UpdateServiceCommand({
|
|
34472
34608
|
cluster: properties["Cluster"],
|
|
@@ -34475,7 +34611,7 @@ var ECSProvider = class {
|
|
|
34475
34611
|
desiredCount: properties["DesiredCount"],
|
|
34476
34612
|
networkConfiguration: this.convertNetworkConfiguration(properties["NetworkConfiguration"]),
|
|
34477
34613
|
capacityProviderStrategy: capacityProviderStrategyInput,
|
|
34478
|
-
deploymentConfiguration:
|
|
34614
|
+
deploymentConfiguration: deploymentConfigurationInput,
|
|
34479
34615
|
placementConstraints: placementConstraintsInput,
|
|
34480
34616
|
placementStrategy: placementStrategyInput,
|
|
34481
34617
|
platformVersion: platformVersionInput,
|
|
@@ -35187,6 +35323,93 @@ var ECSProvider = class {
|
|
|
35187
35323
|
return pascalToCamelCaseKeys(config, DEPLOYMENT_CONFIG_PRESERVE_KEYS);
|
|
35188
35324
|
}
|
|
35189
35325
|
/**
|
|
35326
|
+
* UPDATE-path `DeploymentConfiguration` resolver: the verbatim conversion
|
|
35327
|
+
* above, plus the ONE member-level removal reset real AWS measurement found
|
|
35328
|
+
* in this property tree (issue #1861).
|
|
35329
|
+
*
|
|
35330
|
+
* The rule this keys on is previous-present / current-absent — exactly the
|
|
35331
|
+
* semantic `clearOnUpdateRemoval` implements for TOP-LEVEL properties, which
|
|
35332
|
+
* is why this reuses that helper rather than re-spelling the predicate. What
|
|
35333
|
+
* is new is only the DEPTH: the member lives inside a nested struct the
|
|
35334
|
+
* template STILL DECLARES.
|
|
35335
|
+
*
|
|
35336
|
+
* Scope is deliberately narrow, and each boundary is measured rather than
|
|
35337
|
+
* reasoned about (us-east-1, 2026-08-13; the full A/B table is in the long
|
|
35338
|
+
* comment inside `updateService`):
|
|
35339
|
+
* - Only `DeploymentCircuitBreaker`'s two OPTIONAL members
|
|
35340
|
+
* (`ResetOnHealthyTask`, `ThresholdConfiguration`). Its REQUIRED sibling
|
|
35341
|
+
* `Rollback` reads as REPLACED, so one block holds members with
|
|
35342
|
+
* different semantics and no row generalizes to its neighbours.
|
|
35343
|
+
* - NOT the depth-1 siblings `LinearConfiguration` / `CanaryConfiguration`
|
|
35344
|
+
* / `LifecycleHooks[].TimeoutConfiguration`: those are already PARITY
|
|
35345
|
+
* because the ECS API ITSELF replaces the struct and default-fills the
|
|
35346
|
+
* omitted member, so adding a reset there would be the divergence.
|
|
35347
|
+
* - NOT the whole-struct cases. Removing the whole `DeploymentConfiguration`
|
|
35348
|
+
* resets nothing under either engine, and the parent
|
|
35349
|
+
* `DeploymentCircuitBreaker` disappearing entirely was never measured —
|
|
35350
|
+
* so BOTH sides must still declare the block for a member omission to
|
|
35351
|
+
* read as a removal.
|
|
35352
|
+
* - NOT "always send the defaults": a member the template NEVER declared
|
|
35353
|
+
* is left alone by CloudFormation even when the block around it changes,
|
|
35354
|
+
* and `clearOnUpdateRemoval` returning `undefined` for that case is what
|
|
35355
|
+
* keeps cdkd from clobbering an out-of-band value CFn preserves.
|
|
35356
|
+
* - NOT scoped by scheduling strategy, and that is a deliberate omission
|
|
35357
|
+
* rather than an unconsidered one. The A/B ran on a ROLLING / REPLICA /
|
|
35358
|
+
* Fargate service and DAEMON was never exercised, so the question is
|
|
35359
|
+
* fair — and DAEMON is genuinely REACHABLE here, because the SDK states
|
|
35360
|
+
* that CODE_DEPLOY / EXTERNAL controllers do not support DAEMON, so a
|
|
35361
|
+
* DAEMON service necessarily runs the ECS rolling-update type and can
|
|
35362
|
+
* carry a circuit breaker. What makes the omission safe is narrower and
|
|
35363
|
+
* PER-FIELD: the SDK documents these TWO members' defaults with no
|
|
35364
|
+
* strategy dimension. It is not a general property of this block —
|
|
35365
|
+
* `minimumHealthyPercent`'s default IS strategy-dependent (100% replica
|
|
35366
|
+
* vs 0% DAEMON), and stays correct only because cdkd passes it through
|
|
35367
|
+
* rather than defaulting it.
|
|
35368
|
+
*
|
|
35369
|
+
* The previous side stays RAW CFn PascalCase on purpose: `clearOnUpdateRemoval`
|
|
35370
|
+
* only inspects it for PRESENCE, the same shortcut the top-level call sites
|
|
35371
|
+
* in `updateService` take.
|
|
35372
|
+
*
|
|
35373
|
+
* SCOPE OF THAT PRESENCE TEST — state it as what it literally is, because
|
|
35374
|
+
* the obvious reading is stronger than the truth. It means "the previous
|
|
35375
|
+
* BAG carried this member", NOT "the previous TEMPLATE declared it". Those
|
|
35376
|
+
* coincide on the deploy path, where `previousProperties` is the last
|
|
35377
|
+
* deployed template. They do NOT at `drift --revert`
|
|
35378
|
+
* (`src/cli/commands/drift.ts`, the `provider.update(...)` call that passes
|
|
35379
|
+
* `outcome.awsProperties`), where the previous bag is an AWS READBACK: a
|
|
35380
|
+
* member the template never declared but AWS reports is present there, so a
|
|
35381
|
+
* desired side that omits it reads as a removal and gets the default sent.
|
|
35382
|
+
* The window is narrow — the revert path's own desired bag is built from
|
|
35383
|
+
* `observedProperties`, which normally carries the member too, and its
|
|
35384
|
+
* `preserveUntemplated` arm recurses via `mergeUntemplatedValue` and keeps
|
|
35385
|
+
* AWS-only members — but it is real when an `observedProperties` snapshot
|
|
35386
|
+
* predates AWS reporting the member. This is NOT specific to this resolver:
|
|
35387
|
+
* every `clearOnUpdateRemoval` site in the codebase — 78 across 14 provider
|
|
35388
|
+
* files before this PR, 80 with this resolver's two — carries the same
|
|
35389
|
+
* exposure, so fixing it belongs to that shared contract, not here. It is
|
|
35390
|
+
* written down and unit-pinned so the next member copied onto this pattern
|
|
35391
|
+
* does not inherit the premise unchecked.
|
|
35392
|
+
*/
|
|
35393
|
+
resolveDeploymentConfiguration(properties, previousProperties) {
|
|
35394
|
+
const desired = this.convertDeploymentConfiguration(properties["DeploymentConfiguration"]);
|
|
35395
|
+
const desiredBreaker = desired?.deploymentCircuitBreaker;
|
|
35396
|
+
if (!desired || !isPlainCfnObject(desiredBreaker)) return desired;
|
|
35397
|
+
const previousDeploymentConfiguration = previousProperties["DeploymentConfiguration"];
|
|
35398
|
+
const previousBreaker = isPlainCfnObject(previousDeploymentConfiguration) ? previousDeploymentConfiguration["DeploymentCircuitBreaker"] : void 0;
|
|
35399
|
+
if (!isPlainCfnObject(previousBreaker)) return desired;
|
|
35400
|
+
const resetOnHealthyTask = clearOnUpdateRemoval(desiredBreaker.resetOnHealthyTask, previousBreaker["ResetOnHealthyTask"], CIRCUIT_BREAKER_RESET_ON_HEALTHY_TASK_DEFAULT);
|
|
35401
|
+
const thresholdConfiguration = clearOnUpdateRemoval(desiredBreaker.thresholdConfiguration, previousBreaker["ThresholdConfiguration"], { ...CIRCUIT_BREAKER_THRESHOLD_CONFIGURATION_DEFAULT });
|
|
35402
|
+
if (resetOnHealthyTask === desiredBreaker.resetOnHealthyTask && thresholdConfiguration === desiredBreaker.thresholdConfiguration) return desired;
|
|
35403
|
+
return {
|
|
35404
|
+
...desired,
|
|
35405
|
+
deploymentCircuitBreaker: {
|
|
35406
|
+
...desiredBreaker,
|
|
35407
|
+
...resetOnHealthyTask !== void 0 ? { resetOnHealthyTask } : {},
|
|
35408
|
+
...thresholdConfiguration !== void 0 ? { thresholdConfiguration } : {}
|
|
35409
|
+
}
|
|
35410
|
+
};
|
|
35411
|
+
}
|
|
35412
|
+
/**
|
|
35190
35413
|
* Convert the CFn PascalCase `CapacityProviderStrategy` array to the ECS SDK
|
|
35191
35414
|
* camelCase input shape (issue #1165). Each element `{CapacityProvider,
|
|
35192
35415
|
* Weight, Base}` is a pure first-letter case flip to `{capacityProvider,
|
|
@@ -59086,7 +59309,6 @@ async function runDestroyForStack(stackName, state, ctx) {
|
|
|
59086
59309
|
}
|
|
59087
59310
|
}
|
|
59088
59311
|
result.skippedEmpty = true;
|
|
59089
|
-
result.interrupted ||= emptyInterrupted;
|
|
59090
59312
|
return result;
|
|
59091
59313
|
}
|
|
59092
59314
|
const needsStrongRefCheck = !!(state.outputs && Object.keys(state.outputs).length > 0);
|
|
@@ -59159,6 +59381,7 @@ async function runDestroyForStack(stackName, state, ctx) {
|
|
|
59159
59381
|
const renderer = getLiveRenderer();
|
|
59160
59382
|
let draining = false;
|
|
59161
59383
|
let lockHeld = false;
|
|
59384
|
+
let statePreserved = false;
|
|
59162
59385
|
const sigintHandler = () => {
|
|
59163
59386
|
if (draining) {
|
|
59164
59387
|
if (lockHeld) {
|
|
@@ -59475,6 +59698,7 @@ async function runDestroyForStack(stackName, state, ctx) {
|
|
|
59475
59698
|
result.interrupted = draining;
|
|
59476
59699
|
await saveChain;
|
|
59477
59700
|
const preserveState = result.errorCount > 0 || result.interrupted || result.skippedCount > 0;
|
|
59701
|
+
statePreserved = preserveState;
|
|
59478
59702
|
if (!preserveState) {
|
|
59479
59703
|
await ctx.stateBackend.deleteState(stackName, regionForState);
|
|
59480
59704
|
logger.debug("State deleted");
|
|
@@ -59522,7 +59746,7 @@ async function runDestroyForStack(stackName, state, ctx) {
|
|
|
59522
59746
|
process.removeListener("SIGINT", sigintHandler);
|
|
59523
59747
|
restoreBaseRegionAndClients();
|
|
59524
59748
|
}
|
|
59525
|
-
result.interrupted ||= draining;
|
|
59749
|
+
result.interrupted ||= draining && statePreserved;
|
|
59526
59750
|
}
|
|
59527
59751
|
return result;
|
|
59528
59752
|
}
|
|
@@ -68582,6 +68806,7 @@ async function destroyCommand(stackArgs, options) {
|
|
|
68582
68806
|
let totalSkipped = 0;
|
|
68583
68807
|
let interrupted = false;
|
|
68584
68808
|
const runInterrupted = () => interrupted || interruptWatch.interrupted();
|
|
68809
|
+
let stoppedEarly = false;
|
|
68585
68810
|
let stackNames;
|
|
68586
68811
|
if (options.all) stackNames = candidateStacks.map((s) => s.stackName);
|
|
68587
68812
|
else if (stackPatterns.length > 0) stackNames = matchStacks(candidateStacks, stackPatterns).map((s) => s.stackName);
|
|
@@ -68622,7 +68847,7 @@ async function destroyCommand(stackArgs, options) {
|
|
|
68622
68847
|
arr.push(ref);
|
|
68623
68848
|
stateRefsByName.set(ref.stackName, arr);
|
|
68624
68849
|
}
|
|
68625
|
-
for (const stackName of stackNames) {
|
|
68850
|
+
for (const [stackIndex, stackName] of stackNames.entries()) {
|
|
68626
68851
|
logger.info(`\nPreparing to destroy stack: ${stackName}`);
|
|
68627
68852
|
const refs = stateRefsByName.get(stackName) ?? [];
|
|
68628
68853
|
const synthStack = appStacks.find((s) => s.stackName === stackName);
|
|
@@ -68658,7 +68883,10 @@ async function destroyCommand(stackArgs, options) {
|
|
|
68658
68883
|
totalErrors++;
|
|
68659
68884
|
continue;
|
|
68660
68885
|
}
|
|
68661
|
-
if (runInterrupted())
|
|
68886
|
+
if (runInterrupted()) {
|
|
68887
|
+
stoppedEarly = true;
|
|
68888
|
+
break;
|
|
68889
|
+
}
|
|
68662
68890
|
const eventRecorder = startRunRecorder({
|
|
68663
68891
|
backend: stateBackend,
|
|
68664
68892
|
stackName,
|
|
@@ -68666,6 +68894,8 @@ async function destroyCommand(stackArgs, options) {
|
|
|
68666
68894
|
command: "destroy"
|
|
68667
68895
|
});
|
|
68668
68896
|
let destroyRunResult = "SUCCEEDED";
|
|
68897
|
+
let stackInterrupted = false;
|
|
68898
|
+
let stackCancelled = false;
|
|
68669
68899
|
try {
|
|
68670
68900
|
const result = await interruptWatch.runStack(() => withNestedStackContext({
|
|
68671
68901
|
stateBackend,
|
|
@@ -68709,6 +68939,8 @@ async function destroyCommand(stackArgs, options) {
|
|
|
68709
68939
|
})));
|
|
68710
68940
|
totalErrors += result.errorCount;
|
|
68711
68941
|
totalSkipped += result.skippedCount;
|
|
68942
|
+
stackInterrupted = result.interrupted;
|
|
68943
|
+
stackCancelled = result.cancelled === true;
|
|
68712
68944
|
if (result.interrupted) interrupted = true;
|
|
68713
68945
|
destroyRunResult = result.errorCount > 0 || result.skippedCount > 0 ? "FAILED" : "SUCCEEDED";
|
|
68714
68946
|
eventRecorder.record({
|
|
@@ -68733,12 +68965,15 @@ async function destroyCommand(stackArgs, options) {
|
|
|
68733
68965
|
await purgeEventsAfterDestroy(new DeploymentEventsReader(stateBackend), stackName, stackTargetRegion, {
|
|
68734
68966
|
purgeEvents: options.purgeEvents,
|
|
68735
68967
|
runResult: destroyRunResult,
|
|
68736
|
-
interrupted:
|
|
68968
|
+
interrupted: stackInterrupted || stackCancelled
|
|
68737
68969
|
}, logger);
|
|
68738
|
-
if (runInterrupted())
|
|
68970
|
+
if (runInterrupted()) {
|
|
68971
|
+
if (stackIndex < stackNames.length - 1) stoppedEarly = true;
|
|
68972
|
+
break;
|
|
68973
|
+
}
|
|
68739
68974
|
}
|
|
68740
68975
|
if (totalErrors > 0) throw new PartialFailureError(`Destroy completed with ${totalErrors} resource error(s). State preserved — inspect 'cdkd state show <stack>' and re-run 'cdkd destroy' to retry. If the same resource keeps failing, 'cdkd state orphan <stack>' removes the state record without deleting AWS resources.`);
|
|
68741
|
-
if (
|
|
68976
|
+
if (interrupted || interruptWatch.interrupted() && stoppedEarly) throw new PartialFailureError(`Destroy interrupted by Ctrl-C. State preserved — re-run 'cdkd destroy' to finish.`);
|
|
68742
68977
|
if (totalSkipped > 0) throw new PartialFailureError(`Destroy skipped ${totalSkipped} entr${totalSkipped === 1 ? "y" : "ies"} cdkd could not address, so the underlying resources may still exist in AWS. A skipped nested stack counts as ONE entry and may cover several of its own resources — the per-stack summaries above give the exact breakdown. State preserved (the records are kept). Repair the physicalId in state.json and re-run 'cdkd destroy', or delete the resources by hand and drop the records with 'cdkd state orphan <stack>'.`);
|
|
68743
68978
|
} finally {
|
|
68744
68979
|
unforwardSigterm();
|
|
@@ -73337,12 +73572,24 @@ async function stateDestroyCommand(stackArgs, options) {
|
|
|
73337
73572
|
process.stdout.write(`\nWARNING: This destroys ${stackNames.length} stack(s) and removes their state records:\n`);
|
|
73338
73573
|
for (const name of stackNames) process.stdout.write(` - ${name}\n`);
|
|
73339
73574
|
process.stdout.write("\n");
|
|
73575
|
+
if (process.stdin.isTTY !== true) throw new CdkdError("The state destroy --all confirmation prompt cannot run in a non-interactive environment. Pass --yes / -y to confirm the batch, or run the command from a real terminal.", "NON_INTERACTIVE_CONFIRM");
|
|
73340
73576
|
const rl = readline$1.createInterface({
|
|
73341
73577
|
input: process.stdin,
|
|
73342
73578
|
output: process.stdout
|
|
73343
73579
|
});
|
|
73344
|
-
|
|
73345
|
-
|
|
73580
|
+
let answer;
|
|
73581
|
+
try {
|
|
73582
|
+
answer = await rl.question(`Destroy all ${stackNames.length} stack(s)? (y/N): `, { signal: interruptWatch.signal });
|
|
73583
|
+
} catch (error) {
|
|
73584
|
+
if (interruptWatch.interrupted() && isPromptAbortError(error)) {
|
|
73585
|
+
process.stderr.write("\nDestroy cancelled — nothing was destroyed.\n");
|
|
73586
|
+
rl.close();
|
|
73587
|
+
process.exit(130);
|
|
73588
|
+
}
|
|
73589
|
+
throw error;
|
|
73590
|
+
} finally {
|
|
73591
|
+
rl.close();
|
|
73592
|
+
}
|
|
73346
73593
|
const trimmed = answer.trim().toLowerCase();
|
|
73347
73594
|
if (trimmed !== "y" && trimmed !== "yes") {
|
|
73348
73595
|
logger.info("Destroy cancelled");
|
|
@@ -73354,7 +73601,31 @@ async function stateDestroyCommand(stackArgs, options) {
|
|
|
73354
73601
|
let totalSkipped = 0;
|
|
73355
73602
|
let interrupted = false;
|
|
73356
73603
|
const runInterrupted = () => interrupted || interruptWatch.interrupted();
|
|
73357
|
-
|
|
73604
|
+
let stoppedEarly = false;
|
|
73605
|
+
/**
|
|
73606
|
+
* Would any stack AFTER `stackIndex` actually have been destroyed?
|
|
73607
|
+
*
|
|
73608
|
+
* `stackIndex < stackNames.length - 1` answers "does an INDEX remain",
|
|
73609
|
+
* which is not the same question and over-claims: with
|
|
73610
|
+
* `--stack-region us-east-1`, a later name whose only state record lives in
|
|
73611
|
+
* eu-west-1 is warn-and-skipped by the `targets.length === 0` branch below
|
|
73612
|
+
* — it was never going to be destroyed, so a signal before it leaves
|
|
73613
|
+
* nothing undone and the run must still exit 0. Measured on
|
|
73614
|
+
* `state destroy --all --yes --stack-region us-east-1` with StackA in
|
|
73615
|
+
* us-east-1 and StackB only in eu-west-1: the index form exited 2 having
|
|
73616
|
+
* dispatched StackA, i.e. every target it had.
|
|
73617
|
+
*
|
|
73618
|
+
* The predicate mirrors that branch's own filter over `stateRefs`, the
|
|
73619
|
+
* same list the loop selects from. `destroy.ts` needs no counterpart: its
|
|
73620
|
+
* `candidateStacks` is already pre-filtered by the set of stack names that
|
|
73621
|
+
* have state, so an index there IS a target.
|
|
73622
|
+
*/
|
|
73623
|
+
const targetRemainsAfter = (stackIndex) => stackNames.slice(stackIndex + 1).some((name) => {
|
|
73624
|
+
const laterRefs = stateRefs.filter((r) => r.stackName === name);
|
|
73625
|
+
if (!options.stackRegion) return laterRefs.length > 0;
|
|
73626
|
+
return laterRefs.some((r) => r.region === options.stackRegion || !r.region);
|
|
73627
|
+
});
|
|
73628
|
+
for (const [stackIndex, stackName] of stackNames.entries()) {
|
|
73358
73629
|
const refs = stateRefs.filter((r) => r.stackName === stackName);
|
|
73359
73630
|
let targets;
|
|
73360
73631
|
if (options.stackRegion) {
|
|
@@ -73368,14 +73639,17 @@ async function stateDestroyCommand(stackArgs, options) {
|
|
|
73368
73639
|
const regions = refs.map((r) => r.region ?? "(legacy)").join(", ");
|
|
73369
73640
|
throw new Error(`Stack '${stackName}' has state in multiple regions: ${regions}. Use --stack-region <region> to pick one.`);
|
|
73370
73641
|
}
|
|
73371
|
-
for (const ref of targets) {
|
|
73642
|
+
for (const [refIndex, ref] of targets.entries()) {
|
|
73372
73643
|
logger.info(`\nPreparing to destroy stack: ${stackName}${ref.region ? ` (${ref.region})` : ""}`);
|
|
73373
73644
|
const stateResult = await setup.stateBackend.getState(stackName, ref.region ?? setup.region);
|
|
73374
73645
|
if (!stateResult) {
|
|
73375
73646
|
logger.warn(`No state found for stack ${stackName}${ref.region ? ` in ${ref.region}` : ""}, skipping`);
|
|
73376
73647
|
continue;
|
|
73377
73648
|
}
|
|
73378
|
-
if (runInterrupted())
|
|
73649
|
+
if (runInterrupted()) {
|
|
73650
|
+
stoppedEarly = true;
|
|
73651
|
+
break;
|
|
73652
|
+
}
|
|
73379
73653
|
const result = await interruptWatch.runStack(() => withNestedStackContext({
|
|
73380
73654
|
stateBackend: setup.stateBackend,
|
|
73381
73655
|
lockManager: setup.lockManager,
|
|
@@ -73418,12 +73692,18 @@ async function stateDestroyCommand(stackArgs, options) {
|
|
|
73418
73692
|
totalErrors += result.errorCount;
|
|
73419
73693
|
totalSkipped += result.skippedCount;
|
|
73420
73694
|
if (result.interrupted) interrupted = true;
|
|
73421
|
-
if (runInterrupted())
|
|
73695
|
+
if (runInterrupted()) {
|
|
73696
|
+
if (refIndex < targets.length - 1) stoppedEarly = true;
|
|
73697
|
+
break;
|
|
73698
|
+
}
|
|
73699
|
+
}
|
|
73700
|
+
if (runInterrupted()) {
|
|
73701
|
+
if (targetRemainsAfter(stackIndex)) stoppedEarly = true;
|
|
73702
|
+
break;
|
|
73422
73703
|
}
|
|
73423
|
-
if (runInterrupted()) break;
|
|
73424
73704
|
}
|
|
73425
73705
|
if (totalErrors > 0) throw new PartialFailureError(`Destroy completed with ${totalErrors} resource error(s). State preserved — inspect 'cdkd state show <stack>' and re-run 'cdkd state destroy' to retry. If the same resource keeps failing, 'cdkd state orphan <stack>' removes the state record without deleting AWS resources.`);
|
|
73426
|
-
if (
|
|
73706
|
+
if (interrupted || interruptWatch.interrupted() && stoppedEarly) throw new PartialFailureError(`Destroy interrupted by Ctrl-C. State preserved — re-run 'cdkd state destroy' to finish.`);
|
|
73427
73707
|
if (totalSkipped > 0) throw new PartialFailureError(`Destroy skipped ${totalSkipped} entr${totalSkipped === 1 ? "y" : "ies"} cdkd could not address, so the underlying resources may still exist in AWS. A skipped nested stack counts as ONE entry and may cover several of its own resources — the per-stack summaries above give the exact breakdown. State preserved (the records are kept). Repair the physicalId in state.json and re-run 'cdkd state destroy', or delete the resources by hand and drop the records with 'cdkd state orphan <stack>'.`);
|
|
73428
73708
|
} finally {
|
|
73429
73709
|
unforwardSigterm();
|
|
@@ -87705,4 +87985,4 @@ function buildProgram() {
|
|
|
87705
87985
|
|
|
87706
87986
|
//#endregion
|
|
87707
87987
|
export { buildProgram };
|
|
87708
|
-
//# sourceMappingURL=program-
|
|
87988
|
+
//# sourceMappingURL=program-XLMSMzWe.js.map
|