@go-to-k/cdkd 0.284.24 → 0.284.26
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-Bh0odlSk.js → asg-provider-CS5j-5z6.js} +2 -2
- package/dist/{asg-provider-Bh0odlSk.js.map → asg-provider-CS5j-5z6.js.map} +1 -1
- package/dist/cli.js +521 -176
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-BJQbni1s.js → deploy-engine-DNCs229s.js} +294 -34
- package/dist/deploy-engine-DNCs229s.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-BJQbni1s.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as
|
|
2
|
+
import { $ as isInterruptedWaitError, $n as LocalMigrateError, $t as escapeRegExp, A as isStatefulRecreateTargetSync, An as CFN_TEMPLATE_BODY_LIMIT, At as s3BucketArn, B as collectPublishedOutputNames, Bn as processStackMessages, Bt as DagBuilder, C as createPreDeleteFinalSnapshot, Cn as resolveCaptureObservedState, Ct as TEMPLATE_SOURCED_RULES, D as makeCanonicalizePropertiesFn, Dn as resolveUseCdkBootstrapAssets, Dt as maskSecretsInText, E as unsupportedFinalSnapshotError, En as resolveStateBucketWithDefaultAndSource, Et as isSingleDynamicReferenceToken, F as gray, Fn as expectedOwnerParam, Ft as applyRoleArnIfSet, G as IAMRoleProvider, Gn as resetAwsClients, Gt as shouldRetainResource, H as isExportAliasCollision, Hn as resolveBucketRegion, Ht as LockManager, I as green, In as PARTITION_TABLE, It as DiffCalculator, J as ProviderRegistry, Jn as CdkdError, Jt as WorkGraph, K as collectInlinePolicyNamesManagedBySiblings, Kn as setAwsClients, Kt as AssetPublisher, L as red, Ln as canonicalizeRegion, Lt as INTRINSIC_KEYS, M as formatResourceLine, Mn as MIGRATE_TMP_PREFIX, Mt as s3BucketDualStackDomainName, N as bold, Nn as findLargeInlineResources, Nt as s3BucketRegionalDomainName, O as extractDeploymentEventError, On as stateBucketExistenceConfirmed, Ot as redactSecretsForState, P as cyan, Pn as uploadCfnTemplate, Pt as s3BucketWebsiteUrl, Q as endCommandInterruptScope, Qn as LocalInvokeBuildError$1, Qt as rewriteTemplateAssetReferences, R as yellow, Rn as derivePartitionAndUrlSuffix, Rt as describeTypeWithThrottleRetry, S as ccRoutedFinalSnapshotError, Sn as resolveAutoAssetStorage, St as STATE_SOURCED_READBACK_RULES, T as refusesFinalSnapshot, Tn as resolveStateBucketWithDefault, Tt as dynamicReferenceTokens, U as secretBearingStateKeyWarning, Un as AwsClients, Ut as S3StateBackend, V as exportAliasCollisionScrubWarning, Vt as TemplateParser, W as stateKeySecretExposure, Wn as getAwsClients, Wt as rebuildClientForBucketRegion, X as findSilentDropProperties, Xt as createAssetRedirectResolver, Y as findActionableSilentDrops, Yt as buildAssetRedirectMap, Z as beginCommandInterruptScope, Zn as DeployCancelledError, Zt as loadPublishableAssetManifest, _ as IMPLICIT_DELETE_DEPENDENCIES, _n as Synthesizer, _r as isRetryableTransientError, _t as replayWarn, a as DeploymentEventsStore, an as parseBootstrapMarker, ar as ProvisioningError, at as IntrinsicFunctionResolver, b as PRE_DELETE_SNAPSHOT_TYPES, bn as getLegacyStateBucketName, bt as requireConfigString, c as producerRegionsFromState, cn as validateContainerRepoName, cr as StackHasActiveImportsError, ct as refStateLookupFromResource, d as updatePartialMessage, dn as formatDockerLoginError, dt as resolveExplicitPhysicalId, en as stripControlChars, er as LocalStartServiceError, et as startInterruptWatch, f as updatePartialReason, fn as getDockerCmd, ft as assertRegionMatch, g as maskingRetryLogger, gn as getDockerImageBySourceHash, gr as isMarkedNonRetryable, gt as readConfigString, h as withResourceDeadline, hn as AssetManifestLoader, hr as withErrorHandling, ht as configStringRefusal, i as DeploymentEventsReader, in as getBootstrapMarkerKey, ir as PartialFailureError, it as isTerminationProtectionPropagationError, j as renderStatefulReason, jn as CFN_TEMPLATE_URL_LIMIT, jt as s3BucketDomainName, k as MULTI_REGION_RECREATE_BLOCKED_TYPES, kn as warnDeprecatedNoPrefixCliFlag, kt as scrubResourceRecord, l as replayFailedOperations, ln as buildDenyExternalAccessPolicy, lr as StackTerminationProtectionError, lt as WAFv2WebACLProvider, m as deleteSkipReason, mn as runDockerStreaming, mr as normalizeAwsError, mt as configBooleanRefusal, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as BOOTSTRAP_MARKER_PREFIX, nr as MissingCdkCliError, nt as slowCcOperationTimeoutMs, o as planFailedOps, on as readBootstrapMarkerBody, or as ResourceTimeoutError, ot as cfnRefValueFromPhysicalId, p as UNSPECIFIED_SKIP_REASON, pn as runDockerForeground, pt as coerceCfnBoolean, q as clearOnUpdateRemoval, qt as stringifyValue, r as DeployEngine, rn as ensureAssetStorage, rr as NestedStackChildDirectDestroyError, rt as disableInstanceApiTermination, s as planRollback, sn as validateAssetBucketName, sr as ResourceUpdateNotSupportedError, st as getAccountInfo, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as AssetModeResolver, tt as CloudControlProvider, u as replayRollback, un as buildDockerImage, ut as normalizeAwsTagsToCfn, v as computeImplicitDeleteEdges, vn as synthesisStatusMessage, vr as isThrottlingError, vt as requireConfigArray, w as isFinalSnapshotError, wn as resolveSkipPrefix, wt as createSecretMasker, x as buildFinalSnapshotIdentifier, xn as resolveApp, xt as STATE_SOURCED_CROSS_GENERATION_RULES, y as ATOMIC_FINAL_SNAPSHOT_TYPES, yn as getDefaultStateBucketName, yr as markNonRetryable, yt as requireConfigObject, z as collectDeclaredOutputNames, zn as AssemblyReader, zt as withRetry } from "./deploy-engine-DNCs229s.js";
|
|
3
3
|
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";
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-CS5j-5z6.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";
|
|
@@ -865,6 +865,165 @@ function namedCliRegion(optionRegion) {
|
|
|
865
865
|
function rawCliRegion(optionRegion) {
|
|
866
866
|
return optionRegion || process.env["AWS_REGION"] || void 0;
|
|
867
867
|
}
|
|
868
|
+
/**
|
|
869
|
+
* The region a command OPERATES on.
|
|
870
|
+
*
|
|
871
|
+
* Order: `--region` -> `AWS_REGION` -> `AWS_DEFAULT_REGION` -> the region the
|
|
872
|
+
* AWS SDK's own chain resolves (i.e. the selected profile's `region =` line)
|
|
873
|
+
* -> `us-east-1`.
|
|
874
|
+
*
|
|
875
|
+
* The profile step is what issue
|
|
876
|
+
* [#2029](https://github.com/go-to-k/cdkd/issues/2029) adds, and it removes an
|
|
877
|
+
* incoherence rather than adding a preference. cdkd ALREADY consulted the
|
|
878
|
+
* profile: every command builds its pre-flight bag as
|
|
879
|
+
* `new AwsClients({ ...(options.region && { region }) })`, so with no flag the
|
|
880
|
+
* bag is region-less and the SDK resolves the profile. Only the region VALUE -
|
|
881
|
+
* the one that keys the state file, keys the bootstrap marker and answers
|
|
882
|
+
* `AWS::Region` in every user template - fell back to the literal. Measured on
|
|
883
|
+
* an env-agnostic stack with `region = ap-northeast-1` in the profile and no
|
|
884
|
+
* flag or env var:
|
|
885
|
+
*
|
|
886
|
+
* ```text
|
|
887
|
+
* State bucket '...' is in 'us-east-1' (client was 'ap-northeast-1'); rebuilding S3 client.
|
|
888
|
+
* Getting state for stack: CdkdBasicExample (us-east-1)
|
|
889
|
+
* Resolved Ref to pseudo parameter: AWS::Region -> us-east-1
|
|
890
|
+
* ```
|
|
891
|
+
*
|
|
892
|
+
* One command, two regions: the pre-flight talked to the profile's region
|
|
893
|
+
* while the resources, their ARNs and their state landed in us-east-1. Only an
|
|
894
|
+
* ENV-AGNOSTIC stack is affected - a stack with `env: { region }` pinned in CDK
|
|
895
|
+
* carries its own region through the Cloud Assembly and never reaches this
|
|
896
|
+
* fallback (`assembly-reader.ts` maps CDK's `unknown-region` to `undefined`).
|
|
897
|
+
*
|
|
898
|
+
* `AWS_DEFAULT_REGION` sits between `AWS_REGION` and the profile because the
|
|
899
|
+
* AWS CLI honours it and the JS SDK does NOT (measured: with only that variable
|
|
900
|
+
* set, `STSClient({}).config.region()` returns the PROFILE's region, not the
|
|
901
|
+
* variable's). Reading it here is what stops cdkd disagreeing with the CLI a
|
|
902
|
+
* user just ran.
|
|
903
|
+
*
|
|
904
|
+
* Returning the SOURCE, not just the region, is what lets a caller tell "the
|
|
905
|
+
* user asked for this region" from "we inferred it" - which is the difference
|
|
906
|
+
* that decides whether the reconciliation in
|
|
907
|
+
* {@link reconcileRegionWithLegacyDefault} may move the answer back.
|
|
908
|
+
*/
|
|
909
|
+
async function resolveEffectiveRegion(options) {
|
|
910
|
+
const flag = canonicalizeRegion(options.region);
|
|
911
|
+
if (flag) return {
|
|
912
|
+
region: flag,
|
|
913
|
+
source: "flag"
|
|
914
|
+
};
|
|
915
|
+
const awsRegion = canonicalizeRegion(process.env["AWS_REGION"]);
|
|
916
|
+
if (awsRegion) return {
|
|
917
|
+
region: awsRegion,
|
|
918
|
+
source: "env"
|
|
919
|
+
};
|
|
920
|
+
const defaultRegionEnv = canonicalizeRegion(process.env["AWS_DEFAULT_REGION"]);
|
|
921
|
+
if (defaultRegionEnv) return {
|
|
922
|
+
region: defaultRegionEnv,
|
|
923
|
+
source: "default-env"
|
|
924
|
+
};
|
|
925
|
+
const fromProfile = canonicalizeRegion(await resolveSdkDefaultRegion(options.profile));
|
|
926
|
+
if (fromProfile) return {
|
|
927
|
+
region: fromProfile,
|
|
928
|
+
source: "profile"
|
|
929
|
+
};
|
|
930
|
+
return {
|
|
931
|
+
region: LEGACY_DEFAULT_REGION,
|
|
932
|
+
source: "default"
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* The region cdkd used to fall back to before issue #2029, and still falls
|
|
937
|
+
* back to when nothing at all answers.
|
|
938
|
+
*
|
|
939
|
+
* Named rather than inlined because the reconciliation below has to ask
|
|
940
|
+
* "is there state under the OLD default?", and that question only means
|
|
941
|
+
* something while the two are the same constant.
|
|
942
|
+
*/
|
|
943
|
+
const LEGACY_DEFAULT_REGION = "us-east-1";
|
|
944
|
+
/**
|
|
945
|
+
* Ask the AWS SDK's own chain which region it would use.
|
|
946
|
+
*
|
|
947
|
+
* A throwaway client, sampled once and destroyed: `src/utils/aws-clients.ts`
|
|
948
|
+
* is explicit that a region-less bag's lazy members resolve independently and
|
|
949
|
+
* need not agree with each other, so nothing here is reused for real work.
|
|
950
|
+
* Returns `undefined` rather than throwing when the chain answers nothing -
|
|
951
|
+
* a client with no resolvable region REJECTS, and that rejection is the normal
|
|
952
|
+
* "no region configured" signal rather than an error worth surfacing.
|
|
953
|
+
*/
|
|
954
|
+
async function resolveSdkDefaultRegion(profile) {
|
|
955
|
+
const { STSClient } = await import("@aws-sdk/client-sts");
|
|
956
|
+
let client;
|
|
957
|
+
try {
|
|
958
|
+
client = new STSClient({ ...profile && { profile } });
|
|
959
|
+
return await client.config.region() || void 0;
|
|
960
|
+
} catch {
|
|
961
|
+
return;
|
|
962
|
+
} finally {
|
|
963
|
+
client?.destroy?.();
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* The marker-key twin of {@link reconcileRegionWithLegacyDefault}.
|
|
968
|
+
*
|
|
969
|
+
* `cdkd bootstrap` WRITES `cdkd-bootstrap/{region}.json`; `cdkd gc` and
|
|
970
|
+
* `cdkd bootstrap --destroy` READ it. Issue
|
|
971
|
+
* [#2029](https://github.com/go-to-k/cdkd/issues/2029) moves what `{region}`
|
|
972
|
+
* resolves to when the user names none, and a previous attempt at it moved the
|
|
973
|
+
* READ side alone — which silently broke that pairing: for a user with a
|
|
974
|
+
* non-us-east-1 profile, `cdkd gc` reported "not opted in" and the teardown
|
|
975
|
+
* "nothing to delete" while the asset bucket and ECR repo stayed alive and
|
|
976
|
+
* billing. So all three commands resolve through THIS function, and the pairing
|
|
977
|
+
* holds by construction rather than by three call sites agreeing.
|
|
978
|
+
*
|
|
979
|
+
* The reconciliation itself is the same rule as the state-key one: an inferred
|
|
980
|
+
* region yields to an existing marker under the old default, a named region
|
|
981
|
+
* never does, and a first bootstrap goes to the profile's region because there
|
|
982
|
+
* is nothing to strand.
|
|
983
|
+
*
|
|
984
|
+
* Note this is deliberately NOT `readBootstrapMarkerBody`: that helper answers
|
|
985
|
+
* "read the marker for THIS region, trying both spellings", which is a
|
|
986
|
+
* different question and would conflate a case difference with a region
|
|
987
|
+
* difference. Here both probes use the canonical key, because the question is
|
|
988
|
+
* only which REGION owns a marker.
|
|
989
|
+
*/
|
|
990
|
+
async function reconcileMarkerRegionWithLegacyDefault(input) {
|
|
991
|
+
const { effective, probe, markerKeyFor, logger } = input;
|
|
992
|
+
if (!regionNeedsReconciliation(effective)) return effective.region;
|
|
993
|
+
const markerExists = async (region) => {
|
|
994
|
+
try {
|
|
995
|
+
return await probe.getRawObject(markerKeyFor(region)) !== null;
|
|
996
|
+
} catch (error) {
|
|
997
|
+
if (error.name !== "NoSuchBucket") throw error;
|
|
998
|
+
logger.debug(`[region] no state bucket yet, so no bootstrap marker for ${region} (first bootstrap).`);
|
|
999
|
+
return false;
|
|
1000
|
+
}
|
|
1001
|
+
};
|
|
1002
|
+
if (await markerExists(effective.region)) {
|
|
1003
|
+
logger.debug(`[region] asset storage exists in ${effective.region} (from your AWS profile); using it.`);
|
|
1004
|
+
return effective.region;
|
|
1005
|
+
}
|
|
1006
|
+
if (!await markerExists("us-east-1")) return effective.region;
|
|
1007
|
+
logger.info(`cdkd asset storage exists in ${LEGACY_DEFAULT_REGION}, but your AWS profile resolves ${effective.region}. Continuing to use ${LEGACY_DEFAULT_REGION} so the existing storage is not orphaned. Pass '--region ${effective.region}' to target your profile's region, or '--region ${LEGACY_DEFAULT_REGION}' to silence this message.`);
|
|
1008
|
+
return LEGACY_DEFAULT_REGION;
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Whether {@link resolveEffectiveRegion}'s answer is subject to the
|
|
1012
|
+
* reconciliation — i.e. whether cdkd INFERRED it rather than being told.
|
|
1013
|
+
*
|
|
1014
|
+
* `flag` and `env` (`AWS_REGION`) are the user speaking, and are obeyed as
|
|
1015
|
+
* given. `default-env` (`AWS_DEFAULT_REGION`) is NOT: cdkd never read that
|
|
1016
|
+
* variable before, so acting on it is a new inference. `default` is already
|
|
1017
|
+
* the legacy region, so there is nothing to reconcile against.
|
|
1018
|
+
*
|
|
1019
|
+
* Exported so a caller can skip CONSTRUCTING a probe client it will not use —
|
|
1020
|
+
* the reconciliation functions return early on the same condition, but by then
|
|
1021
|
+
* the caller has already paid for an S3 client and a state backend.
|
|
1022
|
+
*/
|
|
1023
|
+
function regionNeedsReconciliation(effective) {
|
|
1024
|
+
if (effective.source === "flag" || effective.source === "env") return false;
|
|
1025
|
+
return effective.region !== LEGACY_DEFAULT_REGION;
|
|
1026
|
+
}
|
|
868
1027
|
|
|
869
1028
|
//#endregion
|
|
870
1029
|
//#region src/cli/commands/state-file-keys.ts
|
|
@@ -1159,15 +1318,43 @@ async function bootstrapDestroyCommand(options) {
|
|
|
1159
1318
|
roleArn: options.roleArn,
|
|
1160
1319
|
region: namedCliRegion(options.region)
|
|
1161
1320
|
});
|
|
1162
|
-
const
|
|
1321
|
+
const effective = await resolveEffectiveRegion(options);
|
|
1322
|
+
const accountClients = new AwsClients({
|
|
1323
|
+
region: effective.region,
|
|
1324
|
+
...options.profile && { profile: options.profile }
|
|
1325
|
+
});
|
|
1326
|
+
const accountId = (await accountClients.sts.send(new GetCallerIdentityCommand({}))).Account;
|
|
1327
|
+
const bucketName = options.stateBucket ?? getDefaultStateBucketName(accountId);
|
|
1328
|
+
let region = effective.region;
|
|
1329
|
+
if (regionNeedsReconciliation(effective)) {
|
|
1330
|
+
const probeBackend = new S3StateBackend(new S3Client({
|
|
1331
|
+
region: effective.region,
|
|
1332
|
+
...options.profile && { profile: options.profile }
|
|
1333
|
+
}), {
|
|
1334
|
+
bucket: bucketName,
|
|
1335
|
+
prefix: STATE_PREFIX
|
|
1336
|
+
}, {
|
|
1337
|
+
region: effective.region,
|
|
1338
|
+
...options.profile && { profile: options.profile }
|
|
1339
|
+
});
|
|
1340
|
+
try {
|
|
1341
|
+
region = await reconcileMarkerRegionWithLegacyDefault({
|
|
1342
|
+
effective,
|
|
1343
|
+
probe: probeBackend,
|
|
1344
|
+
markerKeyFor: getBootstrapMarkerKey,
|
|
1345
|
+
logger
|
|
1346
|
+
});
|
|
1347
|
+
} finally {
|
|
1348
|
+
probeBackend.destroyClient();
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1163
1351
|
const rawRegion = rawCliRegion(options.region) ?? region;
|
|
1164
|
-
const awsClients = new AwsClients({
|
|
1352
|
+
const awsClients = region === effective.region ? accountClients : new AwsClients({
|
|
1165
1353
|
region,
|
|
1166
1354
|
...options.profile && { profile: options.profile }
|
|
1167
1355
|
});
|
|
1356
|
+
if (awsClients !== accountClients) accountClients.destroy();
|
|
1168
1357
|
setAwsClients(awsClients);
|
|
1169
|
-
const accountId = (await awsClients.sts.send(new GetCallerIdentityCommand({}))).Account;
|
|
1170
|
-
const bucketName = options.stateBucket ?? getDefaultStateBucketName(accountId);
|
|
1171
1358
|
const markerS3Client = new S3Client({
|
|
1172
1359
|
region,
|
|
1173
1360
|
...options.profile && { profile: options.profile }
|
|
@@ -1291,19 +1478,19 @@ async function bootstrapCommand(options) {
|
|
|
1291
1478
|
if (options.verbose) logger.setLevel("debug");
|
|
1292
1479
|
logger.info("Starting cdkd bootstrap...");
|
|
1293
1480
|
logger.debug("Options:", options);
|
|
1294
|
-
const rawRegion = rawCliRegion(options.region)
|
|
1481
|
+
const rawRegion = rawCliRegion(options.region);
|
|
1295
1482
|
foldRegionOption(options);
|
|
1296
1483
|
await applyRoleArnIfSet({
|
|
1297
1484
|
roleArn: options.roleArn,
|
|
1298
1485
|
region: options.region
|
|
1299
1486
|
});
|
|
1300
|
-
const
|
|
1301
|
-
|
|
1487
|
+
const effective = await resolveEffectiveRegion(options);
|
|
1488
|
+
let awsClients = new AwsClients({
|
|
1489
|
+
region: effective.region,
|
|
1302
1490
|
...options.profile && { profile: options.profile }
|
|
1303
1491
|
});
|
|
1304
1492
|
setAwsClients(awsClients);
|
|
1305
|
-
|
|
1306
|
-
const region = namedCliRegion(options.region) ?? "us-east-1";
|
|
1493
|
+
let s3Client = awsClients.s3;
|
|
1307
1494
|
let bucketName;
|
|
1308
1495
|
let accountId;
|
|
1309
1496
|
if (options.stateBucket) {
|
|
@@ -1315,6 +1502,39 @@ async function bootstrapCommand(options) {
|
|
|
1315
1502
|
bucketName = getDefaultStateBucketName(accountId);
|
|
1316
1503
|
logger.info(`Using default state bucket: ${bucketName}`);
|
|
1317
1504
|
}
|
|
1505
|
+
let region = effective.region;
|
|
1506
|
+
if (regionNeedsReconciliation(effective)) {
|
|
1507
|
+
const probeBackend = new S3StateBackend(new S3Client({
|
|
1508
|
+
region: effective.region,
|
|
1509
|
+
...options.profile && { profile: options.profile }
|
|
1510
|
+
}), {
|
|
1511
|
+
bucket: bucketName,
|
|
1512
|
+
prefix: "cdkd"
|
|
1513
|
+
}, {
|
|
1514
|
+
region: effective.region,
|
|
1515
|
+
...options.profile && { profile: options.profile }
|
|
1516
|
+
});
|
|
1517
|
+
try {
|
|
1518
|
+
region = await reconcileMarkerRegionWithLegacyDefault({
|
|
1519
|
+
effective,
|
|
1520
|
+
probe: probeBackend,
|
|
1521
|
+
markerKeyFor: getBootstrapMarkerKey,
|
|
1522
|
+
logger
|
|
1523
|
+
});
|
|
1524
|
+
} finally {
|
|
1525
|
+
probeBackend.destroyClient();
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
if (region !== effective.region) {
|
|
1529
|
+
const held = new AwsClients({
|
|
1530
|
+
region,
|
|
1531
|
+
...options.profile && { profile: options.profile }
|
|
1532
|
+
});
|
|
1533
|
+
awsClients.destroy();
|
|
1534
|
+
awsClients = held;
|
|
1535
|
+
setAwsClients(awsClients);
|
|
1536
|
+
s3Client = awsClients.s3;
|
|
1537
|
+
}
|
|
1318
1538
|
const rebuiltStateBucketClient = await rebuildClientForBucketRegion(s3Client, bucketName, { ...options.profile && { profile: options.profile } });
|
|
1319
1539
|
const stateBucketS3 = rebuiltStateBucketClient ?? s3Client;
|
|
1320
1540
|
try {
|
|
@@ -1401,7 +1621,7 @@ async function bootstrapCommand(options) {
|
|
|
1401
1621
|
ecrClient,
|
|
1402
1622
|
stateBackend,
|
|
1403
1623
|
accountId,
|
|
1404
|
-
region: rawRegion,
|
|
1624
|
+
region: rawRegion ?? region,
|
|
1405
1625
|
force: options.force,
|
|
1406
1626
|
...options.assetBucket && { assetBucketName: options.assetBucket },
|
|
1407
1627
|
...options.containerRepo && { containerRepoName: options.containerRepo }
|
|
@@ -1774,6 +1994,7 @@ function createListCommand() {
|
|
|
1774
1994
|
* handler of its own).
|
|
1775
1995
|
*/
|
|
1776
1996
|
function forwardSigtermToSigint() {
|
|
1997
|
+
beginCommandInterruptScope();
|
|
1777
1998
|
const handler = () => {
|
|
1778
1999
|
if (process.listenerCount("SIGINT") === 0) process.exit(143);
|
|
1779
2000
|
process.emit("SIGINT", "SIGINT");
|
|
@@ -1781,6 +2002,7 @@ function forwardSigtermToSigint() {
|
|
|
1781
2002
|
process.on("SIGTERM", handler);
|
|
1782
2003
|
return () => {
|
|
1783
2004
|
process.removeListener("SIGTERM", handler);
|
|
2005
|
+
endCommandInterruptScope();
|
|
1784
2006
|
};
|
|
1785
2007
|
}
|
|
1786
2008
|
|
|
@@ -14839,28 +15061,37 @@ const DELETE_INDEX_WAIT_PROCEED_NOTE = `the DeleteTable goes ahead anyway and AW
|
|
|
14839
15061
|
async function waitForIndexesSettled(opts) {
|
|
14840
15062
|
const { tableName, logicalId, logger, describeTable, maxAttempts, proceedNote } = opts;
|
|
14841
15063
|
const budgetWait = opts.budgetWait;
|
|
15064
|
+
const watch = startInterruptWatch(`index settle wait on ${tableName}`);
|
|
14842
15065
|
let attempt = 0;
|
|
14843
|
-
|
|
14844
|
-
attempt
|
|
14845
|
-
|
|
14846
|
-
if (
|
|
14847
|
-
|
|
14848
|
-
|
|
14849
|
-
if (isThrottlingError(err)) {
|
|
14850
|
-
logger.debug(`DescribeTable throttled while waiting for indexes on ${tableName} (attempt ${attempt}/${maxAttempts}); still waiting: ${message}`);
|
|
14851
|
-
await new Promise((resolve) => setTimeout(resolve, INDEX_SETTLE_POLL_INTERVAL_MS));
|
|
14852
|
-
continue;
|
|
15066
|
+
try {
|
|
15067
|
+
while (budgetWait ? budgetWait.nextPoll() : attempt < maxAttempts) {
|
|
15068
|
+
attempt += 1;
|
|
15069
|
+
if (watch.isInterrupted()) {
|
|
15070
|
+
logger.debug(`Interrupted while waiting for indexes on ${tableName} (${logicalId}) after ${attempt - 1} DescribeTable polls; ${proceedNote}`);
|
|
15071
|
+
return;
|
|
14853
15072
|
}
|
|
14854
|
-
|
|
14855
|
-
|
|
15073
|
+
try {
|
|
15074
|
+
if (!hasTransitionalIndex((await describeTable()).Table?.GlobalSecondaryIndexes ?? [])) return;
|
|
15075
|
+
} catch (err) {
|
|
15076
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
15077
|
+
if (isThrottlingError(err)) {
|
|
15078
|
+
logger.debug(`DescribeTable throttled while waiting for indexes on ${tableName} (attempt ${attempt}/${maxAttempts}); still waiting: ${message}`);
|
|
15079
|
+
await new Promise((resolve) => setTimeout(resolve, INDEX_SETTLE_POLL_INTERVAL_MS));
|
|
15080
|
+
continue;
|
|
15081
|
+
}
|
|
15082
|
+
if (err instanceof ResourceNotFoundException$1) {
|
|
15083
|
+
logger.debug(`Table ${tableName} (${logicalId}) no longer exists while waiting for indexes`);
|
|
15084
|
+
return;
|
|
15085
|
+
}
|
|
15086
|
+
const errorName = err instanceof Error ? err.name : typeof err;
|
|
15087
|
+
logger.debug(`DescribeTable failed while waiting for indexes on ${tableName} (${logicalId}): ${message}`);
|
|
15088
|
+
logger.warn(`DescribeTable failed (${errorName}) while waiting for indexes on ${tableName} (${logicalId}), so cdkd stopped waiting; ${proceedNote} Re-run with --verbose for AWS's own message.`);
|
|
14856
15089
|
return;
|
|
14857
15090
|
}
|
|
14858
|
-
|
|
14859
|
-
logger.debug(`DescribeTable failed while waiting for indexes on ${tableName} (${logicalId}): ${message}`);
|
|
14860
|
-
logger.warn(`DescribeTable failed (${errorName}) while waiting for indexes on ${tableName} (${logicalId}), so cdkd stopped waiting; ${proceedNote} Re-run with --verbose for AWS's own message.`);
|
|
14861
|
-
return;
|
|
15091
|
+
await new Promise((resolve) => setTimeout(resolve, INDEX_SETTLE_POLL_INTERVAL_MS));
|
|
14862
15092
|
}
|
|
14863
|
-
|
|
15093
|
+
} finally {
|
|
15094
|
+
watch.dispose();
|
|
14864
15095
|
}
|
|
14865
15096
|
const pollSeconds = INDEX_SETTLE_POLL_INTERVAL_MS / 1e3;
|
|
14866
15097
|
const pollsRun = budgetWait ? budgetWait.pollsRun : maxAttempts;
|
|
@@ -14965,24 +15196,31 @@ function indexBusyRetryWarning(opts) {
|
|
|
14965
15196
|
*/
|
|
14966
15197
|
async function deleteTableWithIndexBusyRetry(opts) {
|
|
14967
15198
|
let deleteAttempts = 0;
|
|
14968
|
-
|
|
14969
|
-
|
|
14970
|
-
|
|
14971
|
-
|
|
14972
|
-
|
|
14973
|
-
|
|
14974
|
-
|
|
14975
|
-
|
|
14976
|
-
|
|
14977
|
-
|
|
14978
|
-
|
|
14979
|
-
|
|
14980
|
-
|
|
14981
|
-
|
|
14982
|
-
|
|
14983
|
-
|
|
14984
|
-
|
|
14985
|
-
|
|
15199
|
+
const watch = startInterruptWatch(`DynamoDB ${opts.typeLabel} ${opts.logicalId} delete`);
|
|
15200
|
+
try {
|
|
15201
|
+
await withRetry(async () => {
|
|
15202
|
+
if (deleteAttempts++ > 0) {
|
|
15203
|
+
if (deleteAttempts === 2) opts.logger.warn(indexBusyRetryWarning({
|
|
15204
|
+
typeLabel: opts.typeLabel,
|
|
15205
|
+
logicalId: opts.logicalId,
|
|
15206
|
+
physicalId: opts.physicalId,
|
|
15207
|
+
attemptNumber: deleteAttempts,
|
|
15208
|
+
maxRetries: opts.maxRetries
|
|
15209
|
+
}));
|
|
15210
|
+
await opts.reArm();
|
|
15211
|
+
}
|
|
15212
|
+
await opts.deleteTable();
|
|
15213
|
+
}, opts.logicalId, {
|
|
15214
|
+
maxRetries: opts.maxRetries,
|
|
15215
|
+
isRetryable: (message) => isIndexBusyDeleteError(message) && (opts.shouldKeepRetrying?.() ?? true),
|
|
15216
|
+
logger: opts.logger,
|
|
15217
|
+
isInterrupted: watch.isInterrupted,
|
|
15218
|
+
onInterrupted: watch.onInterrupted,
|
|
15219
|
+
sleep: (ms) => opts.sleepSeam.sleep ? opts.sleepSeam.sleep(ms) : new Promise((resolve) => setTimeout(resolve, ms))
|
|
15220
|
+
});
|
|
15221
|
+
} finally {
|
|
15222
|
+
watch.dispose();
|
|
15223
|
+
}
|
|
14986
15224
|
}
|
|
14987
15225
|
|
|
14988
15226
|
//#endregion
|
|
@@ -18658,94 +18896,111 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
18658
18896
|
const oldEnabled = oldSettings !== void 0 && oldSettings !== null;
|
|
18659
18897
|
const newEnabled = newSettings !== void 0 && newSettings !== null;
|
|
18660
18898
|
if (!oldEnabled && !newEnabled) return;
|
|
18661
|
-
|
|
18662
|
-
|
|
18663
|
-
|
|
18664
|
-
|
|
18665
|
-
|
|
18899
|
+
const watch = startInterruptWatch(`DynamoDB auto-scaling for ${tableName}`);
|
|
18900
|
+
try {
|
|
18901
|
+
if (newEnabled) {
|
|
18902
|
+
const minCapacity = Number(newSettings["MinCapacity"] ?? 0);
|
|
18903
|
+
const maxCapacity = Number(newSettings["MaxCapacity"] ?? 0);
|
|
18904
|
+
if (!Number.isFinite(minCapacity) || !Number.isFinite(maxCapacity)) {
|
|
18905
|
+
warn(`Cannot apply auto-scaling diff on ${tableName} (${dimension}): MinCapacity / MaxCapacity must be numbers, got ${maskSecrets(String(newSettings["MinCapacity"]))} / ${maskSecrets(String(newSettings["MaxCapacity"]))}`);
|
|
18906
|
+
return;
|
|
18907
|
+
}
|
|
18908
|
+
try {
|
|
18909
|
+
await withRetry(() => asClient.send(new RegisterScalableTargetCommand({
|
|
18910
|
+
ServiceNamespace: "dynamodb",
|
|
18911
|
+
ResourceId: resourceId,
|
|
18912
|
+
ScalableDimension: dimension,
|
|
18913
|
+
MinCapacity: minCapacity,
|
|
18914
|
+
MaxCapacity: maxCapacity
|
|
18915
|
+
})), `${resourceId} (${dimension})`, {
|
|
18916
|
+
isRetryable: (_message, error) => isThrottlingError(error),
|
|
18917
|
+
maxRetries: asMaxRetries,
|
|
18918
|
+
isInterrupted: watch.isInterrupted,
|
|
18919
|
+
onInterrupted: watch.onInterrupted,
|
|
18920
|
+
...autoScalingRetryDelays.sleep ? { sleep: autoScalingRetryDelays.sleep } : {}
|
|
18921
|
+
});
|
|
18922
|
+
} catch (err) {
|
|
18923
|
+
if (isInterruptedWaitError(err)) throw err;
|
|
18924
|
+
warn(`Could not register auto-scaling target on ${tableName} (${dimension}): ${err instanceof Error ? err.message : String(err)}. Run: aws application-autoscaling register-scalable-target --service-namespace dynamodb --resource-id ${resourceId} --scalable-dimension ${dimension} --min-capacity ${minCapacity} --max-capacity ${maxCapacity}`);
|
|
18925
|
+
return;
|
|
18926
|
+
}
|
|
18927
|
+
const tttCfg = newSettings["TargetTrackingScalingPolicyConfiguration"] ?? {};
|
|
18928
|
+
const targetValue = Number(tttCfg["TargetValue"]);
|
|
18929
|
+
if (!Number.isFinite(targetValue)) {
|
|
18930
|
+
warn(`Auto-scaling target registered on ${tableName} (${dimension}) but TargetValue is missing or non-numeric — skipping PutScalingPolicy. Provide TargetTrackingScalingPolicyConfiguration.TargetValue in the template.`);
|
|
18931
|
+
return;
|
|
18932
|
+
}
|
|
18933
|
+
const targetTrackingConfig = {
|
|
18934
|
+
PredefinedMetricSpecification: { PredefinedMetricType: metricType },
|
|
18935
|
+
TargetValue: targetValue
|
|
18936
|
+
};
|
|
18937
|
+
if (tttCfg["ScaleInCooldown"] !== void 0) targetTrackingConfig["ScaleInCooldown"] = Number(tttCfg["ScaleInCooldown"]);
|
|
18938
|
+
if (tttCfg["ScaleOutCooldown"] !== void 0) targetTrackingConfig["ScaleOutCooldown"] = Number(tttCfg["ScaleOutCooldown"]);
|
|
18939
|
+
if (tttCfg["DisableScaleIn"] !== void 0) targetTrackingConfig["DisableScaleIn"] = Boolean(tttCfg["DisableScaleIn"]);
|
|
18940
|
+
try {
|
|
18941
|
+
await withRetry(() => asClient.send(new PutScalingPolicyCommand({
|
|
18942
|
+
PolicyName: policyName,
|
|
18943
|
+
ServiceNamespace: "dynamodb",
|
|
18944
|
+
ResourceId: resourceId,
|
|
18945
|
+
ScalableDimension: dimension,
|
|
18946
|
+
PolicyType: "TargetTrackingScaling",
|
|
18947
|
+
TargetTrackingScalingPolicyConfiguration: targetTrackingConfig
|
|
18948
|
+
})), `${policyName}`, {
|
|
18949
|
+
isRetryable: (_message, error) => isThrottlingError(error),
|
|
18950
|
+
maxRetries: asMaxRetries,
|
|
18951
|
+
isInterrupted: watch.isInterrupted,
|
|
18952
|
+
onInterrupted: watch.onInterrupted,
|
|
18953
|
+
...autoScalingRetryDelays.sleep ? { sleep: autoScalingRetryDelays.sleep } : {}
|
|
18954
|
+
});
|
|
18955
|
+
this.logger.debug(`Upserted auto-scaling policy ${policyName} on ${tableName} (${dimension})`);
|
|
18956
|
+
} catch (err) {
|
|
18957
|
+
if (isInterruptedWaitError(err)) throw err;
|
|
18958
|
+
warn(`Could not put auto-scaling policy on ${tableName} (${dimension}): ${err instanceof Error ? err.message : String(err)}. Run: aws application-autoscaling put-scaling-policy --policy-name ${policyName} --service-namespace dynamodb --resource-id ${resourceId} --scalable-dimension ${dimension} --policy-type TargetTrackingScaling`);
|
|
18959
|
+
}
|
|
18666
18960
|
return;
|
|
18667
18961
|
}
|
|
18962
|
+
const isObjectNotFound = (err) => {
|
|
18963
|
+
if (!(err instanceof Error)) return false;
|
|
18964
|
+
const name = err.name ?? "";
|
|
18965
|
+
const msg = err.message ?? "";
|
|
18966
|
+
return name === "ObjectNotFoundException" || msg.includes("No scaling policy found") || msg.includes("No scalable target found");
|
|
18967
|
+
};
|
|
18668
18968
|
try {
|
|
18669
|
-
await withRetry(() => asClient.send(new
|
|
18969
|
+
await withRetry(() => asClient.send(new DeleteScalingPolicyCommand({
|
|
18970
|
+
PolicyName: policyName,
|
|
18670
18971
|
ServiceNamespace: "dynamodb",
|
|
18671
18972
|
ResourceId: resourceId,
|
|
18672
|
-
ScalableDimension: dimension
|
|
18673
|
-
MinCapacity: minCapacity,
|
|
18674
|
-
MaxCapacity: maxCapacity
|
|
18973
|
+
ScalableDimension: dimension
|
|
18675
18974
|
})), `${resourceId} (${dimension})`, {
|
|
18676
18975
|
isRetryable: (_message, error) => isThrottlingError(error),
|
|
18677
18976
|
maxRetries: asMaxRetries,
|
|
18977
|
+
isInterrupted: watch.isInterrupted,
|
|
18978
|
+
onInterrupted: watch.onInterrupted,
|
|
18678
18979
|
...autoScalingRetryDelays.sleep ? { sleep: autoScalingRetryDelays.sleep } : {}
|
|
18679
18980
|
});
|
|
18680
18981
|
} catch (err) {
|
|
18681
|
-
|
|
18682
|
-
|
|
18982
|
+
if (isInterruptedWaitError(err)) throw err;
|
|
18983
|
+
if (!isObjectNotFound(err)) warn(`Could not delete auto-scaling policy on ${tableName} (${dimension}): ${err instanceof Error ? err.message : String(err)}. Run: aws application-autoscaling delete-scaling-policy --policy-name ${policyName} --service-namespace dynamodb --resource-id ${resourceId} --scalable-dimension ${dimension}`);
|
|
18683
18984
|
}
|
|
18684
|
-
const tttCfg = newSettings["TargetTrackingScalingPolicyConfiguration"] ?? {};
|
|
18685
|
-
const targetValue = Number(tttCfg["TargetValue"]);
|
|
18686
|
-
if (!Number.isFinite(targetValue)) {
|
|
18687
|
-
warn(`Auto-scaling target registered on ${tableName} (${dimension}) but TargetValue is missing or non-numeric — skipping PutScalingPolicy. Provide TargetTrackingScalingPolicyConfiguration.TargetValue in the template.`);
|
|
18688
|
-
return;
|
|
18689
|
-
}
|
|
18690
|
-
const targetTrackingConfig = {
|
|
18691
|
-
PredefinedMetricSpecification: { PredefinedMetricType: metricType },
|
|
18692
|
-
TargetValue: targetValue
|
|
18693
|
-
};
|
|
18694
|
-
if (tttCfg["ScaleInCooldown"] !== void 0) targetTrackingConfig["ScaleInCooldown"] = Number(tttCfg["ScaleInCooldown"]);
|
|
18695
|
-
if (tttCfg["ScaleOutCooldown"] !== void 0) targetTrackingConfig["ScaleOutCooldown"] = Number(tttCfg["ScaleOutCooldown"]);
|
|
18696
|
-
if (tttCfg["DisableScaleIn"] !== void 0) targetTrackingConfig["DisableScaleIn"] = Boolean(tttCfg["DisableScaleIn"]);
|
|
18697
18985
|
try {
|
|
18698
|
-
await withRetry(() => asClient.send(new
|
|
18699
|
-
PolicyName: policyName,
|
|
18986
|
+
await withRetry(() => asClient.send(new DeregisterScalableTargetCommand({
|
|
18700
18987
|
ServiceNamespace: "dynamodb",
|
|
18701
18988
|
ResourceId: resourceId,
|
|
18702
|
-
ScalableDimension: dimension
|
|
18703
|
-
|
|
18704
|
-
TargetTrackingScalingPolicyConfiguration: targetTrackingConfig
|
|
18705
|
-
})), `${policyName}`, {
|
|
18989
|
+
ScalableDimension: dimension
|
|
18990
|
+
})), `${resourceId} (${dimension})`, {
|
|
18706
18991
|
isRetryable: (_message, error) => isThrottlingError(error),
|
|
18707
18992
|
maxRetries: asMaxRetries,
|
|
18993
|
+
isInterrupted: watch.isInterrupted,
|
|
18994
|
+
onInterrupted: watch.onInterrupted,
|
|
18708
18995
|
...autoScalingRetryDelays.sleep ? { sleep: autoScalingRetryDelays.sleep } : {}
|
|
18709
18996
|
});
|
|
18710
|
-
this.logger.debug(`
|
|
18997
|
+
this.logger.debug(`Deregistered auto-scaling target ${resourceId} (${dimension})`);
|
|
18711
18998
|
} catch (err) {
|
|
18712
|
-
|
|
18999
|
+
if (isInterruptedWaitError(err)) throw err;
|
|
19000
|
+
if (!isObjectNotFound(err)) warn(`Could not deregister auto-scaling target on ${tableName} (${dimension}): ${err instanceof Error ? err.message : String(err)}. Run: aws application-autoscaling deregister-scalable-target --service-namespace dynamodb --resource-id ${resourceId} --scalable-dimension ${dimension}`);
|
|
18713
19001
|
}
|
|
18714
|
-
|
|
18715
|
-
|
|
18716
|
-
const isObjectNotFound = (err) => {
|
|
18717
|
-
if (!(err instanceof Error)) return false;
|
|
18718
|
-
const name = err.name ?? "";
|
|
18719
|
-
const msg = err.message ?? "";
|
|
18720
|
-
return name === "ObjectNotFoundException" || msg.includes("No scaling policy found") || msg.includes("No scalable target found");
|
|
18721
|
-
};
|
|
18722
|
-
try {
|
|
18723
|
-
await withRetry(() => asClient.send(new DeleteScalingPolicyCommand({
|
|
18724
|
-
PolicyName: policyName,
|
|
18725
|
-
ServiceNamespace: "dynamodb",
|
|
18726
|
-
ResourceId: resourceId,
|
|
18727
|
-
ScalableDimension: dimension
|
|
18728
|
-
})), `${resourceId} (${dimension})`, {
|
|
18729
|
-
isRetryable: (_message, error) => isThrottlingError(error),
|
|
18730
|
-
maxRetries: asMaxRetries,
|
|
18731
|
-
...autoScalingRetryDelays.sleep ? { sleep: autoScalingRetryDelays.sleep } : {}
|
|
18732
|
-
});
|
|
18733
|
-
} catch (err) {
|
|
18734
|
-
if (!isObjectNotFound(err)) warn(`Could not delete auto-scaling policy on ${tableName} (${dimension}): ${err instanceof Error ? err.message : String(err)}. Run: aws application-autoscaling delete-scaling-policy --policy-name ${policyName} --service-namespace dynamodb --resource-id ${resourceId} --scalable-dimension ${dimension}`);
|
|
18735
|
-
}
|
|
18736
|
-
try {
|
|
18737
|
-
await withRetry(() => asClient.send(new DeregisterScalableTargetCommand({
|
|
18738
|
-
ServiceNamespace: "dynamodb",
|
|
18739
|
-
ResourceId: resourceId,
|
|
18740
|
-
ScalableDimension: dimension
|
|
18741
|
-
})), `${resourceId} (${dimension})`, {
|
|
18742
|
-
isRetryable: (_message, error) => isThrottlingError(error),
|
|
18743
|
-
maxRetries: asMaxRetries,
|
|
18744
|
-
...autoScalingRetryDelays.sleep ? { sleep: autoScalingRetryDelays.sleep } : {}
|
|
18745
|
-
});
|
|
18746
|
-
this.logger.debug(`Deregistered auto-scaling target ${resourceId} (${dimension})`);
|
|
18747
|
-
} catch (err) {
|
|
18748
|
-
if (!isObjectNotFound(err)) warn(`Could not deregister auto-scaling target on ${tableName} (${dimension}): ${err instanceof Error ? err.message : String(err)}. Run: aws application-autoscaling deregister-scalable-target --service-namespace dynamodb --resource-id ${resourceId} --scalable-dimension ${dimension}`);
|
|
19002
|
+
} finally {
|
|
19003
|
+
watch.dispose();
|
|
18749
19004
|
}
|
|
18750
19005
|
}
|
|
18751
19006
|
/**
|
|
@@ -18943,6 +19198,7 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
18943
19198
|
}
|
|
18944
19199
|
} catch (describeErr) {
|
|
18945
19200
|
if (describeErr instanceof ProvisioningError) throw describeErr;
|
|
19201
|
+
if (isInterruptedWaitError(describeErr)) throw describeErr;
|
|
18946
19202
|
if (!(describeErr instanceof ResourceNotFoundException$1)) {
|
|
18947
19203
|
const cause = describeErr instanceof Error ? describeErr : void 0;
|
|
18948
19204
|
throw new ProvisioningError(`Failed to describe DynamoDB GlobalTable ${logicalId} before delete: ${describeErr instanceof Error ? describeErr.message : String(describeErr)}`, resourceType, logicalId, physicalId, cause);
|
|
@@ -19654,12 +19910,20 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
19654
19910
|
*/
|
|
19655
19911
|
async waitForReplicaGone(tableName, region, logicalId, maxAttempts = REPLICA_GONE_WAIT_ATTEMPTS, budget) {
|
|
19656
19912
|
const wait = beginDeleteWait(maxAttempts, budget);
|
|
19657
|
-
|
|
19658
|
-
|
|
19659
|
-
|
|
19660
|
-
|
|
19661
|
-
|
|
19662
|
-
|
|
19913
|
+
const watch = startInterruptWatch(`DynamoDB replica ${region} teardown on ${tableName}`);
|
|
19914
|
+
try {
|
|
19915
|
+
while (wait.nextPoll()) {
|
|
19916
|
+
if (watch.isInterrupted()) throw watch.onInterrupted();
|
|
19917
|
+
try {
|
|
19918
|
+
if (!(await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }))).Table?.Replicas?.find((r) => r.RegionName === region)) return;
|
|
19919
|
+
await new Promise((resolve) => setTimeout(resolve, INDEX_SETTLE_POLL_INTERVAL_MS));
|
|
19920
|
+
} catch (err) {
|
|
19921
|
+
if (err instanceof ResourceNotFoundException$1) return;
|
|
19922
|
+
throw err;
|
|
19923
|
+
}
|
|
19924
|
+
}
|
|
19925
|
+
} finally {
|
|
19926
|
+
watch.dispose();
|
|
19663
19927
|
}
|
|
19664
19928
|
throw new ProvisioningError(`Replica ${region} for table ${tableName} did not disappear within ${wait.pollsRun}s${wait.note()}`, "AWS::DynamoDB::GlobalTable", logicalId, tableName);
|
|
19665
19929
|
}
|
|
@@ -19679,12 +19943,23 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
19679
19943
|
*/
|
|
19680
19944
|
async waitForTableGone(tableName, logicalId, maxAttempts = 600, budget) {
|
|
19681
19945
|
const wait = beginDeleteWait(maxAttempts, budget);
|
|
19682
|
-
|
|
19683
|
-
|
|
19684
|
-
|
|
19685
|
-
|
|
19686
|
-
|
|
19687
|
-
|
|
19946
|
+
const watch = startInterruptWatch(`DynamoDB table ${tableName} deletion wait`);
|
|
19947
|
+
try {
|
|
19948
|
+
while (wait.nextPoll()) {
|
|
19949
|
+
if (watch.isInterrupted()) {
|
|
19950
|
+
this.logger.warn(`DynamoDB GlobalTable ${logicalId}: interrupted while waiting for ${tableName} to disappear. AWS ACCEPTED the DeleteTable and will finish it; cdkd stopped watching.`);
|
|
19951
|
+
return;
|
|
19952
|
+
}
|
|
19953
|
+
try {
|
|
19954
|
+
await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }));
|
|
19955
|
+
await new Promise((resolve) => setTimeout(resolve, INDEX_SETTLE_POLL_INTERVAL_MS));
|
|
19956
|
+
} catch (err) {
|
|
19957
|
+
if (err instanceof ResourceNotFoundException$1) return;
|
|
19958
|
+
throw err;
|
|
19959
|
+
}
|
|
19960
|
+
}
|
|
19961
|
+
} finally {
|
|
19962
|
+
watch.dispose();
|
|
19688
19963
|
}
|
|
19689
19964
|
if (wait.cutShort) {
|
|
19690
19965
|
this.logger.warn(`DynamoDB GlobalTable ${logicalId}: DeleteTable on ${tableName} was ACCEPTED by AWS but cdkd stopped waiting for the table to disappear${wait.note()} The delete completes on AWS's side; a later describe (or a re-run of this destroy) sees it gone.`);
|
|
@@ -35399,13 +35674,23 @@ var ELBv2Provider = class {
|
|
|
35399
35674
|
try {
|
|
35400
35675
|
const listenerAttributes = this.normalizeAttributes(properties["ListenerAttributes"]);
|
|
35401
35676
|
if (listenerAttributes.length > 0) {
|
|
35402
|
-
|
|
35403
|
-
|
|
35404
|
-
|
|
35405
|
-
|
|
35677
|
+
const watch = startInterruptWatch(`ELBv2 Listener ${logicalId} attributes`);
|
|
35678
|
+
try {
|
|
35679
|
+
await withRetry(() => this.getClient().send(new ModifyListenerAttributesCommand({
|
|
35680
|
+
ListenerArn: listenerArn,
|
|
35681
|
+
Attributes: listenerAttributes
|
|
35682
|
+
})), logicalId, {
|
|
35683
|
+
logger: this.maskedRetryLogger(maskSecrets),
|
|
35684
|
+
isInterrupted: watch.isInterrupted,
|
|
35685
|
+
onInterrupted: watch.onInterrupted
|
|
35686
|
+
});
|
|
35687
|
+
} finally {
|
|
35688
|
+
watch.dispose();
|
|
35689
|
+
}
|
|
35406
35690
|
this.logger.debug(`Applied ${listenerAttributes.length} ListenerAttribute(s) for ${logicalId}`);
|
|
35407
35691
|
}
|
|
35408
35692
|
} catch (innerError) {
|
|
35693
|
+
if (isInterruptedWaitError(innerError)) this.logger.warn(maskerOrIdentity(maskSecrets)(`Interrupted after creating Listener ${logicalId} (${listenerArn}) but before its attributes were applied. Nothing in cdkd state refers to it, so cdkd is deleting it now — left behind it would fail the next deploy with DuplicateListener and neither rollback nor destroy could reach it. If that delete does not complete, remove it manually: aws elbv2 delete-listener --listener-arn ${listenerArn}`));
|
|
35409
35694
|
try {
|
|
35410
35695
|
await this.getClient().send(new DeleteListenerCommand({ ListenerArn: listenerArn }));
|
|
35411
35696
|
this.logger.debug(`Cleaned up partially-created Listener ${logicalId} (${listenerArn}) after attributes-wiring failure`);
|
|
@@ -35442,10 +35727,19 @@ var ELBv2Provider = class {
|
|
|
35442
35727
|
}));
|
|
35443
35728
|
const submittedAttrs = this.diffAttributes(this.normalizeAttributes(properties["ListenerAttributes"]), this.normalizeAttributes(previousProperties["ListenerAttributes"]), this.attributeRemovalResolver(LISTENER_ATTRIBUTE_DEFAULTS));
|
|
35444
35729
|
if (submittedAttrs.length > 0) {
|
|
35445
|
-
|
|
35446
|
-
|
|
35447
|
-
|
|
35448
|
-
|
|
35730
|
+
const watch = startInterruptWatch(`ELBv2 Listener ${logicalId} attributes`);
|
|
35731
|
+
try {
|
|
35732
|
+
await withRetry(() => this.getClient().send(new ModifyListenerAttributesCommand({
|
|
35733
|
+
ListenerArn: physicalId,
|
|
35734
|
+
Attributes: submittedAttrs
|
|
35735
|
+
})), logicalId, {
|
|
35736
|
+
logger: this.maskedRetryLogger(maskSecrets),
|
|
35737
|
+
isInterrupted: watch.isInterrupted,
|
|
35738
|
+
onInterrupted: watch.onInterrupted
|
|
35739
|
+
});
|
|
35740
|
+
} finally {
|
|
35741
|
+
watch.dispose();
|
|
35742
|
+
}
|
|
35449
35743
|
this.logger.debug(`Applied ${submittedAttrs.length} ListenerAttributes change(s) for ${logicalId}`);
|
|
35450
35744
|
}
|
|
35451
35745
|
await this.applyTagDiff(physicalId, previousProperties["Tags"], properties["Tags"]);
|
|
@@ -42539,16 +42833,23 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
|
|
|
42539
42833
|
this.logger.debug(`Deleting Cloud Map namespace ${logicalId}: ${physicalId}`);
|
|
42540
42834
|
const client = this.getClient();
|
|
42541
42835
|
try {
|
|
42542
|
-
|
|
42543
|
-
|
|
42544
|
-
|
|
42545
|
-
|
|
42546
|
-
|
|
42547
|
-
|
|
42548
|
-
|
|
42549
|
-
|
|
42550
|
-
|
|
42551
|
-
|
|
42836
|
+
const watch = startInterruptWatch(`Cloud Map namespace ${logicalId}`);
|
|
42837
|
+
try {
|
|
42838
|
+
await withRetry(async () => {
|
|
42839
|
+
const operationId = (await client.send(new DeleteNamespaceCommand({ Id: physicalId }))).OperationId;
|
|
42840
|
+
if (operationId) await this.pollOperation(operationId, logicalId, resourceType);
|
|
42841
|
+
}, logicalId, {
|
|
42842
|
+
maxRetries: 24,
|
|
42843
|
+
initialDelayMs: 3e3,
|
|
42844
|
+
maxDelayMs: 15e3,
|
|
42845
|
+
isRetryable: (message, error) => error instanceof ResourceInUse || /has associated services/i.test(message),
|
|
42846
|
+
logger: this.logger,
|
|
42847
|
+
isInterrupted: watch.isInterrupted,
|
|
42848
|
+
onInterrupted: watch.onInterrupted
|
|
42849
|
+
});
|
|
42850
|
+
} finally {
|
|
42851
|
+
watch.dispose();
|
|
42852
|
+
}
|
|
42552
42853
|
this.logger.debug(`Successfully deleted Cloud Map namespace ${logicalId}`);
|
|
42553
42854
|
} catch (error) {
|
|
42554
42855
|
if (error instanceof NamespaceNotFound) {
|
|
@@ -42748,13 +43049,23 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
|
|
|
42748
43049
|
try {
|
|
42749
43050
|
const attrs = this.normalizeServiceAttributes(properties["ServiceAttributes"]);
|
|
42750
43051
|
if (Object.keys(attrs).length > 0) {
|
|
42751
|
-
|
|
42752
|
-
|
|
42753
|
-
|
|
42754
|
-
|
|
43052
|
+
const watch = startInterruptWatch(`Cloud Map service ${logicalId} attributes`);
|
|
43053
|
+
try {
|
|
43054
|
+
await withRetry(() => client.send(new UpdateServiceAttributesCommand({
|
|
43055
|
+
ServiceId: serviceId,
|
|
43056
|
+
Attributes: attrs
|
|
43057
|
+
})), logicalId, {
|
|
43058
|
+
logger: this.maskedRetryLogger(maskSecrets),
|
|
43059
|
+
isInterrupted: watch.isInterrupted,
|
|
43060
|
+
onInterrupted: watch.onInterrupted
|
|
43061
|
+
});
|
|
43062
|
+
} finally {
|
|
43063
|
+
watch.dispose();
|
|
43064
|
+
}
|
|
42755
43065
|
this.logger.debug(`Applied ${Object.keys(attrs).length} ServiceAttribute(s) for ${logicalId}`);
|
|
42756
43066
|
}
|
|
42757
43067
|
} catch (innerError) {
|
|
43068
|
+
if (isInterruptedWaitError(innerError)) this.logger.warn(maskerOrIdentity(maskSecrets)(`Interrupted after creating ServiceDiscovery Service ${logicalId} (${serviceId}) but before its ServiceAttributes were applied. Nothing in cdkd state refers to it, so cdkd is deleting it now — left behind it would fail the next deploy on a name collision AND block deletion of its namespace with ResourceInUse. If that delete does not complete, remove it manually: aws servicediscovery delete-service --id ${serviceId}`));
|
|
42758
43069
|
try {
|
|
42759
43070
|
await client.send(new DeleteServiceCommand$1({ Id: serviceId }));
|
|
42760
43071
|
this.logger.debug(`Cleaned up partially-created ServiceDiscovery Service ${logicalId} (${serviceId}) after ServiceAttributes wiring failure`);
|
|
@@ -42824,19 +43135,34 @@ var ServiceDiscoveryProvider = class ServiceDiscoveryProvider {
|
|
|
42824
43135
|
}))).OperationId;
|
|
42825
43136
|
if (operationId) await this.pollOperation(operationId, logicalId, resourceType, maskSecrets);
|
|
42826
43137
|
}
|
|
42827
|
-
if (hasAttrUpsert) {
|
|
42828
|
-
|
|
42829
|
-
|
|
42830
|
-
|
|
42831
|
-
|
|
42832
|
-
|
|
42833
|
-
|
|
42834
|
-
|
|
42835
|
-
|
|
42836
|
-
|
|
42837
|
-
|
|
42838
|
-
|
|
42839
|
-
|
|
43138
|
+
if (hasAttrUpsert || hasAttrRemove) {
|
|
43139
|
+
const attrWatch = startInterruptWatch(`Cloud Map service ${logicalId} attributes`);
|
|
43140
|
+
try {
|
|
43141
|
+
if (hasAttrUpsert) {
|
|
43142
|
+
await withRetry(() => client.send(new UpdateServiceAttributesCommand({
|
|
43143
|
+
ServiceId: physicalId,
|
|
43144
|
+
Attributes: upsertAttrs
|
|
43145
|
+
})), logicalId, {
|
|
43146
|
+
logger: this.maskedRetryLogger(maskSecrets),
|
|
43147
|
+
isInterrupted: attrWatch.isInterrupted,
|
|
43148
|
+
onInterrupted: attrWatch.onInterrupted
|
|
43149
|
+
});
|
|
43150
|
+
this.logger.debug(`Applied ${Object.keys(upsertAttrs).length} ServiceAttribute change(s) for ${logicalId}`);
|
|
43151
|
+
}
|
|
43152
|
+
if (hasAttrRemove) {
|
|
43153
|
+
await withRetry(() => client.send(new DeleteServiceAttributesCommand({
|
|
43154
|
+
ServiceId: physicalId,
|
|
43155
|
+
Attributes: removedAttrKeys
|
|
43156
|
+
})), logicalId, {
|
|
43157
|
+
logger: this.maskedRetryLogger(maskSecrets),
|
|
43158
|
+
isInterrupted: attrWatch.isInterrupted,
|
|
43159
|
+
onInterrupted: attrWatch.onInterrupted
|
|
43160
|
+
});
|
|
43161
|
+
this.logger.debug(`Removed ${removedAttrKeys.length} ServiceAttribute(s) for ${logicalId}`);
|
|
43162
|
+
}
|
|
43163
|
+
} finally {
|
|
43164
|
+
attrWatch.dispose();
|
|
43165
|
+
}
|
|
42840
43166
|
}
|
|
42841
43167
|
this.logger.debug(`Successfully updated service discovery service ${logicalId}`);
|
|
42842
43168
|
return {
|
|
@@ -58198,7 +58524,7 @@ async function runDestroyForStack(stackName, state, ctx) {
|
|
|
58198
58524
|
} catch (error) {
|
|
58199
58525
|
renderer.removeTask(logicalId);
|
|
58200
58526
|
const msg = error instanceof Error ? error.message : String(error);
|
|
58201
|
-
if (!isFinalSnapshotError(error) && (msg.includes("does not exist") || msg.includes("not found") || msg.includes("No policy found") || msg.includes("NoSuchEntity") || msg.includes("NotFoundException"))) {
|
|
58527
|
+
if (!isInterruptedWaitError(error) && !isFinalSnapshotError(error) && (msg.includes("does not exist") || msg.includes("not found") || msg.includes("No policy found") || msg.includes("NoSuchEntity") || msg.includes("NotFoundException"))) {
|
|
58202
58528
|
logger.debug(` ${logicalId} already deleted, removing from state`);
|
|
58203
58529
|
result.deletedCount++;
|
|
58204
58530
|
ctx.eventRecorder?.record({
|
|
@@ -58282,11 +58608,19 @@ async function runDestroyForStack(stackName, state, ctx) {
|
|
|
58282
58608
|
logger.warn(`\n${yellow("⚠")} ${bold(`Stack ${stackName} partially destroyed`)} (${green(result.deletedCount)} deleted${retainedSuffix}${skippedSuffix}, ${red(result.errorCount)} errors). State preserved — re-run 'cdkd destroy' / 'cdkd state destroy' to clean up. If the same resource keeps failing, ${orphanHint} is the last resort: it removes the state record without deleting AWS resources.` + skippedClause);
|
|
58283
58609
|
}
|
|
58284
58610
|
} finally {
|
|
58285
|
-
|
|
58286
|
-
|
|
58287
|
-
|
|
58288
|
-
|
|
58289
|
-
|
|
58611
|
+
try {
|
|
58612
|
+
try {
|
|
58613
|
+
renderer.stop();
|
|
58614
|
+
} catch (rendererError) {
|
|
58615
|
+
logger.debug(`Live renderer teardown failed (continuing to release the lock): ${rendererError instanceof Error ? rendererError.message : String(rendererError)}`);
|
|
58616
|
+
}
|
|
58617
|
+
await saveChain;
|
|
58618
|
+
logger.debug("Releasing lock...");
|
|
58619
|
+
await ctx.lockManager.releaseLock(stackName, regionForState);
|
|
58620
|
+
} finally {
|
|
58621
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
58622
|
+
}
|
|
58623
|
+
result.interrupted ||= draining;
|
|
58290
58624
|
if (destroyAwsClients) {
|
|
58291
58625
|
destroyAwsClients.destroy();
|
|
58292
58626
|
process.env["AWS_REGION"] = ctx.baseRegion;
|
|
@@ -72041,26 +72375,37 @@ async function gcCommand(options) {
|
|
|
72041
72375
|
roleArn: options.roleArn,
|
|
72042
72376
|
region: namedCliRegion(options.region)
|
|
72043
72377
|
});
|
|
72044
|
-
const
|
|
72045
|
-
const
|
|
72046
|
-
|
|
72047
|
-
region,
|
|
72378
|
+
const effective = await resolveEffectiveRegion(options);
|
|
72379
|
+
const accountClients = new AwsClients({
|
|
72380
|
+
region: effective.region,
|
|
72048
72381
|
...options.profile && { profile: options.profile }
|
|
72049
72382
|
});
|
|
72050
|
-
|
|
72051
|
-
const accountId = (await awsClients.sts.send(new GetCallerIdentityCommand({}))).Account;
|
|
72383
|
+
const accountId = (await accountClients.sts.send(new GetCallerIdentityCommand({}))).Account;
|
|
72052
72384
|
const bucketName = options.stateBucket ?? getDefaultStateBucketName(accountId);
|
|
72053
72385
|
const markerS3Client = new S3Client({
|
|
72054
|
-
region,
|
|
72386
|
+
region: effective.region,
|
|
72055
72387
|
...options.profile && { profile: options.profile }
|
|
72056
72388
|
});
|
|
72057
72389
|
const stateBackend = new S3StateBackend(markerS3Client, {
|
|
72058
72390
|
bucket: bucketName,
|
|
72059
72391
|
prefix: "cdkd"
|
|
72060
72392
|
}, {
|
|
72393
|
+
region: effective.region,
|
|
72394
|
+
...options.profile && { profile: options.profile }
|
|
72395
|
+
});
|
|
72396
|
+
const region = regionNeedsReconciliation(effective) ? await reconcileMarkerRegionWithLegacyDefault({
|
|
72397
|
+
effective,
|
|
72398
|
+
probe: stateBackend,
|
|
72399
|
+
markerKeyFor: getBootstrapMarkerKey,
|
|
72400
|
+
logger
|
|
72401
|
+
}) : effective.region;
|
|
72402
|
+
const rawRegion = rawCliRegion(options.region) ?? region;
|
|
72403
|
+
const awsClients = region === effective.region ? accountClients : new AwsClients({
|
|
72061
72404
|
region,
|
|
72062
72405
|
...options.profile && { profile: options.profile }
|
|
72063
72406
|
});
|
|
72407
|
+
if (awsClients !== accountClients) accountClients.destroy();
|
|
72408
|
+
setAwsClients(awsClients);
|
|
72064
72409
|
const ecrClient = new ECRClient({
|
|
72065
72410
|
region,
|
|
72066
72411
|
...options.profile && { profile: options.profile }
|
|
@@ -72139,7 +72484,7 @@ async function gcCommand(options) {
|
|
|
72139
72484
|
* Create the `cdkd gc` command (upstream `cdk gc` parity naming).
|
|
72140
72485
|
*/
|
|
72141
72486
|
function createGcCommand() {
|
|
72142
|
-
const cmd = new Command("gc").description("Garbage-collect unreferenced objects / images from ONE region's cdkd-owned asset storage (asset bucket + container-asset ECR repo). References are collected from every cdkd state file; CDK bootstrap storage is never touched (use cdk gc for that).").option("--state-bucket <bucket>", "S3 bucket holding cdkd state (default: cdkd-state-{accountId})").addOption(new Option("--older-than <duration>", "Never delete an object / image newer than this age, even when unreferenced (protects in-flight publishes and recent rollback targets). Accepts <n>d / <n>h.").default(DEFAULT_OLDER_THAN_MS, "30d").argParser(parseOlderThan)).option("--dry-run", "Print the reclaim plan (per-item list + totals) without deleting", false).addOption(new Option("--region <region>", "Region whose cdkd asset storage to garbage-collect (defaults to AWS_REGION
|
|
72487
|
+
const cmd = new Command("gc").description("Garbage-collect unreferenced objects / images from ONE region's cdkd-owned asset storage (asset bucket + container-asset ECR repo). References are collected from every cdkd state file; CDK bootstrap storage is never touched (use cdk gc for that).").option("--state-bucket <bucket>", "S3 bucket holding cdkd state (default: cdkd-state-{accountId})").addOption(new Option("--older-than <duration>", "Never delete an object / image newer than this age, even when unreferenced (protects in-flight publishes and recent rollback targets). Accepts <n>d / <n>h.").default(DEFAULT_OLDER_THAN_MS, "30d").argParser(parseOlderThan)).option("--dry-run", "Print the reclaim plan (per-item list + totals) without deleting", false).addOption(new Option("--region <region>", "Region whose cdkd asset storage to garbage-collect (defaults to AWS_REGION, then AWS_DEFAULT_REGION, then your AWS profile's region, then us-east-1)")).action(withErrorHandling(async (options) => {
|
|
72143
72488
|
await gcCommand(options);
|
|
72144
72489
|
}));
|
|
72145
72490
|
commonOptions.forEach((opt) => cmd.addOption(opt));
|
|
@@ -83914,7 +84259,7 @@ function createMigrateCommand() {
|
|
|
83914
84259
|
*/
|
|
83915
84260
|
function buildProgram() {
|
|
83916
84261
|
const program = new Command();
|
|
83917
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.
|
|
84262
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.284.26");
|
|
83918
84263
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
83919
84264
|
const { profile } = actionCommand.optsWithGlobals();
|
|
83920
84265
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|