@go-to-k/cdkd 0.280.28 → 0.280.30
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-C3rBE_Ni.js → asg-provider-DZ05yRbJ.js} +2 -2
- package/dist/{asg-provider-C3rBE_Ni.js.map → asg-provider-DZ05yRbJ.js.map} +1 -1
- package/dist/cli.js +263 -56
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-Dkep5i7x.js → deploy-engine-POCXyXPl.js} +6 -4
- package/dist/deploy-engine-POCXyXPl.js.map +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-Dkep5i7x.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as requireConfigObject, $t as AssemblyReader, A as green, At as runDockerForeground, B as disableInstanceApiTermination, Bt as resolveCaptureObservedState, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as StackTerminationProtectionError, Ct as getBootstrapMarkerKey, D as bold, Dt as buildDockerImage, E as formatResourceLine, Et as validateContainerRepoName, F as clearOnUpdateRemoval, Ft as synthesisStatusMessage, G as WAFv2WebACLProvider, Gt as stateBucketExistenceConfirmed, H as IntrinsicFunctionResolver, Ht as resolveStateBucketWithDefault, I as ProviderRegistry, It as getDefaultStateBucketName, J as assertRegionMatch, Jt as CFN_TEMPLATE_URL_LIMIT, K as normalizeAwsTagsToCfn, Kt as warnDeprecatedNoPrefixCliFlag, L as findActionableSilentDrops, Lt as getLegacyStateBucketName, M as yellow, Mt as AssetManifestLoader, N as IAMRoleProvider, Nt as getDockerImageBySourceHash, O as cyan, On as normalizeAwsError, Ot as formatDockerLoginError, P as collectInlinePolicyNamesManagedBySiblings, Pt as Synthesizer, Q as requireConfigArray, Qt as expectedOwnerParam, R as CloudControlProvider, Rt as resolveApp, S as extractDeploymentEventError, Sn as StackHasActiveImportsError, St as ensureAssetStorage, T as renderStatefulReason, Tt as validateAssetBucketName, U as cfnRefValueFromPhysicalId, Ut as resolveStateBucketWithDefaultAndSource, V as isTerminationProtectionPropagationError, Vt as resolveSkipPrefix, W as refStateLookupFromResource, Wt as resolveUseCdkBootstrapAssets, X as readConfigString, Xt as findLargeInlineResources, Y as configStringRefusal, Yt as MIGRATE_TMP_PREFIX, Z as replayWarn, Zt as uploadCfnTemplate, _ as createPreDeleteFinalSnapshot, _n as NestedStackChildDirectDestroyError, _t as createAssetRedirectResolver, a as DeploymentEventsStore, an as resetAwsClients, at as isRetryableTransientError, b as unsupportedFinalSnapshotError, bn as ResourceTimeoutError, bt as AssetModeResolver, c as replayFailedOperations, cn as CdkdError, ct as TemplateParser, d as IMPLICIT_DELETE_DEPENDENCIES, dn as DeployCancelledError, dt as rebuildClientForBucketRegion, en as processStackMessages, et as requireConfigString, f as computeImplicitDeleteEdges, fn as LocalInvokeBuildError$1, ft as shouldRetainResource, g as ccRoutedFinalSnapshotError, gn as MissingCdkCliError, gt as buildAssetRedirectMap, h as buildFinalSnapshotIdentifier, ht as WorkGraph, i as DeploymentEventsReader, in as getAwsClients, it as withRetry, j as red, jt as runDockerStreaming, k as gray, kn as withErrorHandling, kt as getDockerCmd, l as replayRollback, lt as LockManager, m as PRE_DELETE_SNAPSHOT_TYPES, mn as LocalStartServiceError, mt as stringifyValue, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as resolveBucketRegion, nt as DiffCalculator, o as planFailedOps, on as setAwsClients, ot as isThrottlingError, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalMigrateError, pt as AssetPublisher, q as resolveExplicitPhysicalId, qt as CFN_TEMPLATE_BODY_LIMIT, r as DeployEngine, rn as AwsClients, rt as describeTypeWithThrottleRetry, s as planRollback, st as DagBuilder, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as applyRoleArnIfSet, u as withResourceDeadline, ut as S3StateBackend, v as isFinalSnapshotError, vn as PartialFailureError, vt as loadPublishableAssetManifest, w as isStatefulRecreateTargetSync, wt as parseBootstrapMarker, x as makeCanonicalizePropertiesFn, xn as ResourceUpdateNotSupportedError, xt as BOOTSTRAP_MARKER_PREFIX, y as refusesFinalSnapshot, yn as ProvisioningError, yt as rewriteTemplateAssetReferences, z as slowCcOperationTimeoutMs, zt as resolveAutoAssetStorage } from "./deploy-engine-
|
|
2
|
+
import { $ as requireConfigObject, $t as AssemblyReader, A as green, At as runDockerForeground, B as disableInstanceApiTermination, Bt as resolveCaptureObservedState, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as StackTerminationProtectionError, Ct as getBootstrapMarkerKey, D as bold, Dt as buildDockerImage, E as formatResourceLine, Et as validateContainerRepoName, F as clearOnUpdateRemoval, Ft as synthesisStatusMessage, G as WAFv2WebACLProvider, Gt as stateBucketExistenceConfirmed, H as IntrinsicFunctionResolver, Ht as resolveStateBucketWithDefault, I as ProviderRegistry, It as getDefaultStateBucketName, J as assertRegionMatch, Jt as CFN_TEMPLATE_URL_LIMIT, K as normalizeAwsTagsToCfn, Kt as warnDeprecatedNoPrefixCliFlag, L as findActionableSilentDrops, Lt as getLegacyStateBucketName, M as yellow, Mt as AssetManifestLoader, N as IAMRoleProvider, Nt as getDockerImageBySourceHash, O as cyan, On as normalizeAwsError, Ot as formatDockerLoginError, P as collectInlinePolicyNamesManagedBySiblings, Pt as Synthesizer, Q as requireConfigArray, Qt as expectedOwnerParam, R as CloudControlProvider, Rt as resolveApp, S as extractDeploymentEventError, Sn as StackHasActiveImportsError, St as ensureAssetStorage, T as renderStatefulReason, Tt as validateAssetBucketName, U as cfnRefValueFromPhysicalId, Ut as resolveStateBucketWithDefaultAndSource, V as isTerminationProtectionPropagationError, Vt as resolveSkipPrefix, W as refStateLookupFromResource, Wt as resolveUseCdkBootstrapAssets, X as readConfigString, Xt as findLargeInlineResources, Y as configStringRefusal, Yt as MIGRATE_TMP_PREFIX, Z as replayWarn, Zt as uploadCfnTemplate, _ as createPreDeleteFinalSnapshot, _n as NestedStackChildDirectDestroyError, _t as createAssetRedirectResolver, a as DeploymentEventsStore, an as resetAwsClients, at as isRetryableTransientError, b as unsupportedFinalSnapshotError, bn as ResourceTimeoutError, bt as AssetModeResolver, c as replayFailedOperations, cn as CdkdError, ct as TemplateParser, d as IMPLICIT_DELETE_DEPENDENCIES, dn as DeployCancelledError, dt as rebuildClientForBucketRegion, en as processStackMessages, et as requireConfigString, f as computeImplicitDeleteEdges, fn as LocalInvokeBuildError$1, ft as shouldRetainResource, g as ccRoutedFinalSnapshotError, gn as MissingCdkCliError, gt as buildAssetRedirectMap, h as buildFinalSnapshotIdentifier, ht as WorkGraph, i as DeploymentEventsReader, in as getAwsClients, it as withRetry, j as red, jt as runDockerStreaming, k as gray, kn as withErrorHandling, kt as getDockerCmd, l as replayRollback, lt as LockManager, m as PRE_DELETE_SNAPSHOT_TYPES, mn as LocalStartServiceError, mt as stringifyValue, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as resolveBucketRegion, nt as DiffCalculator, o as planFailedOps, on as setAwsClients, ot as isThrottlingError, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalMigrateError, pt as AssetPublisher, q as resolveExplicitPhysicalId, qt as CFN_TEMPLATE_BODY_LIMIT, r as DeployEngine, rn as AwsClients, rt as describeTypeWithThrottleRetry, s as planRollback, st as DagBuilder, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as applyRoleArnIfSet, u as withResourceDeadline, ut as S3StateBackend, v as isFinalSnapshotError, vn as PartialFailureError, vt as loadPublishableAssetManifest, w as isStatefulRecreateTargetSync, wt as parseBootstrapMarker, x as makeCanonicalizePropertiesFn, xn as ResourceUpdateNotSupportedError, xt as BOOTSTRAP_MARKER_PREFIX, y as refusesFinalSnapshot, yn as ProvisioningError, yt as rewriteTemplateAssetReferences, z as slowCcOperationTimeoutMs, zt as resolveAutoAssetStorage } from "./deploy-engine-POCXyXPl.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-DZ05yRbJ.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";
|
|
@@ -17989,6 +17989,67 @@ function narrowRouteDestinations(properties) {
|
|
|
17989
17989
|
narrowed
|
|
17990
17990
|
};
|
|
17991
17991
|
}
|
|
17992
|
+
/** The `AuthorizeSecurityGroupIngress` default protocol: "all protocols". */
|
|
17993
|
+
const SG_INGRESS_IP_PROTOCOL_DEFAULT = "-1";
|
|
17994
|
+
/** The refusal path both the create guard and the canonicalizer report under. */
|
|
17995
|
+
const SG_INGRESS_IP_PROTOCOL_PATH = "AWS::EC2::SecurityGroupIngress IpProtocol";
|
|
17996
|
+
/**
|
|
17997
|
+
* Resolve the `IpProtocol` an `AWS::EC2::SecurityGroupIngress` rule actually
|
|
17998
|
+
* SENDS, plus the bag that describes it (issue #1633).
|
|
17999
|
+
*
|
|
18000
|
+
* ONE source for a decision three call sites have to agree on, exactly as
|
|
18001
|
+
* `narrowRouteDestinations` is for the Route destinations: the create arm
|
|
18002
|
+
* (which authorizes the rule), the update arm (which revokes then re-creates
|
|
18003
|
+
* it), and the DIFF-side canonicalizer. If those disagreed, state and template
|
|
18004
|
+
* would each be normalized to a different protocol and the fix would become
|
|
18005
|
+
* the bug — the lesson `.claude/rules/providers.md` records from #1591.
|
|
18006
|
+
*
|
|
18007
|
+
* Two shapes reach the wire as something other than what the template wrote,
|
|
18008
|
+
* and BOTH have to be recorded or they become permanent phantom drift, because
|
|
18009
|
+
* `readSecurityGroupIngressCurrentState` can only ever return what AWS holds:
|
|
18010
|
+
*
|
|
18011
|
+
* - a MALFORMED value (`''` / `{}` / `true` / an explicit `null`), which
|
|
18012
|
+
* `requireConfigString` refuses on the template-path create and — under
|
|
18013
|
+
* `onUnusable` — warn-substitutes the default on the replay-reachable paths;
|
|
18014
|
+
* - a finite NUMBER, the accepted-by-design shape from an unquoted YAML
|
|
18015
|
+
* `IpProtocol: -1` (issue #1513), which is stringified before it is sent.
|
|
18016
|
+
*
|
|
18017
|
+
* The second is not announced by a warning, and does not need to be: unlike a
|
|
18018
|
+
* dropped destination key it loses NOTHING — `-1` and `'-1'` name the same
|
|
18019
|
+
* protocol — so there is no silent loss for `effectiveProperties` to launder
|
|
18020
|
+
* into a clean record, which is the hazard the "already ANNOUNCED" clause of
|
|
18021
|
+
* the `effectiveProperties` contract exists to guard against. Recording the
|
|
18022
|
+
* string additionally fixes the delete path, which forwards the state record
|
|
18023
|
+
* to `buildIpPermission` and would otherwise hand the EC2 API a number.
|
|
18024
|
+
*
|
|
18025
|
+
* `narrowed` is a FRESH object only when the sent value differs from the
|
|
18026
|
+
* declared one, so an ordinary `IpProtocol: 'tcp'` bag is returned untouched
|
|
18027
|
+
* and compares byte-for-byte as before.
|
|
18028
|
+
*
|
|
18029
|
+
* @param onUnusable Passed straight through to `requireConfigString`: absent,
|
|
18030
|
+
* a malformed value THROWS (the template-path create, where the user can fix
|
|
18031
|
+
* the template); supplied, it warns and takes the default. The canonicalizer
|
|
18032
|
+
* passes a no-op — a diff must never throw, and it must never warn either,
|
|
18033
|
+
* since the provisioning path already announces the same substitution.
|
|
18034
|
+
*/
|
|
18035
|
+
function narrowIngressIpProtocol(properties, onUnusable) {
|
|
18036
|
+
const declared = properties["IpProtocol"];
|
|
18037
|
+
const ipProtocol = requireConfigString(declared, SG_INGRESS_IP_PROTOCOL_DEFAULT, SG_INGRESS_IP_PROTOCOL_PATH, {
|
|
18038
|
+
coerceNumber: true,
|
|
18039
|
+
...onUnusable && { onUnusable }
|
|
18040
|
+
});
|
|
18041
|
+
if (declared === void 0 || declared === ipProtocol) return {
|
|
18042
|
+
ipProtocol,
|
|
18043
|
+
narrowed: properties
|
|
18044
|
+
};
|
|
18045
|
+
return {
|
|
18046
|
+
ipProtocol,
|
|
18047
|
+
narrowed: {
|
|
18048
|
+
...properties,
|
|
18049
|
+
IpProtocol: ipProtocol
|
|
18050
|
+
}
|
|
18051
|
+
};
|
|
18052
|
+
}
|
|
17992
18053
|
/**
|
|
17993
18054
|
* AWS EC2 Networking Provider
|
|
17994
18055
|
*
|
|
@@ -19170,10 +19231,8 @@ var EC2Provider = class {
|
|
|
19170
19231
|
this.logger.debug(`Creating SecurityGroupIngress ${logicalId}`);
|
|
19171
19232
|
const groupId = properties["GroupId"];
|
|
19172
19233
|
if (!groupId) throw new ProvisioningError(`GroupId is required for SecurityGroupIngress ${logicalId}`, resourceType, logicalId);
|
|
19173
|
-
const ipProtocol =
|
|
19174
|
-
|
|
19175
|
-
...onUnusableProtocol && { onUnusable: onUnusableProtocol }
|
|
19176
|
-
});
|
|
19234
|
+
const { ipProtocol, narrowed } = narrowIngressIpProtocol(properties, onUnusableProtocol);
|
|
19235
|
+
const effectiveProperties = narrowed === properties ? void 0 : narrowed;
|
|
19177
19236
|
const fromPort = properties["FromPort"];
|
|
19178
19237
|
const toPort = properties["ToPort"];
|
|
19179
19238
|
try {
|
|
@@ -19188,7 +19247,8 @@ var EC2Provider = class {
|
|
|
19188
19247
|
this.logger.debug(`Successfully created SecurityGroupIngress ${logicalId}: ${physicalId}`);
|
|
19189
19248
|
return {
|
|
19190
19249
|
physicalId,
|
|
19191
|
-
attributes: {}
|
|
19250
|
+
attributes: {},
|
|
19251
|
+
...effectiveProperties && { effectiveProperties }
|
|
19192
19252
|
};
|
|
19193
19253
|
} catch (error) {
|
|
19194
19254
|
if (error instanceof Error && error.message.includes("already exists")) {
|
|
@@ -19196,7 +19256,8 @@ var EC2Provider = class {
|
|
|
19196
19256
|
this.logger.debug(`SecurityGroupIngress ${logicalId} already exists, treating as success`);
|
|
19197
19257
|
return {
|
|
19198
19258
|
physicalId,
|
|
19199
|
-
attributes: {}
|
|
19259
|
+
attributes: {},
|
|
19260
|
+
...effectiveProperties && { effectiveProperties }
|
|
19200
19261
|
};
|
|
19201
19262
|
}
|
|
19202
19263
|
const cause = error instanceof Error ? error : void 0;
|
|
@@ -19215,7 +19276,8 @@ var EC2Provider = class {
|
|
|
19215
19276
|
return {
|
|
19216
19277
|
physicalId: createResult.physicalId,
|
|
19217
19278
|
wasReplaced: true,
|
|
19218
|
-
...createResult.attributes && { attributes: createResult.attributes }
|
|
19279
|
+
...createResult.attributes && { attributes: createResult.attributes },
|
|
19280
|
+
...createResult.effectiveProperties && { effectiveProperties: createResult.effectiveProperties }
|
|
19219
19281
|
};
|
|
19220
19282
|
} catch (error) {
|
|
19221
19283
|
if (error instanceof CdkdError) throw error;
|
|
@@ -20293,7 +20355,9 @@ var EC2Provider = class {
|
|
|
20293
20355
|
}
|
|
20294
20356
|
}
|
|
20295
20357
|
/**
|
|
20296
|
-
* The diff-side half of the
|
|
20358
|
+
* The diff-side half of the two EC2 narrowings that report
|
|
20359
|
+
* `effectiveProperties` — Route multi-destination (issue #1591) and
|
|
20360
|
+
* SecurityGroupIngress `IpProtocol` (issue #1633).
|
|
20297
20361
|
*
|
|
20298
20362
|
* `createRoute` sends exactly one destination key and reports the narrowed
|
|
20299
20363
|
* bag via `effectiveProperties`, so state carries one key. Without the same
|
|
@@ -20306,8 +20370,21 @@ var EC2Provider = class {
|
|
|
20306
20370
|
*
|
|
20307
20371
|
* Shares `narrowRouteDestinations` with the provisioning path so the two
|
|
20308
20372
|
* cannot disagree about which key survives.
|
|
20373
|
+
*
|
|
20374
|
+
* The SecurityGroupIngress arm is the same shape and needs the same second
|
|
20375
|
+
* half for the same reason: `IpProtocol` is create-only on that type in the
|
|
20376
|
+
* registry schema, so normalizing state alone would make the template's
|
|
20377
|
+
* original value read as a changed immutable property — a REPLACEMENT, whose
|
|
20378
|
+
* create passes no context and so gets no `onUnusable` downgrade, turning a
|
|
20379
|
+
* previously-green no-op deploy into a hard failure. Normalizing BOTH sides
|
|
20380
|
+
* is also what keeps the fix correct for records written BEFORE it existed,
|
|
20381
|
+
* which still carry the un-narrowed value.
|
|
20309
20382
|
*/
|
|
20310
20383
|
canonicalizeDesiredProperties(resourceType, properties) {
|
|
20384
|
+
if (resourceType === "AWS::EC2::SecurityGroupIngress") {
|
|
20385
|
+
const { narrowed } = narrowIngressIpProtocol(properties, () => {});
|
|
20386
|
+
return narrowed;
|
|
20387
|
+
}
|
|
20311
20388
|
if (resourceType !== "AWS::EC2::Route") return properties;
|
|
20312
20389
|
const { declared, narrowed } = narrowRouteDestinations(properties);
|
|
20313
20390
|
return declared.length > 1 ? narrowed : properties;
|
|
@@ -20789,12 +20866,33 @@ function flattenIpPermissions(perms, direction) {
|
|
|
20789
20866
|
* Direction-sensitive: ingress uses `Source*` fields, egress uses
|
|
20790
20867
|
* `Destination*`.
|
|
20791
20868
|
*/
|
|
20869
|
+
/**
|
|
20870
|
+
* The `IpProtocol` component of a security-group rule identity key.
|
|
20871
|
+
*
|
|
20872
|
+
* A NUMBER is stringified so a record written BEFORE issue #1633 still matches:
|
|
20873
|
+
* AWS always reports the protocol as a string, so a legacy record holding the
|
|
20874
|
+
* number `-1` keyed as `{"p":-1}` against AWS's `{"p":"-1"}` and matched
|
|
20875
|
+
* nothing — `readSecurityGroupIngressCurrentState` returned `undefined` and
|
|
20876
|
+
* `cdkd drift` reported the rule as GONE, forever. The canonicalizer makes such
|
|
20877
|
+
* a record's diff NO_CHANGE, so no deploy ever rewrites it either; normalizing
|
|
20878
|
+
* at the KEY is what heals the existing population with no migration.
|
|
20879
|
+
*
|
|
20880
|
+
* Anything that is neither a string nor a number is passed through UNCHANGED
|
|
20881
|
+
* rather than blanket-`String()`-ed: a malformed object would otherwise key as
|
|
20882
|
+
* the useless `'[object Object]'`, silently collapsing every malformed record
|
|
20883
|
+
* onto one bucket. Such a rule matches nothing either way — this keeps the
|
|
20884
|
+
* non-match honest instead of manufacturing a fake identity.
|
|
20885
|
+
*/
|
|
20886
|
+
function sgProtocolKey(value) {
|
|
20887
|
+
if (typeof value === "number") return String(value);
|
|
20888
|
+
return value ?? "-1";
|
|
20889
|
+
}
|
|
20792
20890
|
function sgRuleKey(rule, direction) {
|
|
20793
20891
|
const peerKey = direction === "egress" ? rule["DestinationSecurityGroupId"] : rule["SourceSecurityGroupId"];
|
|
20794
20892
|
const prefixKey = direction === "egress" ? rule["DestinationPrefixListId"] : rule["SourcePrefixListId"];
|
|
20795
20893
|
const peerOwner = direction === "ingress" ? rule["SourceSecurityGroupOwnerId"] : void 0;
|
|
20796
20894
|
return JSON.stringify({
|
|
20797
|
-
p: rule["IpProtocol"]
|
|
20895
|
+
p: sgProtocolKey(rule["IpProtocol"]),
|
|
20798
20896
|
f: rule["FromPort"] ?? null,
|
|
20799
20897
|
t: rule["ToPort"] ?? null,
|
|
20800
20898
|
c4: rule["CidrIp"] ?? null,
|
|
@@ -53260,7 +53358,7 @@ function isServiceManagedTagKey(key) {
|
|
|
53260
53358
|
* provider binds it, and that tag is REQUIRED for managed scaling. Because any
|
|
53261
53359
|
* CDK ASG declares at least a `Name` tag, `Tags` is template-DECLARED, so the
|
|
53262
53360
|
* #1498 carve-out (undeclared + captured-empty) correctly does not apply — and
|
|
53263
|
-
* the tag list is an ARRAY, which {@link
|
|
53361
|
+
* the tag list is an ARRAY, which {@link findRevertUnbaselinedAwsKeys}'s walk
|
|
53264
53362
|
* compares wholesale and never descends into. Post-revert the ASG kept the
|
|
53265
53363
|
* capacity provider with its managed scaling silently broken (verified live
|
|
53266
53364
|
* 2026-08-10).
|
|
@@ -53332,43 +53430,48 @@ function findRevertPreservedTagKeys(drifts, desiredProperties, awsProperties) {
|
|
|
53332
53430
|
return [...preserved].sort();
|
|
53333
53431
|
}
|
|
53334
53432
|
/**
|
|
53335
|
-
* The AWS-authored property paths a `--revert`
|
|
53336
|
-
* resource whose state predates observed-capture (issue #1478
|
|
53433
|
+
* The AWS-authored property paths a `--revert` LEAVES UNTOUCHED, for a
|
|
53434
|
+
* resource whose state predates observed-capture (issue #1478, semantics
|
|
53435
|
+
* settled by issue #1626).
|
|
53337
53436
|
*
|
|
53338
|
-
* The mechanism
|
|
53339
|
-
*
|
|
53340
|
-
*
|
|
53341
|
-
*
|
|
53342
|
-
*
|
|
53343
|
-
*
|
|
53344
|
-
*
|
|
53345
|
-
*
|
|
53346
|
-
*
|
|
53347
|
-
* the template does not fully declare, on state written before
|
|
53437
|
+
* The mechanism: `runRevert` picks the revert baseline as
|
|
53438
|
+
* `observedProperties ?? properties`. When `observedProperties` is absent, the
|
|
53439
|
+
* desired side is the raw TEMPLATE while the previous side is the AWS-CURRENT
|
|
53440
|
+
* snapshot — so {@link buildRevertNewProperties}, which overwrites each drifted
|
|
53441
|
+
* top-level key with the desired sub-shape wholesale, has no value to carry for
|
|
53442
|
+
* any AWS-authored key inside that subtree. For a Glue Iceberg table that
|
|
53443
|
+
* reaches `table_type` / `metadata_location`, the same exposure as issue #1461
|
|
53444
|
+
* by a different trigger; the general case is ANY resource where AWS writes
|
|
53445
|
+
* into a bag the template does not fully declare, on state written before
|
|
53348
53446
|
* observed-capture.
|
|
53349
53447
|
*
|
|
53350
|
-
*
|
|
53351
|
-
*
|
|
53352
|
-
*
|
|
53353
|
-
*
|
|
53354
|
-
*
|
|
53355
|
-
*
|
|
53448
|
+
* #1478 shipped the **warn and proceed** semantic — the values were still
|
|
53449
|
+
* erased, the user was merely told first — while explicitly leaving the door
|
|
53450
|
+
* open to "treat 'no observedProperties' as 'previous is unknown' so the merge
|
|
53451
|
+
* preserves". Issue #1626 walked through that door for the reasons in
|
|
53452
|
+
* {@link mergeUntemplatedValue}: on this baseline cdkd cannot tell an
|
|
53453
|
+
* AWS-authored value from an out-of-band change, and resetting on a coin flip
|
|
53454
|
+
* is the worse error. So the paths below are now REPORTED AS PRESERVED, and
|
|
53455
|
+
* `mergeUntemplatedValue` is what makes that true by merging them into the bag
|
|
53456
|
+
* `--revert` actually SENDS — which is the only side a wholesale-replace
|
|
53457
|
+
* provider consults. This function is unchanged in what it computes — only in
|
|
53458
|
+
* what the caller does about it.
|
|
53356
53459
|
*
|
|
53357
53460
|
* Scoped deliberately:
|
|
53358
53461
|
*
|
|
53359
53462
|
* - **Only drifted top-level keys.** Non-drifted keys keep their AWS-current
|
|
53360
|
-
* value in `buildRevertNewProperties`, so nothing under them is
|
|
53463
|
+
* value in `buildRevertNewProperties`, so nothing under them is at stake.
|
|
53361
53464
|
* - **Only when `observedProperties` is absent.** With it present the desired
|
|
53362
53465
|
* side is the deploy-time AWS snapshot, which already carries AWS-authored
|
|
53363
|
-
* fields —
|
|
53364
|
-
* change, and
|
|
53466
|
+
* fields — removing one there is a legitimate revert of a real console
|
|
53467
|
+
* change, and reporting it would be noise on every run.
|
|
53365
53468
|
* - **Only keys that vanish.** A key present on both sides with a different
|
|
53366
|
-
* VALUE is the drift the user asked to revert,
|
|
53469
|
+
* VALUE is the drift the user asked to revert, and it reverts normally.
|
|
53367
53470
|
*
|
|
53368
53471
|
* @returns dotted paths, sorted, e.g. `['Parameters.metadata_location']`.
|
|
53369
53472
|
*/
|
|
53370
|
-
function
|
|
53371
|
-
const
|
|
53473
|
+
function findRevertUnbaselinedAwsKeys(drifts, desiredProperties, awsProperties) {
|
|
53474
|
+
const missing = /* @__PURE__ */ new Set();
|
|
53372
53475
|
const driftedTopLevelKeys = /* @__PURE__ */ new Set();
|
|
53373
53476
|
for (const d of drifts) {
|
|
53374
53477
|
const topLevelKey = d.path.split(".", 1)[0];
|
|
@@ -53376,9 +53479,9 @@ function findRevertDroppedAwsKeys(drifts, desiredProperties, awsProperties) {
|
|
|
53376
53479
|
}
|
|
53377
53480
|
for (const key of driftedTopLevelKeys) {
|
|
53378
53481
|
if (!(key in desiredProperties)) continue;
|
|
53379
|
-
collectMissingPaths(awsProperties[key], desiredProperties[key], key,
|
|
53482
|
+
collectMissingPaths(awsProperties[key], desiredProperties[key], key, missing, isTagListKey(key) ? isServiceManagedTagKey : void 0);
|
|
53380
53483
|
}
|
|
53381
|
-
return [...
|
|
53484
|
+
return [...missing].sort();
|
|
53382
53485
|
}
|
|
53383
53486
|
/**
|
|
53384
53487
|
* Walk the AWS-side value against the desired-side value, recording every path
|
|
@@ -53412,11 +53515,12 @@ function findRevertDroppedAwsKeys(drifts, desiredProperties, awsProperties) {
|
|
|
53412
53515
|
*/
|
|
53413
53516
|
function collectMissingPaths(awsValue, desiredValue, path, out, isPreservedKey) {
|
|
53414
53517
|
if (isKeyedList(awsValue)) {
|
|
53415
|
-
|
|
53518
|
+
const desiredIsEmptyArray = Array.isArray(desiredValue) && desiredValue.length === 0;
|
|
53519
|
+
if (!isKeyedList(desiredValue) && !desiredIsEmptyArray) {
|
|
53416
53520
|
if (desiredValue === void 0) out.add(path);
|
|
53417
53521
|
return;
|
|
53418
53522
|
}
|
|
53419
|
-
const desiredKeys = new Set(desiredValue.map((e) => e.Key));
|
|
53523
|
+
const desiredKeys = new Set(isKeyedList(desiredValue) ? desiredValue.map((e) => e.Key) : []);
|
|
53420
53524
|
for (const entry of awsValue) {
|
|
53421
53525
|
if (desiredKeys.has(entry.Key)) continue;
|
|
53422
53526
|
if (isPreservedKey?.(entry.Key)) continue;
|
|
@@ -53441,10 +53545,12 @@ function collectMissingPaths(awsValue, desiredValue, path, out, isPreservedKey)
|
|
|
53441
53545
|
/**
|
|
53442
53546
|
* A NON-EMPTY array whose every element is an object carrying a string `Key`.
|
|
53443
53547
|
*
|
|
53444
|
-
* Emptiness is
|
|
53445
|
-
*
|
|
53446
|
-
*
|
|
53447
|
-
*
|
|
53548
|
+
* Emptiness is excluded on the AWS side: `[]` carries no identities, so there
|
|
53549
|
+
* is nothing to report. It is NOT excluded on the DESIRED side any more
|
|
53550
|
+
* (issue #1626) — an empty baseline list means every AWS entry is unbaselined,
|
|
53551
|
+
* and since the only caller runs on the template-only baseline where
|
|
53552
|
+
* {@link mergeUntemplatedValue} PRESERVES all of them, reporting nothing would
|
|
53553
|
+
* leave the plan under-claiming what survives.
|
|
53448
53554
|
*/
|
|
53449
53555
|
function isKeyedList(value) {
|
|
53450
53556
|
return Array.isArray(value) && value.length > 0 && value.every((e) => typeof e === "object" && e !== null && typeof e.Key === "string");
|
|
@@ -53453,6 +53559,95 @@ function isPlainRecord(value) {
|
|
|
53453
53559
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
53454
53560
|
}
|
|
53455
53561
|
/**
|
|
53562
|
+
* Merge the AWS-current value with the revert baseline, KEEPING every path the
|
|
53563
|
+
* baseline does not declare (issue
|
|
53564
|
+
* [#1626](https://github.com/go-to-k/cdkd/issues/1626) items 2 + 3).
|
|
53565
|
+
*
|
|
53566
|
+
* ## The contract this settles
|
|
53567
|
+
*
|
|
53568
|
+
* `provider.update(logicalId, physicalId, type, newProperties,
|
|
53569
|
+
* previousProperties)` has ONE parameter list and TWO callers:
|
|
53570
|
+
*
|
|
53571
|
+
* - the deploy engine passes the state-recorded `properties` — the
|
|
53572
|
+
* LAST-DEPLOYED TEMPLATE — so a path present on the previous side and absent
|
|
53573
|
+
* from the desired side means exactly one thing: **the user removed it from
|
|
53574
|
+
* the template**;
|
|
53575
|
+
* - `runRevert` passes the FULL `readCurrentState` snapshot, so the same
|
|
53576
|
+
* absence ALSO covers **a path the template never declared at all**.
|
|
53577
|
+
*
|
|
53578
|
+
* Both provider shapes then destroy that path, by different routes. One that
|
|
53579
|
+
* KEY-DIFFS a collection reads the absence as a REMOVAL — ELBv2's
|
|
53580
|
+
* `LoadBalancerAttributes`, where `readCurrentState` deliberately emits every
|
|
53581
|
+
* attribute AWS reports, puts ~18 untemplated attributes on the removal path
|
|
53582
|
+
* against a template declaring one or two. One that REPLACES a bag wholesale
|
|
53583
|
+
* (`PutBucketTagging` is documented full-replace; the same holds for every
|
|
53584
|
+
* `Put*Configuration`) never consults the previous side at all and simply
|
|
53585
|
+
* sends a bag the untemplated path is missing from.
|
|
53586
|
+
*
|
|
53587
|
+
* That second shape is why the fix lands on the DESIRED side rather than by
|
|
53588
|
+
* trimming `previousProperties`: trimming stops the key-diffing removal but
|
|
53589
|
+
* leaves the wholesale-replace drop untouched, so half the reported paths
|
|
53590
|
+
* would still be erased while the plan claimed otherwise. Merging into the bag
|
|
53591
|
+
* cdkd actually SENDS covers both, and is what makes the plan's "left
|
|
53592
|
+
* untouched" line true rather than aspirational.
|
|
53593
|
+
*
|
|
53594
|
+
* ## Scoped to the baseline that cannot tell the two apart
|
|
53595
|
+
*
|
|
53596
|
+
* Applied ONLY when `observedProperties` is absent — the same gate as
|
|
53597
|
+
* {@link findRevertUnbaselinedAwsKeys}'s notice, and for the same reason. With
|
|
53598
|
+
* observed-capture present the baseline IS a deploy-time AWS snapshot, so a
|
|
53599
|
+
* path AWS reports and the baseline lacks was genuinely added out-of-band and
|
|
53600
|
+
* removing it is the revert the user asked for (the `drift-revert` integ
|
|
53601
|
+
* fixture requires exactly that for an injected tag). Without it the baseline
|
|
53602
|
+
* is the raw TEMPLATE, and "AWS has it, the template does not" is
|
|
53603
|
+
* indistinguishable from "AWS authored it" — so the honest answer is to leave
|
|
53604
|
+
* it alone. `findRevertUnbaselinedAwsKeys`'s docstring already named this as
|
|
53605
|
+
* one of the stricter options #1478 did not foreclose ("treat 'no
|
|
53606
|
+
* observedProperties' as 'previous is unknown' so the merge preserves"); this
|
|
53607
|
+
* is that option.
|
|
53608
|
+
*
|
|
53609
|
+
* The structural rules mirror {@link collectMissingPaths} case for case —
|
|
53610
|
+
* plain records descend, KEYED `[{Key, Value}]` lists merge by `Key`,
|
|
53611
|
+
* positional arrays and scalars are taken from the baseline wholesale — so
|
|
53612
|
+
* every path the plan names as preserved IS kept, and a unit test pins that
|
|
53613
|
+
* correspondence by diffing the flag-on and flag-off outputs.
|
|
53614
|
+
*
|
|
53615
|
+
* A service-authored tag is absent from both sides of that comparison and
|
|
53616
|
+
* stays consistent: `findRevertUnbaselinedAwsKeys` omits it
|
|
53617
|
+
* (`isServiceManagedTagKey`) because the #1501 carve-out already preserves it
|
|
53618
|
+
* on the DEFAULT path, so it is not something this flag rescues and reporting
|
|
53619
|
+
* it would warn about a loss that cannot happen. The test pins that it
|
|
53620
|
+
* survives under BOTH settings.
|
|
53621
|
+
*
|
|
53622
|
+
* A path the baseline DOES declare always wins, which is what keeps the revert
|
|
53623
|
+
* a revert: a drifted value is reset, and one AWS dropped is re-added.
|
|
53624
|
+
*/
|
|
53625
|
+
function mergeUntemplatedValue(awsValue, desiredValue) {
|
|
53626
|
+
const desiredIsEmptyArray = Array.isArray(desiredValue) && desiredValue.length === 0;
|
|
53627
|
+
if (isKeyedList(awsValue) && (isKeyedList(desiredValue) || desiredIsEmptyArray)) {
|
|
53628
|
+
const desiredEntries = isKeyedList(desiredValue) ? desiredValue : [];
|
|
53629
|
+
const desiredByKey = new Map(desiredEntries.map((e) => [e.Key, e]));
|
|
53630
|
+
const merged = [];
|
|
53631
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
53632
|
+
for (const entry of awsValue) {
|
|
53633
|
+
if (emitted.has(entry.Key)) continue;
|
|
53634
|
+
emitted.add(entry.Key);
|
|
53635
|
+
merged.push(desiredByKey.get(entry.Key) ?? entry);
|
|
53636
|
+
}
|
|
53637
|
+
for (const entry of desiredEntries) {
|
|
53638
|
+
if (emitted.has(entry.Key)) continue;
|
|
53639
|
+
emitted.add(entry.Key);
|
|
53640
|
+
merged.push(entry);
|
|
53641
|
+
}
|
|
53642
|
+
return merged;
|
|
53643
|
+
}
|
|
53644
|
+
if (!isPlainRecord(awsValue) || !isPlainRecord(desiredValue)) return desiredValue;
|
|
53645
|
+
const merged = {};
|
|
53646
|
+
for (const [key, value] of Object.entries(awsValue)) merged[key] = key in desiredValue ? mergeUntemplatedValue(value, desiredValue[key]) : value;
|
|
53647
|
+
for (const [key, value] of Object.entries(desiredValue)) if (!(key in awsValue)) merged[key] = value;
|
|
53648
|
+
return merged;
|
|
53649
|
+
}
|
|
53650
|
+
/**
|
|
53456
53651
|
* Build the `newProperties` object passed to `provider.update` during
|
|
53457
53652
|
* `--revert`. Strategy:
|
|
53458
53653
|
*
|
|
@@ -53494,8 +53689,16 @@ function isPlainRecord(value) {
|
|
|
53494
53689
|
* (`Tags[0].Value`) — array drifts surface as a single entry on the
|
|
53495
53690
|
* parent path — so `path.split('.', 1)` is always safe to extract the
|
|
53496
53691
|
* top-level key.
|
|
53692
|
+
*
|
|
53693
|
+
* `preserveUntemplated` (issue #1626) switches the overlay from WHOLESALE to a
|
|
53694
|
+
* deep merge for the drifted subtrees, keeping every path the baseline does
|
|
53695
|
+
* not declare. `runRevert` sets it exactly when the resource has no
|
|
53696
|
+
* `observedProperties` — see {@link mergeUntemplatedValue} for why that is the
|
|
53697
|
+
* only baseline on which it is correct, and why the fix has to live on this
|
|
53698
|
+
* side rather than on `previousProperties`.
|
|
53497
53699
|
*/
|
|
53498
|
-
function buildRevertNewProperties(drifts, desiredProperties, awsProperties) {
|
|
53700
|
+
function buildRevertNewProperties(drifts, desiredProperties, awsProperties, options = {}) {
|
|
53701
|
+
const preserveUntemplated = options.preserveUntemplated === true;
|
|
53499
53702
|
const result = { ...awsProperties };
|
|
53500
53703
|
for (const d of drifts) {
|
|
53501
53704
|
const topLevelKey = d.path.split(".", 1)[0];
|
|
@@ -53503,8 +53706,11 @@ function buildRevertNewProperties(drifts, desiredProperties, awsProperties) {
|
|
|
53503
53706
|
if (topLevelKey in desiredProperties) {
|
|
53504
53707
|
const desiredValue = desiredProperties[topLevelKey];
|
|
53505
53708
|
const awsValue = awsProperties[topLevelKey];
|
|
53506
|
-
|
|
53507
|
-
|
|
53709
|
+
if (preserveUntemplated) result[topLevelKey] = mergeUntemplatedValue(awsValue, desiredValue);
|
|
53710
|
+
else {
|
|
53711
|
+
const baselineIsTagList = isCfnTagList(desiredValue) || Array.isArray(desiredValue) && desiredValue.length === 0;
|
|
53712
|
+
result[topLevelKey] = isTagListKey(topLevelKey) && baselineIsTagList && isCfnTagList(awsValue) ? mergeTagListForRevert(desiredValue, awsValue) : desiredValue;
|
|
53713
|
+
}
|
|
53508
53714
|
}
|
|
53509
53715
|
}
|
|
53510
53716
|
return result;
|
|
@@ -53567,7 +53773,7 @@ async function runRevert(reports, providerRegistry, stateConfig, awsClients, opt
|
|
|
53567
53773
|
provisionedBy: stateResource.provisionedBy
|
|
53568
53774
|
}).provider;
|
|
53569
53775
|
const desiredProperties = stateResource.observedProperties ?? stateResource.properties ?? {};
|
|
53570
|
-
const newProperties = buildRevertNewProperties(outcome.changes, desiredProperties, outcome.awsProperties);
|
|
53776
|
+
const newProperties = buildRevertNewProperties(outcome.changes, desiredProperties, outcome.awsProperties, { preserveUntemplated: stateResource.observedProperties === void 0 });
|
|
53571
53777
|
try {
|
|
53572
53778
|
await withRetry(() => provider.update(outcome.logicalId, stateResource.physicalId, outcome.resourceType, newProperties, outcome.awsProperties), outcome.logicalId, { logger: { debug: (msg) => logger.debug(msg) } });
|
|
53573
53779
|
totalSucceeded++;
|
|
@@ -53634,16 +53840,17 @@ function printRevertPlan(reports) {
|
|
|
53634
53840
|
const tagWord = preserved.length === 1 ? "tag" : "tags";
|
|
53635
53841
|
process.stdout.write(` ! reverting this tag list KEEPS ${preserved.length} AWS-authored ${tagWord} the baseline does not carry:\n`);
|
|
53636
53842
|
for (const path of preserved) process.stdout.write(` ${path}\n`);
|
|
53637
|
-
|
|
53843
|
+
const othersRevert = stateResource.observedProperties === void 0 ? "" : `Every other tag reverts normally. `;
|
|
53844
|
+
process.stdout.write(` ${othersRevert}A service may require these (ECS needs AmazonECSManaged for managed scaling); 'aws:'-prefixed keys are AWS-reserved and cannot be removed by hand.\n`);
|
|
53638
53845
|
}
|
|
53639
53846
|
}
|
|
53640
53847
|
if (stateResource && stateResource.observedProperties === void 0) {
|
|
53641
|
-
const
|
|
53642
|
-
if (
|
|
53643
|
-
const word =
|
|
53644
|
-
process.stdout.write(` ! this resource has no observed-capture baseline, so the revert pushes the raw TEMPLATE and
|
|
53645
|
-
for (const path of
|
|
53646
|
-
process.stdout.write(
|
|
53848
|
+
const unbaselined = findRevertUnbaselinedAwsKeys(o.changes, stateResource.properties ?? {}, o.awsProperties);
|
|
53849
|
+
if (unbaselined.length > 0) {
|
|
53850
|
+
const word = unbaselined.length === 1 ? "value" : "values";
|
|
53851
|
+
process.stdout.write(` ! this resource has no observed-capture baseline, so the revert pushes the raw TEMPLATE and LEAVES ${unbaselined.length} AWS-authored ${word} untouched:\n`);
|
|
53852
|
+
for (const path of unbaselined) process.stdout.write(` ${path}\n`);
|
|
53853
|
+
process.stdout.write(` The template does not declare these, so cdkd cannot tell an AWS-authored value from an out-of-band change and will not reset either (issue #1626). Run 'cdkd state refresh-observed ${report.stackName}' (or re-deploy) to populate observedProperties if you want them reverted too.\n`);
|
|
53647
53854
|
}
|
|
53648
53855
|
}
|
|
53649
53856
|
}
|
|
@@ -70595,7 +70802,7 @@ function createMigrateCommand() {
|
|
|
70595
70802
|
*/
|
|
70596
70803
|
function buildProgram() {
|
|
70597
70804
|
const program = new Command();
|
|
70598
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.280.
|
|
70805
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.280.30");
|
|
70599
70806
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
70600
70807
|
const { profile } = actionCommand.optsWithGlobals();
|
|
70601
70808
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|