@go-to-k/cdkd 0.46.1 → 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 +149 -14
- package/dist/cli.js.map +2 -2
- package/dist/go-to-k-cdkd-0.47.0.tgz +0 -0
- package/dist/index.js +95 -4
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.46.1.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;
|
|
@@ -36947,10 +36961,23 @@ var DeployEngine = class {
|
|
|
36947
36961
|
this.options.noRollback = options.noRollback ?? false;
|
|
36948
36962
|
this.options.resourceWarnAfterMs = options.resourceWarnAfterMs ?? DEFAULT_RESOURCE_WARN_AFTER_MS;
|
|
36949
36963
|
this.options.resourceTimeoutMs = options.resourceTimeoutMs ?? DEFAULT_RESOURCE_TIMEOUT_MS;
|
|
36964
|
+
this.options.captureObservedState = options.captureObservedState ?? true;
|
|
36950
36965
|
}
|
|
36951
36966
|
logger = getLogger().child("DeployEngine");
|
|
36952
36967
|
resolver;
|
|
36953
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();
|
|
36954
36981
|
/**
|
|
36955
36982
|
* Target region for this stack. Required — load-bearing for the
|
|
36956
36983
|
* region-prefixed S3 state key and recorded in state.json for
|
|
@@ -36963,6 +36990,61 @@ var DeployEngine = class {
|
|
|
36963
36990
|
async deploy(stackName, template) {
|
|
36964
36991
|
return withStackName(stackName, () => this.doDeploy(stackName, template));
|
|
36965
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
|
+
}
|
|
36966
37048
|
async doDeploy(stackName, template) {
|
|
36967
37049
|
const startTime = Date.now();
|
|
36968
37050
|
this.logger.debug(`Starting deployment for stack: ${stackName}`);
|
|
@@ -37079,6 +37161,7 @@ var DeployEngine = class {
|
|
|
37079
37161
|
progress,
|
|
37080
37162
|
migrationPending
|
|
37081
37163
|
);
|
|
37164
|
+
await this.drainObservedCaptures(newState.resources);
|
|
37082
37165
|
const newEtag = await this.stateBackend.saveState(stackName, this.stackRegion, newState);
|
|
37083
37166
|
this.logger.debug(`State saved (ETag: ${newEtag})`);
|
|
37084
37167
|
const durationMs = Date.now() - startTime;
|
|
@@ -37094,6 +37177,7 @@ var DeployEngine = class {
|
|
|
37094
37177
|
} finally {
|
|
37095
37178
|
renderer.stop();
|
|
37096
37179
|
process.removeListener("SIGINT", sigintHandler);
|
|
37180
|
+
this.observedCaptureTasks.clear();
|
|
37097
37181
|
try {
|
|
37098
37182
|
await this.lockManager.releaseLock(stackName, this.stackRegion);
|
|
37099
37183
|
this.logger.debug("Lock released");
|
|
@@ -37647,6 +37731,13 @@ var DeployEngine = class {
|
|
|
37647
37731
|
...result.attributes && { attributes: result.attributes },
|
|
37648
37732
|
...dependencies && dependencies.length > 0 && { dependencies }
|
|
37649
37733
|
};
|
|
37734
|
+
this.kickOffObservedCapture(
|
|
37735
|
+
provider,
|
|
37736
|
+
logicalId,
|
|
37737
|
+
result.physicalId,
|
|
37738
|
+
resourceType,
|
|
37739
|
+
resolvedProps
|
|
37740
|
+
);
|
|
37650
37741
|
if (counts)
|
|
37651
37742
|
counts.created++;
|
|
37652
37743
|
if (progress)
|
|
@@ -37725,6 +37816,13 @@ var DeployEngine = class {
|
|
|
37725
37816
|
...createResult.attributes && { attributes: createResult.attributes },
|
|
37726
37817
|
...dependencies && dependencies.length > 0 && { dependencies }
|
|
37727
37818
|
};
|
|
37819
|
+
this.kickOffObservedCapture(
|
|
37820
|
+
provider,
|
|
37821
|
+
logicalId,
|
|
37822
|
+
createResult.physicalId,
|
|
37823
|
+
resourceType,
|
|
37824
|
+
resolvedProps
|
|
37825
|
+
);
|
|
37728
37826
|
if (counts)
|
|
37729
37827
|
counts.updated++;
|
|
37730
37828
|
if (progress)
|
|
@@ -37803,6 +37901,13 @@ var DeployEngine = class {
|
|
|
37803
37901
|
...result.attributes && { attributes: result.attributes },
|
|
37804
37902
|
...dependencies && dependencies.length > 0 && { dependencies }
|
|
37805
37903
|
};
|
|
37904
|
+
this.kickOffObservedCapture(
|
|
37905
|
+
provider,
|
|
37906
|
+
logicalId,
|
|
37907
|
+
result.physicalId,
|
|
37908
|
+
resourceType,
|
|
37909
|
+
resolvedProps
|
|
37910
|
+
);
|
|
37806
37911
|
if (counts)
|
|
37807
37912
|
counts.updated++;
|
|
37808
37913
|
if (progress)
|
|
@@ -38281,6 +38386,7 @@ Deploying stack: ${stackInfo.stackName}${stackRegion !== baseRegion ? ` (region:
|
|
|
38281
38386
|
concurrency: options.concurrency,
|
|
38282
38387
|
dryRun: options.dryRun,
|
|
38283
38388
|
noRollback: !options.rollback,
|
|
38389
|
+
captureObservedState: resolveCaptureObservedState(options.captureObservedState),
|
|
38284
38390
|
...options.resourceWarnAfter?.globalMs !== void 0 && {
|
|
38285
38391
|
resourceWarnAfterMs: options.resourceWarnAfter.globalMs
|
|
38286
38392
|
},
|
|
@@ -38984,7 +39090,8 @@ async function runDriftForStack(stackName, region, stateBackend, providerRegistr
|
|
|
38984
39090
|
continue;
|
|
38985
39091
|
}
|
|
38986
39092
|
const ignorePaths = provider.getDriftUnknownPaths ? provider.getDriftUnknownPaths(resource.resourceType) : [];
|
|
38987
|
-
const
|
|
39093
|
+
const baseline = resource.observedProperties ?? resource.properties ?? {};
|
|
39094
|
+
const changes = calculateResourceDrift(baseline, aws, { ignorePaths });
|
|
38988
39095
|
if (changes.length === 0) {
|
|
38989
39096
|
outcomes.push({ kind: "clean", logicalId, resourceType: resource.resourceType });
|
|
38990
39097
|
} else {
|
|
@@ -39056,14 +39163,13 @@ async function runAccept(reports, stateBackend, stateConfig, awsClients, options
|
|
|
39056
39163
|
const existing = resources[outcome.logicalId];
|
|
39057
39164
|
if (!existing)
|
|
39058
39165
|
continue;
|
|
39059
|
-
const
|
|
39166
|
+
const hasObserved = existing.observedProperties !== void 0;
|
|
39167
|
+
const baselineSource = hasObserved ? existing.observedProperties : existing.properties ?? {};
|
|
39168
|
+
const newBaseline = JSON.parse(JSON.stringify(baselineSource));
|
|
39060
39169
|
for (const change of outcome.changes) {
|
|
39061
|
-
setAtPath(
|
|
39170
|
+
setAtPath(newBaseline, change.path, change.awsValue);
|
|
39062
39171
|
}
|
|
39063
|
-
resources[outcome.logicalId] = {
|
|
39064
|
-
...existing,
|
|
39065
|
-
properties: newProperties
|
|
39066
|
-
};
|
|
39172
|
+
resources[outcome.logicalId] = hasObserved ? { ...existing, observedProperties: newBaseline } : { ...existing, properties: newBaseline };
|
|
39067
39173
|
}
|
|
39068
39174
|
const newState = {
|
|
39069
39175
|
...report.state,
|
|
@@ -39130,13 +39236,14 @@ async function runRevert(reports, providerRegistry, stateConfig, awsClients, opt
|
|
|
39130
39236
|
return;
|
|
39131
39237
|
}
|
|
39132
39238
|
const provider = providerRegistry.getProvider(outcome.resourceType);
|
|
39239
|
+
const desiredProperties = stateResource.observedProperties ?? stateResource.properties ?? {};
|
|
39133
39240
|
try {
|
|
39134
39241
|
await withRetry(
|
|
39135
39242
|
() => provider.update(
|
|
39136
39243
|
outcome.logicalId,
|
|
39137
39244
|
stateResource.physicalId,
|
|
39138
39245
|
outcome.resourceType,
|
|
39139
|
-
|
|
39246
|
+
desiredProperties,
|
|
39140
39247
|
outcome.awsProperties
|
|
39141
39248
|
),
|
|
39142
39249
|
outcome.logicalId,
|
|
@@ -42209,6 +42316,7 @@ async function importCommand(stackArg, options) {
|
|
|
42209
42316
|
existingState,
|
|
42210
42317
|
selectiveMode
|
|
42211
42318
|
);
|
|
42319
|
+
await captureObservedForImportedResources(stackState, providerRegistry, logger);
|
|
42212
42320
|
const saveOptions = {};
|
|
42213
42321
|
if (existingEtag) {
|
|
42214
42322
|
saveOptions.expectedEtag = existingEtag;
|
|
@@ -42409,7 +42517,7 @@ function buildStackState(stackName, region, rows, templateParser, template, exis
|
|
|
42409
42517
|
};
|
|
42410
42518
|
}
|
|
42411
42519
|
return {
|
|
42412
|
-
version:
|
|
42520
|
+
version: STATE_SCHEMA_VERSION_CURRENT,
|
|
42413
42521
|
stackName,
|
|
42414
42522
|
region,
|
|
42415
42523
|
resources,
|
|
@@ -42502,6 +42610,33 @@ function createImportCommand() {
|
|
|
42502
42610
|
function collectMultiple(value, previous) {
|
|
42503
42611
|
return [...previous ?? [], value];
|
|
42504
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
|
+
}
|
|
42505
42640
|
|
|
42506
42641
|
// src/cli/index.ts
|
|
42507
42642
|
var SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -42531,7 +42666,7 @@ function reorderArgs(argv) {
|
|
|
42531
42666
|
}
|
|
42532
42667
|
async function main() {
|
|
42533
42668
|
const program = new Command14();
|
|
42534
|
-
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");
|
|
42535
42670
|
program.addCommand(createBootstrapCommand());
|
|
42536
42671
|
program.addCommand(createSynthCommand());
|
|
42537
42672
|
program.addCommand(createListCommand());
|