@go-to-k/cdkd 0.284.6 → 0.284.7
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-B9L-88MC.js → asg-provider-DOdJgPLS.js} +2 -2
- package/dist/{asg-provider-B9L-88MC.js.map → asg-provider-DOdJgPLS.js.map} +1 -1
- package/dist/cli.js +276 -32
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-Ce7kNQp2.js → deploy-engine-CJncZQEi.js} +3 -3
- package/dist/{deploy-engine-Ce7kNQp2.js.map → deploy-engine-CJncZQEi.js.map} +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as IntrinsicFunctionResolver, $n as StackHasActiveImportsError, $t as validateContainerRepoName, A as formatResourceLine, An as AssemblyReader, At as describeTypeWithThrottleRetry, B as isExportAliasCollision, Bt as WorkGraph, C as refusesFinalSnapshot, Cn as MIGRATE_TMP_PREFIX, Ct as s3BucketDomainName, D as MULTI_REGION_RECREATE_BLOCKED_TYPES, Dn as PARTITION_TABLE, Dt as applyRoleArnIfSet, E as extractDeploymentEventError, En as expectedOwnerParam, Et as s3BucketWebsiteUrl, F as red, Fn as getAwsClients, Ft as S3StateBackend, G as clearOnUpdateRemoval, Gn as LocalStartServiceError, Gt as escapeRegExp, H as stateKeySecretExposure, Hn as DeployCancelledError, Ht as createAssetRedirectResolver, I as yellow, In as resetAwsClients, It as rebuildClientForBucketRegion, J as findSilentDropProperties, Jn as NestedStackChildDirectDestroyError, Jt as BOOTSTRAP_MARKER_PREFIX, K as ProviderRegistry, Kt as stripControlChars, L as collectDeclaredOutputNames, Ln as setAwsClients, Lt as shouldRetainResource, M as cyan, Mt as DagBuilder, N as gray, Nn as resolveBucketRegion, Nt as TemplateParser, O as isStatefulRecreateTargetSync, On as canonicalizeRegion, Ot as DiffCalculator, P as green, Pn as AwsClients, Pt as LockManager, Q as isTerminationProtectionPropagationError, Qn as ResourceUpdateNotSupportedError, Qt as validateAssetBucketName, R as collectPublishedOutputNames, Rt as AssetPublisher, S as isFinalSnapshotError, Sn as CFN_TEMPLATE_URL_LIMIT, St as s3BucketArn, T as makeCanonicalizePropertiesFn, Tn as uploadCfnTemplate, Tt as s3BucketRegionalDomainName, U as IAMRoleProvider, Un as LocalInvokeBuildError$1, Ut as loadPublishableAssetManifest, V as secretBearingStateKeyWarning, Vt as buildAssetRedirectMap, W as collectInlinePolicyNamesManagedBySiblings, Wn as LocalMigrateError, Wt as rewriteTemplateAssetReferences, X as slowCcOperationTimeoutMs, Xn as ProvisioningError, Xt as getBootstrapMarkerKey, Y as CloudControlProvider, Yn as PartialFailureError, Yt as ensureAssetStorage, Z as disableInstanceApiTermination, Zn as ResourceTimeoutError, Zt as parseBootstrapMarker, _ as ATOMIC_FINAL_SNAPSHOT_TYPES, _n as resolveStateBucketWithDefaultAndSource, _t as TEMPLATE_SOURCED_RULES, a as DeploymentEventsStore, an as runDockerStreaming, ar as normalizeAwsError, at as resolveExplicitPhysicalId, b as ccRoutedFinalSnapshotError, bn as warnDeprecatedNoPrefixCliFlag, bt as redactSecretsForState, c as replayFailedOperations, cn as Synthesizer, cr as isRetryableTransientError, ct as configBooleanRefusal, d as updatePartialReason, dn as getLegacyStateBucketName, dt as replayWarn, en as buildDenyExternalAccessPolicy, er as StackTerminationProtectionError, et as cfnRefValueFromPhysicalId, f as UNSPECIFIED_SKIP_REASON, fn as resolveApp, ft as requireConfigArray, g as computeImplicitDeleteEdges, gn as resolveStateBucketWithDefault, gt as STATE_SOURCED_READBACK_RULES, h as IMPLICIT_DELETE_DEPENDENCIES, hn as resolveSkipPrefix, ht as STATE_SOURCED_CROSS_GENERATION_RULES, i as DeploymentEventsReader, in as runDockerForeground, it as normalizeAwsTagsToCfn, j as bold, jn as processStackMessages, jt as withRetry, k as renderStatefulReason, kn as derivePartitionAndUrlSuffix, kt as INTRINSIC_KEYS, l as replayRollback, ln as synthesisStatusMessage, lr as isThrottlingError, lt as configStringRefusal, m as withResourceDeadline, mn as resolveCaptureObservedState, mt as requireConfigString, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as formatDockerLoginError, nt as refStateLookupFromResource, o as planFailedOps, on as AssetManifestLoader, or as withErrorHandling, ot as assertRegionMatch, p as deleteSkipReason, pn as resolveAutoAssetStorage, pt as requireConfigObject, q as findActionableSilentDrops, qn as MissingCdkCliError, qt as AssetModeResolver, r as DeployEngine, rn as getDockerCmd, rt as WAFv2WebACLProvider, s as planRollback, sn as getDockerImageBySourceHash, sr as isMarkedNonRetryable, st as coerceCfnBoolean, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as buildDockerImage, tt as getAccountInfo, u as updatePartialMessage, un as getDefaultStateBucketName, ur as markNonRetryable, ut as readConfigString, v as PRE_DELETE_SNAPSHOT_TYPES, vn as resolveUseCdkBootstrapAssets, vt as createSecretMasker, w as unsupportedFinalSnapshotError, wn as findLargeInlineResources, wt as s3BucketDualStackDomainName, x as createPreDeleteFinalSnapshot, xn as CFN_TEMPLATE_BODY_LIMIT, xt as scrubResourceRecord, y as buildFinalSnapshotIdentifier, yn as stateBucketExistenceConfirmed, yt as maskSecretsInText, z as exportAliasCollisionScrubWarning, zn as CdkdError, zt as stringifyValue } from "./deploy-engine-
|
|
2
|
+
import { $ as IntrinsicFunctionResolver, $n as StackHasActiveImportsError, $t as validateContainerRepoName, A as formatResourceLine, An as AssemblyReader, At as describeTypeWithThrottleRetry, B as isExportAliasCollision, Bt as WorkGraph, C as refusesFinalSnapshot, Cn as MIGRATE_TMP_PREFIX, Ct as s3BucketDomainName, D as MULTI_REGION_RECREATE_BLOCKED_TYPES, Dn as PARTITION_TABLE, Dt as applyRoleArnIfSet, E as extractDeploymentEventError, En as expectedOwnerParam, Et as s3BucketWebsiteUrl, F as red, Fn as getAwsClients, Ft as S3StateBackend, G as clearOnUpdateRemoval, Gn as LocalStartServiceError, Gt as escapeRegExp, H as stateKeySecretExposure, Hn as DeployCancelledError, Ht as createAssetRedirectResolver, I as yellow, In as resetAwsClients, It as rebuildClientForBucketRegion, J as findSilentDropProperties, Jn as NestedStackChildDirectDestroyError, Jt as BOOTSTRAP_MARKER_PREFIX, K as ProviderRegistry, Kt as stripControlChars, L as collectDeclaredOutputNames, Ln as setAwsClients, Lt as shouldRetainResource, M as cyan, Mt as DagBuilder, N as gray, Nn as resolveBucketRegion, Nt as TemplateParser, O as isStatefulRecreateTargetSync, On as canonicalizeRegion, Ot as DiffCalculator, P as green, Pn as AwsClients, Pt as LockManager, Q as isTerminationProtectionPropagationError, Qn as ResourceUpdateNotSupportedError, Qt as validateAssetBucketName, R as collectPublishedOutputNames, Rt as AssetPublisher, S as isFinalSnapshotError, Sn as CFN_TEMPLATE_URL_LIMIT, St as s3BucketArn, T as makeCanonicalizePropertiesFn, Tn as uploadCfnTemplate, Tt as s3BucketRegionalDomainName, U as IAMRoleProvider, Un as LocalInvokeBuildError$1, Ut as loadPublishableAssetManifest, V as secretBearingStateKeyWarning, Vt as buildAssetRedirectMap, W as collectInlinePolicyNamesManagedBySiblings, Wn as LocalMigrateError, Wt as rewriteTemplateAssetReferences, X as slowCcOperationTimeoutMs, Xn as ProvisioningError, Xt as getBootstrapMarkerKey, Y as CloudControlProvider, Yn as PartialFailureError, Yt as ensureAssetStorage, Z as disableInstanceApiTermination, Zn as ResourceTimeoutError, Zt as parseBootstrapMarker, _ as ATOMIC_FINAL_SNAPSHOT_TYPES, _n as resolveStateBucketWithDefaultAndSource, _t as TEMPLATE_SOURCED_RULES, a as DeploymentEventsStore, an as runDockerStreaming, ar as normalizeAwsError, at as resolveExplicitPhysicalId, b as ccRoutedFinalSnapshotError, bn as warnDeprecatedNoPrefixCliFlag, bt as redactSecretsForState, c as replayFailedOperations, cn as Synthesizer, cr as isRetryableTransientError, ct as configBooleanRefusal, d as updatePartialReason, dn as getLegacyStateBucketName, dt as replayWarn, en as buildDenyExternalAccessPolicy, er as StackTerminationProtectionError, et as cfnRefValueFromPhysicalId, f as UNSPECIFIED_SKIP_REASON, fn as resolveApp, ft as requireConfigArray, g as computeImplicitDeleteEdges, gn as resolveStateBucketWithDefault, gt as STATE_SOURCED_READBACK_RULES, h as IMPLICIT_DELETE_DEPENDENCIES, hn as resolveSkipPrefix, ht as STATE_SOURCED_CROSS_GENERATION_RULES, i as DeploymentEventsReader, in as runDockerForeground, it as normalizeAwsTagsToCfn, j as bold, jn as processStackMessages, jt as withRetry, k as renderStatefulReason, kn as derivePartitionAndUrlSuffix, kt as INTRINSIC_KEYS, l as replayRollback, ln as synthesisStatusMessage, lr as isThrottlingError, lt as configStringRefusal, m as withResourceDeadline, mn as resolveCaptureObservedState, mt as requireConfigString, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as formatDockerLoginError, nt as refStateLookupFromResource, o as planFailedOps, on as AssetManifestLoader, or as withErrorHandling, ot as assertRegionMatch, p as deleteSkipReason, pn as resolveAutoAssetStorage, pt as requireConfigObject, q as findActionableSilentDrops, qn as MissingCdkCliError, qt as AssetModeResolver, r as DeployEngine, rn as getDockerCmd, rt as WAFv2WebACLProvider, s as planRollback, sn as getDockerImageBySourceHash, sr as isMarkedNonRetryable, st as coerceCfnBoolean, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as buildDockerImage, tt as getAccountInfo, u as updatePartialMessage, un as getDefaultStateBucketName, ur as markNonRetryable, ut as readConfigString, v as PRE_DELETE_SNAPSHOT_TYPES, vn as resolveUseCdkBootstrapAssets, vt as createSecretMasker, w as unsupportedFinalSnapshotError, wn as findLargeInlineResources, wt as s3BucketDualStackDomainName, x as createPreDeleteFinalSnapshot, xn as CFN_TEMPLATE_BODY_LIMIT, xt as scrubResourceRecord, y as buildFinalSnapshotIdentifier, yn as stateBucketExistenceConfirmed, yt as maskSecretsInText, z as exportAliasCollisionScrubWarning, zn as CdkdError, zt as stringifyValue } from "./deploy-engine-CJncZQEi.js";
|
|
3
3
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, 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";
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-DOdJgPLS.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";
|
|
@@ -995,12 +995,66 @@ async function deleteContainerRepo(containerRepo, region, profile, logger) {
|
|
|
995
995
|
ecrClient.destroy();
|
|
996
996
|
}
|
|
997
997
|
}
|
|
998
|
-
/**
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
998
|
+
/** The region spelling a bootstrap marker key carries, e.g. `Us-East-1`. */
|
|
999
|
+
function markerRegionOfKey(markerKey) {
|
|
1000
|
+
return markerKey.slice(BOOTSTRAP_MARKER_PREFIX.length, -5);
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Partition the state bucket's bootstrap markers, relative to the ONE this run
|
|
1004
|
+
* resolved, into the two kinds that must not be silently destroyed with the
|
|
1005
|
+
* bucket:
|
|
1006
|
+
*
|
|
1007
|
+
* - `otherRegions` — markers for a DIFFERENT region, still opted in.
|
|
1008
|
+
* - `sameRegionKeys` — markers for THIS region under a different SPELLING of
|
|
1009
|
+
* its name. The teardown deletes exactly one key, so every other spelling
|
|
1010
|
+
* survives, naming asset storage this run did not touch.
|
|
1011
|
+
*
|
|
1012
|
+
* Both come from ONE listing, and the second kind is why the listing rather
|
|
1013
|
+
* than a per-key probe is the source: a probe is only as deep as the keys it
|
|
1014
|
+
* guesses (`us-east-1` / `US-EAST-1`), while a region can hold a marker under
|
|
1015
|
+
* any spelling (`Us-East-1`), and the listing sees all of them.
|
|
1016
|
+
*
|
|
1017
|
+
* `region` arrives CANONICAL (issue #1995) while the key segments are whatever
|
|
1018
|
+
* `cdkd bootstrap` actually wrote, which is not folded (issue #1820) — so the
|
|
1019
|
+
* two sides are compared through {@link canonicalizeRegion} rather than
|
|
1020
|
+
* directly. A raw compare re-introduces this command's own bug in a different
|
|
1021
|
+
* flag: with a marker at `cdkd-bootstrap/US-EAST-1.json`,
|
|
1022
|
+
* `--destroy --region US-EAST-1 --include-state-bucket` would list `US-EAST-1`
|
|
1023
|
+
* as an "other" region, refuse with `STATE_BUCKET_HOLDS_MARKERS` naming the
|
|
1024
|
+
* very region being torn down, and tell the user to run the command they just
|
|
1025
|
+
* ran. It aborts BEFORE the teardown, so the bucket and repo survive too.
|
|
1026
|
+
*
|
|
1027
|
+
* Other regions are deduplicated by the folded name, so a region bootstrapped
|
|
1028
|
+
* twice under two spellings is reported once rather than twice.
|
|
1029
|
+
*
|
|
1030
|
+
* The comparison folds but the REPORTED value stays the RAW key segment, and
|
|
1031
|
+
* that asymmetry is load-bearing. The error tells the user to run
|
|
1032
|
+
* `cdkd bootstrap --destroy --region <r>` for each name printed, and only the
|
|
1033
|
+
* raw spelling actually reaches the marker: `--region eu-west-1` against a
|
|
1034
|
+
* marker at `cdkd-bootstrap/EU-WEST-1.json` probes the canonical key (absent),
|
|
1035
|
+
* finds `rawMarkerKey === markerKey`, skips the second probe, and reports
|
|
1036
|
+
* "nothing to delete". Printing the folded name would hand the user a command
|
|
1037
|
+
* that cannot work.
|
|
1038
|
+
*/
|
|
1039
|
+
async function listBootstrapMarkerSiblings(stateBackend, region, resolvedMarkerKey) {
|
|
1040
|
+
const segments = (await stateBackend.listRawKeys(BOOTSTRAP_MARKER_PREFIX)).filter((k) => k.endsWith(".json")).map((k) => {
|
|
1041
|
+
const raw = k.slice(BOOTSTRAP_MARKER_PREFIX.length, -5);
|
|
1042
|
+
return {
|
|
1043
|
+
key: k,
|
|
1044
|
+
raw,
|
|
1045
|
+
folded: canonicalizeRegion(raw)
|
|
1046
|
+
};
|
|
1047
|
+
}).filter((e) => e.raw.length > 0);
|
|
1048
|
+
const byCanonical = /* @__PURE__ */ new Map();
|
|
1049
|
+
for (const { raw, folded } of segments) {
|
|
1050
|
+
if (folded === region) continue;
|
|
1051
|
+
if (!byCanonical.has(folded)) byCanonical.set(folded, raw);
|
|
1052
|
+
}
|
|
1053
|
+
const sameRegionKeys = segments.filter((e) => e.folded === region && e.key !== resolvedMarkerKey).map((e) => e.key);
|
|
1054
|
+
return {
|
|
1055
|
+
otherRegions: [...byCanonical.values()],
|
|
1056
|
+
sameRegionKeys
|
|
1057
|
+
};
|
|
1004
1058
|
}
|
|
1005
1059
|
/**
|
|
1006
1060
|
* `cdkd bootstrap --destroy` implementation. Dispatched from the bootstrap
|
|
@@ -1013,14 +1067,16 @@ async function bootstrapDestroyCommand(options) {
|
|
|
1013
1067
|
logger.debug("Options:", options);
|
|
1014
1068
|
await applyRoleArnIfSet({
|
|
1015
1069
|
roleArn: options.roleArn,
|
|
1016
|
-
region: options.region
|
|
1070
|
+
region: canonicalizeRegion(options.region)
|
|
1017
1071
|
});
|
|
1072
|
+
const rawRegion = options.region || process.env["AWS_REGION"] || "us-east-1";
|
|
1073
|
+
const region = canonicalizeRegion(rawRegion);
|
|
1074
|
+
const clientRegion = canonicalizeRegion(options.region || process.env["AWS_REGION"]) || void 0;
|
|
1018
1075
|
const awsClients = new AwsClients({
|
|
1019
|
-
...
|
|
1076
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
1020
1077
|
...options.profile && { profile: options.profile }
|
|
1021
1078
|
});
|
|
1022
1079
|
setAwsClients(awsClients);
|
|
1023
|
-
const region = options.region || process.env["AWS_REGION"] || "us-east-1";
|
|
1024
1080
|
const accountId = (await awsClients.sts.send(new GetCallerIdentityCommand({}))).Account;
|
|
1025
1081
|
const bucketName = options.stateBucket ?? getDefaultStateBucketName(accountId);
|
|
1026
1082
|
const markerS3Client = new S3Client({
|
|
@@ -1036,9 +1092,18 @@ async function bootstrapDestroyCommand(options) {
|
|
|
1036
1092
|
});
|
|
1037
1093
|
try {
|
|
1038
1094
|
const markerKey = getBootstrapMarkerKey(region);
|
|
1095
|
+
const rawMarkerKey = getBootstrapMarkerKey(rawRegion);
|
|
1039
1096
|
let markerBody;
|
|
1097
|
+
let resolvedMarkerKey = markerKey;
|
|
1040
1098
|
try {
|
|
1041
1099
|
markerBody = await stateBackend.getRawObject(markerKey);
|
|
1100
|
+
if (markerBody === null && rawMarkerKey !== markerKey) {
|
|
1101
|
+
markerBody = await stateBackend.getRawObject(rawMarkerKey);
|
|
1102
|
+
if (markerBody !== null) {
|
|
1103
|
+
resolvedMarkerKey = rawMarkerKey;
|
|
1104
|
+
logger.debug(`Bootstrap marker found at the un-folded key '${rawMarkerKey}' (none at '${markerKey}') — an upper-cased region was used at 'cdkd bootstrap' time.`);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1042
1107
|
} catch (error) {
|
|
1043
1108
|
if (error.name === "NoSuchBucket") {
|
|
1044
1109
|
logger.info(`State bucket '${bucketName}' does not exist — this account/region was never bootstrapped; nothing to delete.`);
|
|
@@ -1046,14 +1111,29 @@ async function bootstrapDestroyCommand(options) {
|
|
|
1046
1111
|
}
|
|
1047
1112
|
throw error;
|
|
1048
1113
|
}
|
|
1114
|
+
let markerSiblings = {
|
|
1115
|
+
otherRegions: [],
|
|
1116
|
+
sameRegionKeys: []
|
|
1117
|
+
};
|
|
1118
|
+
let markerListingSucceeded = true;
|
|
1119
|
+
try {
|
|
1120
|
+
markerSiblings = await listBootstrapMarkerSiblings(stateBackend, region, resolvedMarkerKey);
|
|
1121
|
+
} catch (error) {
|
|
1122
|
+
markerListingSucceeded = false;
|
|
1123
|
+
if (options.includeStateBucket) throw new CdkdError(`Cannot verify which bootstrap markers the state bucket '${bucketName}' still holds (listing '${BOOTSTRAP_MARKER_PREFIX}' failed), and --include-state-bucket deletes every one of them. Grant s3:ListBucket on this bucket and re-run.`, "STATE_BUCKET_MARKER_SCAN_FAILED", error);
|
|
1124
|
+
logger.warn(`Could not list bootstrap markers under '${BOOTSTRAP_MARKER_PREFIX}' (${error instanceof Error ? error.message : String(error)}). Continuing — this only means a marker for this region under another spelling of its name would not be reported.`);
|
|
1125
|
+
}
|
|
1049
1126
|
let marker;
|
|
1050
1127
|
if (markerBody === null) {
|
|
1051
|
-
|
|
1128
|
+
const probed = rawMarkerKey === markerKey ? markerKey : `${markerKey}, ${rawMarkerKey}`;
|
|
1129
|
+
logger.info(`No bootstrap marker for region '${region}' (${probed}) — asset storage is not opted in for this region (or was already destroyed); nothing to delete.`);
|
|
1130
|
+
if (markerSiblings.sameRegionKeys.length > 0) for (const siblingKey of markerSiblings.sameRegionKeys) logger.warn(`A bootstrap marker for this region DOES exist at '${siblingKey}', under a different spelling of the region name — its asset bucket and ECR repository are still alive. Re-run 'cdkd bootstrap --destroy --region ${markerRegionOfKey(siblingKey)}' to tear it down.`);
|
|
1052
1131
|
if (!options.includeStateBucket) {
|
|
1053
|
-
logger.
|
|
1132
|
+
if (!markerListingSucceeded) logger.warn(`The bootstrap-marker listing could not be made, so it is NOT known whether this region has a marker under another spelling of its name. Check 's3://${bucketName}/${BOOTSTRAP_MARKER_PREFIX}' before re-bootstrapping — re-running 'cdkd bootstrap' while one exists writes a SECOND marker with DEFAULT names, and the next destroy would then tear down that storage while the storage the existing marker names survives.`);
|
|
1133
|
+
else if (markerSiblings.sameRegionKeys.length === 0) logger.info(`If the asset bucket / ECR repo still exist without a marker, re-run 'cdkd bootstrap --region ${region}' to recreate the marker, then destroy again.`);
|
|
1054
1134
|
return;
|
|
1055
1135
|
}
|
|
1056
|
-
} else marker = parseBootstrapMarker(markerBody,
|
|
1136
|
+
} else marker = parseBootstrapMarker(markerBody, resolvedMarkerKey);
|
|
1057
1137
|
if (marker && !options.force) {
|
|
1058
1138
|
logger.info("Scanning stack state for references to the asset storage...");
|
|
1059
1139
|
const referencing = await scanStateReferences(stateBackend, [marker.assetBucket, marker.containerRepo]);
|
|
@@ -1065,14 +1145,19 @@ async function bootstrapDestroyCommand(options) {
|
|
|
1065
1145
|
const listing = stateKeys.map((k) => ` - ${describeStateKey(k)} [${k}]`).join("\n");
|
|
1066
1146
|
throw new CdkdError(`Refusing to delete state bucket '${bucketName}': ${stateKeys.length} stack(s) still have state in it:\n${listing}\nDestroy every stack ('cdkd destroy' / 'cdkd state destroy') before deleting the state bucket.`, "STATE_BUCKET_NOT_EMPTY");
|
|
1067
1147
|
}
|
|
1068
|
-
|
|
1069
|
-
if (
|
|
1148
|
+
if (markerSiblings.otherRegions.length > 0) throw new CdkdError(`Refusing to delete state bucket '${bucketName}': region(s) ${markerSiblings.otherRegions.join(", ")} are still opted in to cdkd asset storage (their bootstrap markers live in this bucket). Run 'cdkd bootstrap --destroy --region <r>' for each first.`, "STATE_BUCKET_HOLDS_MARKERS");
|
|
1149
|
+
if (markerSiblings.sameRegionKeys.length > 0) {
|
|
1150
|
+
const canonicalStillPresent = markerBody !== null && resolvedMarkerKey === markerKey;
|
|
1151
|
+
const listing = markerSiblings.sameRegionKeys.map((k) => canonicalStillPresent ? ` - ${k}` : ` - ${k} (cdkd bootstrap --destroy --region ${markerRegionOfKey(k)})`).join("\n");
|
|
1152
|
+
const remedy = canonicalStillPresent ? `Run 'cdkd bootstrap --destroy --region ${region}' WITHOUT --include-state-bucket ONCE: it tears down '${resolvedMarkerKey}' and then prints the exact --region spelling for each marker still standing. Follow those, then re-run with --include-state-bucket. (No per-marker spelling is given here because '${resolvedMarkerKey}' still exists and would be resolved first — and simply repeating this same command would not clear the rest, since once it is gone this region's canonical key no longer resolves anything.)` : "Run the command shown against each marker above, then re-run with --include-state-bucket.";
|
|
1153
|
+
throw new CdkdError(`Refusing to delete state bucket '${bucketName}': region '${region}' has ${markerSiblings.sameRegionKeys.length} further bootstrap marker(s) under a different spelling of its name, which this teardown does not delete:\n${listing}\n${remedy}\nDestroy each one first — deleting the state bucket now would remove those markers while the asset storage they name survives, with no record of its names.`, "STATE_BUCKET_HOLDS_SIBLING_MARKERS");
|
|
1154
|
+
}
|
|
1070
1155
|
}
|
|
1071
1156
|
const planLines = [];
|
|
1072
1157
|
if (marker) {
|
|
1073
1158
|
planLines.push(`Asset bucket: s3://${marker.assetBucket} (region ${region}, all contents)`);
|
|
1074
1159
|
planLines.push(`Container-asset ECR repository: ${marker.containerRepo} (region ${region}, all images)`);
|
|
1075
|
-
planLines.push(`Bootstrap marker: s3://${bucketName}/${
|
|
1160
|
+
planLines.push(`Bootstrap marker: s3://${bucketName}/${resolvedMarkerKey} (deleted last)`);
|
|
1076
1161
|
}
|
|
1077
1162
|
if (options.includeStateBucket) planLines.push(`State bucket: s3://${bucketName} (ALL contents, including all versions)`);
|
|
1078
1163
|
if (!await promptBootstrapDestroyConfirm({
|
|
@@ -1085,8 +1170,10 @@ async function bootstrapDestroyCommand(options) {
|
|
|
1085
1170
|
if (marker) {
|
|
1086
1171
|
await emptyAndDeleteBucket(awsClients.s3, marker.assetBucket, accountId, "Asset bucket", logger);
|
|
1087
1172
|
await deleteContainerRepo(marker.containerRepo, region, options.profile, logger);
|
|
1088
|
-
await stateBackend.deleteRawObjects([
|
|
1089
|
-
logger.info(`✓ Deleted bootstrap marker (${
|
|
1173
|
+
await stateBackend.deleteRawObjects([resolvedMarkerKey]);
|
|
1174
|
+
logger.info(`✓ Deleted bootstrap marker (${resolvedMarkerKey})`);
|
|
1175
|
+
for (const siblingKey of markerSiblings.sameRegionKeys) logger.warn(`A further bootstrap marker for this region remains at '${siblingKey}' (this region was bootstrapped under more than one spelling of its name). It names asset storage that was NOT destroyed by this run — re-run 'cdkd bootstrap --destroy --region ${markerRegionOfKey(siblingKey)}' to tear that one down too.`);
|
|
1176
|
+
if (!markerListingSucceeded) logger.warn(`The bootstrap-marker listing could not be made, so a marker under another spelling of this region may still be live. Check 's3://${bucketName}/${BOOTSTRAP_MARKER_PREFIX}'.`);
|
|
1090
1177
|
logger.info(`\ncdkd asset storage is now OFF for region ${region}: future deploys in this region fall back to legacy mode (CDK bootstrap destinations) unless the region is bootstrapped again.`);
|
|
1091
1178
|
}
|
|
1092
1179
|
if (options.includeStateBucket) {
|
|
@@ -69864,6 +69951,107 @@ const KEY_TERMINATORS = "[^\\s\"'?]";
|
|
|
69864
69951
|
*/
|
|
69865
69952
|
const GC_EXTRA_URL_SUFFIXES = [];
|
|
69866
69953
|
/**
|
|
69954
|
+
* Every AWS partition's URL suffix. The asset-reference matchers match the
|
|
69955
|
+
* suffix against this CLOSED SET instead of hardcoding one literal
|
|
69956
|
+
* (issue #1781).
|
|
69957
|
+
*
|
|
69958
|
+
* Before this list, all three matchers spelled `amazonaws\.com` inline, so a
|
|
69959
|
+
* state file written in `aws-cn` / `us-iso*` / `us-isob*` (recording
|
|
69960
|
+
* `amazonaws.com.cn` / `c2s.ic.gov` / `sc2s.sgov.gov` hosts) matched none of
|
|
69961
|
+
* them and its assets read as UNREFERENCED.
|
|
69962
|
+
*
|
|
69963
|
+
* **What that actually deleted, measured rather than assumed.** The blast
|
|
69964
|
+
* radius is narrower than "every live asset" but still a silent, irreversible
|
|
69965
|
+
* delete:
|
|
69966
|
+
*
|
|
69967
|
+
* - **S3 was already rescued**, by accident. {@link CONTENT_HASH_KEY_RE}
|
|
69968
|
+
* collects `<sha256>.<ext>` tokens out of ANY string regardless of host, and
|
|
69969
|
+
* cdkd's file assets are content-addressed with an extension — so a
|
|
69970
|
+
* non-commercial `https://…amazonaws.com.cn/<sha256>.zip` still had its key
|
|
69971
|
+
* protected by the name-independent pass. Measured on a real `--dry-run`
|
|
69972
|
+
* against a seeded `cn-north-1` state file: 71 of 72 objects, including the
|
|
69973
|
+
* referenced one, were protected with the matchers still broken.
|
|
69974
|
+
* - **ECR was NOT rescued, and is where the bug actually deleted.** cdkd's
|
|
69975
|
+
* image tags are bare 64-hex with no `.<ext>` tail, so the content-hash pass
|
|
69976
|
+
* cannot see them, and digests are `sha256:<hex>` — also no tail. With the
|
|
69977
|
+
* ECR matcher missing the suffix, a referenced non-commercial image is
|
|
69978
|
+
* selected for deletion. That is the case the live test reproduced.
|
|
69979
|
+
*
|
|
69980
|
+
* Either way the failure direction is the opposite of the #1758 / #1745 sites,
|
|
69981
|
+
* which merely emit a host that does not resolve.
|
|
69982
|
+
*
|
|
69983
|
+
* Two properties of this list are load-bearing:
|
|
69984
|
+
*
|
|
69985
|
+
* - It is the union over ALL partitions, not
|
|
69986
|
+
* `derivePartitionAndUrlSuffix(region).urlSuffix` for the caller's region.
|
|
69987
|
+
* The scan reads EVERY state file in the state bucket, written by any cdkd
|
|
69988
|
+
* binary for any region, so it must match every partition at once; a single
|
|
69989
|
+
* derived literal would still delete a `cn-north-1` stack's images during a
|
|
69990
|
+
* `us-east-1` gc run.
|
|
69991
|
+
* - It is a SUPERSET of the arms {@link derivePartitionAndUrlSuffix} knows
|
|
69992
|
+
* (issue #1764). A suffix missing HERE deletes live assets, which is the
|
|
69993
|
+
* irreversible direction, so this list must lead that table rather than
|
|
69994
|
+
* follow it.
|
|
69995
|
+
*
|
|
69996
|
+
* The superset property is now MECHANICAL rather than hand-maintained (issue
|
|
69997
|
+
* #1785): the list is built FROM {@link PARTITION_TABLE} plus
|
|
69998
|
+
* {@link GC_EXTRA_URL_SUFFIXES}, so a partition arm added to that table
|
|
69999
|
+
* propagates here in the same commit instead of silently outrunning gc. It
|
|
70000
|
+
* used to be a hand-written literal, and the test meant to fence it iterated a
|
|
70001
|
+
* hand-written region list — so a new arm in the derive table redded nothing.
|
|
70002
|
+
*
|
|
70003
|
+
* It is a closed SET rather than a `[^/\s]+` wildcard so a look-alike host
|
|
70004
|
+
* (`https://<assetBucket>.s3.<region>.example.com/<key>`) is not treated as a
|
|
70005
|
+
* cdkd asset reference. That direction only ever over-PROTECTS, but it would
|
|
70006
|
+
* also let any string embedding the bucket name pin an object forever, which
|
|
70007
|
+
* quietly turns gc into a no-op.
|
|
70008
|
+
*
|
|
70009
|
+
* NOTE this is a weaker check than `src/utils/ecr-uri.ts`, which CAPTURES the
|
|
70010
|
+
* suffix and validates it against the region the host names. That is the right
|
|
70011
|
+
* shape there (one host, one caller-known region) and the wrong shape here:
|
|
70012
|
+
* the region in a scanned string is not necessarily the region whose partition
|
|
70013
|
+
* the suffix must belong to, and pairing them would re-inherit #1764's missing
|
|
70014
|
+
* arms on the irreversible side.
|
|
70015
|
+
*/
|
|
70016
|
+
const AWS_URL_SUFFIXES = [.../* @__PURE__ */ new Set([...PARTITION_TABLE.map((row) => row.urlSuffix), ...GC_EXTRA_URL_SUFFIXES])];
|
|
70017
|
+
/**
|
|
70018
|
+
* Spell an ASCII literal so it matches case-INSENSITIVELY inside a
|
|
70019
|
+
* case-SENSITIVE `RegExp`: `s3` → `[sS]3`, `amazonaws.com` →
|
|
70020
|
+
* `[aA][mM][aA][zZ][oO][nN][aA][wW][sS]\.[cC][oO][mM]`. EVERY character goes
|
|
70021
|
+
* through {@link escapeRegExp} first and the ASCII letters are folded after,
|
|
70022
|
+
* so the result is exactly as strict as an escaped literal outside the letters
|
|
70023
|
+
* it folds.
|
|
70024
|
+
*
|
|
70025
|
+
* This exists because gc must fold only PART of a pattern (issue #1847). Host
|
|
70026
|
+
* names are case-insensitive, so
|
|
70027
|
+
* `https://<assetBucket>.s3.<region>.AMAZONAWS.COM/<key>` in a state file names
|
|
70028
|
+
* the same object as the lower-cased spelling — and a reference gc misses reads
|
|
70029
|
+
* as UNREFERENCED, so the live object is DELETED. The obvious fix, the `i` flag
|
|
70030
|
+
* the ECR matcher carries, is per-REGEX and therefore all-or-nothing: it cannot
|
|
70031
|
+
* fold the host while leaving a KEY or a path-segment bucket exact, and those
|
|
70032
|
+
* segments are genuinely case-sensitive at S3. Which segment gets folded is
|
|
70033
|
+
* decided per shape at the matchers themselves — see the block above
|
|
70034
|
+
* `s3UriRe` for the authority / path-segment / DNS-label split.
|
|
70035
|
+
*
|
|
70036
|
+
* Two further properties are load-bearing:
|
|
70037
|
+
*
|
|
70038
|
+
* - Folding by character class rather than by flag leaves
|
|
70039
|
+
* {@link URL_SUFFIX_ALTERNATION} — SHARED with the ECR matcher — spelled
|
|
70040
|
+
* exactly as that matcher's own review settled it, so the S3 side cannot
|
|
70041
|
+
* quietly redefine the constant the ECR side reads (issues #1792 / #1793).
|
|
70042
|
+
* - A two-character class is not `/k/i`. Under a `u` flag the `i` flag
|
|
70043
|
+
* canonicalizes U+212A KELVIN SIGN onto ASCII `k` — the hazard
|
|
70044
|
+
* `src/utils/ecr-uri.ts` guards its region / suffix checks against —
|
|
70045
|
+
* whereas `[kK]` matches those two code points and nothing else, with or
|
|
70046
|
+
* without `u`. (Measured, and pinned by `tests/unit/cli/gc.test.ts`:
|
|
70047
|
+
* `/k/iu` matches U+212A, `/[kK]/u` does not.) The matchers below carry no
|
|
70048
|
+
* `u` flag today, so this is about what adding one later could silently
|
|
70049
|
+
* widen: with classes, nothing.
|
|
70050
|
+
*/
|
|
70051
|
+
function caseFoldLiteral(literal) {
|
|
70052
|
+
return escapeRegExp(literal).replace(/[A-Za-z]/g, (ch) => `[${ch.toLowerCase()}${ch.toUpperCase()}]`);
|
|
70053
|
+
}
|
|
70054
|
+
/**
|
|
69867
70055
|
* `(?:amazonaws\.com\.cn|amazonaws\.com|...)` — {@link AWS_URL_SUFFIXES} as a
|
|
69868
70056
|
* non-capturing alternation, LONGEST FIRST so `amazonaws.com.cn` is tried
|
|
69869
70057
|
* before its own `amazonaws.com` prefix instead of relying on backtracking.
|
|
@@ -69873,8 +70061,48 @@ const GC_EXTRA_URL_SUFFIXES = [];
|
|
|
69873
70061
|
* the ECR digest), and this alternation sits BEFORE those groups in all three
|
|
69874
70062
|
* patterns. Making it capturing would shift every index by one and silently
|
|
69875
70063
|
* collect the suffix as if it were a key.
|
|
70064
|
+
*
|
|
70065
|
+
* `spellSuffix` decides how each suffix is written out, so the SET, the
|
|
70066
|
+
* longest-first order and the non-capturing property keep exactly one
|
|
70067
|
+
* definition while the two consumers differ in case handling — see
|
|
70068
|
+
* {@link URL_SUFFIX_ALTERNATION_CASE_FOLDED}.
|
|
70069
|
+
*/
|
|
70070
|
+
function buildUrlSuffixAlternation(spellSuffix) {
|
|
70071
|
+
return `(?:${[...AWS_URL_SUFFIXES].sort((a, b) => b.length - a.length).map(spellSuffix).join("|")})`;
|
|
70072
|
+
}
|
|
70073
|
+
/**
|
|
70074
|
+
* The case-SENSITIVE spelling, consumed by {@link ECR_REGISTRY_HOST}.
|
|
70075
|
+
*
|
|
70076
|
+
* Folding THIS constant would be inert for its only consumer — the ECR matcher
|
|
70077
|
+
* carries the `i` flag, so it already matches an upper-cased suffix either way.
|
|
70078
|
+
* The constraint is the other direction: this is the spelling the ECR side's
|
|
70079
|
+
* own review settled on (issues #1792 / #1793), and the S3 matchers must widen
|
|
70080
|
+
* through their own {@link URL_SUFFIX_ALTERNATION_CASE_FOLDED} copy rather than
|
|
70081
|
+
* by editing the shared one, so an S3-side change can never reach the ECR
|
|
70082
|
+
* grammar.
|
|
70083
|
+
*/
|
|
70084
|
+
const URL_SUFFIX_ALTERNATION = buildUrlSuffixAlternation(escapeRegExp);
|
|
70085
|
+
/**
|
|
70086
|
+
* The case-FOLDED spelling, consumed by the three S3 matchers (issue #1847).
|
|
70087
|
+
* Same suffix set, same order, letters folded per {@link caseFoldLiteral} — so
|
|
70088
|
+
* an upper-cased or mixed-case host suffix collects the key. Still a CLOSED
|
|
70089
|
+
* set: folding widens the SPELLING of each suffix, never which suffixes count.
|
|
69876
70090
|
*/
|
|
69877
|
-
const
|
|
70091
|
+
const URL_SUFFIX_ALTERNATION_CASE_FOLDED = buildUrlSuffixAlternation(caseFoldLiteral);
|
|
70092
|
+
/**
|
|
70093
|
+
* `[sS]3`, case-folded (issue #1847). One constant for BOTH places the literal
|
|
70094
|
+
* `s3` appears: the `s3://` URI SCHEME and the `s3` HOST LABEL of the two HTTPS
|
|
70095
|
+
* shapes. The two roles differ but the spelling and the fold argument are the
|
|
70096
|
+
* same, and a single constant is what stops the three matchers drifting into
|
|
70097
|
+
* two different spellings of it.
|
|
70098
|
+
*/
|
|
70099
|
+
const S3_SEGMENT_CASE_FOLDED = caseFoldLiteral("s3");
|
|
70100
|
+
/**
|
|
70101
|
+
* `[hH][tT][tT][pP][sS]` — the URI scheme, case-folded (issue #1847). A scheme
|
|
70102
|
+
* is case-insensitive by RFC 3986 §3.1, and folding a fixed literal that is
|
|
70103
|
+
* neither the bucket name nor the key can only ever over-PROTECT.
|
|
70104
|
+
*/
|
|
70105
|
+
const HTTPS_SCHEME_CASE_FOLDED = caseFoldLiteral("https");
|
|
69878
70106
|
/**
|
|
69879
70107
|
* The HOST half of an ECR registry, built from the ONE authoritative FORM TABLE
|
|
69880
70108
|
* in `src/utils/ecr-uri.ts` (issue #1793) instead of spelled a second time here.
|
|
@@ -69967,16 +70195,21 @@ function tryDecodeBase64Text(value) {
|
|
|
69967
70195
|
* account's or region's URI can only over-protect (keep more), never
|
|
69968
70196
|
* delete more.
|
|
69969
70197
|
*
|
|
69970
|
-
* `<urlSuffix>` in the two S3 shapes is
|
|
69971
|
-
* EVERY partition's suffix at
|
|
69972
|
-
*
|
|
70198
|
+
* `<urlSuffix>` in the two S3 shapes is
|
|
70199
|
+
* {@link URL_SUFFIX_ALTERNATION_CASE_FOLDED}, i.e. EVERY partition's suffix at
|
|
70200
|
+
* once — see {@link AWS_URL_SUFFIXES} for why a per-region derived literal is
|
|
70201
|
+
* the wrong shape here (issue #1781). Every S3 HOST segment is matched
|
|
70202
|
+
* case-insensitively, including the bucket label of the virtual-hosted shape,
|
|
70203
|
+
* while the KEY and the bucket in the other two shapes stay EXACT (issue
|
|
70204
|
+
* #1847); the per-shape reasoning is in the block above `s3UriRe`.
|
|
69973
70205
|
*/
|
|
69974
70206
|
function buildReferenceExtractors(marker) {
|
|
69975
|
-
const
|
|
70207
|
+
const bucketExact = escapeRegExp(marker.assetBucket);
|
|
70208
|
+
const bucketFolded = caseFoldLiteral(marker.assetBucket);
|
|
69976
70209
|
const repo = escapeRegExp(marker.containerRepo);
|
|
69977
|
-
const s3UriRe = new RegExp(
|
|
69978
|
-
const virtualHostedRe = new RegExp(
|
|
69979
|
-
const pathStyleRe = new RegExp(
|
|
70210
|
+
const s3UriRe = new RegExp(`${S3_SEGMENT_CASE_FOLDED}://${bucketExact}/(${KEY_TERMINATORS}+)`, "g");
|
|
70211
|
+
const virtualHostedRe = new RegExp(`${HTTPS_SCHEME_CASE_FOLDED}://${bucketFolded}\\.${S3_SEGMENT_CASE_FOLDED}[^/\\s]*\\.${URL_SUFFIX_ALTERNATION_CASE_FOLDED}/(${KEY_TERMINATORS}+)`, "g");
|
|
70212
|
+
const pathStyleRe = new RegExp(`${HTTPS_SCHEME_CASE_FOLDED}://${S3_SEGMENT_CASE_FOLDED}[^/\\s]*\\.${URL_SUFFIX_ALTERNATION_CASE_FOLDED}/${bucketExact}/(${KEY_TERMINATORS}+)`, "g");
|
|
69980
70213
|
const ecrRe = new RegExp(`${ECR_REGISTRY_HOST}/${repo}(?::([A-Za-z0-9_][A-Za-z0-9._-]{0,127}))?(?:@(sha256:[0-9a-f]{64}))?`, "gi");
|
|
69981
70214
|
const extractOnce = (value, refs) => {
|
|
69982
70215
|
for (const re of [
|
|
@@ -70245,9 +70478,10 @@ async function gcCommand(options) {
|
|
|
70245
70478
|
logger.debug("Options:", options);
|
|
70246
70479
|
await applyRoleArnIfSet({
|
|
70247
70480
|
roleArn: options.roleArn,
|
|
70248
|
-
region: options.region
|
|
70481
|
+
region: canonicalizeRegion(options.region)
|
|
70249
70482
|
});
|
|
70250
|
-
const
|
|
70483
|
+
const rawRegion = options.region || process.env["AWS_REGION"] || "us-east-1";
|
|
70484
|
+
const region = canonicalizeRegion(rawRegion);
|
|
70251
70485
|
const awsClients = new AwsClients({
|
|
70252
70486
|
region,
|
|
70253
70487
|
...options.profile && { profile: options.profile }
|
|
@@ -70272,9 +70506,18 @@ async function gcCommand(options) {
|
|
|
70272
70506
|
});
|
|
70273
70507
|
try {
|
|
70274
70508
|
const markerKey = getBootstrapMarkerKey(region);
|
|
70509
|
+
const rawMarkerKey = getBootstrapMarkerKey(rawRegion);
|
|
70275
70510
|
let markerBody;
|
|
70511
|
+
let resolvedMarkerKey = markerKey;
|
|
70276
70512
|
try {
|
|
70277
70513
|
markerBody = await stateBackend.getRawObject(markerKey);
|
|
70514
|
+
if (markerBody === null && rawMarkerKey !== markerKey) {
|
|
70515
|
+
markerBody = await stateBackend.getRawObject(rawMarkerKey);
|
|
70516
|
+
if (markerBody !== null) {
|
|
70517
|
+
resolvedMarkerKey = rawMarkerKey;
|
|
70518
|
+
logger.debug(`Bootstrap marker found at the un-folded key '${rawMarkerKey}' (none at '${markerKey}') — an upper-cased region was used at 'cdkd bootstrap' time.`);
|
|
70519
|
+
}
|
|
70520
|
+
}
|
|
70278
70521
|
} catch (error) {
|
|
70279
70522
|
if (error.name === "NoSuchBucket") {
|
|
70280
70523
|
logger.info(`State bucket '${bucketName}' does not exist — this account was never bootstrapped; nothing to garbage-collect.`);
|
|
@@ -70283,10 +70526,11 @@ async function gcCommand(options) {
|
|
|
70283
70526
|
throw error;
|
|
70284
70527
|
}
|
|
70285
70528
|
if (markerBody === null) {
|
|
70286
|
-
|
|
70529
|
+
const probed = rawMarkerKey === markerKey ? markerKey : `${markerKey}, ${rawMarkerKey}`;
|
|
70530
|
+
logger.info(`No bootstrap marker for region '${region}' (${probed}) — the region is not opted in to cdkd asset storage; nothing to garbage-collect. (CDK bootstrap storage is 'cdk gc' territory.)`);
|
|
70287
70531
|
return;
|
|
70288
70532
|
}
|
|
70289
|
-
const marker = parseBootstrapMarker(markerBody,
|
|
70533
|
+
const marker = parseBootstrapMarker(markerBody, resolvedMarkerKey);
|
|
70290
70534
|
const lockKeys = await listAllLockKeys(stateBackend);
|
|
70291
70535
|
if (lockKeys.length > 0) {
|
|
70292
70536
|
const listing = lockKeys.map((k) => ` - ${describeStateKey(k, LOCK_FILE_SUFFIX)} [${k}]`).join("\n");
|
|
@@ -82117,7 +82361,7 @@ function createMigrateCommand() {
|
|
|
82117
82361
|
*/
|
|
82118
82362
|
function buildProgram() {
|
|
82119
82363
|
const program = new Command();
|
|
82120
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.
|
|
82364
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.7");
|
|
82121
82365
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
82122
82366
|
const { profile } = actionCommand.optsWithGlobals();
|
|
82123
82367
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|