@go-to-k/cdkd 0.280.20 → 0.280.22
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-BWT5cpBO.js → asg-provider-DVoWhe5E.js} +2 -2
- package/dist/{asg-provider-BWT5cpBO.js.map → asg-provider-DVoWhe5E.js.map} +1 -1
- package/dist/cli.js +160 -22
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-CC3BV-Gp.js → deploy-engine-DObU-2va.js} +3 -3
- package/dist/{deploy-engine-CC3BV-Gp.js.map → deploy-engine-DObU-2va.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 requireConfigString, $t as processStackMessages, A as red, At as runDockerStreaming, B as isTerminationProtectionPropagationError, Bt as resolveSkipPrefix, C as isStatefulRecreateTargetSync, Ct as parseBootstrapMarker, D as cyan, Dn as normalizeAwsError, Dt as formatDockerLoginError, E as bold, Et as buildDockerImage, F as ProviderRegistry, Ft as getDefaultStateBucketName, G as normalizeAwsTagsToCfn, Gt as warnDeprecatedNoPrefixCliFlag, H as cfnRefValueFromPhysicalId, Ht as resolveStateBucketWithDefaultAndSource, I as findActionableSilentDrops, It as getLegacyStateBucketName, J as configStringRefusal, Jt as MIGRATE_TMP_PREFIX, K as resolveExplicitPhysicalId, Kt as CFN_TEMPLATE_BODY_LIMIT, L as CloudControlProvider, Lt as resolveApp, M as IAMRoleProvider, Mt as getDockerImageBySourceHash, N as collectInlinePolicyNamesManagedBySiblings, Nt as Synthesizer, O as gray, On as withErrorHandling, Ot as getDockerCmd, P as clearOnUpdateRemoval, Pt as synthesisStatusMessage, Q as requireConfigObject, Qt as AssemblyReader, R as slowCcOperationTimeoutMs, Rt as resolveAutoAssetStorage, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, Sn as StackTerminationProtectionError, St as getBootstrapMarkerKey, T as formatResourceLine, Tt as validateContainerRepoName, U as refStateLookupFromResource, Ut as resolveUseCdkBootstrapAssets, V as IntrinsicFunctionResolver, Vt as resolveStateBucketWithDefault, W as WAFv2WebACLProvider, Wt as stateBucketExistenceConfirmed, X as replayWarn, Xt as uploadCfnTemplate, Y as readConfigString, Yt as findLargeInlineResources, Z as requireConfigArray, Zt as expectedOwnerParam, _ as createPreDeleteFinalSnapshot, _n as PartialFailureError, _t as loadPublishableAssetManifest, a as DeploymentEventsStore, an as setAwsClients, at as isThrottlingError, b as unsupportedFinalSnapshotError, bn as ResourceUpdateNotSupportedError, bt as BOOTSTRAP_MARKER_PREFIX, c as replayFailedOperations, ct as LockManager, d as IMPLICIT_DELETE_DEPENDENCIES, dn as LocalInvokeBuildError$1, dt as shouldRetainResource, et as applyRoleArnIfSet, f as computeImplicitDeleteEdges, fn as LocalMigrateError, ft as AssetPublisher, g as ccRoutedFinalSnapshotError, gn as NestedStackChildDirectDestroyError, gt as createAssetRedirectResolver, h as buildFinalSnapshotIdentifier, hn as MissingCdkCliError, ht as buildAssetRedirectMap, i as DeploymentEventsReader, in as resetAwsClients, it as isRetryableTransientError, j as yellow, jt as AssetManifestLoader, k as green, kt as runDockerForeground, l as replayRollback, lt as S3StateBackend, m as PRE_DELETE_SNAPSHOT_TYPES, mt as WorkGraph, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as AwsClients, nt as describeTypeWithThrottleRetry, o as planFailedOps, ot as DagBuilder, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalStartServiceError, pt as stringifyValue, q as assertRegionMatch, qt as CFN_TEMPLATE_URL_LIMIT, r as DeployEngine, rn as getAwsClients, rt as withRetry, s as planRollback, sn as CdkdError, st as TemplateParser, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveBucketRegion, tt as DiffCalculator, u as withResourceDeadline, un as DeployCancelledError, ut as rebuildClientForBucketRegion, v as isFinalSnapshotError, vn as ProvisioningError, vt as rewriteTemplateAssetReferences, w as renderStatefulReason, wt as validateAssetBucketName, x as extractDeploymentEventError, xn as StackHasActiveImportsError, xt as ensureAssetStorage, y as refusesFinalSnapshot, yn as ResourceTimeoutError, yt as AssetModeResolver, z as disableInstanceApiTermination, zt as resolveCaptureObservedState } from "./deploy-engine-
|
|
2
|
+
import { $ as requireConfigString, $t as processStackMessages, A as red, At as runDockerStreaming, B as isTerminationProtectionPropagationError, Bt as resolveSkipPrefix, C as isStatefulRecreateTargetSync, Ct as parseBootstrapMarker, D as cyan, Dn as normalizeAwsError, Dt as formatDockerLoginError, E as bold, Et as buildDockerImage, F as ProviderRegistry, Ft as getDefaultStateBucketName, G as normalizeAwsTagsToCfn, Gt as warnDeprecatedNoPrefixCliFlag, H as cfnRefValueFromPhysicalId, Ht as resolveStateBucketWithDefaultAndSource, I as findActionableSilentDrops, It as getLegacyStateBucketName, J as configStringRefusal, Jt as MIGRATE_TMP_PREFIX, K as resolveExplicitPhysicalId, Kt as CFN_TEMPLATE_BODY_LIMIT, L as CloudControlProvider, Lt as resolveApp, M as IAMRoleProvider, Mt as getDockerImageBySourceHash, N as collectInlinePolicyNamesManagedBySiblings, Nt as Synthesizer, O as gray, On as withErrorHandling, Ot as getDockerCmd, P as clearOnUpdateRemoval, Pt as synthesisStatusMessage, Q as requireConfigObject, Qt as AssemblyReader, R as slowCcOperationTimeoutMs, Rt as resolveAutoAssetStorage, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, Sn as StackTerminationProtectionError, St as getBootstrapMarkerKey, T as formatResourceLine, Tt as validateContainerRepoName, U as refStateLookupFromResource, Ut as resolveUseCdkBootstrapAssets, V as IntrinsicFunctionResolver, Vt as resolveStateBucketWithDefault, W as WAFv2WebACLProvider, Wt as stateBucketExistenceConfirmed, X as replayWarn, Xt as uploadCfnTemplate, Y as readConfigString, Yt as findLargeInlineResources, Z as requireConfigArray, Zt as expectedOwnerParam, _ as createPreDeleteFinalSnapshot, _n as PartialFailureError, _t as loadPublishableAssetManifest, a as DeploymentEventsStore, an as setAwsClients, at as isThrottlingError, b as unsupportedFinalSnapshotError, bn as ResourceUpdateNotSupportedError, bt as BOOTSTRAP_MARKER_PREFIX, c as replayFailedOperations, ct as LockManager, d as IMPLICIT_DELETE_DEPENDENCIES, dn as LocalInvokeBuildError$1, dt as shouldRetainResource, et as applyRoleArnIfSet, f as computeImplicitDeleteEdges, fn as LocalMigrateError, ft as AssetPublisher, g as ccRoutedFinalSnapshotError, gn as NestedStackChildDirectDestroyError, gt as createAssetRedirectResolver, h as buildFinalSnapshotIdentifier, hn as MissingCdkCliError, ht as buildAssetRedirectMap, i as DeploymentEventsReader, in as resetAwsClients, it as isRetryableTransientError, j as yellow, jt as AssetManifestLoader, k as green, kt as runDockerForeground, l as replayRollback, lt as S3StateBackend, m as PRE_DELETE_SNAPSHOT_TYPES, mt as WorkGraph, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as AwsClients, nt as describeTypeWithThrottleRetry, o as planFailedOps, ot as DagBuilder, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalStartServiceError, pt as stringifyValue, q as assertRegionMatch, qt as CFN_TEMPLATE_URL_LIMIT, r as DeployEngine, rn as getAwsClients, rt as withRetry, s as planRollback, sn as CdkdError, st as TemplateParser, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveBucketRegion, tt as DiffCalculator, u as withResourceDeadline, un as DeployCancelledError, ut as rebuildClientForBucketRegion, v as isFinalSnapshotError, vn as ProvisioningError, vt as rewriteTemplateAssetReferences, w as renderStatefulReason, wt as validateAssetBucketName, x as extractDeploymentEventError, xn as StackHasActiveImportsError, xt as ensureAssetStorage, y as refusesFinalSnapshot, yn as ResourceTimeoutError, yt as AssetModeResolver, z as disableInstanceApiTermination, zt as resolveCaptureObservedState } from "./deploy-engine-DObU-2va.js";
|
|
3
3
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as withSkipPrefix, i as runStackBuffered, n as getLogger, o as PATTERN_B_NAME_OPTIONS, p as withStackName, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-BYMEE-BS.js";
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-DVoWhe5E.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";
|
|
@@ -11857,6 +11857,51 @@ function readCapacityNumber(block, member) {
|
|
|
11857
11857
|
return Number.isFinite(value) ? value : void 0;
|
|
11858
11858
|
}
|
|
11859
11859
|
/**
|
|
11860
|
+
* Whether a desired `GlobalSecondaryIndexes[]` entry declares BOTH capacity
|
|
11861
|
+
* members usably — the same test the BillingMode flip's per-index block applies
|
|
11862
|
+
* before it forwards the index, expressed as a predicate for the pre-flip
|
|
11863
|
+
* removal's look-ahead (issue #1617).
|
|
11864
|
+
*
|
|
11865
|
+
* BOTH members are required, exactly as in the flip block: an entry declaring
|
|
11866
|
+
* only one is treated like an absent one there, so accepting it here would let
|
|
11867
|
+
* the pre-flip delete run ahead of a flip AWS still rejects.
|
|
11868
|
+
*/
|
|
11869
|
+
function hasUsableDeclaredCapacity(entry) {
|
|
11870
|
+
const declared = entry?.["ProvisionedThroughput"];
|
|
11871
|
+
return readCapacityNumber(declared, "ReadCapacityUnits") !== void 0 && readCapacityNumber(declared, "WriteCapacityUnits") !== void 0;
|
|
11872
|
+
}
|
|
11873
|
+
/**
|
|
11874
|
+
* Whether the desired TABLE-level `ProvisionedThroughput` is one the flip can
|
|
11875
|
+
* actually send (issue #1617 PR review).
|
|
11876
|
+
*
|
|
11877
|
+
* Deliberately NOT {@link hasUsableDeclaredCapacity}: the flip defaults each
|
|
11878
|
+
* member to 5 when absent (`Number(pt['ReadCapacityUnits'] ?? 5)`), so a
|
|
11879
|
+
* half-declared TABLE capacity is sendable while a half-declared per-INDEX one
|
|
11880
|
+
* is not.
|
|
11881
|
+
*
|
|
11882
|
+
* It mirrors the flip's GATE as well as its arithmetic, which the first version
|
|
11883
|
+
* did not and a second review round caught — both halves of that miss are real:
|
|
11884
|
+
*
|
|
11885
|
+
* - the gate is plain TRUTHINESS (`properties['ProvisionedThroughput']`), so a
|
|
11886
|
+
* non-object truthy value sends `{5, 5}` and SUCCEEDS. Requiring a plain
|
|
11887
|
+
* object refused it and skipped a removal that would have been fine.
|
|
11888
|
+
* - a DECLARED member that coerces to `0` (`''`, `false`, `[]`) is finite, so
|
|
11889
|
+
* an arithmetic-only check accepted it — while AWS rejects a capacity below
|
|
11890
|
+
* 1, which is precisely the deterministic rejection this predicate exists to
|
|
11891
|
+
* keep a delete from running ahead of.
|
|
11892
|
+
*/
|
|
11893
|
+
function hasUsableTableCapacity(value) {
|
|
11894
|
+
if (!value) return false;
|
|
11895
|
+
if (typeof value !== "object" || Array.isArray(value)) return true;
|
|
11896
|
+
const pt = value;
|
|
11897
|
+
for (const member of ["ReadCapacityUnits", "WriteCapacityUnits"]) {
|
|
11898
|
+
if (pt[member] === void 0) continue;
|
|
11899
|
+
const n = Number(pt[member]);
|
|
11900
|
+
if (!Number.isFinite(n) || n < 1) return false;
|
|
11901
|
+
}
|
|
11902
|
+
return true;
|
|
11903
|
+
}
|
|
11904
|
+
/**
|
|
11860
11905
|
* `DescribeTable` poll budget (seconds, 1 poll/s) after an ordinary
|
|
11861
11906
|
* `UpdateTable` — capacity, TTL, tags, table class. Calibrated on those and
|
|
11862
11907
|
* sufficient for them.
|
|
@@ -11997,6 +12042,41 @@ var DynamoDBTableProvider = class {
|
|
|
11997
12042
|
const billingMode = billingModeUnusable ? prevBillingMode : requestedBillingMode;
|
|
11998
12043
|
const billingOrThroughputChanged = billingMode !== prevBillingMode || JSON.stringify(properties["ProvisionedThroughput"]) !== JSON.stringify(previousProperties["ProvisionedThroughput"]);
|
|
11999
12044
|
if (billingOrThroughputChanged && billingMode === "PROVISIONED" && (prevBillingMode === "PAY_PER_REQUEST" || liveBillingMode === "PAY_PER_REQUEST") && properties["OnDemandThroughput"] !== void 0) throw new ProvisioningError(`AWS::DynamoDB::Table ${logicalId}: the template flips BillingMode to PROVISIONED${properties["BillingMode"] === void 0 ? " (BillingMode is absent from the template, which CloudFormation resets to PROVISIONED)" : ""} while still declaring OnDemandThroughput, which AWS accepts only on a PAY_PER_REQUEST table. Nothing was applied. Remove OnDemandThroughput, or keep BillingMode: PAY_PER_REQUEST.`, logicalId, resourceType);
|
|
12045
|
+
const liveIndexes = table?.GlobalSecondaryIndexes ?? [];
|
|
12046
|
+
const desiredIndexByName = /* @__PURE__ */ new Map();
|
|
12047
|
+
for (const entry of Array.isArray(properties["GlobalSecondaryIndexes"]) ? properties["GlobalSecondaryIndexes"] : []) {
|
|
12048
|
+
const name = entry?.["IndexName"];
|
|
12049
|
+
if (typeof name === "string") desiredIndexByName.set(name, entry);
|
|
12050
|
+
}
|
|
12051
|
+
const preFlipDeletedIndexNames = /* @__PURE__ */ new Set();
|
|
12052
|
+
const desiredIndexesUnusable = properties["GlobalSecondaryIndexes"] != null && !Array.isArray(properties["GlobalSecondaryIndexes"]);
|
|
12053
|
+
if (billingOrThroughputChanged && billingMode === "PROVISIONED" && liveBillingMode === "PAY_PER_REQUEST" && liveIndexes.length > 0 && !desiredIndexesUnusable) {
|
|
12054
|
+
const previousIndexNames = new Set((Array.isArray(previousProperties["GlobalSecondaryIndexes"]) ? previousProperties["GlobalSecondaryIndexes"] : []).map((entry) => entry?.["IndexName"]).filter((name) => typeof name === "string"));
|
|
12055
|
+
const removable = [];
|
|
12056
|
+
const remainingWithoutCapacity = [];
|
|
12057
|
+
for (const live of liveIndexes) {
|
|
12058
|
+
const indexName = live.IndexName;
|
|
12059
|
+
if (typeof indexName !== "string") continue;
|
|
12060
|
+
if (!desiredIndexByName.has(indexName)) {
|
|
12061
|
+
if (previousIndexNames.has(indexName)) removable.push(indexName);
|
|
12062
|
+
else remainingWithoutCapacity.push(indexName);
|
|
12063
|
+
continue;
|
|
12064
|
+
}
|
|
12065
|
+
if (!hasUsableDeclaredCapacity(desiredIndexByName.get(indexName))) remainingWithoutCapacity.push(indexName);
|
|
12066
|
+
}
|
|
12067
|
+
const tableCapacityUnusable = !hasUsableTableCapacity(properties["ProvisionedThroughput"]);
|
|
12068
|
+
if (removable.length > 0 && (remainingWithoutCapacity.length > 0 || tableCapacityUnusable)) {
|
|
12069
|
+
const reasons = [];
|
|
12070
|
+
if (remainingWithoutCapacity.length > 0) reasons.push(`index(es) ${remainingWithoutCapacity.join(", ")} stay live and declare no usable per-index ProvisionedThroughput (both ReadCapacityUnits and WriteCapacityUnits are required there)`);
|
|
12071
|
+
if (tableCapacityUnusable) reasons.push(`the template declares no usable table-level ProvisionedThroughput`);
|
|
12072
|
+
this.logger.warn(`AWS::DynamoDB::Table ${logicalId}: this deploy removes global secondary index(es) ${removable.join(", ")} and flips BillingMode to PROVISIONED. The removal would normally be applied BEFORE the flip, but ${reasons.join(" and ")}, so AWS rejects the flip either way. Nothing was removed.`);
|
|
12073
|
+
} else if (removable.length > 0) {
|
|
12074
|
+
this.logger.debug(`Deleting GSI(s) ${removable.join(", ")} on DynamoDB table ${physicalId} before the BillingMode flip to PROVISIONED`);
|
|
12075
|
+
await this.runGsiOps(physicalId, removable.map((name) => ({ Delete: { IndexName: name } })), void 0);
|
|
12076
|
+
for (const name of removable) preFlipDeletedIndexNames.add(name);
|
|
12077
|
+
}
|
|
12078
|
+
}
|
|
12079
|
+
const currentLiveIndexNames = table ? new Set(liveIndexes.map((live) => live.IndexName).filter((name) => typeof name === "string").filter((name) => !preFlipDeletedIndexNames.has(name))) : void 0;
|
|
12000
12080
|
const gsiHandledByBillingFlip = /* @__PURE__ */ new Set();
|
|
12001
12081
|
if (billingOrThroughputChanged || tableClassChanged) {
|
|
12002
12082
|
const updateInput = { TableName: physicalId };
|
|
@@ -12009,20 +12089,14 @@ var DynamoDBTableProvider = class {
|
|
|
12009
12089
|
WriteCapacityUnits: Number(pt["WriteCapacityUnits"] ?? 5)
|
|
12010
12090
|
};
|
|
12011
12091
|
}
|
|
12012
|
-
const
|
|
12013
|
-
if (billingOrThroughputChanged && billingMode === "PROVISIONED" && liveBillingMode === "PAY_PER_REQUEST" &&
|
|
12014
|
-
const desiredIndexes = Array.isArray(properties["GlobalSecondaryIndexes"]) ? properties["GlobalSecondaryIndexes"] : [];
|
|
12015
|
-
const desiredByName = /* @__PURE__ */ new Map();
|
|
12016
|
-
for (const entry of desiredIndexes) {
|
|
12017
|
-
const name = entry?.["IndexName"];
|
|
12018
|
-
if (typeof name === "string") desiredByName.set(name, entry);
|
|
12019
|
-
}
|
|
12092
|
+
const flipLiveIndexes = liveIndexes.filter((live) => typeof live.IndexName !== "string" || !preFlipDeletedIndexNames.has(live.IndexName));
|
|
12093
|
+
if (billingOrThroughputChanged && billingMode === "PROVISIONED" && liveBillingMode === "PAY_PER_REQUEST" && flipLiveIndexes.length > 0) {
|
|
12020
12094
|
const indexUpdates = [];
|
|
12021
12095
|
const unspecified = [];
|
|
12022
|
-
for (const live of
|
|
12096
|
+
for (const live of flipLiveIndexes) {
|
|
12023
12097
|
const indexName = live.IndexName;
|
|
12024
12098
|
if (typeof indexName !== "string") continue;
|
|
12025
|
-
const declared =
|
|
12099
|
+
const declared = desiredIndexByName.get(indexName)?.["ProvisionedThroughput"];
|
|
12026
12100
|
const read = readCapacityNumber(declared, "ReadCapacityUnits");
|
|
12027
12101
|
const write = readCapacityNumber(declared, "WriteCapacityUnits");
|
|
12028
12102
|
if (read === void 0 || write === void 0) {
|
|
@@ -12039,7 +12113,7 @@ var DynamoDBTableProvider = class {
|
|
|
12039
12113
|
gsiHandledByBillingFlip.add(indexName);
|
|
12040
12114
|
}
|
|
12041
12115
|
if (indexUpdates.length > 0) updateInput.GlobalSecondaryIndexUpdates = indexUpdates;
|
|
12042
|
-
if (unspecified.length > 0) this.logger.warn(`AWS::DynamoDB::Table ${logicalId}: flipping BillingMode to PROVISIONED, but the template declares no usable ProvisionedThroughput for live index(es) ${unspecified.join(", ")}. AWS requires per-index capacity in the same UpdateTable and will reject the flip naming them. Declare GlobalSecondaryIndexes[].ProvisionedThroughput (both ReadCapacityUnits and WriteCapacityUnits) for each.
|
|
12116
|
+
if (unspecified.length > 0) this.logger.warn(`AWS::DynamoDB::Table ${logicalId}: flipping BillingMode to PROVISIONED, but the template declares no usable ProvisionedThroughput for live index(es) ${unspecified.join(", ")}. AWS requires per-index capacity in the same UpdateTable and will reject the flip naming them. Declare GlobalSecondaryIndexes[].ProvisionedThroughput (both ReadCapacityUnits and WriteCapacityUnits) for each. An index this deploy REMOVES is deleted BEFORE the flip automatically (issue #1617), so a name here is one the template still keeps, one that exists in AWS but in neither the template nor cdkd state (created out of band), or one whose pre-flip removal was skipped for the reason warned above.`);
|
|
12043
12117
|
}
|
|
12044
12118
|
await this.dynamoDBClient.send(new UpdateTableCommand(updateInput));
|
|
12045
12119
|
if (billingMode !== liveBillingMode) await this.waitForTableAndIndexesActive(physicalId, BILLING_FLIP_ACTIVE_WAIT_ATTEMPTS);
|
|
@@ -12117,7 +12191,7 @@ var DynamoDBTableProvider = class {
|
|
|
12117
12191
|
this.logger.debug(`Disabled DynamoDB Stream on table ${physicalId}`);
|
|
12118
12192
|
}
|
|
12119
12193
|
}
|
|
12120
|
-
if (JSON.stringify(properties["GlobalSecondaryIndexes"]) !== JSON.stringify(previousProperties["GlobalSecondaryIndexes"])) await this.applyGsiUpdates(physicalId, resourceType, logicalId, previousProperties["GlobalSecondaryIndexes"], properties["GlobalSecondaryIndexes"], properties["AttributeDefinitions"], gsiHandledByBillingFlip);
|
|
12194
|
+
if (JSON.stringify(properties["GlobalSecondaryIndexes"]) !== JSON.stringify(previousProperties["GlobalSecondaryIndexes"])) await this.applyGsiUpdates(physicalId, resourceType, logicalId, previousProperties["GlobalSecondaryIndexes"], properties["GlobalSecondaryIndexes"], properties["AttributeDefinitions"], gsiHandledByBillingFlip, currentLiveIndexNames);
|
|
12121
12195
|
if (JSON.stringify(properties["PointInTimeRecoverySpecification"]) !== JSON.stringify(previousProperties["PointInTimeRecoverySpecification"])) await this.applyPointInTimeRecovery(physicalId, properties["PointInTimeRecoverySpecification"], previousProperties["PointInTimeRecoverySpecification"]);
|
|
12122
12196
|
if (JSON.stringify(properties["TimeToLiveSpecification"]) !== JSON.stringify(previousProperties["TimeToLiveSpecification"])) {
|
|
12123
12197
|
this.assertNoActiveTtlAttributeNameChange(logicalId, resourceType, physicalId, properties["TimeToLiveSpecification"], previousProperties["TimeToLiveSpecification"]);
|
|
@@ -12555,13 +12629,20 @@ var DynamoDBTableProvider = class {
|
|
|
12555
12629
|
* a remove-then-add pair, so this method throws on such a change rather than
|
|
12556
12630
|
* silently dropping it.
|
|
12557
12631
|
*/
|
|
12558
|
-
async applyGsiUpdates(physicalId, resourceType, logicalId, previousGsis, desiredGsis, desiredAttributeDefinitions, handledByBillingFlip = /* @__PURE__ */ new Set()) {
|
|
12632
|
+
async applyGsiUpdates(physicalId, resourceType, logicalId, previousGsis, desiredGsis, desiredAttributeDefinitions, handledByBillingFlip = /* @__PURE__ */ new Set(), liveIndexNames) {
|
|
12559
12633
|
const prev = previousGsis ?? [];
|
|
12560
12634
|
const desired = desiredGsis ?? [];
|
|
12561
12635
|
const prevByName = new Map(prev.filter((g) => g.IndexName).map((g) => [g.IndexName, g]));
|
|
12562
12636
|
const desiredByName = new Map(desired.filter((g) => g.IndexName).map((g) => [g.IndexName, g]));
|
|
12563
12637
|
const ops = [];
|
|
12564
|
-
for (const name of prevByName.keys())
|
|
12638
|
+
for (const name of prevByName.keys()) {
|
|
12639
|
+
if (desiredByName.has(name)) continue;
|
|
12640
|
+
if (liveIndexNames !== void 0 && !liveIndexNames.has(name)) {
|
|
12641
|
+
this.logger.debug(`GSI ${name} is recorded in cdkd state but not live on DynamoDB table ${physicalId}; skipping its Delete (already removed)`);
|
|
12642
|
+
continue;
|
|
12643
|
+
}
|
|
12644
|
+
ops.push({ Delete: { IndexName: name } });
|
|
12645
|
+
}
|
|
12565
12646
|
for (const [name, gsi] of desiredByName) if (!prevByName.has(name)) {
|
|
12566
12647
|
if (!gsi.KeySchema) throw new ProvisioningError(`GlobalSecondaryIndex ${name} on DynamoDB table ${logicalId} is missing KeySchema`, resourceType, logicalId, physicalId);
|
|
12567
12648
|
ops.push({ Create: {
|
|
@@ -12579,6 +12660,18 @@ var DynamoDBTableProvider = class {
|
|
|
12579
12660
|
ProvisionedThroughput: gsi.ProvisionedThroughput
|
|
12580
12661
|
} });
|
|
12581
12662
|
}
|
|
12663
|
+
await this.runGsiOps(physicalId, ops, desiredAttributeDefinitions);
|
|
12664
|
+
}
|
|
12665
|
+
/**
|
|
12666
|
+
* Issue a list of `GlobalSecondaryIndexUpdates` ops, one per `UpdateTable`,
|
|
12667
|
+
* waiting for the table AND every index to return to ACTIVE between each.
|
|
12668
|
+
*
|
|
12669
|
+
* Extracted from {@link applyGsiUpdates} so the pre-flip GSI removal (issue
|
|
12670
|
+
* #1617) drives the identical call + wait sequence rather than a second copy
|
|
12671
|
+
* of it — the wait is what makes the next op (there, the BillingMode flip)
|
|
12672
|
+
* legal.
|
|
12673
|
+
*/
|
|
12674
|
+
async runGsiOps(physicalId, ops, desiredAttributeDefinitions) {
|
|
12582
12675
|
for (const op of ops) {
|
|
12583
12676
|
const input = {
|
|
12584
12677
|
TableName: physicalId,
|
|
@@ -52933,7 +53026,7 @@ function findRevertDroppedAwsKeys(drifts, desiredProperties, awsProperties) {
|
|
|
52933
53026
|
}
|
|
52934
53027
|
for (const key of driftedTopLevelKeys) {
|
|
52935
53028
|
if (!(key in desiredProperties)) continue;
|
|
52936
|
-
collectMissingPaths(awsProperties[key], desiredProperties[key], key, dropped);
|
|
53029
|
+
collectMissingPaths(awsProperties[key], desiredProperties[key], key, dropped, isTagListKey(key) ? isServiceManagedTagKey : void 0);
|
|
52937
53030
|
}
|
|
52938
53031
|
return [...dropped].sort();
|
|
52939
53032
|
}
|
|
@@ -52941,12 +53034,46 @@ function findRevertDroppedAwsKeys(drifts, desiredProperties, awsProperties) {
|
|
|
52941
53034
|
* Walk the AWS-side value against the desired-side value, recording every path
|
|
52942
53035
|
* present on the AWS side and absent on the desired side.
|
|
52943
53036
|
*
|
|
52944
|
-
*
|
|
52945
|
-
* drift comparator itself treats arrays as single values (its paths never
|
|
53037
|
+
* Plain objects are descended into. A POSITIONAL array is compared wholesale —
|
|
53038
|
+
* the drift comparator itself treats arrays as single values (its paths never
|
|
52946
53039
|
* carry an index), so an element-wise walk here would report positions the
|
|
52947
53040
|
* rest of the revert path cannot reason about.
|
|
52948
|
-
|
|
52949
|
-
|
|
53041
|
+
*
|
|
53042
|
+
* A KEYED list is the exception, and it is the shape with the most to drop
|
|
53043
|
+
* (issue [#1626](https://github.com/go-to-k/cdkd/issues/1626)). An
|
|
53044
|
+
* `[{Key, Value}]` list is semantically a MAP that CFn spells as an array —
|
|
53045
|
+
* ELBv2 `LoadBalancerAttributes`, `ListenerAttributes`, `TargetGroupAttributes`
|
|
53046
|
+
* and every `Tags` list are this shape — so its entries have stable identities,
|
|
53047
|
+
* not positions, and `Key` is exactly what the user would act on. Skipping it
|
|
53048
|
+
* left the pass blind precisely where `readCurrentState` returns AWS's FULL
|
|
53049
|
+
* attribute set (~20 entries) against a template that declares one or two: the
|
|
53050
|
+
* plan warned about nothing while the revert was about to touch eighteen keys.
|
|
53051
|
+
*
|
|
53052
|
+
* Keyed-list entries are reported in BRACKET form (`LoadBalancerAttributes
|
|
53053
|
+
* [deletion_protection.enabled]`) rather than dotted, because attribute keys
|
|
53054
|
+
* contain dots themselves and a dotted path would be ambiguous with a nested
|
|
53055
|
+
* object.
|
|
53056
|
+
*
|
|
53057
|
+
* `isPreservedKey` exempts entries the revert does NOT actually drop — the
|
|
53058
|
+
* caller passes {@link isServiceManagedTagKey} for a top-level TAG LIST, whose
|
|
53059
|
+
* revert goes through {@link mergeTagListForRevert} and keeps `aws:`-prefixed /
|
|
53060
|
+
* `AmazonECSManaged` entries (issue #1501). Reporting those would be a warning
|
|
53061
|
+
* about a loss that cannot happen.
|
|
53062
|
+
*/
|
|
53063
|
+
function collectMissingPaths(awsValue, desiredValue, path, out, isPreservedKey) {
|
|
53064
|
+
if (isKeyedList(awsValue)) {
|
|
53065
|
+
if (!isKeyedList(desiredValue)) {
|
|
53066
|
+
if (desiredValue === void 0) out.add(path);
|
|
53067
|
+
return;
|
|
53068
|
+
}
|
|
53069
|
+
const desiredKeys = new Set(desiredValue.map((e) => e.Key));
|
|
53070
|
+
for (const entry of awsValue) {
|
|
53071
|
+
if (desiredKeys.has(entry.Key)) continue;
|
|
53072
|
+
if (isPreservedKey?.(entry.Key)) continue;
|
|
53073
|
+
out.add(`${path}[${entry.Key}]`);
|
|
53074
|
+
}
|
|
53075
|
+
return;
|
|
53076
|
+
}
|
|
52950
53077
|
if (!isPlainRecord(awsValue)) return;
|
|
52951
53078
|
if (!isPlainRecord(desiredValue)) {
|
|
52952
53079
|
if (desiredValue === void 0) out.add(path);
|
|
@@ -52961,6 +53088,17 @@ function collectMissingPaths(awsValue, desiredValue, path, out) {
|
|
|
52961
53088
|
collectMissingPaths(value, desiredValue[key], childPath, out);
|
|
52962
53089
|
}
|
|
52963
53090
|
}
|
|
53091
|
+
/**
|
|
53092
|
+
* A NON-EMPTY array whose every element is an object carrying a string `Key`.
|
|
53093
|
+
*
|
|
53094
|
+
* Emptiness is deliberately excluded: `[]` carries no identities, so treating
|
|
53095
|
+
* it as keyed would report nothing on the AWS side and — as the DESIRED side —
|
|
53096
|
+
* would claim every AWS entry is dropped, which is already what the
|
|
53097
|
+
* whole-list arm reports more legibly.
|
|
53098
|
+
*/
|
|
53099
|
+
function isKeyedList(value) {
|
|
53100
|
+
return Array.isArray(value) && value.length > 0 && value.every((e) => typeof e === "object" && e !== null && typeof e.Key === "string");
|
|
53101
|
+
}
|
|
52964
53102
|
function isPlainRecord(value) {
|
|
52965
53103
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
52966
53104
|
}
|
|
@@ -70107,7 +70245,7 @@ function createMigrateCommand() {
|
|
|
70107
70245
|
*/
|
|
70108
70246
|
function buildProgram() {
|
|
70109
70247
|
const program = new Command();
|
|
70110
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.280.
|
|
70248
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.280.22");
|
|
70111
70249
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
70112
70250
|
const { profile } = actionCommand.optsWithGlobals();
|
|
70113
70251
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|