@go-to-k/cdkd 0.46.0 → 0.47.0
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/cli.js +239 -21
- package/dist/cli.js.map +2 -2
- package/dist/go-to-k-cdkd-0.47.0.tgz +0 -0
- package/dist/index.js +107 -4
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.46.0.tgz +0 -0
package/dist/cli.js
CHANGED
|
@@ -629,6 +629,10 @@ var deployOptions = [
|
|
|
629
629
|
new Option("--dry-run", "Show changes without applying").default(false),
|
|
630
630
|
new Option("--skip-assets", "Skip asset publishing").default(false),
|
|
631
631
|
new Option("--no-rollback", "Skip rollback on deployment failure"),
|
|
632
|
+
new Option(
|
|
633
|
+
"--no-capture-observed-state",
|
|
634
|
+
"Skip capturing AWS-current properties after each create/update (adds a fire-and-forget readCurrentState per resource so cdkd drift can compare against the real deploy-time AWS snapshot instead of the template). On by default. Disable when deploy speed matters more than rich drift detection \u2014 falls back to comparing against template properties (the pre-v3 behavior)."
|
|
635
|
+
),
|
|
632
636
|
noWaitOption,
|
|
633
637
|
aggressiveVpcParallelOption,
|
|
634
638
|
new Option(
|
|
@@ -1351,6 +1355,16 @@ function resolveApp(cliApp) {
|
|
|
1351
1355
|
const cdkJson = loadCdkJson();
|
|
1352
1356
|
return cdkJson?.app ?? void 0;
|
|
1353
1357
|
}
|
|
1358
|
+
function resolveCaptureObservedState(cliValue) {
|
|
1359
|
+
if (cliValue === false)
|
|
1360
|
+
return false;
|
|
1361
|
+
const cdkJson = loadCdkJson();
|
|
1362
|
+
const cdkdContext = cdkJson?.context?.["cdkd"];
|
|
1363
|
+
const v = cdkdContext?.["captureObservedState"];
|
|
1364
|
+
if (typeof v === "boolean")
|
|
1365
|
+
return v;
|
|
1366
|
+
return true;
|
|
1367
|
+
}
|
|
1354
1368
|
function resolveStateBucketWithSource(cliBucket) {
|
|
1355
1369
|
if (cliBucket)
|
|
1356
1370
|
return { bucket: cliBucket, source: "cli-flag" };
|
|
@@ -3727,8 +3741,8 @@ import {
|
|
|
3727
3741
|
} from "@aws-sdk/client-s3";
|
|
3728
3742
|
|
|
3729
3743
|
// src/types/state.ts
|
|
3730
|
-
var
|
|
3731
|
-
var
|
|
3744
|
+
var STATE_SCHEMA_VERSION_CURRENT = 3;
|
|
3745
|
+
var STATE_SCHEMA_VERSIONS_READABLE = [1, 2, 3];
|
|
3732
3746
|
|
|
3733
3747
|
// src/utils/aws-region-resolver.ts
|
|
3734
3748
|
import { GetBucketLocationCommand, S3Client as S3Client3 } from "@aws-sdk/client-s3";
|
|
@@ -4237,9 +4251,9 @@ var S3StateBackend = class {
|
|
|
4237
4251
|
);
|
|
4238
4252
|
}
|
|
4239
4253
|
const v = parsed.version;
|
|
4240
|
-
if (v !==
|
|
4254
|
+
if (v !== void 0 && !STATE_SCHEMA_VERSIONS_READABLE.includes(v)) {
|
|
4241
4255
|
throw new StateError(
|
|
4242
|
-
`Unsupported state schema version ${String(v)} for stack '${stackName}'. This cdkd binary supports versions ${
|
|
4256
|
+
`Unsupported state schema version ${String(v)} for stack '${stackName}'. This cdkd binary supports versions ${STATE_SCHEMA_VERSIONS_READABLE.join(", ")}. Upgrade cdkd to a version that supports schema ${String(v)}.`
|
|
4243
4257
|
);
|
|
4244
4258
|
}
|
|
4245
4259
|
return parsed;
|
|
@@ -9175,6 +9189,18 @@ var IAMRoleProvider = class {
|
|
|
9175
9189
|
}
|
|
9176
9190
|
return result;
|
|
9177
9191
|
}
|
|
9192
|
+
/**
|
|
9193
|
+
* `Policies` (inline policy bodies) are intentionally omitted from
|
|
9194
|
+
* `readCurrentState`: surfacing the names without bodies would
|
|
9195
|
+
* guarantee a `PolicyDocument`-shaped drift on every role, and
|
|
9196
|
+
* fetching every body costs one extra `GetRolePolicy` per inline
|
|
9197
|
+
* policy. Tell the drift comparator to skip the whole subtree until a
|
|
9198
|
+
* dedicated PR adds proper inline-policy drift via per-name
|
|
9199
|
+
* `GetRolePolicy`.
|
|
9200
|
+
*/
|
|
9201
|
+
getDriftUnknownPaths() {
|
|
9202
|
+
return ["Policies"];
|
|
9203
|
+
}
|
|
9178
9204
|
/**
|
|
9179
9205
|
* Adopt an existing IAM role into cdkd state.
|
|
9180
9206
|
*
|
|
@@ -13568,6 +13594,18 @@ var SNSTopicProvider = class {
|
|
|
13568
13594
|
}
|
|
13569
13595
|
return result;
|
|
13570
13596
|
}
|
|
13597
|
+
/**
|
|
13598
|
+
* `DeliveryStatusLogging` fans out to per-protocol attributes
|
|
13599
|
+
* (`{Protocol}SuccessFeedbackRoleArn` etc.) whose round-trip back to the
|
|
13600
|
+
* CFn array shape is not yet implemented; `Subscription` is managed via
|
|
13601
|
+
* separate `AWS::SNS::Subscription` resources rather than the Topic
|
|
13602
|
+
* itself. Both are absent from `readCurrentState`, so tell the drift
|
|
13603
|
+
* comparator to skip them and avoid the guaranteed false-positive that
|
|
13604
|
+
* would fire on every clean run when the user did template either.
|
|
13605
|
+
*/
|
|
13606
|
+
getDriftUnknownPaths() {
|
|
13607
|
+
return ["DeliveryStatusLogging", "Subscription"];
|
|
13608
|
+
}
|
|
13571
13609
|
/**
|
|
13572
13610
|
* Adopt an existing SNS topic into cdkd state.
|
|
13573
13611
|
*
|
|
@@ -14843,6 +14881,17 @@ var LambdaFunctionProvider = class {
|
|
|
14843
14881
|
throw err;
|
|
14844
14882
|
}
|
|
14845
14883
|
}
|
|
14884
|
+
/**
|
|
14885
|
+
* `Code: { S3Bucket, S3Key }` is set on create / update but `GetFunction`
|
|
14886
|
+
* only returns a pre-signed URL for the deployed code, never the original
|
|
14887
|
+
* asset key — so a state-recorded `Code` value can never match an
|
|
14888
|
+
* AWS-current snapshot. Tell the drift comparator to skip the whole
|
|
14889
|
+
* `Code` subtree to avoid the guaranteed false-positive that would fire
|
|
14890
|
+
* on every clean run.
|
|
14891
|
+
*/
|
|
14892
|
+
getDriftUnknownPaths() {
|
|
14893
|
+
return ["Code"];
|
|
14894
|
+
}
|
|
14846
14895
|
/**
|
|
14847
14896
|
* Adopt an existing Lambda function into cdkd state.
|
|
14848
14897
|
*
|
|
@@ -15986,6 +16035,16 @@ var LambdaLayerVersionProvider = class {
|
|
|
15986
16035
|
}
|
|
15987
16036
|
return result;
|
|
15988
16037
|
}
|
|
16038
|
+
/**
|
|
16039
|
+
* `Content: { S3Bucket, S3Key }` is set on create but
|
|
16040
|
+
* `GetLayerVersionByArn` only returns a pre-signed URL for the deployed
|
|
16041
|
+
* content — the original asset key is unrecoverable. Tell the drift
|
|
16042
|
+
* comparator to skip the whole `Content` subtree to avoid the guaranteed
|
|
16043
|
+
* false-positive that would fire on every clean run.
|
|
16044
|
+
*/
|
|
16045
|
+
getDriftUnknownPaths() {
|
|
16046
|
+
return ["Content"];
|
|
16047
|
+
}
|
|
15989
16048
|
/**
|
|
15990
16049
|
* Adopt an existing Lambda layer version into cdkd state.
|
|
15991
16050
|
*
|
|
@@ -17519,6 +17578,16 @@ var SecretsManagerSecretProvider = class {
|
|
|
17519
17578
|
throw err;
|
|
17520
17579
|
}
|
|
17521
17580
|
}
|
|
17581
|
+
/**
|
|
17582
|
+
* `SecretString` and `GenerateSecretString` are set on create but
|
|
17583
|
+
* `DescribeSecret` does not return the secret value (that lives behind
|
|
17584
|
+
* `GetSecretValue`, which we deliberately never call to avoid surfacing
|
|
17585
|
+
* plaintext through drift). Tell the drift comparator to skip both keys
|
|
17586
|
+
* so they don't fire guaranteed false-positive drift on every clean run.
|
|
17587
|
+
*/
|
|
17588
|
+
getDriftUnknownPaths() {
|
|
17589
|
+
return ["SecretString", "GenerateSecretString"];
|
|
17590
|
+
}
|
|
17522
17591
|
/**
|
|
17523
17592
|
* Adopt an existing Secrets Manager secret into cdkd state.
|
|
17524
17593
|
*
|
|
@@ -36892,10 +36961,23 @@ var DeployEngine = class {
|
|
|
36892
36961
|
this.options.noRollback = options.noRollback ?? false;
|
|
36893
36962
|
this.options.resourceWarnAfterMs = options.resourceWarnAfterMs ?? DEFAULT_RESOURCE_WARN_AFTER_MS;
|
|
36894
36963
|
this.options.resourceTimeoutMs = options.resourceTimeoutMs ?? DEFAULT_RESOURCE_TIMEOUT_MS;
|
|
36964
|
+
this.options.captureObservedState = options.captureObservedState ?? true;
|
|
36895
36965
|
}
|
|
36896
36966
|
logger = getLogger().child("DeployEngine");
|
|
36897
36967
|
resolver;
|
|
36898
36968
|
interrupted = false;
|
|
36969
|
+
/**
|
|
36970
|
+
* In-flight `provider.readCurrentState` promises kicked off after a
|
|
36971
|
+
* successful CREATE / UPDATE. The deploy critical path does NOT
|
|
36972
|
+
* `await` these; instead they're drained at the end of `doDeploy`
|
|
36973
|
+
* (success path only) and the resolved values are merged into
|
|
36974
|
+
* `ResourceState.observedProperties` before the final state save.
|
|
36975
|
+
*
|
|
36976
|
+
* Each Promise resolves to the AWS-current snapshot, or `undefined`
|
|
36977
|
+
* if the provider does not implement `readCurrentState` or the call
|
|
36978
|
+
* threw — never rejects, so an unhandled-rejection cannot escape.
|
|
36979
|
+
*/
|
|
36980
|
+
observedCaptureTasks = /* @__PURE__ */ new Map();
|
|
36899
36981
|
/**
|
|
36900
36982
|
* Target region for this stack. Required — load-bearing for the
|
|
36901
36983
|
* region-prefixed S3 state key and recorded in state.json for
|
|
@@ -36908,6 +36990,61 @@ var DeployEngine = class {
|
|
|
36908
36990
|
async deploy(stackName, template) {
|
|
36909
36991
|
return withStackName(stackName, () => this.doDeploy(stackName, template));
|
|
36910
36992
|
}
|
|
36993
|
+
/**
|
|
36994
|
+
* Kick off `provider.readCurrentState` for a freshly-created/updated
|
|
36995
|
+
* resource without blocking the deploy critical path. The promise
|
|
36996
|
+
* lands in `observedCaptureTasks` keyed by `logicalId`; the deploy's
|
|
36997
|
+
* success-path drain (`drainObservedCaptures`) awaits the full set
|
|
36998
|
+
* and merges the resolved values into `ResourceState.observedProperties`
|
|
36999
|
+
* before the final state save.
|
|
37000
|
+
*
|
|
37001
|
+
* Errors are swallowed at the Promise level — readCurrentState
|
|
37002
|
+
* failing must not fail the deploy. The map entry resolves to
|
|
37003
|
+
* `undefined` for failures and for providers without
|
|
37004
|
+
* `readCurrentState`; both translate to "no observedProperties" at
|
|
37005
|
+
* the merge step, which is fine: drift falls back to comparing
|
|
37006
|
+
* against `properties`.
|
|
37007
|
+
*/
|
|
37008
|
+
kickOffObservedCapture(provider, logicalId, physicalId, resourceType, resolvedProps) {
|
|
37009
|
+
if (this.options.captureObservedState !== true)
|
|
37010
|
+
return;
|
|
37011
|
+
if (!provider.readCurrentState)
|
|
37012
|
+
return;
|
|
37013
|
+
const promise = provider.readCurrentState(physicalId, logicalId, resourceType, resolvedProps).catch((err) => {
|
|
37014
|
+
this.logger.debug(
|
|
37015
|
+
`observedProperties capture for ${logicalId} (${resourceType}) failed: ${err instanceof Error ? err.message : String(err)} \u2014 drift will fall back to template properties for this resource until the next successful deploy.`
|
|
37016
|
+
);
|
|
37017
|
+
return void 0;
|
|
37018
|
+
});
|
|
37019
|
+
this.observedCaptureTasks.set(logicalId, promise);
|
|
37020
|
+
}
|
|
37021
|
+
/**
|
|
37022
|
+
* Wait for every in-flight `readCurrentState` promise from the
|
|
37023
|
+
* deploy's success path, then merge each resolved snapshot into the
|
|
37024
|
+
* matching `ResourceState.observedProperties`. After this runs the
|
|
37025
|
+
* map is drained so a subsequent deploy starts fresh.
|
|
37026
|
+
*
|
|
37027
|
+
* Called from `doDeploy` immediately before the final `saveState`.
|
|
37028
|
+
* The rollback / failure paths intentionally do NOT call this — a
|
|
37029
|
+
* failed deploy's partial state is already inconsistent, and waiting
|
|
37030
|
+
* on potentially many in-flight reads would slow down the rollback
|
|
37031
|
+
* itself.
|
|
37032
|
+
*/
|
|
37033
|
+
async drainObservedCaptures(stateResources) {
|
|
37034
|
+
if (this.observedCaptureTasks.size === 0)
|
|
37035
|
+
return;
|
|
37036
|
+
const entries = Array.from(this.observedCaptureTasks.entries());
|
|
37037
|
+
this.observedCaptureTasks.clear();
|
|
37038
|
+
const resolved = await Promise.all(entries.map(([, p]) => p));
|
|
37039
|
+
for (let i = 0; i < entries.length; i++) {
|
|
37040
|
+
const logicalId = entries[i][0];
|
|
37041
|
+
const observed = resolved[i];
|
|
37042
|
+
const target = stateResources[logicalId];
|
|
37043
|
+
if (target && observed !== void 0) {
|
|
37044
|
+
target.observedProperties = observed;
|
|
37045
|
+
}
|
|
37046
|
+
}
|
|
37047
|
+
}
|
|
36911
37048
|
async doDeploy(stackName, template) {
|
|
36912
37049
|
const startTime = Date.now();
|
|
36913
37050
|
this.logger.debug(`Starting deployment for stack: ${stackName}`);
|
|
@@ -37024,6 +37161,7 @@ var DeployEngine = class {
|
|
|
37024
37161
|
progress,
|
|
37025
37162
|
migrationPending
|
|
37026
37163
|
);
|
|
37164
|
+
await this.drainObservedCaptures(newState.resources);
|
|
37027
37165
|
const newEtag = await this.stateBackend.saveState(stackName, this.stackRegion, newState);
|
|
37028
37166
|
this.logger.debug(`State saved (ETag: ${newEtag})`);
|
|
37029
37167
|
const durationMs = Date.now() - startTime;
|
|
@@ -37039,6 +37177,7 @@ var DeployEngine = class {
|
|
|
37039
37177
|
} finally {
|
|
37040
37178
|
renderer.stop();
|
|
37041
37179
|
process.removeListener("SIGINT", sigintHandler);
|
|
37180
|
+
this.observedCaptureTasks.clear();
|
|
37042
37181
|
try {
|
|
37043
37182
|
await this.lockManager.releaseLock(stackName, this.stackRegion);
|
|
37044
37183
|
this.logger.debug("Lock released");
|
|
@@ -37592,6 +37731,13 @@ var DeployEngine = class {
|
|
|
37592
37731
|
...result.attributes && { attributes: result.attributes },
|
|
37593
37732
|
...dependencies && dependencies.length > 0 && { dependencies }
|
|
37594
37733
|
};
|
|
37734
|
+
this.kickOffObservedCapture(
|
|
37735
|
+
provider,
|
|
37736
|
+
logicalId,
|
|
37737
|
+
result.physicalId,
|
|
37738
|
+
resourceType,
|
|
37739
|
+
resolvedProps
|
|
37740
|
+
);
|
|
37595
37741
|
if (counts)
|
|
37596
37742
|
counts.created++;
|
|
37597
37743
|
if (progress)
|
|
@@ -37670,6 +37816,13 @@ var DeployEngine = class {
|
|
|
37670
37816
|
...createResult.attributes && { attributes: createResult.attributes },
|
|
37671
37817
|
...dependencies && dependencies.length > 0 && { dependencies }
|
|
37672
37818
|
};
|
|
37819
|
+
this.kickOffObservedCapture(
|
|
37820
|
+
provider,
|
|
37821
|
+
logicalId,
|
|
37822
|
+
createResult.physicalId,
|
|
37823
|
+
resourceType,
|
|
37824
|
+
resolvedProps
|
|
37825
|
+
);
|
|
37673
37826
|
if (counts)
|
|
37674
37827
|
counts.updated++;
|
|
37675
37828
|
if (progress)
|
|
@@ -37748,6 +37901,13 @@ var DeployEngine = class {
|
|
|
37748
37901
|
...result.attributes && { attributes: result.attributes },
|
|
37749
37902
|
...dependencies && dependencies.length > 0 && { dependencies }
|
|
37750
37903
|
};
|
|
37904
|
+
this.kickOffObservedCapture(
|
|
37905
|
+
provider,
|
|
37906
|
+
logicalId,
|
|
37907
|
+
result.physicalId,
|
|
37908
|
+
resourceType,
|
|
37909
|
+
resolvedProps
|
|
37910
|
+
);
|
|
37751
37911
|
if (counts)
|
|
37752
37912
|
counts.updated++;
|
|
37753
37913
|
if (progress)
|
|
@@ -38226,6 +38386,7 @@ Deploying stack: ${stackInfo.stackName}${stackRegion !== baseRegion ? ` (region:
|
|
|
38226
38386
|
concurrency: options.concurrency,
|
|
38227
38387
|
dryRun: options.dryRun,
|
|
38228
38388
|
noRollback: !options.rollback,
|
|
38389
|
+
captureObservedState: resolveCaptureObservedState(options.captureObservedState),
|
|
38229
38390
|
...options.resourceWarnAfter?.globalMs !== void 0 && {
|
|
38230
38391
|
resourceWarnAfterMs: options.resourceWarnAfter.globalMs
|
|
38231
38392
|
},
|
|
@@ -38531,19 +38692,34 @@ import { Command as Command6, Option as Option3 } from "commander";
|
|
|
38531
38692
|
init_aws_clients();
|
|
38532
38693
|
|
|
38533
38694
|
// src/analyzer/drift-calculator.ts
|
|
38534
|
-
function calculateResourceDrift(stateProperties, awsProperties) {
|
|
38695
|
+
function calculateResourceDrift(stateProperties, awsProperties, options) {
|
|
38535
38696
|
const drifts = [];
|
|
38697
|
+
const ignore = options?.ignorePaths ?? [];
|
|
38536
38698
|
for (const key of Object.keys(stateProperties)) {
|
|
38537
|
-
|
|
38699
|
+
if (isIgnoredPath(key, ignore))
|
|
38700
|
+
continue;
|
|
38701
|
+
diffAt(key, stateProperties[key], awsProperties[key], drifts, ignore);
|
|
38538
38702
|
}
|
|
38539
38703
|
return drifts;
|
|
38540
38704
|
}
|
|
38541
|
-
function
|
|
38705
|
+
function isIgnoredPath(path, ignorePaths) {
|
|
38706
|
+
for (const entry of ignorePaths) {
|
|
38707
|
+
if (path === entry)
|
|
38708
|
+
return true;
|
|
38709
|
+
if (path.startsWith(`${entry}.`))
|
|
38710
|
+
return true;
|
|
38711
|
+
}
|
|
38712
|
+
return false;
|
|
38713
|
+
}
|
|
38714
|
+
function diffAt(path, stateValue, awsValue, out, ignorePaths) {
|
|
38542
38715
|
if (deepEqual(stateValue, awsValue))
|
|
38543
38716
|
return;
|
|
38544
38717
|
if (isPlainObject(stateValue) && isPlainObject(awsValue) && !Array.isArray(stateValue) && !Array.isArray(awsValue)) {
|
|
38545
38718
|
for (const key of Object.keys(stateValue)) {
|
|
38546
|
-
|
|
38719
|
+
const childPath = `${path}.${key}`;
|
|
38720
|
+
if (isIgnoredPath(childPath, ignorePaths))
|
|
38721
|
+
continue;
|
|
38722
|
+
diffAt(childPath, stateValue[key], awsValue[key], out, ignorePaths);
|
|
38547
38723
|
}
|
|
38548
38724
|
return;
|
|
38549
38725
|
}
|
|
@@ -38721,9 +38897,6 @@ async function driftCommand(stacks, options) {
|
|
|
38721
38897
|
logger.setLevel("debug");
|
|
38722
38898
|
}
|
|
38723
38899
|
warnIfDeprecatedRegion(options);
|
|
38724
|
-
if (!options.all && stacks.length === 0) {
|
|
38725
|
-
throw new Error("Stack name is required. Usage: cdkd drift <stack> [<stack>...] | --all");
|
|
38726
|
-
}
|
|
38727
38900
|
if (options.accept && options.revert) {
|
|
38728
38901
|
throw new Error(
|
|
38729
38902
|
"--accept and --revert are mutually exclusive. Use --accept to update cdkd state from AWS, or --revert to push cdkd state values back into AWS."
|
|
@@ -38804,6 +38977,21 @@ function resolveTargetRefs(stacks, stateRefs, options) {
|
|
|
38804
38977
|
}
|
|
38805
38978
|
return stateRefs;
|
|
38806
38979
|
}
|
|
38980
|
+
if (stacks.length === 0) {
|
|
38981
|
+
const candidates = options.stackRegion ? stateRefs.filter((r) => r.region === options.stackRegion) : stateRefs;
|
|
38982
|
+
if (candidates.length === 0) {
|
|
38983
|
+
throw new Error(
|
|
38984
|
+
"No stacks found in state bucket. Run `cdkd deploy` first, or pass --all explicitly."
|
|
38985
|
+
);
|
|
38986
|
+
}
|
|
38987
|
+
if (candidates.length === 1) {
|
|
38988
|
+
return [candidates[0]];
|
|
38989
|
+
}
|
|
38990
|
+
const listing = candidates.map((r) => `${r.stackName}${r.region ? ` (${r.region})` : ""}`).join(", ");
|
|
38991
|
+
throw new Error(
|
|
38992
|
+
`Multiple stacks found in state: ${listing}. Specify stack name(s) or use --all.`
|
|
38993
|
+
);
|
|
38994
|
+
}
|
|
38807
38995
|
const out = [];
|
|
38808
38996
|
for (const stackName of stacks) {
|
|
38809
38997
|
const matches = stateRefs.filter((r) => r.stackName === stackName);
|
|
@@ -38901,7 +39089,9 @@ async function runDriftForStack(stackName, region, stateBackend, providerRegistr
|
|
|
38901
39089
|
});
|
|
38902
39090
|
continue;
|
|
38903
39091
|
}
|
|
38904
|
-
const
|
|
39092
|
+
const ignorePaths = provider.getDriftUnknownPaths ? provider.getDriftUnknownPaths(resource.resourceType) : [];
|
|
39093
|
+
const baseline = resource.observedProperties ?? resource.properties ?? {};
|
|
39094
|
+
const changes = calculateResourceDrift(baseline, aws, { ignorePaths });
|
|
38905
39095
|
if (changes.length === 0) {
|
|
38906
39096
|
outcomes.push({ kind: "clean", logicalId, resourceType: resource.resourceType });
|
|
38907
39097
|
} else {
|
|
@@ -38973,14 +39163,13 @@ async function runAccept(reports, stateBackend, stateConfig, awsClients, options
|
|
|
38973
39163
|
const existing = resources[outcome.logicalId];
|
|
38974
39164
|
if (!existing)
|
|
38975
39165
|
continue;
|
|
38976
|
-
const
|
|
39166
|
+
const hasObserved = existing.observedProperties !== void 0;
|
|
39167
|
+
const baselineSource = hasObserved ? existing.observedProperties : existing.properties ?? {};
|
|
39168
|
+
const newBaseline = JSON.parse(JSON.stringify(baselineSource));
|
|
38977
39169
|
for (const change of outcome.changes) {
|
|
38978
|
-
setAtPath(
|
|
39170
|
+
setAtPath(newBaseline, change.path, change.awsValue);
|
|
38979
39171
|
}
|
|
38980
|
-
resources[outcome.logicalId] = {
|
|
38981
|
-
...existing,
|
|
38982
|
-
properties: newProperties
|
|
38983
|
-
};
|
|
39172
|
+
resources[outcome.logicalId] = hasObserved ? { ...existing, observedProperties: newBaseline } : { ...existing, properties: newBaseline };
|
|
38984
39173
|
}
|
|
38985
39174
|
const newState = {
|
|
38986
39175
|
...report.state,
|
|
@@ -39047,13 +39236,14 @@ async function runRevert(reports, providerRegistry, stateConfig, awsClients, opt
|
|
|
39047
39236
|
return;
|
|
39048
39237
|
}
|
|
39049
39238
|
const provider = providerRegistry.getProvider(outcome.resourceType);
|
|
39239
|
+
const desiredProperties = stateResource.observedProperties ?? stateResource.properties ?? {};
|
|
39050
39240
|
try {
|
|
39051
39241
|
await withRetry(
|
|
39052
39242
|
() => provider.update(
|
|
39053
39243
|
outcome.logicalId,
|
|
39054
39244
|
stateResource.physicalId,
|
|
39055
39245
|
outcome.resourceType,
|
|
39056
|
-
|
|
39246
|
+
desiredProperties,
|
|
39057
39247
|
outcome.awsProperties
|
|
39058
39248
|
),
|
|
39059
39249
|
outcome.logicalId,
|
|
@@ -42126,6 +42316,7 @@ async function importCommand(stackArg, options) {
|
|
|
42126
42316
|
existingState,
|
|
42127
42317
|
selectiveMode
|
|
42128
42318
|
);
|
|
42319
|
+
await captureObservedForImportedResources(stackState, providerRegistry, logger);
|
|
42129
42320
|
const saveOptions = {};
|
|
42130
42321
|
if (existingEtag) {
|
|
42131
42322
|
saveOptions.expectedEtag = existingEtag;
|
|
@@ -42326,7 +42517,7 @@ function buildStackState(stackName, region, rows, templateParser, template, exis
|
|
|
42326
42517
|
};
|
|
42327
42518
|
}
|
|
42328
42519
|
return {
|
|
42329
|
-
version:
|
|
42520
|
+
version: STATE_SCHEMA_VERSION_CURRENT,
|
|
42330
42521
|
stackName,
|
|
42331
42522
|
region,
|
|
42332
42523
|
resources,
|
|
@@ -42419,6 +42610,33 @@ function createImportCommand() {
|
|
|
42419
42610
|
function collectMultiple(value, previous) {
|
|
42420
42611
|
return [...previous ?? [], value];
|
|
42421
42612
|
}
|
|
42613
|
+
async function captureObservedForImportedResources(stackState, providerRegistry, logger) {
|
|
42614
|
+
const entries = Object.entries(stackState.resources);
|
|
42615
|
+
if (entries.length === 0)
|
|
42616
|
+
return;
|
|
42617
|
+
await Promise.all(
|
|
42618
|
+
entries.map(async ([logicalId, resource]) => {
|
|
42619
|
+
try {
|
|
42620
|
+
const provider = providerRegistry.getProvider(resource.resourceType);
|
|
42621
|
+
if (!provider.readCurrentState)
|
|
42622
|
+
return;
|
|
42623
|
+
const observed = await provider.readCurrentState(
|
|
42624
|
+
resource.physicalId,
|
|
42625
|
+
logicalId,
|
|
42626
|
+
resource.resourceType,
|
|
42627
|
+
resource.properties ?? {}
|
|
42628
|
+
);
|
|
42629
|
+
if (observed !== void 0) {
|
|
42630
|
+
resource.observedProperties = observed;
|
|
42631
|
+
}
|
|
42632
|
+
} catch (err) {
|
|
42633
|
+
logger.debug(
|
|
42634
|
+
`observedProperties capture for imported ${logicalId} (${resource.resourceType}) failed: ${err instanceof Error ? err.message : String(err)} \u2014 drift will fall back to template properties for this resource until the next successful deploy.`
|
|
42635
|
+
);
|
|
42636
|
+
}
|
|
42637
|
+
})
|
|
42638
|
+
);
|
|
42639
|
+
}
|
|
42422
42640
|
|
|
42423
42641
|
// src/cli/index.ts
|
|
42424
42642
|
var SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -42448,7 +42666,7 @@ function reorderArgs(argv) {
|
|
|
42448
42666
|
}
|
|
42449
42667
|
async function main() {
|
|
42450
42668
|
const program = new Command14();
|
|
42451
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
42669
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.47.0");
|
|
42452
42670
|
program.addCommand(createBootstrapCommand());
|
|
42453
42671
|
program.addCommand(createSynthCommand());
|
|
42454
42672
|
program.addCommand(createListCommand());
|