@go-to-k/cdkd 0.288.6 → 0.289.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/README.md +24 -14
- package/dist/{asg-provider-CC6BMuNK.js → asg-provider-1XVtAjhm.js} +3 -3
- package/dist/{asg-provider-CC6BMuNK.js.map → asg-provider-1XVtAjhm.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-umIP3xus.js → deploy-engine-B6pEfNd9.js} +406 -31
- package/dist/deploy-engine-B6pEfNd9.js.map +1 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{logger-Dw-3y48G.js → logger-D2Md1kTq.js} +77 -2
- package/dist/logger-D2Md1kTq.js.map +1 -0
- package/dist/{program-CxMjCbiG.js → program-Bg60hZhr.js} +866 -105
- package/dist/{program-CxMjCbiG.js.map → program-Bg60hZhr.js.map} +1 -1
- package/dist/{version-4xTzOaA1.js → version-CCKo6fK2.js} +2 -2
- package/dist/{version-4xTzOaA1.js.map → version-CCKo6fK2.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-umIP3xus.js.map +0 -1
- package/dist/logger-Dw-3y48G.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as applyDefaultNameForFallback, f as explicitNamePropertyFor, g as looksLikeCdkdGeneratedName, h as getCurrentStackName, m as generateResourceNameWithFallback, n as getLogger, p as generateResourceName, r as isStdoutReservedForPayload, s as getLiveRenderer, v as withStackName } from "./logger-D2Md1kTq.js";
|
|
2
2
|
import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-CCKo6fK2.js";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
6
6
|
import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetBucketReplicationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -9054,7 +9054,8 @@ const STATE_SCHEMA_VERSIONS_READABLE = [
|
|
|
9054
9054
|
6,
|
|
9055
9055
|
7,
|
|
9056
9056
|
8,
|
|
9057
|
-
9
|
|
9057
|
+
9,
|
|
9058
|
+
10
|
|
9058
9059
|
];
|
|
9059
9060
|
/**
|
|
9060
9061
|
* Returns true when a recorded `DeletionPolicy` should prevent cdkd from
|
|
@@ -9126,6 +9127,46 @@ function exportNamesCarriedFrom(previous) {
|
|
|
9126
9127
|
function skippedOutputsCarriedFrom(previous) {
|
|
9127
9128
|
return previous.skippedOutputs === void 0 ? {} : { skippedOutputs: previous.skippedOutputs };
|
|
9128
9129
|
}
|
|
9130
|
+
/**
|
|
9131
|
+
* Carry {@link StackState.orphans} across a rebuild that does not re-decide it
|
|
9132
|
+
* (issue #2934): absent stays absent, present stays as it was.
|
|
9133
|
+
*
|
|
9134
|
+
* **Every** field-enumerating `StackState` literal must spread this. The field
|
|
9135
|
+
* is the only record that a `Retain`-orphaned resource exists at all, so a save
|
|
9136
|
+
* that omits it does not merely lose a hint — it makes a live, billing AWS
|
|
9137
|
+
* resource untrackable and re-opens the deploy loop the record closes. That is
|
|
9138
|
+
* a stronger duty than {@link skippedOutputsCarriedFrom}'s, whose loss costs a
|
|
9139
|
+
* diff preview.
|
|
9140
|
+
*
|
|
9141
|
+
* Writers that re-DECIDE the set do not call this: the rollback arms append,
|
|
9142
|
+
* and the deploy drops an entry once it has been adopted or found absent from
|
|
9143
|
+
* AWS. Spread-form writers (`{ ...previous, ... }`) carry the field already and
|
|
9144
|
+
* need nothing.
|
|
9145
|
+
*/
|
|
9146
|
+
function orphansCarriedFrom(previous) {
|
|
9147
|
+
return previous.orphans === void 0 ? {} : { orphans: previous.orphans };
|
|
9148
|
+
}
|
|
9149
|
+
/**
|
|
9150
|
+
* The `orphans` set a post-rollback save should persist (issue #2934): what the
|
|
9151
|
+
* record already carried, plus what THIS rollback just left in AWS.
|
|
9152
|
+
*
|
|
9153
|
+
* A merge rather than a carry, because a rollback both inherits and produces.
|
|
9154
|
+
* Keyed by `logicalId`, newest wins: a resource orphaned twice (deploy fails,
|
|
9155
|
+
* user retries, it fails again) has one live AWS resource, and the later record
|
|
9156
|
+
* describes the deploy that actually left it there. Keeping both would make the
|
|
9157
|
+
* next adoption pick arbitrarily between two states of the same resource.
|
|
9158
|
+
*
|
|
9159
|
+
* Returns `{}` when there is nothing on either side, so a stack that has never
|
|
9160
|
+
* orphaned anything keeps a byte-identical `state.json` and an old binary sees
|
|
9161
|
+
* exactly what it saw before.
|
|
9162
|
+
*/
|
|
9163
|
+
function orphansAfterRollback(previous, newlyOrphaned) {
|
|
9164
|
+
if (previous.orphans === void 0 && newlyOrphaned.length === 0) return {};
|
|
9165
|
+
const byLogicalId = /* @__PURE__ */ new Map();
|
|
9166
|
+
for (const entry of previous.orphans ?? []) byLogicalId.set(entry.logicalId, entry);
|
|
9167
|
+
for (const entry of newlyOrphaned) byLogicalId.set(entry.logicalId, entry);
|
|
9168
|
+
return { orphans: [...byLogicalId.values()] };
|
|
9169
|
+
}
|
|
9129
9170
|
|
|
9130
9171
|
//#endregion
|
|
9131
9172
|
//#region src/types/rollback-journal.ts
|
|
@@ -9673,7 +9714,7 @@ var S3StateBackend = class {
|
|
|
9673
9714
|
const { expectedEtag, migrateLegacy } = options;
|
|
9674
9715
|
const body = {
|
|
9675
9716
|
...state,
|
|
9676
|
-
version:
|
|
9717
|
+
version: 10,
|
|
9677
9718
|
stackName,
|
|
9678
9719
|
region
|
|
9679
9720
|
};
|
|
@@ -14726,13 +14767,22 @@ function redactSecretsForState(bag, secrets, source, rules = TEMPLATE_DERIVED_RU
|
|
|
14726
14767
|
* GENERATION as the observed bag beside them (issue #1917 review). Left
|
|
14727
14768
|
* unspecified, the derivation below is right for every other caller, whose
|
|
14728
14769
|
* `properties` reach this function untouched.
|
|
14770
|
+
*
|
|
14771
|
+
* That derivation's fail-closed arm asks TWO questions, not one — the secrets
|
|
14772
|
+
* map must be EMPTY and the observed bag must be one THIS RUN produced (issue
|
|
14773
|
+
* [#2906](https://github.com/go-to-k/cdkd/issues/2906)). A caller re-writing a
|
|
14774
|
+
* PRIOR generation's `observedProperties` unchanged therefore keeps it intact
|
|
14775
|
+
* rather than masking positions it cannot pair. A caller whose bag IS fresh but
|
|
14776
|
+
* which does not route it through {@link markSameGenerationBag} should pass
|
|
14777
|
+
* {@link STATE_SOURCED_BASELINE_RULES} through `observedRules` rather than lean
|
|
14778
|
+
* on the derivation.
|
|
14729
14779
|
*/
|
|
14730
14780
|
function scrubResourceRecord(record, secrets, sourceProperties, observedRules) {
|
|
14731
14781
|
if (secrets.size === 0 && sourceProperties === void 0 && !record.observedProperties) return record;
|
|
14732
14782
|
const next = { ...record };
|
|
14733
14783
|
next.properties = redactSecretsForState(record.properties, secrets, sourceProperties);
|
|
14734
14784
|
if (record.attributes) next.attributes = redactSecretsForState(record.attributes, secrets);
|
|
14735
|
-
if (record.observedProperties) next.observedProperties = redactSecretsForState(record.observedProperties, secrets, sourceProperties ?? next.properties, observedRules ?? (sourceProperties !== void 0 ? TEMPLATE_SOURCED_RULES : secrets.size === 0 ? STATE_SOURCED_BASELINE_RULES : STATE_SOURCED_READBACK_RULES));
|
|
14785
|
+
if (record.observedProperties) next.observedProperties = redactSecretsForState(record.observedProperties, secrets, sourceProperties ?? next.properties, observedRules ?? (sourceProperties !== void 0 ? TEMPLATE_SOURCED_RULES : secrets.size === 0 && isSameGenerationBag(record.observedProperties) ? STATE_SOURCED_BASELINE_RULES : STATE_SOURCED_READBACK_RULES));
|
|
14736
14786
|
return next;
|
|
14737
14787
|
}
|
|
14738
14788
|
/**
|
|
@@ -27587,7 +27637,7 @@ var CloudControlProvider = class {
|
|
|
27587
27637
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
27588
27638
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
27589
27639
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
27590
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
27640
|
+
const { ASGProvider } = await import("./asg-provider-1XVtAjhm.js").then((n) => n.n);
|
|
27591
27641
|
const asgProvider = new ASGProvider();
|
|
27592
27642
|
return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
27593
27643
|
}
|
|
@@ -31138,7 +31188,7 @@ var ProviderRegistry = class {
|
|
|
31138
31188
|
buildUnroutableSilentDropMessage(resourceType, drops) {
|
|
31139
31189
|
const details = drops.map((d) => ` - ${d.property}: ${d.rationale}`).join("\n");
|
|
31140
31190
|
const overrideHint = drops.map((d) => `${resourceType}:${d.property}`).join(",");
|
|
31141
|
-
return `${resourceType} uses properties cdkd's SDK Provider does not handle, and this type cannot fall back to Cloud Control API (${isNonProvisionable(resourceType) ? "ProvisioningType: NON_PROVISIONABLE — Cloud Control has no handlers for it" : "the type's SDK provider opts out of the Cloud Control fallback (disableCcApiFallback)"}):\n${details}\nRemove the properties, or force the SDK provider path and accept the drop via --
|
|
31191
|
+
return `${resourceType} uses properties cdkd's SDK Provider does not handle, and this type cannot fall back to Cloud Control API (${isNonProvisionable(resourceType) ? "ProvisioningType: NON_PROVISIONABLE — Cloud Control has no handlers for it" : "the type's SDK provider opts out of the Cloud Control fallback (disableCcApiFallback)"}):\n${details}\nRemove the properties, or force the SDK provider path and accept the drop via --prefer-sdk-route ${overrideHint} (the provider may still reject the resource if the property is required).`;
|
|
31142
31192
|
}
|
|
31143
31193
|
/**
|
|
31144
31194
|
* Legacy entry point that returns just the provider. Delegates to
|
|
@@ -31295,15 +31345,23 @@ var ProviderRegistry = class {
|
|
|
31295
31345
|
reportSilentDropDecisions(resources) {
|
|
31296
31346
|
for (const { logicalId, resourceType, properties, provisionedBy } of resources) {
|
|
31297
31347
|
const drops = findSilentDropProperties(resourceType, properties);
|
|
31298
|
-
const
|
|
31348
|
+
const stickyCc = provisionedBy === "cc-api" && !STICKY_CC_MIGRATION_EXEMPT.has(resourceType);
|
|
31349
|
+
const overridden = stickyCc ? [] : findAcceptedSilentDrops(resourceType, properties, this.allowedUnsupportedProperties);
|
|
31299
31350
|
const autoRouted = drops.map(({ property }) => property).filter((property) => !this.allowedUnsupportedProperties.has(`${resourceType}:${property}`));
|
|
31300
31351
|
if (autoRouted.length > 0) {
|
|
31301
31352
|
const provider = this.providers.get(resourceType);
|
|
31302
31353
|
if (isNonProvisionable(resourceType) || provider?.disableCcApiFallback === true) throw new Error(`${logicalId}: ${this.buildUnroutableSilentDropMessage(resourceType, drops.filter((d) => autoRouted.includes(d.property)))}`);
|
|
31303
|
-
const message = `${logicalId} (${resourceType}): routing via Cloud Control API (cdkd's SDK Provider does not yet wire ${autoRouted.join(", ")} — CC API will forward the full property map. Override via --
|
|
31354
|
+
const message = `${logicalId} (${resourceType}): routing via Cloud Control API (cdkd's SDK Provider does not yet wire ${autoRouted.join(", ")} — CC API will forward the full property map. Override via --prefer-sdk-route ${autoRouted.map((p) => `${resourceType}:${p}`).join(",")}.)`;
|
|
31304
31355
|
if (provisionedBy === "cc-api") this.logger.debug(message);
|
|
31305
31356
|
else this.logger.info(message);
|
|
31306
31357
|
}
|
|
31358
|
+
const named = drops.map(({ property }) => property).filter((property) => this.allowedUnsupportedProperties.has(`${resourceType}:${property}`));
|
|
31359
|
+
if (named.length > 0 && (stickyCc || autoRouted.length > 0)) {
|
|
31360
|
+
const list = named.join(", ");
|
|
31361
|
+
const isAre = named.length === 1 ? "is" : "are";
|
|
31362
|
+
const [cause, remedy] = stickyCc ? ["this resource's state record already routes it to Cloud Control (provisionedBy: cc-api), which is decided before any property is consulted", "Returning this resource to the SDK provider is a destroy-and-recreate, not a flag change — see docs/cli-deploy-safety.md. Widening --prefer-sdk-route alone cannot do it."] : [`${autoRouted.join(", ")} ${autoRouted.length === 1 ? "is" : "are"} not covered by it, and one uncovered property routes the whole RESOURCE to Cloud Control`, `To keep the resource on its SDK provider, add ${autoRouted.map((p) => `${resourceType}:${p}`).join(",")} to --prefer-sdk-route as well.`];
|
|
31363
|
+
this.logger.warn(`${logicalId} (${resourceType}): --prefer-sdk-route had no effect for ${list} — ${cause}. Cloud Control forwards the full property map, so ${list} ${isAre} written to AWS after all. ${remedy}`);
|
|
31364
|
+
}
|
|
31307
31365
|
if (overridden.length > 0) {
|
|
31308
31366
|
const createOnly = getPropertyCoverage(resourceType)?.createOnlyDrops;
|
|
31309
31367
|
const reroutable = overridden.filter((p) => createOnly?.has(p) !== true);
|
|
@@ -31314,7 +31372,7 @@ var ProviderRegistry = class {
|
|
|
31314
31372
|
const one = needsRecreate.length === 1;
|
|
31315
31373
|
remedies.push(`${needsRecreate.join(", ")} ${one ? "is" : "are"} create-only, so removing the override does not apply ${one ? "it" : "them"} either -- a create-only property can only be applied by recreating the resource.`);
|
|
31316
31374
|
}
|
|
31317
|
-
this.logger.warn(`${logicalId} (${resourceType}): ${overridden.join(", ")} will be silently dropped (--
|
|
31375
|
+
this.logger.warn(`${logicalId} (${resourceType}): ${overridden.join(", ")} will be silently dropped (--prefer-sdk-route override accepted). ${remedies.join(" ")}`);
|
|
31318
31376
|
}
|
|
31319
31377
|
this.reportUnrecognizedProperties(logicalId, resourceType, properties, {
|
|
31320
31378
|
provisionedBy,
|
|
@@ -31378,7 +31436,7 @@ var ProviderRegistry = class {
|
|
|
31378
31436
|
const propList = unrecognized.join(", ");
|
|
31379
31437
|
const overrideHint = unrecognized.map((p) => `${resourceType}:${p}`).join(",");
|
|
31380
31438
|
const one = unrecognized.length === 1;
|
|
31381
|
-
this.logger.warn(`${logicalId} (${resourceType}): ${propList} ${one ? "is" : "are"} not in cdkd's CFn schema snapshot for this type, so ${one ? "it" : "they"} will NOT reach AWS — the deploy will still report success. Anything of these shapes looks the same here: a misspelled name (fix the spelling); a read-only attribute, which is not settable on any engine (remove it); or a property AWS published after cdkd's snapshot, which cdkd should be routing via Cloud Control — please report that one: ${unsupportedPropertyIssueUrl(resourceType, unrecognized[0])}${one ? "" : ` (link is for ${unrecognized[0]})`}. If the drop is intended — an addPropertyOverride escape hatch — silence this via --
|
|
31439
|
+
this.logger.warn(`${logicalId} (${resourceType}): ${propList} ${one ? "is" : "are"} not in cdkd's CFn schema snapshot for this type, so ${one ? "it" : "they"} will NOT reach AWS — the deploy will still report success. Anything of these shapes looks the same here: a misspelled name (fix the spelling); a read-only attribute, which is not settable on any engine (remove it); or a property AWS published after cdkd's snapshot, which cdkd should be routing via Cloud Control — please report that one: ${unsupportedPropertyIssueUrl(resourceType, unrecognized[0])}${one ? "" : ` (link is for ${unrecognized[0]})`}. If the drop is intended — an addPropertyOverride escape hatch — silence this via --prefer-sdk-route ${overrideHint}.`);
|
|
31382
31440
|
}
|
|
31383
31441
|
/**
|
|
31384
31442
|
* Pure-functional discovery of every resource whose template uses one or
|
|
@@ -33459,6 +33517,209 @@ var DagExecutor = class {
|
|
|
33459
33517
|
}
|
|
33460
33518
|
};
|
|
33461
33519
|
|
|
33520
|
+
//#endregion
|
|
33521
|
+
//#region src/deployment/orphan-adoption.ts
|
|
33522
|
+
/**
|
|
33523
|
+
* True when the template still declares `logicalId` with `resourceType` and
|
|
33524
|
+
* supplies no explicit physical name for it.
|
|
33525
|
+
*
|
|
33526
|
+
* In that shape the name this deploy would generate equals the recorded
|
|
33527
|
+
* `physicalId` BY CONSTRUCTION — `generateResourceName` is deterministic over
|
|
33528
|
+
* exactly (stack name, logical id), truncation included, with no random
|
|
33529
|
+
* component. That matters because the engine cannot DERIVE the name to compare:
|
|
33530
|
+
* the per-type `ResourceNameOptions` live in the providers, and
|
|
33531
|
+
* `looksLikeCdkdGeneratedName`'s own doc records that the engine does not know
|
|
33532
|
+
* which type maps to which.
|
|
33533
|
+
*
|
|
33534
|
+
* An explicitly named resource is never adopted here. Two different reasons
|
|
33535
|
+
* converge on that: a template-supplied name may belong to someone else
|
|
33536
|
+
* entirely, and if the user CHANGED the name there is no collision to solve —
|
|
33537
|
+
* the ordinary CREATE succeeds and the orphan simply stays behind.
|
|
33538
|
+
*/
|
|
33539
|
+
function templateStillDeclares(template, record, nameProperties) {
|
|
33540
|
+
const declared = template.Resources?.[record.logicalId];
|
|
33541
|
+
if (!declared) return false;
|
|
33542
|
+
if (declared.Type !== record.state.resourceType) return false;
|
|
33543
|
+
const properties = declared.Properties ?? {};
|
|
33544
|
+
return !nameProperties.some((p) => properties[p] !== void 0 && properties[p] !== null);
|
|
33545
|
+
}
|
|
33546
|
+
/**
|
|
33547
|
+
* Types the allow-list admits but adoption must still refuse (issue #2934).
|
|
33548
|
+
*
|
|
33549
|
+
* The primary gate asks "does cdkd derive a name for this type", which is the
|
|
33550
|
+
* by-construction premise restated. `AWS::Lambda::LayerVersion` passes it —
|
|
33551
|
+
* `FALLBACK_NAME_RULES` carries `LayerName` — while its provider records the
|
|
33552
|
+
* `LayerVersionArn` as the physical id (`lambda-layer-provider.ts:143`), so the
|
|
33553
|
+
* premise is false for it anyway. `AWS::ECS::TaskDefinition` is here as a BELT:
|
|
33554
|
+
* it is absent from both name tables today, so the primary gate already refuses
|
|
33555
|
+
* it, and an entry added there later (its `Family` is a plausible one) would
|
|
33556
|
+
* silently re-admit a type round 5 proved harmful.
|
|
33557
|
+
*
|
|
33558
|
+
* **Membership is decided by the three CONSEQUENCES, never by the id's SHAPE.**
|
|
33559
|
+
* An earlier revision of this doc said "the provider records an AWS-minted
|
|
33560
|
+
* identifier while `explicitNamePropertyFor` answers" — and at least six
|
|
33561
|
+
* admitted types satisfy that: `AWS::SNS::Topic` (ARN), `AWS::Cognito::UserPool`
|
|
33562
|
+
* (`userPoolId`), both ELBv2 types, `AWS::StepFunctions::StateMachine`,
|
|
33563
|
+
* `AWS::ECS::Service`. A maintainer applying it would add SNS Topic — the
|
|
33564
|
+
* module header's own motivating type — and gut the feature. The tests are:
|
|
33565
|
+
*
|
|
33566
|
+
* 1. the create API mints a NEW resource instead of colliding on the name, so
|
|
33567
|
+
* there is no loop to break;
|
|
33568
|
+
* 2. `update()` refuses outright, so an adopted resource's next change is a
|
|
33569
|
+
* REPLACEMENT;
|
|
33570
|
+
* 3. that replacement's delete destroys the very copy `DeletionPolicy: Retain`
|
|
33571
|
+
* preserved.
|
|
33572
|
+
*
|
|
33573
|
+
* All three, together. SNS Topic fails (1): `CreateTopic` returns the EXISTING
|
|
33574
|
+
* topic rather than minting a new one, which is exactly the shape adoption is
|
|
33575
|
+
* for — and is why the module header calls it the type that "raises nothing at
|
|
33576
|
+
* all".
|
|
33577
|
+
*/
|
|
33578
|
+
const ADOPTION_REFUSED_TYPES = /* @__PURE__ */ new Set(["AWS::Lambda::LayerVersion", "AWS::ECS::TaskDefinition"]);
|
|
33579
|
+
/**
|
|
33580
|
+
* Decide what to do with each orphan record, before the diff runs.
|
|
33581
|
+
*
|
|
33582
|
+
* Ordering is load-bearing: the AWS existence check runs for EVERY record,
|
|
33583
|
+
* INDEPENDENTLY of whether the template still declares it. The reverse order
|
|
33584
|
+
* left a record whose logical id had left the template never checked, so it
|
|
33585
|
+
* never dropped — present, blocking nothing, surfacing nowhere, and with no
|
|
33586
|
+
* per-record command to clear it.
|
|
33587
|
+
*/
|
|
33588
|
+
async function planOrphanAdoption(params) {
|
|
33589
|
+
const outcome = {
|
|
33590
|
+
adopted: {},
|
|
33591
|
+
remaining: [],
|
|
33592
|
+
refusals: [],
|
|
33593
|
+
notices: []
|
|
33594
|
+
};
|
|
33595
|
+
if (params.records.length === 0) return outcome;
|
|
33596
|
+
let siblingClaims;
|
|
33597
|
+
const claims = async () => siblingClaims ??= await params.readSiblingClaims();
|
|
33598
|
+
for (const record of params.records) {
|
|
33599
|
+
const { logicalId, state } = record;
|
|
33600
|
+
if (params.managedLogicalIds.has(logicalId)) {
|
|
33601
|
+
params.logger.debug(`orphan ${logicalId}: already present in state.resources — dropping the stale record`);
|
|
33602
|
+
continue;
|
|
33603
|
+
}
|
|
33604
|
+
let provider;
|
|
33605
|
+
try {
|
|
33606
|
+
provider = params.getProvider(state.resourceType, state.provisionedBy);
|
|
33607
|
+
} catch (error) {
|
|
33608
|
+
outcome.remaining.push(record);
|
|
33609
|
+
outcome.notices.push(`${logicalId} (${state.resourceType}) is still in AWS as ${state.physicalId} from an earlier rollback, but this build cannot route that type (${error instanceof Error ? error.message : String(error)}) — cdkd is not adopting it.`);
|
|
33610
|
+
continue;
|
|
33611
|
+
}
|
|
33612
|
+
if (!provider.import) {
|
|
33613
|
+
outcome.remaining.push(record);
|
|
33614
|
+
outcome.notices.push(`${logicalId} (${state.resourceType}) was left in AWS by an earlier rollback as ${state.physicalId}, but its provider cannot verify it — cdkd is not adopting it.`);
|
|
33615
|
+
continue;
|
|
33616
|
+
}
|
|
33617
|
+
let found;
|
|
33618
|
+
try {
|
|
33619
|
+
found = await provider.import({
|
|
33620
|
+
logicalId,
|
|
33621
|
+
resourceType: state.resourceType,
|
|
33622
|
+
stackName: params.stackName,
|
|
33623
|
+
region: params.region,
|
|
33624
|
+
properties: state.properties,
|
|
33625
|
+
knownPhysicalId: state.physicalId
|
|
33626
|
+
});
|
|
33627
|
+
} catch (error) {
|
|
33628
|
+
outcome.notices.push(`${logicalId} (${state.resourceType}) is recorded as left in AWS as ${state.physicalId}, but cdkd could not confirm it exists (${error instanceof Error ? error.message : String(error)}) — keeping the record and not adopting it this run.`);
|
|
33629
|
+
outcome.remaining.push(record);
|
|
33630
|
+
continue;
|
|
33631
|
+
}
|
|
33632
|
+
if (found !== null && found.physicalId !== state.physicalId) {
|
|
33633
|
+
outcome.remaining.push(record);
|
|
33634
|
+
outcome.notices.push(`${logicalId} (${state.resourceType}): cdkd asked about ${state.physicalId} and its provider answered for ${found.physicalId} — not adopting.`);
|
|
33635
|
+
continue;
|
|
33636
|
+
}
|
|
33637
|
+
if (found === null) {
|
|
33638
|
+
params.logger.debug(`orphan ${logicalId}: ${state.physicalId} no longer exists in AWS — dropping the record`);
|
|
33639
|
+
continue;
|
|
33640
|
+
}
|
|
33641
|
+
if (params.nameProperties(state.resourceType).length === 0 || ADOPTION_REFUSED_TYPES.has(state.resourceType)) {
|
|
33642
|
+
outcome.remaining.push(record);
|
|
33643
|
+
const why = ADOPTION_REFUSED_TYPES.has(state.resourceType) ? "a new deploy of it mints a new resource rather than colliding, and cdkd cannot update one in place — so adopting it would end in a replacement that destroys what DeletionPolicy: Retain preserved" : "cdkd does not derive that resource's physical name, so a new deploy mints a new resource instead of colliding";
|
|
33644
|
+
outcome.notices.push(`${logicalId} (${state.resourceType}) is still in AWS as ${state.physicalId} from an earlier rollback. cdkd does not re-adopt this type: ${why}. Delete it yourself when you no longer need it.`);
|
|
33645
|
+
continue;
|
|
33646
|
+
}
|
|
33647
|
+
if (!templateStillDeclares(params.template, record, params.nameProperties(state.resourceType))) {
|
|
33648
|
+
outcome.remaining.push(record);
|
|
33649
|
+
outcome.notices.push(`${logicalId} (${state.resourceType}) is still in AWS as ${state.physicalId} from an earlier rollback. This deploy does not create it under that name, so cdkd is leaving it alone.`);
|
|
33650
|
+
continue;
|
|
33651
|
+
}
|
|
33652
|
+
if ((await claims()).has(state.physicalId)) {
|
|
33653
|
+
outcome.refusals.push(`${logicalId}: ${state.physicalId} is already recorded by another cdkd stack. cdkd will not adopt a resource another stack manages.`);
|
|
33654
|
+
outcome.remaining.push(record);
|
|
33655
|
+
continue;
|
|
33656
|
+
}
|
|
33657
|
+
const refreshed = Object.create(null);
|
|
33658
|
+
for (const [key, value] of Object.entries(found.attributes ?? {})) {
|
|
33659
|
+
if (carriesSecretMask(value)) continue;
|
|
33660
|
+
refreshed[key] = value;
|
|
33661
|
+
}
|
|
33662
|
+
const mergedAttributes = Object.create(null);
|
|
33663
|
+
for (const [key, value] of Object.entries(refreshed)) mergedAttributes[key] = value;
|
|
33664
|
+
for (const [key, value] of Object.entries(state.attributes ?? {})) mergedAttributes[key] = value;
|
|
33665
|
+
outcome.adopted[logicalId] = {
|
|
33666
|
+
...state,
|
|
33667
|
+
...Object.keys(mergedAttributes).length > 0 || state.attributes ? { attributes: mergedAttributes } : {}
|
|
33668
|
+
};
|
|
33669
|
+
}
|
|
33670
|
+
return outcome;
|
|
33671
|
+
}
|
|
33672
|
+
/**
|
|
33673
|
+
* Every physical id recorded by a cdkd stack OTHER than `selfStackName` in
|
|
33674
|
+
* `selfRegion` (issue #2934).
|
|
33675
|
+
*
|
|
33676
|
+
* The orphan record proves cdkd created something under a name; it cannot
|
|
33677
|
+
* prove no one has since taken that resource over. A `cdkd import` into a
|
|
33678
|
+
* different stack during the rollback-to-redeploy window would leave one
|
|
33679
|
+
* physical id in two state files, and either stack's `cdkd destroy` would
|
|
33680
|
+
* then delete the other's live resource. This is the check that refuses it.
|
|
33681
|
+
*
|
|
33682
|
+
* Best-effort per sibling: a state file that fails to load is SKIPPED rather
|
|
33683
|
+
* than failing the caller, because the alternative is that one unreadable
|
|
33684
|
+
* record in an unrelated stack blocks every adoption in the account. That
|
|
33685
|
+
* makes the result a lower bound on what is claimed — stated here because it
|
|
33686
|
+
* is the direction that can let a wrong adoption through, and the reason this
|
|
33687
|
+
* check is one of several rather than the only one.
|
|
33688
|
+
*
|
|
33689
|
+
* Reachability is bounded by what this backend can see: another ACCOUNT's
|
|
33690
|
+
* bucket, and a stack deployed against a different `--state-bucket`, are
|
|
33691
|
+
* invisible here by construction.
|
|
33692
|
+
*
|
|
33693
|
+
* SHARED by `cdkd deploy` and `cdkd diff` (issue go-to-k/cdkd#2943) rather
|
|
33694
|
+
* than copied: the two must agree on which records they refuse, or the
|
|
33695
|
+
* preview stops predicting the deploy — which is the defect that issue is
|
|
33696
|
+
* about. A second implementation is the way they would drift apart.
|
|
33697
|
+
*/
|
|
33698
|
+
function makeSiblingClaimReader(params) {
|
|
33699
|
+
const { stateBackend, selfStackName, selfRegion, logger } = params;
|
|
33700
|
+
return async () => {
|
|
33701
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
33702
|
+
let refs;
|
|
33703
|
+
try {
|
|
33704
|
+
refs = await stateBackend.listStacks();
|
|
33705
|
+
} catch (error) {
|
|
33706
|
+
logger.debug(`orphan adoption: could not list sibling stacks — ${error instanceof Error ? error.message : String(error)}`);
|
|
33707
|
+
return claimed;
|
|
33708
|
+
}
|
|
33709
|
+
for (const ref of refs) {
|
|
33710
|
+
if (ref.stackName === selfStackName && ref.region === selfRegion) continue;
|
|
33711
|
+
if (ref.region === void 0) continue;
|
|
33712
|
+
try {
|
|
33713
|
+
const sibling = await stateBackend.getState(ref.stackName, ref.region);
|
|
33714
|
+
for (const record of Object.values(sibling?.state.resources ?? {})) claimed.add(record.physicalId);
|
|
33715
|
+
} catch (error) {
|
|
33716
|
+
logger.debug(`orphan adoption: skipping unreadable state for ${ref.stackName} — ${error instanceof Error ? error.message : String(error)}`);
|
|
33717
|
+
}
|
|
33718
|
+
}
|
|
33719
|
+
return claimed;
|
|
33720
|
+
};
|
|
33721
|
+
}
|
|
33722
|
+
|
|
33462
33723
|
//#endregion
|
|
33463
33724
|
//#region src/types/deployment-events.ts
|
|
33464
33725
|
/**
|
|
@@ -35085,7 +35346,8 @@ async function replayRollback(operations, stateResources, stackName, ctx, option
|
|
|
35085
35346
|
const result = {
|
|
35086
35347
|
failures: 0,
|
|
35087
35348
|
warnings: 0,
|
|
35088
|
-
interrupted: false
|
|
35349
|
+
interrupted: false,
|
|
35350
|
+
orphaned: []
|
|
35089
35351
|
};
|
|
35090
35352
|
if (operations.length === 0) {
|
|
35091
35353
|
ctx.logger.info("No completed operations to roll back.");
|
|
@@ -35103,7 +35365,7 @@ async function replayRollback(operations, stateResources, stackName, ctx, option
|
|
|
35103
35365
|
result.interrupted = true;
|
|
35104
35366
|
break;
|
|
35105
35367
|
}
|
|
35106
|
-
await replaySingle(otherOps[i], stateResources, stackName, ctx, resolver, orphanLogicalIds, result, options.afterOp, options.isInterrupted);
|
|
35368
|
+
await replaySingle(otherOps[i], stateResources, stackName, ctx, resolver, orphanLogicalIds, result, options.onOrphan, options.afterOp, options.isInterrupted);
|
|
35107
35369
|
}
|
|
35108
35370
|
if (!result.interrupted && createOps.length > 0) {
|
|
35109
35371
|
const sorted = sortRollbackCreates(createOps, stateResources);
|
|
@@ -35112,7 +35374,7 @@ async function replayRollback(operations, stateResources, stackName, ctx, option
|
|
|
35112
35374
|
result.interrupted = true;
|
|
35113
35375
|
break;
|
|
35114
35376
|
}
|
|
35115
|
-
await replaySingle(op, stateResources, stackName, ctx, resolver, orphanLogicalIds, result, options.afterOp, options.isInterrupted);
|
|
35377
|
+
await replaySingle(op, stateResources, stackName, ctx, resolver, orphanLogicalIds, result, options.onOrphan, options.afterOp, options.isInterrupted);
|
|
35116
35378
|
}
|
|
35117
35379
|
}
|
|
35118
35380
|
ctx.logger.info("Rollback completed. Some resources may remain if deletion failed.");
|
|
@@ -35605,7 +35867,7 @@ function recordAfterRollbackUpdate(restored, result) {
|
|
|
35605
35867
|
function recordedPropertiesAfterReplayCreate(restored, result) {
|
|
35606
35868
|
return result.effectiveProperties === void 0 ? restored.properties : { ...result.effectiveProperties };
|
|
35607
35869
|
}
|
|
35608
|
-
async function replaySingle(op, stateResources, stackName, ctx, resolver, orphanLogicalIds, result, afterOp, isInterrupted) {
|
|
35870
|
+
async function replaySingle(op, stateResources, stackName, ctx, resolver, orphanLogicalIds, result, onOrphan, afterOp, isInterrupted) {
|
|
35609
35871
|
const action = classifyRollbackOp(op, stateResources, orphanLogicalIds);
|
|
35610
35872
|
const { logger } = ctx;
|
|
35611
35873
|
/**
|
|
@@ -35674,6 +35936,15 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
35674
35936
|
const record = stateResources[op.logicalId];
|
|
35675
35937
|
const orphanProvisionedBy = effectiveProvisionedBy(record, op.provisionedBy);
|
|
35676
35938
|
createRollbackRoute = orphanProvisionedBy;
|
|
35939
|
+
if (record) {
|
|
35940
|
+
const orphaned = {
|
|
35941
|
+
logicalId: op.logicalId,
|
|
35942
|
+
orphanedAt: Date.now(),
|
|
35943
|
+
state: record
|
|
35944
|
+
};
|
|
35945
|
+
result.orphaned.push(orphaned);
|
|
35946
|
+
onOrphan?.(orphaned);
|
|
35947
|
+
}
|
|
35677
35948
|
delete stateResources[op.logicalId];
|
|
35678
35949
|
logger.info(` Rollback: Leaving ${op.logicalId} (${op.resourceType}) in AWS (DeletionPolicy: Retain) — removed from state`);
|
|
35679
35950
|
await afterOp?.(op.logicalId);
|
|
@@ -35956,7 +36227,8 @@ async function replayFailedOperations(failedOps, stateResources, stackName, ctx,
|
|
|
35956
36227
|
failures: 0,
|
|
35957
36228
|
warnings: 0,
|
|
35958
36229
|
interrupted: false,
|
|
35959
|
-
remainingFailedOps: []
|
|
36230
|
+
remainingFailedOps: [],
|
|
36231
|
+
orphaned: []
|
|
35960
36232
|
};
|
|
35961
36233
|
const { logger } = ctx;
|
|
35962
36234
|
const resolver = new ReplayResolvers(ctx.region);
|
|
@@ -35998,8 +36270,18 @@ async function replayFailedOperations(failedOps, stateResources, stackName, ctx,
|
|
|
35998
36270
|
result.warnings++;
|
|
35999
36271
|
break;
|
|
36000
36272
|
case "orphan-failed-create-retain": {
|
|
36001
|
-
const
|
|
36273
|
+
const failedCreateRecord = stateResources[op.logicalId];
|
|
36274
|
+
const orphanProvisionedBy = effectiveProvisionedBy(failedCreateRecord, op.provisionedBy);
|
|
36002
36275
|
createRollbackRoute = orphanProvisionedBy;
|
|
36276
|
+
if (failedCreateRecord) {
|
|
36277
|
+
const orphaned = {
|
|
36278
|
+
logicalId: op.logicalId,
|
|
36279
|
+
orphanedAt: Date.now(),
|
|
36280
|
+
state: failedCreateRecord
|
|
36281
|
+
};
|
|
36282
|
+
result.orphaned.push(orphaned);
|
|
36283
|
+
options.onOrphan?.(orphaned);
|
|
36284
|
+
}
|
|
36003
36285
|
delete stateResources[op.logicalId];
|
|
36004
36286
|
logger.info(` Rollback: leaving partially-created ${op.logicalId} (${op.resourceType}) in AWS (DeletionPolicy: Retain) — removed from state`);
|
|
36005
36287
|
await options.afterOp?.(op.logicalId);
|
|
@@ -36919,6 +37201,20 @@ var DeployEngine = class DeployEngine {
|
|
|
36919
37201
|
*/
|
|
36920
37202
|
perResourceTemplateProps = /* @__PURE__ */ new Map();
|
|
36921
37203
|
/**
|
|
37204
|
+
* The resource TYPE each logical id was resolved as during THIS deploy
|
|
37205
|
+
* (issue #2934), the sibling of {@link perResourceSecrets} and
|
|
37206
|
+
* {@link perResourceTemplateProps}.
|
|
37207
|
+
*
|
|
37208
|
+
* Exists only so the orphan-record redaction can tell "these needles belong
|
|
37209
|
+
* to this record" from "a CDK refactor reused this logical id for a different
|
|
37210
|
+
* resource". Keying that on `state.resources` instead was tried and is WRONG
|
|
37211
|
+
* in the direction that matters: an orphan is by definition NOT in
|
|
37212
|
+
* `resources`, so the gate read false for the record being minted, the
|
|
37213
|
+
* needles went empty, and the plaintext survived into `state.json`. The
|
|
37214
|
+
* real-AWS secret fixture caught it.
|
|
37215
|
+
*/
|
|
37216
|
+
perResourceResolvedType = /* @__PURE__ */ new Map();
|
|
37217
|
+
/**
|
|
36922
37218
|
* Resolved secrets recorded while resolving the stack OUTPUTS (a `CfnOutput`
|
|
36923
37219
|
* whose Value resolves a `{{resolve:...}}` reference). Separate from the
|
|
36924
37220
|
* per-resource maps for the same anti-cross-contamination reason. Reset per
|
|
@@ -37051,6 +37347,7 @@ var DeployEngine = class DeployEngine {
|
|
|
37051
37347
|
this.perResourceSecrets = /* @__PURE__ */ new Map();
|
|
37052
37348
|
this.noEchoAttributeResources = /* @__PURE__ */ new Map();
|
|
37053
37349
|
this.perResourceTemplateProps = /* @__PURE__ */ new Map();
|
|
37350
|
+
this.perResourceResolvedType = /* @__PURE__ */ new Map();
|
|
37054
37351
|
this.attemptedResolvedProps = /* @__PURE__ */ new Map();
|
|
37055
37352
|
this.outputSecrets = /* @__PURE__ */ new Map();
|
|
37056
37353
|
this.outputsTemplateSource = Object.create(null);
|
|
@@ -37439,10 +37736,18 @@ var DeployEngine = class DeployEngine {
|
|
|
37439
37736
|
const templateProps = this.perResourceTemplateProps.get(logicalId);
|
|
37440
37737
|
resources[logicalId] = scrubResourceRecord(record, secrets ?? /* @__PURE__ */ new Map(), templateProps);
|
|
37441
37738
|
}
|
|
37739
|
+
const orphans = state.orphans?.map((entry) => {
|
|
37740
|
+
const sameResource = this.perResourceResolvedType.get(entry.logicalId) === entry.state.resourceType;
|
|
37741
|
+
return {
|
|
37742
|
+
...entry,
|
|
37743
|
+
state: scrubResourceRecord(entry.state, (sameResource ? this.perResourceSecrets.get(entry.logicalId) : void 0) ?? /* @__PURE__ */ new Map(), sameResource ? this.perResourceTemplateProps.get(entry.logicalId) : void 0)
|
|
37744
|
+
};
|
|
37745
|
+
});
|
|
37442
37746
|
return {
|
|
37443
37747
|
...state,
|
|
37444
37748
|
resources,
|
|
37445
|
-
outputs: this.redactOutputs(state.outputs)
|
|
37749
|
+
outputs: this.redactOutputs(state.outputs),
|
|
37750
|
+
...orphans === void 0 ? {} : { orphans }
|
|
37446
37751
|
};
|
|
37447
37752
|
}
|
|
37448
37753
|
/**
|
|
@@ -37615,14 +37920,20 @@ var DeployEngine = class DeployEngine {
|
|
|
37615
37920
|
if (this.options.captureObservedState !== true) return;
|
|
37616
37921
|
if (this.options.dryRun === true) return;
|
|
37617
37922
|
let toRefresh = 0;
|
|
37923
|
+
let refused = 0;
|
|
37618
37924
|
const candidates = [];
|
|
37619
37925
|
for (const [logicalId, resource] of Object.entries(stateResources)) {
|
|
37620
37926
|
if (resource.observedProperties !== void 0) continue;
|
|
37927
|
+
if (resource.observedBaselineRefused === true) {
|
|
37928
|
+
refused++;
|
|
37929
|
+
continue;
|
|
37930
|
+
}
|
|
37621
37931
|
candidates.push({
|
|
37622
37932
|
logicalId,
|
|
37623
37933
|
resource
|
|
37624
37934
|
});
|
|
37625
37935
|
}
|
|
37936
|
+
if (refused > 0) this.logger.debug(`observed-properties auto-refresh SKIPPED for ${refused} resource(s) whose baseline a 'cdkd import' run refused (issue #2944): their recorded properties cannot position the redaction, so capturing an AWS readback against them could persist a resolved secret in plaintext. A deploy that actually CHANGES one of them restores its baseline; a NO_CHANGE deploy does not.`);
|
|
37626
37937
|
if (candidates.length === 0) return;
|
|
37627
37938
|
const allSiblings = {};
|
|
37628
37939
|
for (const [lid, res] of Object.entries(stateResources)) allSiblings[lid] = {
|
|
@@ -37672,7 +37983,7 @@ var DeployEngine = class DeployEngine {
|
|
|
37672
37983
|
renderer.start();
|
|
37673
37984
|
const currentStateData = await this.stateBackend.getState(stackName, this.stackRegion);
|
|
37674
37985
|
const currentState = currentStateData?.state ?? {
|
|
37675
|
-
version:
|
|
37986
|
+
version: 10,
|
|
37676
37987
|
region: this.stackRegion,
|
|
37677
37988
|
stackName,
|
|
37678
37989
|
resources: {},
|
|
@@ -37707,6 +38018,9 @@ var DeployEngine = class DeployEngine {
|
|
|
37707
38018
|
const conditions = await this.resolver.evaluateConditions(context);
|
|
37708
38019
|
this.logger.debug(`Evaluated ${Object.keys(conditions).length} conditions: ${Object.keys(conditions).join(", ")}`);
|
|
37709
38020
|
const effectiveTemplate = this.templateParser.filterResourcesByCondition(template, conditions);
|
|
38021
|
+
const orphanCountBeforeAdoption = (currentState.orphans ?? []).length;
|
|
38022
|
+
const orphanPlan = await this.adoptRollbackOrphans(currentState, effectiveTemplate);
|
|
38023
|
+
const orphansChanged = Object.keys(orphanPlan.adopted).length > 0 || (currentState.orphans ?? []).length !== orphanCountBeforeAdoption;
|
|
37710
38024
|
const resourceTypes = new Set(Object.values(effectiveTemplate.Resources || {}).map((r) => r.Type).filter((type) => type !== "AWS::CDK::Metadata"));
|
|
37711
38025
|
this.providerRegistry.validateResourceTypes(resourceTypes);
|
|
37712
38026
|
this.logger.debug(`All resource types validated`);
|
|
@@ -37750,12 +38064,13 @@ var DeployEngine = class DeployEngine {
|
|
|
37750
38064
|
const observedRefresh = this.observedCaptureTasks.size > 0;
|
|
37751
38065
|
if (observedRefresh) await this.drainObservedCaptures(currentState.resources);
|
|
37752
38066
|
const skippedOutputsChanged = !skippedOutputsEqual(currentState.skippedOutputs, this.skippedOutputs);
|
|
37753
|
-
if (observedRefresh || outputsChanged || exportSetChanged || skippedOutputsChanged) try {
|
|
38067
|
+
if (observedRefresh || outputsChanged || exportSetChanged || skippedOutputsChanged || orphansChanged) try {
|
|
37754
38068
|
const refreshedState = {
|
|
37755
|
-
version:
|
|
38069
|
+
version: 10,
|
|
37756
38070
|
region: this.stackRegion,
|
|
37757
38071
|
stackName: currentState.stackName,
|
|
37758
38072
|
resources: currentState.resources,
|
|
38073
|
+
...orphansCarriedFrom(currentState),
|
|
37759
38074
|
outputs: outputsChanged ? resolvedOutputs : persistedOutputs,
|
|
37760
38075
|
...resolutionFailed ? exportNamesCarriedFrom(currentState) : { exportNames: [...this.resolvedExportNames] },
|
|
37761
38076
|
...this.skippedOutputs && { skippedOutputs: { ...this.skippedOutputs } },
|
|
@@ -37876,13 +38191,14 @@ var DeployEngine = class DeployEngine {
|
|
|
37876
38191
|
saveChain = saveChain.then(async () => {
|
|
37877
38192
|
try {
|
|
37878
38193
|
const partialState = {
|
|
37879
|
-
version:
|
|
38194
|
+
version: 10,
|
|
37880
38195
|
region: this.stackRegion,
|
|
37881
38196
|
stackName: currentState.stackName,
|
|
37882
38197
|
resources: newResources,
|
|
37883
38198
|
outputs: currentState.outputs,
|
|
37884
38199
|
...exportNamesCarriedFrom(currentState),
|
|
37885
38200
|
...skippedOutputsCarriedFrom(currentState),
|
|
38201
|
+
...orphansCarriedFrom(currentState),
|
|
37886
38202
|
...crossStackReadsForPartialSave(currentState, this.recordedImports, this.recordedOutputReads),
|
|
37887
38203
|
lastModified: Date.now()
|
|
37888
38204
|
};
|
|
@@ -38009,13 +38325,14 @@ var DeployEngine = class DeployEngine {
|
|
|
38009
38325
|
const initialDeploy = currentEtag === void 0;
|
|
38010
38326
|
try {
|
|
38011
38327
|
const preRollbackState = {
|
|
38012
|
-
version:
|
|
38328
|
+
version: 10,
|
|
38013
38329
|
region: this.stackRegion,
|
|
38014
38330
|
stackName: currentState.stackName,
|
|
38015
38331
|
resources: newResources,
|
|
38016
38332
|
outputs: currentState.outputs,
|
|
38017
38333
|
...exportNamesCarriedFrom(currentState),
|
|
38018
38334
|
...skippedOutputsCarriedFrom(currentState),
|
|
38335
|
+
...orphansCarriedFrom(currentState),
|
|
38019
38336
|
...crossStackReadsForPartialSave(currentState, this.recordedImports, this.recordedOutputReads),
|
|
38020
38337
|
lastModified: Date.now()
|
|
38021
38338
|
};
|
|
@@ -38031,6 +38348,7 @@ var DeployEngine = class DeployEngine {
|
|
|
38031
38348
|
this.logger.warn(`Failed to save partial state before rollback: ${saveError instanceof Error ? saveError.message : String(saveError)}`);
|
|
38032
38349
|
}
|
|
38033
38350
|
let autoRollbackClean = false;
|
|
38351
|
+
let rollbackOrphans = [];
|
|
38034
38352
|
if (error instanceof InterruptedError || isInterruptedWaitError(error)) {
|
|
38035
38353
|
await this.writeRollbackJournalSegment(stackName, completedOperations, failedOperations, "interrupted", initialDeploy);
|
|
38036
38354
|
this.logger.info(`Partial state saved (${Object.keys(newResources).length} resources). Run deploy again to resume, 'cdkd rollback' to revert, or destroy to clean up.`);
|
|
@@ -38042,17 +38360,20 @@ var DeployEngine = class DeployEngine {
|
|
|
38042
38360
|
this.logger.warn("Partial state has been saved. Run 'cdkd deploy' to resume, 'cdkd rollback' to revert, or destroy to clean up.");
|
|
38043
38361
|
} else {
|
|
38044
38362
|
await this.writeRollbackJournalSegment(stackName, completedOperations, failedOperations, "auto-rollback-started", initialDeploy);
|
|
38045
|
-
|
|
38363
|
+
const rollbackResult = await this.performRollback(completedOperations, newResources, stackName, currentState);
|
|
38364
|
+
autoRollbackClean = rollbackResult.failures === 0;
|
|
38365
|
+
rollbackOrphans = rollbackResult.orphaned;
|
|
38046
38366
|
}
|
|
38047
38367
|
try {
|
|
38048
38368
|
const postRollbackState = {
|
|
38049
|
-
version:
|
|
38369
|
+
version: 10,
|
|
38050
38370
|
region: this.stackRegion,
|
|
38051
38371
|
stackName: currentState.stackName,
|
|
38052
38372
|
resources: newResources,
|
|
38053
38373
|
outputs: currentState.outputs,
|
|
38054
38374
|
...exportNamesCarriedFrom(currentState),
|
|
38055
38375
|
...skippedOutputsCarriedFrom(currentState),
|
|
38376
|
+
...orphansAfterRollback(currentState, rollbackOrphans),
|
|
38056
38377
|
...crossStackReadsForPartialSave(currentState, this.recordedImports, this.recordedOutputReads),
|
|
38057
38378
|
lastModified: Date.now()
|
|
38058
38379
|
};
|
|
@@ -38064,13 +38385,14 @@ var DeployEngine = class DeployEngine {
|
|
|
38064
38385
|
try {
|
|
38065
38386
|
const freshEtag = (await this.stateBackend.getState(stackName, this.stackRegion))?.etag;
|
|
38066
38387
|
const postRollbackState = {
|
|
38067
|
-
version:
|
|
38388
|
+
version: 10,
|
|
38068
38389
|
region: this.stackRegion,
|
|
38069
38390
|
stackName: currentState.stackName,
|
|
38070
38391
|
resources: newResources,
|
|
38071
38392
|
outputs: currentState.outputs,
|
|
38072
38393
|
...exportNamesCarriedFrom(currentState),
|
|
38073
38394
|
...skippedOutputsCarriedFrom(currentState),
|
|
38395
|
+
...orphansAfterRollback(currentState, rollbackOrphans),
|
|
38074
38396
|
...crossStackReadsForPartialSave(currentState, this.recordedImports, this.recordedOutputReads),
|
|
38075
38397
|
lastModified: Date.now()
|
|
38076
38398
|
};
|
|
@@ -38096,10 +38418,11 @@ var DeployEngine = class DeployEngine {
|
|
|
38096
38418
|
}
|
|
38097
38419
|
return {
|
|
38098
38420
|
state: {
|
|
38099
|
-
version:
|
|
38421
|
+
version: 10,
|
|
38100
38422
|
region: this.stackRegion,
|
|
38101
38423
|
stackName: currentState.stackName,
|
|
38102
38424
|
resources: newResources,
|
|
38425
|
+
...orphansCarriedFrom(currentState),
|
|
38103
38426
|
outputs,
|
|
38104
38427
|
exportNames: [...this.resolvedExportNames],
|
|
38105
38428
|
...this.skippedOutputs && { skippedOutputs: { ...this.skippedOutputs } },
|
|
@@ -38135,13 +38458,14 @@ var DeployEngine = class DeployEngine {
|
|
|
38135
38458
|
*/
|
|
38136
38459
|
async persistStateAfterOutputFailure(stackName, currentState, newResources, currentEtag, pendingMigration) {
|
|
38137
38460
|
const buildState = () => ({
|
|
38138
|
-
version:
|
|
38461
|
+
version: 10,
|
|
38139
38462
|
region: this.stackRegion,
|
|
38140
38463
|
stackName: currentState.stackName,
|
|
38141
38464
|
resources: newResources,
|
|
38142
38465
|
outputs: currentState.outputs,
|
|
38143
38466
|
...exportNamesCarriedFrom(currentState),
|
|
38144
38467
|
...skippedOutputsCarriedFrom(currentState),
|
|
38468
|
+
...orphansCarriedFrom(currentState),
|
|
38145
38469
|
...crossStackReadsForPartialSave(currentState, this.recordedImports, this.recordedOutputReads),
|
|
38146
38470
|
lastModified: Date.now()
|
|
38147
38471
|
});
|
|
@@ -38169,11 +38493,60 @@ var DeployEngine = class DeployEngine {
|
|
|
38169
38493
|
* command drives identical semantics). Thin wrapper that builds the
|
|
38170
38494
|
* executor context from the engine's collaborators and delegates.
|
|
38171
38495
|
*/
|
|
38496
|
+
/**
|
|
38497
|
+
* Re-adopt what a previous rollback left in AWS, before the diff runs
|
|
38498
|
+
* (issue #2934).
|
|
38499
|
+
*
|
|
38500
|
+
* MUTATES `currentState`: adopted records go into `resources` so the diff
|
|
38501
|
+
* sees them, and `orphans` is replaced by the surviving set so every save
|
|
38502
|
+
* on every path below persists the same object. Mutation rather than a
|
|
38503
|
+
* returned copy because `currentState` is read by ~a dozen later sites and
|
|
38504
|
+
* threading a second binding through all of them is how one gets missed.
|
|
38505
|
+
*
|
|
38506
|
+
* Refusals THROW. A record whose name this deploy is about to request, that
|
|
38507
|
+
* cdkd cannot vouch for, is exactly the go-to-k/cdkd#2916 situation: letting
|
|
38508
|
+
* the deploy run would collide and roll back anyway, adding another orphan
|
|
38509
|
+
* on the way.
|
|
38510
|
+
*/
|
|
38511
|
+
async adoptRollbackOrphans(currentState, effectiveTemplate) {
|
|
38512
|
+
const records = currentState.orphans ?? [];
|
|
38513
|
+
const plan = await planOrphanAdoption({
|
|
38514
|
+
records,
|
|
38515
|
+
managedLogicalIds: new Set(Object.keys(currentState.resources)),
|
|
38516
|
+
template: effectiveTemplate,
|
|
38517
|
+
stackName: currentState.stackName,
|
|
38518
|
+
region: this.stackRegion,
|
|
38519
|
+
getProvider: (type, provisionedBy) => this.providerRegistry.getProviderFor({
|
|
38520
|
+
resourceType: type,
|
|
38521
|
+
...provisionedBy !== void 0 && { provisionedBy }
|
|
38522
|
+
}).provider,
|
|
38523
|
+
nameProperties: (type) => {
|
|
38524
|
+
const property = explicitNamePropertyFor(type);
|
|
38525
|
+
return property === void 0 ? [] : [property];
|
|
38526
|
+
},
|
|
38527
|
+
readSiblingClaims: makeSiblingClaimReader({
|
|
38528
|
+
stateBackend: this.stateBackend,
|
|
38529
|
+
selfStackName: currentState.stackName,
|
|
38530
|
+
selfRegion: this.stackRegion,
|
|
38531
|
+
logger: this.logger
|
|
38532
|
+
}),
|
|
38533
|
+
logger: { debug: (m) => this.logger.debug(m) }
|
|
38534
|
+
});
|
|
38535
|
+
for (const notice of plan.notices) this.logger.info(notice);
|
|
38536
|
+
if (plan.refusals.length > 0) throw new Error(`Deploy refused — cdkd left ${plan.refusals.length} resource(s) in AWS that it cannot safely re-adopt:\n ${plan.refusals.join("\n ")}`);
|
|
38537
|
+
for (const [logicalId, record] of Object.entries(plan.adopted)) {
|
|
38538
|
+
currentState.resources[logicalId] = record;
|
|
38539
|
+
this.logger.info(`Adopting ${logicalId} (${record.resourceType}) left in AWS by an earlier rollback as ${record.physicalId}`);
|
|
38540
|
+
}
|
|
38541
|
+
if (records.length > 0) currentState.orphans = plan.remaining;
|
|
38542
|
+
return plan;
|
|
38543
|
+
}
|
|
38172
38544
|
async performRollback(completedOperations, stateResources, stackName, previousState) {
|
|
38173
38545
|
const result = await replayRollback(completedOperations, stateResources, stackName, this.rollbackExecutorContext(previousState));
|
|
38174
38546
|
return {
|
|
38175
38547
|
failures: result.failures,
|
|
38176
|
-
warnings: result.warnings
|
|
38548
|
+
warnings: result.warnings,
|
|
38549
|
+
orphaned: result.orphaned
|
|
38177
38550
|
};
|
|
38178
38551
|
}
|
|
38179
38552
|
/**
|
|
@@ -38556,6 +38929,7 @@ var DeployEngine = class DeployEngine {
|
|
|
38556
38929
|
const resolvedProps = await this.resolver.resolve(desiredProps, context);
|
|
38557
38930
|
this.refuseRedactedAttributeReads(logicalId, resourceType, context);
|
|
38558
38931
|
this.perResourceTemplateProps.set(logicalId, desiredProps);
|
|
38932
|
+
this.perResourceResolvedType.set(logicalId, resourceType);
|
|
38559
38933
|
const createSecrets = context.recordedSecretValues ?? /* @__PURE__ */ new Map();
|
|
38560
38934
|
recordNestedStackParameterExpressions(createSecrets, resourceType, resolvedProps, desiredProps);
|
|
38561
38935
|
this.auditResolvedAssetReferences(logicalId, resourceType, resolvedProps);
|
|
@@ -38606,6 +38980,7 @@ var DeployEngine = class DeployEngine {
|
|
|
38606
38980
|
const resolvedProps = await this.resolver.resolve(desiredProps, context);
|
|
38607
38981
|
this.refuseRedactedAttributeReads(logicalId, resourceType, context);
|
|
38608
38982
|
this.perResourceTemplateProps.set(logicalId, desiredProps);
|
|
38983
|
+
this.perResourceResolvedType.set(logicalId, resourceType);
|
|
38609
38984
|
recordNestedStackParameterExpressions(updateSecrets, resourceType, resolvedProps, desiredProps);
|
|
38610
38985
|
this.auditResolvedAssetReferences(logicalId, resourceType, resolvedProps);
|
|
38611
38986
|
this.attemptedResolvedProps.set(logicalId, resolvedProps);
|
|
@@ -39595,5 +39970,5 @@ var DeployEngine = class DeployEngine {
|
|
|
39595
39970
|
};
|
|
39596
39971
|
|
|
39597
39972
|
//#endregion
|
|
39598
|
-
export {
|
|
39599
|
-
//# sourceMappingURL=deploy-engine-
|
|
39973
|
+
export { collectInlinePolicyNamesManagedBySiblings as $, describeDockerCapturedOutput as $n, LocalInvokeBuildError as $r, DagBuilder as $t, isStatefulRecreateTargetForReplace as A, orphansAfterRollback as An, uploadCfnTemplate as Ar, coerceCfnBoolean as At, gray as B, AssetModeResolver as Bn, resolveBucketRegion as Br, withSharedDrainBudget as Bt, refusesFinalSnapshot as C, forceQuitRecoveryClause as Cn, resolveUseCdkBootstrapAssets as Cr, isUnboundTemplateParameter as Ct, makeSiblingClaimReader as D, exportNamesCarriedFrom as Dn, CFN_TEMPLATE_URL_LIMIT as Dr, normalizeAwsTagsToCfn as Dt, extractDeploymentEventError as E, DEFAULT_STATE_PREFIX as En, CFN_TEMPLATE_BODY_LIMIT as Er, WAFv2WebACLProvider as Et, isWarmThroughputDecrease as F, WorkGraph as Fn, canonicalizeRegion as Fr, requireConfigArray as Ft, collectPublishedOutputNames as G, isCrossRegionRedirect as Gn, AssetError as Gr, s3BucketWebsiteUrl as Gt, red as H, assertAssetBucketRegion as Hn, getAwsClients as Hr, s3BucketDomainName as Ht, toFiniteNumber as I, buildAssetRedirectMap as In, derivePartitionAndUrlSuffix as Ir, requireConfigObject as It, secretBearing as J, validateAssetBucketName as Jn, CrossAccountSecretRefusalError as Jr, INTRINSIC_KEYS as Jt, exportAliasCollisionScrubWarning as K, parseBootstrapMarker as Kn, CdkdError as Kr, applyRoleArnIfSet as Kt, formatResourceLine as L, createAssetRedirectResolver as Ln, AssemblyReader as Lr, requireConfigString as Lt, renderStatefulReason as M, shouldRetainResource as Mn, displaySafe as Mr, configStringRefusal as Mt, WARM_THROUGHPUT_MEMBERS as N, AssetPublisher as Nn, expectedOwnerParam as Nr, readConfigString as Nt, planOrphanAdoption as O, importableOutputKeys as On, MIGRATE_TMP_PREFIX as Or, resolveExplicitPhysicalId as Ot, coerceWarmThroughput as P, stringifyValue as Pn, PARTITION_TABLE as Pr, replayWarn as Pt, IAMRoleProvider as Q, buildDockerImage as Qn, IntrinsicResolutionRefusalError as Qr, withRetry as Qt, bold as R, loadPublishableAssetManifest as Rn, processStackMessages as Rr, classifyReplaySecretRegion as Rt, isFinalSnapshotError as S, __exportAll as Si, buildLockContentionMessage as Sn, resolveStateBucketWithDefaultAndSource as Sr, getAccountInfo as St, makeCanonicalizePropertiesFn as T, CUSTOM_RESOURCE_RESPONSE_PREFIX as Tn, warnDeprecatedNoPrefixCliFlag as Tr, refStateLookupFromResource as Tt, yellow as U, ensureAssetStorage as Un, resetAwsClients as Ur, s3BucketDualStackDomainName as Ut, green as V, BOOTSTRAP_MARKER_PREFIX as Vn, AwsClients as Vr, s3BucketArn as Vt, collectDeclaredOutputNames as W, getBootstrapMarkerKey as Wn, setAwsClients as Wr, s3BucketRegionalDomainName as Wt, secretSafeKeyDisplay as X, buildDenyExternalAccessPolicy as Xn, DeployCancelledError as Xr, findSilentDropProperties as Xt, secretBearingStateKeyWarning as Y, validateContainerRepoName as Yn, DependencyError as Yr, findActionableSilentDrops as Yt, getCurrentResourceSecrets as Z, describeAwsFailure as Zn, DynamicReferenceRegionAmbiguousError as Zr, describeTypeWithThrottleRetry as Zt, ATOMIC_FINAL_SNAPSHOT_TYPES as _, isThrottlingError as _i, LockManager as _n, resolveApp as _r, isTerminationProtectionPropagationError as _t, DeploymentEventsStore as a, ResourceTimeoutError as ai, carriesSecretMask as an, partitionSensitiveEnv as ar, maskerOrIdentity as at, ccRoutedFinalSnapshotError as b, markRedactedCause as bi, UNRENDERABLE as bn, resolveSkipPrefix as br, cfnRefValueFromPhysicalId as bt, replayFailedOperations as c, StackTerminationProtectionError as ci, errorCauseChain as cn, runDockerStreaming as cr, interruptWatchListenerCount as ct, updatePartialReason as d, formatError as di, maskSecretsInError as dn, AssetManifestLoader as dr, CloudControlProvider as dt, LocalStartServiceError as ei, TemplateParser as en, describeDockerExecFailure as er, clearOnUpdateRemoval as et, withResourceDeadline as f, isCdkdError as fi, maskSecretsInText as fn, getDockerImageBySourceHash as fr, slowCcOperationTimeoutMs as ft, bindingSkippedOutputs as g, isRetryableTransientError as gi, scrubResourceRecord as gn, getLegacyStateBucketName as gr, disableInstanceApiTermination as gt, computeImplicitDeleteEdges as h, isMarkedNonRetryable as hi, redactSecretsForState as hn, getDefaultStateBucketName as hr, deleteSkipReason as ht, DeploymentEventsReader as i, ProvisioningError as ii, TEMPLATE_SOURCED_RULES as in, getDockerCmd as ir, maskDeep as it, isStatefulRecreateTargetSync as j, orphansCarriedFrom as jn, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as jr, configBooleanRefusal as jt, MULTI_REGION_RECREATE_BLOCKED_TYPES as k, importableOutputs as kn, findLargeInlineResources as kr, assertRegionMatch as kt, replayRollback as l, StateError as li, identityKeyFor as ln, escapeRegExp$1 as lr, isInterruptedWaitError as lt, IMPLICIT_DELETE_DEPENDENCIES as m, withErrorHandling as mi, recoverMaskedOutput as mn, synthesisStatusMessage as mr, deleteIndeterminateGuards as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, NestedStackChildDirectDestroyError as ni, STATE_SOURCED_CROSS_GENERATION_RULES as nn, dockerSpawnEnvWithSensitive as nr, wouldReturnToSdkProvider as nt, planFailedOps as o, ResourceUpdateNotSupportedError as oi, createSecretMasker as on, redactDockerArgvValues as or, beginCommandInterruptScope as ot, maskingRetryLogger as p, normalizeAwsError as pi, recordMaskOnlyValue as pn, Synthesizer as pr, UNSPECIFIED_SKIP_REASON as pt, isExportAliasCollision as q, readBootstrapMarkerBody as qn, ConfigError as qr, DiffCalculator as qt, DeployEngine as r, PartialFailureError as ri, STATE_SOURCED_READBACK_RULES as rn, formatDockerLoginError as rr, createMaskedRetryLogger as rt, planRollback as s, StackHasActiveImportsError as si, dynamicReferenceTokens as sn, runDockerForeground as sr, endCommandInterruptScope as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, LockError as ti, STATE_SOURCED_BASELINE_RULES as tn, describeDockerFailure as tr, ProviderRegistry as tt, updatePartialMessage as u, SynthesisError as ui, isSingleDynamicReferenceToken as un, stripControlChars as ur, startInterruptWatch as ut, PRE_DELETE_SNAPSHOT_TYPES as v, isTransientServerError as vi, S3StateBackend as vn, resolveAutoAssetStorage as vr, IntrinsicFunctionResolver as vt, unsupportedFinalSnapshotError as w, shellQuote as wn, stateBucketExistenceConfirmed as wr, parameterTypeMayLoseSecretIdentity as wt, createPreDeleteFinalSnapshot as x, retryClassificationText as xi, buildForceUnlockCommand as xn, resolveStateBucketWithDefault as xr, coerceParameterTypedValue as xt, buildFinalSnapshotIdentifier as y, markNonRetryable as yi, rebuildClientForBucketRegion as yn, resolveCaptureObservedState as yr, carriesDynamicReference as yt, cyan as z, rewriteTemplateAssetReferences as zn, clearBucketRegionCache as zr, producerRegionsFromState as zt };
|
|
39974
|
+
//# sourceMappingURL=deploy-engine-B6pEfNd9.js.map
|