@go-to-k/cdkd 0.296.28 → 0.296.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{asg-provider-B3zcS87C.js → asg-provider-BaabA371.js} +3 -3
- package/dist/{asg-provider-B3zcS87C.js.map → asg-provider-BaabA371.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-D4FdwPCi.js → deploy-engine-BuPc2Dfc.js} +1951 -60
- package/dist/deploy-engine-BuPc2Dfc.js.map +1 -0
- package/dist/{iam-policy-targets-B61e5_Sk.js → iam-policy-targets-Cz2tPxXk.js} +2 -2
- package/dist/{iam-policy-targets-B61e5_Sk.js.map → iam-policy-targets-Cz2tPxXk.js.map} +1 -1
- package/dist/{iam-role-provider-0U4kMuzh.js → iam-role-provider-CAiMUBcP.js} +3 -3
- package/dist/{iam-role-provider-0U4kMuzh.js.map → iam-role-provider-CAiMUBcP.js.map} +1 -1
- package/dist/index.d.ts +121 -55
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/{neptune-provider-DF6Eh4Tl.js → neptune-provider-5yc_bOJ-.js} +3 -3
- package/dist/{neptune-provider-DF6Eh4Tl.js.map → neptune-provider-5yc_bOJ-.js.map} +1 -1
- package/dist/{program-v_QNBjtZ.js → program-CIxuz2zX.js} +557 -41
- package/dist/program-CIxuz2zX.js.map +1 -0
- package/dist/{provider-classes-wbKBBEol.js → provider-classes-DmZ0J68L.js} +216 -26
- package/dist/provider-classes-DmZ0J68L.js.map +1 -0
- package/dist/{rds-provider-BfQsw4hw.js → rds-provider-IBY6fDvN.js} +3 -3
- package/dist/{rds-provider-BfQsw4hw.js.map → rds-provider-IBY6fDvN.js.map} +1 -1
- package/dist/{version-B0JEGOLV.js → version-B__YmKBy.js} +2 -2
- package/dist/{version-B0JEGOLV.js.map → version-B__YmKBy.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-D4FdwPCi.js.map +0 -1
- package/dist/program-v_QNBjtZ.js.map +0 -1
- package/dist/provider-classes-wbKBBEol.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { r as getAwsClients, s as __exportAll } from "./aws-clients-CnmtbrxD.js";
|
|
2
2
|
import { A as UNRENDERABLE, C as looksLikeCdkdGeneratedName, E as withoutGeneratedFallbackName, F as displayStackName, I as isPasteableIdent, M as displayAwsMessage, N as displayIdent, O as SECRET_REF_MAX_CODE_POINTS, P as displaySafe, R as plainIdentOr, S as getCurrentStackName, T as withStackName, V as truncateCodePoints, a as getLogger, b as generateResourceNameWithFallback, d as installLogLineMaskerSource, f as getLiveRenderer, g as applyDefaultNameForFallback, k as STACK_REF_MAX_CODE_POINTS, o as isStdoutReservedForPayload, r as safeStringify$1, t as describeAwsFailure, u as currentLogLineMasker, v as explicitNamePropertyFor, w as withSkipPrefix, x as getCurrentSkipPrefix, y as generateResourceName, z as safeMsg } from "./aws-failure-text-BaWeCWRz.js";
|
|
3
|
-
import { $ as protectedReplacementAdvice, $n as
|
|
3
|
+
import { $ as protectedReplacementAdvice, $n as isAmbiguousOutcomeError, $t as credentialFingerprint, A as isQuotableImportValue, An as UNSHOWABLE_VALUE, At as isAuxiliaryMarkOf, B as isTerminalDeleteFailure, Bn as withheldTargetClause, Bt as recoveryCommandFlags, Ct as RETRY_AUXILIARY_OWNER, Dt as createdResourceIdentityBeforeFailure, E as assertRegionMatch, Et as createdBeforeFailure, F as resolveExplicitPhysicalId, Fn as physicalIdShownBesideCommand, G as runDeleteAttempt, Gn as isNameCollisionErrorFrom, H as observeThenDisableProtection, Ht as CUSTOM_RESOURCE_RESPONSE_PREFIX, In as plainOrDescribed, J as DELETION_PROTECTION_DOC_POINTER, Jn as isReplayedNameCollisionFrom, Kn as isNameCooldownError, L as ProtectionFlipRegistry, Ln as quotedOrDescribed, Lt as formatLockExpiry, M as quotedRemedyLogicalId, Mt as markCreatedBeforeFailure, N as refusalTypeShown, Nn as isInertUnquoted, Nt as UNREPRODUCIBLE_LOCK_CLAUSE, On as SHELL_ACTIVE, Ot as hasCreatedBeforeFailure, Pn as pasteableCommand, Pt as buildForceUnlockCommand, Q as pasteableAwsCommand, Qn as isAmbiguousCcHandlerErrorCode, Qt as clientDefaultsFor, R as compensateProtectionFlip, Rn as shellBoundedDisplay, Rt as formatLockOperation, Sn as SynthesisError, St as withDerivedNameMasks, T as RESOURCE_NOT_FOUND, Tn as normalizeAwsError, U as protectionFlipKey, Un as isUpdateUnsupportedError, Ut as DEFAULT_STATE_PREFIX, Vt as sanitizeRecoveryValue, Wn as hasReplayMayCollide, Wt as injectiveKey, Xn as markReplayMayCollide, Xt as ambientClientDefaults, Yt as withPasteableAwsProfile, Zn as formatRetryClassificationSignals, Zt as ambientCredentialConfig, _n as ResourceTimeoutError, _t as createMaskedLogSinks, an as DependencyError, ar as isMarkedNonRetryable, at as startInterruptWatch, bt as maskDeep, cn as IntrinsicResolutionRefusalError, cr as markRedactedCause, ct as canonicalizeRoute53QueryName, d as prepareRemovalForUpdate, dn as LockError, en as AccountIdUnavailableError, er as isIamPropagationError, f as removalWarning, fn as MacroExpansionError, ft as logicalIdShown, gn as ProvisioningError, gt as stateOrphanRecordRemedy, ht as segmentAfterAnchor, i as holdsSecretDerivedEntry, in as CrossAccountSecretRefusalError, it as isInterruptedWaitError, j as normalizeAwsTagsToCfn, jn as commandHole, jt as markAuxiliaryFailure, kn as SHELL_ACTIVE_WHY, kt as isAuxiliaryFailure, l as withUnchangedSecretPrincipalLists, lr as wrapMaskedAwsError, m as withoutInjectedRemovals, mt as resourceTypeShown, nn as CdkdError, nr as isTransientServerError, or as isThrottlingError, ot as ElapsedBudgetRegistry, pn as MalformedProducerRecordRefusalError, qn as isRecreateRetryableError, qt as producerRecordKey, rr as retryClassificationText, sn as DynamicReferenceRegionAmbiguousError, sr as markNonRetryable, st as monotonicNowMs, tn as AssetError, tr as isRetryableTransientError, ut as compositeIdSeparatorRefusal, vn as ResourceUpdateNotSupportedError, vt as createMaskedRetryLogger, w as withoutServerErrorRetries, wt as carryCreatedBeforeFailure, xn as StateError, xt as maskerOrIdentity, yt as isSecretDerivedValue, z as deleteWithProtectionCompensation, zn as shellQuote, zt as formatLockOwner } from "./iam-policy-targets-Cz2tPxXk.js";
|
|
4
4
|
import { a as setAssumedRoleCredentials, i as getPreAssumeEnvCredentials, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, s as ambientRegion, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-D0q0z4dG.js";
|
|
5
|
-
import { t as getCdkdVersion } from "./version-
|
|
5
|
+
import { t as getCdkdVersion } from "./version-B__YmKBy.js";
|
|
6
6
|
import { createHash, randomUUID } from "node:crypto";
|
|
7
7
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
8
8
|
import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetBucketReplicationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -4937,14 +4937,16 @@ function parseCreateTokenLedger(body) {
|
|
|
4937
4937
|
for (const [logicalId, entry] of Object.entries(rawSent)) if (isSent(entry)) sent[logicalId] = {
|
|
4938
4938
|
base: entry.base,
|
|
4939
4939
|
token: entry.token,
|
|
4940
|
-
firstSentAt: entry.firstSentAt
|
|
4940
|
+
firstSentAt: entry.firstSentAt,
|
|
4941
|
+
...typeof entry.failedAt === "number" && Number.isFinite(entry.failedAt) && { failedAt: entry.failedAt }
|
|
4941
4942
|
};
|
|
4942
4943
|
}
|
|
4943
4944
|
return {
|
|
4944
4945
|
ledgerVersion: 1,
|
|
4945
4946
|
nonce: doc["nonce"],
|
|
4946
4947
|
sent,
|
|
4947
|
-
...doc["stateRecorded"] === true && { stateRecorded: true }
|
|
4948
|
+
...doc["stateRecorded"] === true && { stateRecorded: true },
|
|
4949
|
+
...typeof doc["abandonedAt"] === "number" && Number.isFinite(doc["abandonedAt"]) && { abandonedAt: doc["abandonedAt"] }
|
|
4948
4950
|
};
|
|
4949
4951
|
}
|
|
4950
4952
|
const stateSavedObserver = new AsyncLocalStorage();
|
|
@@ -4955,6 +4957,409 @@ async function notifyStateSaved(stackName, region) {
|
|
|
4955
4957
|
await stateSavedObserver.getStore()?.(stackName, region);
|
|
4956
4958
|
}
|
|
4957
4959
|
|
|
4960
|
+
//#endregion
|
|
4961
|
+
//#region src/state/cross-prefix-stack-scan.ts
|
|
4962
|
+
function journalCanOwnResources(journal) {
|
|
4963
|
+
return recordCanOwnResources({ resources: {} }, journal);
|
|
4964
|
+
}
|
|
4965
|
+
function recordCanOwnResources(record, journal) {
|
|
4966
|
+
const resources = record.resources;
|
|
4967
|
+
if (resources === null || typeof resources !== "object" || Array.isArray(resources)) return true;
|
|
4968
|
+
if (Object.keys(resources).length > 0) return true;
|
|
4969
|
+
const orphans = record.orphans;
|
|
4970
|
+
if (orphans !== void 0 && (!Array.isArray(orphans) || orphans.length > 0)) return true;
|
|
4971
|
+
if (journal === null) return false;
|
|
4972
|
+
const segments = journal.segments;
|
|
4973
|
+
if (!Array.isArray(segments)) return true;
|
|
4974
|
+
return segments.some((seg) => {
|
|
4975
|
+
if (seg === null || typeof seg !== "object" || Array.isArray(seg)) return true;
|
|
4976
|
+
const ops = seg.operations;
|
|
4977
|
+
if (ops !== void 0 && (!Array.isArray(ops) || ops.length > 0)) return true;
|
|
4978
|
+
const failed = seg.failedOperations;
|
|
4979
|
+
if (failed === void 0) return false;
|
|
4980
|
+
if (!Array.isArray(failed)) return true;
|
|
4981
|
+
return failed.some((op) => op === null || typeof op !== "object" || op.physicalId !== void 0);
|
|
4982
|
+
});
|
|
4983
|
+
}
|
|
4984
|
+
var CrossPrefixReadError = class extends Error {
|
|
4985
|
+
key;
|
|
4986
|
+
cause;
|
|
4987
|
+
constructor(key, cause) {
|
|
4988
|
+
super(`reading ${displayIdent(key, { maxCodePoints: 1024 })} failed (${errorName$1(cause, "an unknown error")})`);
|
|
4989
|
+
this.name = "CrossPrefixReadError";
|
|
4990
|
+
this.key = key;
|
|
4991
|
+
this.cause = cause;
|
|
4992
|
+
}
|
|
4993
|
+
};
|
|
4994
|
+
const PROBE_CONCURRENCY = 50;
|
|
4995
|
+
function unwrapCause(error) {
|
|
4996
|
+
return error instanceof CrossPrefixReadError ? error.cause : error;
|
|
4997
|
+
}
|
|
4998
|
+
function isNotImplemented(error) {
|
|
4999
|
+
const name = error?.name;
|
|
5000
|
+
const status = error?.$metadata?.httpStatusCode;
|
|
5001
|
+
return name === "NotImplemented" || status === 501;
|
|
5002
|
+
}
|
|
5003
|
+
function isAccessDenied(error) {
|
|
5004
|
+
let current = error;
|
|
5005
|
+
for (let depth = 0; depth < 5 && current !== null && typeof current === "object"; depth++) {
|
|
5006
|
+
const name = current.name;
|
|
5007
|
+
if (name === "AccessDenied" || name === "Forbidden" || name === "AllAccessDisabled") return true;
|
|
5008
|
+
if (current.$metadata?.httpStatusCode === 403) return true;
|
|
5009
|
+
current = current.cause;
|
|
5010
|
+
}
|
|
5011
|
+
return false;
|
|
5012
|
+
}
|
|
5013
|
+
function candidatePrefixPasses(segments, own) {
|
|
5014
|
+
const seen = /* @__PURE__ */ new Set([own]);
|
|
5015
|
+
const pass = (names) => {
|
|
5016
|
+
const out = [];
|
|
5017
|
+
for (const name of names) {
|
|
5018
|
+
if (seen.has(name)) continue;
|
|
5019
|
+
seen.add(name);
|
|
5020
|
+
out.push(name);
|
|
5021
|
+
}
|
|
5022
|
+
return out;
|
|
5023
|
+
};
|
|
5024
|
+
return [pass(segments), pass(segments.map((p) => `${p}/`))];
|
|
5025
|
+
}
|
|
5026
|
+
async function scanOtherPrefixesForStack(target, stackName, region) {
|
|
5027
|
+
let segments;
|
|
5028
|
+
try {
|
|
5029
|
+
segments = await target.listTopLevelPrefixes();
|
|
5030
|
+
} catch (error) {
|
|
5031
|
+
return isAccessDenied(error) ? {
|
|
5032
|
+
kind: "denied",
|
|
5033
|
+
error,
|
|
5034
|
+
stage: "list"
|
|
5035
|
+
} : {
|
|
5036
|
+
kind: "failed",
|
|
5037
|
+
error
|
|
5038
|
+
};
|
|
5039
|
+
}
|
|
5040
|
+
const found = [];
|
|
5041
|
+
const stale = [];
|
|
5042
|
+
let denied;
|
|
5043
|
+
let failed;
|
|
5044
|
+
const probeAll = async (candidates) => {
|
|
5045
|
+
let next = 0;
|
|
5046
|
+
const worker = async () => {
|
|
5047
|
+
while (found.length === 0 && next < candidates.length) {
|
|
5048
|
+
const prefix = candidates[next++];
|
|
5049
|
+
try {
|
|
5050
|
+
const held = await target.recordUnderPrefix(prefix, stackName, region);
|
|
5051
|
+
if (held === "holder") found.push(prefix);
|
|
5052
|
+
else if (held === "empty") stale.push(prefix);
|
|
5053
|
+
} catch (error) {
|
|
5054
|
+
if (isAccessDenied(error)) denied ??= error;
|
|
5055
|
+
else failed ??= error;
|
|
5056
|
+
}
|
|
5057
|
+
}
|
|
5058
|
+
};
|
|
5059
|
+
await Promise.all(Array.from({ length: Math.min(50, candidates.length) }, () => worker()));
|
|
5060
|
+
};
|
|
5061
|
+
const [segmentsThemselves, twins] = candidatePrefixPasses(segments, target.prefix);
|
|
5062
|
+
await probeAll([...segmentsThemselves, ...twins]);
|
|
5063
|
+
const extra = stale.length > 0 ? { stale } : {};
|
|
5064
|
+
if (found.length > 0) return {
|
|
5065
|
+
kind: "found",
|
|
5066
|
+
prefixes: found,
|
|
5067
|
+
...extra
|
|
5068
|
+
};
|
|
5069
|
+
if (failed !== void 0) return {
|
|
5070
|
+
kind: "failed",
|
|
5071
|
+
error: failed,
|
|
5072
|
+
...extra
|
|
5073
|
+
};
|
|
5074
|
+
if (denied !== void 0) return {
|
|
5075
|
+
kind: "denied",
|
|
5076
|
+
error: denied,
|
|
5077
|
+
stage: "probe",
|
|
5078
|
+
...extra
|
|
5079
|
+
};
|
|
5080
|
+
return {
|
|
5081
|
+
kind: "clear",
|
|
5082
|
+
...extra
|
|
5083
|
+
};
|
|
5084
|
+
}
|
|
5085
|
+
function withSharedListing(target) {
|
|
5086
|
+
let listing;
|
|
5087
|
+
let inFlight = 0;
|
|
5088
|
+
let seq = 0;
|
|
5089
|
+
let prestarted = 0;
|
|
5090
|
+
const keyOf = (stackName, region) => JSON.stringify([stackName, region]);
|
|
5091
|
+
const ranks = /* @__PURE__ */ new Map();
|
|
5092
|
+
const rankOf = (stackName, region) => ranks.get(keyOf(stackName, region)) ?? Number.MAX_SAFE_INTEGER;
|
|
5093
|
+
const heap = [];
|
|
5094
|
+
const before = (a, b) => a.rank < b.rank || a.rank === b.rank && a.seq < b.seq;
|
|
5095
|
+
const swap = (i, j) => {
|
|
5096
|
+
const t = heap[i];
|
|
5097
|
+
heap[i] = heap[j];
|
|
5098
|
+
heap[j] = t;
|
|
5099
|
+
};
|
|
5100
|
+
const siftUp = (i) => {
|
|
5101
|
+
while (i > 0) {
|
|
5102
|
+
const parent = i - 1 >> 1;
|
|
5103
|
+
if (!before(heap[i], heap[parent])) return;
|
|
5104
|
+
swap(i, parent);
|
|
5105
|
+
i = parent;
|
|
5106
|
+
}
|
|
5107
|
+
};
|
|
5108
|
+
const siftDown = (i) => {
|
|
5109
|
+
for (;;) {
|
|
5110
|
+
const l = 2 * i + 1;
|
|
5111
|
+
const r = l + 1;
|
|
5112
|
+
let best = i;
|
|
5113
|
+
if (l < heap.length && before(heap[l], heap[best])) best = l;
|
|
5114
|
+
if (r < heap.length && before(heap[r], heap[best])) best = r;
|
|
5115
|
+
if (best === i) return;
|
|
5116
|
+
swap(i, best);
|
|
5117
|
+
i = best;
|
|
5118
|
+
}
|
|
5119
|
+
};
|
|
5120
|
+
const popBest = () => {
|
|
5121
|
+
const top = heap[0];
|
|
5122
|
+
const last = heap.pop();
|
|
5123
|
+
if (heap.length > 0) {
|
|
5124
|
+
heap[0] = last;
|
|
5125
|
+
siftDown(0);
|
|
5126
|
+
}
|
|
5127
|
+
return top;
|
|
5128
|
+
};
|
|
5129
|
+
const admitBest = () => {
|
|
5130
|
+
while (inFlight < 50 && heap.length > 0) {
|
|
5131
|
+
inFlight++;
|
|
5132
|
+
popBest().resolve();
|
|
5133
|
+
}
|
|
5134
|
+
};
|
|
5135
|
+
const acquire = async (stackName, region) => {
|
|
5136
|
+
const rank = rankOf(stackName, region);
|
|
5137
|
+
if (inFlight < 50 && (heap.length === 0 || heap[0].rank >= rank)) {
|
|
5138
|
+
inFlight++;
|
|
5139
|
+
return;
|
|
5140
|
+
}
|
|
5141
|
+
const key = keyOf(stackName, region);
|
|
5142
|
+
await new Promise((resolve) => {
|
|
5143
|
+
heap.push({
|
|
5144
|
+
key,
|
|
5145
|
+
rank,
|
|
5146
|
+
seq: seq++,
|
|
5147
|
+
resolve
|
|
5148
|
+
});
|
|
5149
|
+
siftUp(heap.length - 1);
|
|
5150
|
+
});
|
|
5151
|
+
};
|
|
5152
|
+
const release = () => {
|
|
5153
|
+
inFlight--;
|
|
5154
|
+
admitBest();
|
|
5155
|
+
};
|
|
5156
|
+
return {
|
|
5157
|
+
prefix: target.prefix,
|
|
5158
|
+
rank: (stackName, region, when) => {
|
|
5159
|
+
const key = keyOf(stackName, region);
|
|
5160
|
+
if (when === "prestart") {
|
|
5161
|
+
if (!ranks.has(key)) ranks.set(key, prestarted++);
|
|
5162
|
+
return;
|
|
5163
|
+
}
|
|
5164
|
+
if (ranks.get(key) === -1) return;
|
|
5165
|
+
ranks.set(key, -1);
|
|
5166
|
+
let moved = false;
|
|
5167
|
+
for (const w of heap) if (w.key === key) {
|
|
5168
|
+
w.rank = -1;
|
|
5169
|
+
moved = true;
|
|
5170
|
+
}
|
|
5171
|
+
if (moved) for (let i = (heap.length >> 1) - 1; i >= 0; i--) siftDown(i);
|
|
5172
|
+
},
|
|
5173
|
+
listTopLevelPrefixes: () => listing ??= target.listTopLevelPrefixes(),
|
|
5174
|
+
recordUnderPrefix: async (prefix, stackName, region) => {
|
|
5175
|
+
await acquire(stackName, region);
|
|
5176
|
+
try {
|
|
5177
|
+
return await target.recordUnderPrefix(prefix, stackName, region);
|
|
5178
|
+
} finally {
|
|
5179
|
+
release();
|
|
5180
|
+
}
|
|
5181
|
+
}
|
|
5182
|
+
};
|
|
5183
|
+
}
|
|
5184
|
+
var CrossPrefixScanCache = class {
|
|
5185
|
+
target;
|
|
5186
|
+
scans = /* @__PURE__ */ new Map();
|
|
5187
|
+
constructor(target) {
|
|
5188
|
+
this.target = withSharedListing(target);
|
|
5189
|
+
}
|
|
5190
|
+
full(stackName, region, when = "now") {
|
|
5191
|
+
this.target.rank(stackName, region, when);
|
|
5192
|
+
const key = JSON.stringify([stackName, region]);
|
|
5193
|
+
let scan = this.scans.get(key);
|
|
5194
|
+
if (scan === void 0) {
|
|
5195
|
+
scan = scanOtherPrefixesForStack(this.target, stackName, region);
|
|
5196
|
+
this.scans.set(key, scan);
|
|
5197
|
+
}
|
|
5198
|
+
return scan;
|
|
5199
|
+
}
|
|
5200
|
+
};
|
|
5201
|
+
const STACK_UNDER_OTHER_PREFIX = "STACK_UNDER_OTHER_STATE_PREFIX";
|
|
5202
|
+
const UNSUPPORTED_SENTENCE = "A stack name is unique per account and region, as in CloudFormation: deploying one stack name under two state prefixes is unsupported, because both deployments use the same cdkd-generated resource names, and a failed deploy, a destroy, or a deploy that deletes or replaces of either one deletes resources the other's record also names.";
|
|
5203
|
+
function subjectText(s) {
|
|
5204
|
+
return `${displayStackName(s.stackName)} (${displayIdent(s.region)})`;
|
|
5205
|
+
}
|
|
5206
|
+
const LISTED_PREFIXES = 5;
|
|
5207
|
+
function prefixesText(prefixes) {
|
|
5208
|
+
const shown = prefixes.slice(0, LISTED_PREFIXES).map((p) => displayIdent(p, { listMember: true })).join(", ");
|
|
5209
|
+
const more = prefixes.length - LISTED_PREFIXES;
|
|
5210
|
+
return more > 0 ? `${shown} and ${more} more` : shown;
|
|
5211
|
+
}
|
|
5212
|
+
function stateCommand(verb, s, prefix) {
|
|
5213
|
+
const account = recoveryCommandFlags({
|
|
5214
|
+
profile: s.recovery?.profile,
|
|
5215
|
+
stateBucket: s.recovery?.stateBucket ?? s.bucket
|
|
5216
|
+
});
|
|
5217
|
+
return pasteableCommand(`cdkd state ${verb}`, [
|
|
5218
|
+
{
|
|
5219
|
+
value: s.stackName,
|
|
5220
|
+
hole: "stack"
|
|
5221
|
+
},
|
|
5222
|
+
{
|
|
5223
|
+
flag: "--stack-region",
|
|
5224
|
+
value: s.region,
|
|
5225
|
+
hole: "region"
|
|
5226
|
+
},
|
|
5227
|
+
{
|
|
5228
|
+
flag: "--state-prefix",
|
|
5229
|
+
value: prefix,
|
|
5230
|
+
hole: "prefix"
|
|
5231
|
+
}
|
|
5232
|
+
], account.flags).command;
|
|
5233
|
+
}
|
|
5234
|
+
function releaseRemedy(s, prefixes, destroyToo) {
|
|
5235
|
+
const first = prefixes[0];
|
|
5236
|
+
if (prefixes.length === 1 && destroyToo) return `\`${stateCommand("destroy", s, first)}\` deletes its resources and its record, \`${stateCommand("orphan", s, first)}\` drops only its record.`;
|
|
5237
|
+
if (prefixes.length === 1) return `drop the other record with \`cdkd state orphan\` (it removes only the record, never a resource): \`${stateCommand("orphan", s, first)}\`.`;
|
|
5238
|
+
return `release each other record with \`cdkd state orphan\` (it removes only the record, never a resource; a destroy under one of them is refused while another still records the stack, and the check stops at the first ones it finds, so re-run it until none is named), e.g. \`${stateCommand("orphan", s, first)}\`, then the same with each other prefix.`;
|
|
5239
|
+
}
|
|
5240
|
+
const VERB = {
|
|
5241
|
+
deploy: "deploy",
|
|
5242
|
+
destroy: "destroy",
|
|
5243
|
+
rollback: "roll back",
|
|
5244
|
+
"deploy-destructive": "deploy",
|
|
5245
|
+
"deploy-late-replace": "replace a resource of"
|
|
5246
|
+
};
|
|
5247
|
+
const NOTHING = {
|
|
5248
|
+
deploy: "No resource of this stack was created.",
|
|
5249
|
+
destroy: "Nothing was deleted.",
|
|
5250
|
+
rollback: "Nothing was reverted or deleted.",
|
|
5251
|
+
"deploy-destructive": "No resource of this stack was changed.",
|
|
5252
|
+
"deploy-late-replace": "That resource is kept, so the new value is not applied; the rest of the deploy goes on."
|
|
5253
|
+
};
|
|
5254
|
+
function deployUnderOtherPrefixMessage(s, prefixes) {
|
|
5255
|
+
return `Refusing to deploy stack ${subjectText(s)}: it is already recorded under another state prefix of bucket ${displayIdent(s.bucket)} (${prefixesText(prefixes)}). ${UNSUPPORTED_SENTENCE} ${NOTHING.deploy} Either deploy it under that prefix (pass the same --state-prefix it was deployed with), give this stack another name, or, if the other deployment is no longer wanted, ${prefixes.length === 1 ? "remove it first: " : ""}` + releaseRemedy(s, prefixes, true);
|
|
5256
|
+
}
|
|
5257
|
+
function destroyUnderOtherPrefixMessage(s, prefixes, action = "destroy") {
|
|
5258
|
+
return `Refusing to ${VERB[action]} stack ${subjectText(s)}: it is also recorded under another state prefix of bucket ${displayIdent(s.bucket)} (${prefixesText(prefixes)}). ${UNSUPPORTED_SENTENCE} ${NOTHING[action]} Keep one record per stack name and region: once you have confirmed which record describes the deployment you want to keep, ${releaseRemedy(s, prefixes, false)} Then re-run.`;
|
|
5259
|
+
}
|
|
5260
|
+
function destructiveDeployUnderOtherPrefixMessage(s, prefixes, action = "deploy-destructive") {
|
|
5261
|
+
return `${action === "deploy-late-replace" ? `Refusing to replace a resource of stack ${subjectText(s)} (a replacement this deploy found only on reading the resource back)` : `Refusing to deploy stack ${subjectText(s)}: this deploy deletes or replaces resources`}, and the stack is also recorded under another state prefix of bucket ${displayIdent(s.bucket)} (${prefixesText(prefixes)}). ${UNSUPPORTED_SENTENCE} ${NOTHING[action]} Keep one record per stack name and region: once you have confirmed which record describes the deployment you want to keep, ${releaseRemedy(s, prefixes, false)} Then re-run.`;
|
|
5262
|
+
}
|
|
5263
|
+
function staleRecordNotice(s, prefixes) {
|
|
5264
|
+
return `Note: bucket ${displayIdent(s.bucket)} also holds a record of stack ${subjectText(s)} under another state prefix (${prefixesText(prefixes)}) that owns no resource -- the leftover of a failed first deploy. It does not block this command; remove it with \`${stateCommand("orphan", s, prefixes[0])}\`${prefixes.length > 1 ? ", and the same with each other prefix" : ""}.`;
|
|
5265
|
+
}
|
|
5266
|
+
function errorName$1(error, fallback) {
|
|
5267
|
+
const subject = error instanceof CrossPrefixReadError ? error.cause : error;
|
|
5268
|
+
return subject !== null && typeof subject === "object" && typeof subject.name === "string" ? displaySafe(subject.name, { asciiOnly: true }) : fallback;
|
|
5269
|
+
}
|
|
5270
|
+
function failedKeyText(error) {
|
|
5271
|
+
return error instanceof CrossPrefixReadError ? ` reading s3 object ${displayIdent(error.key, { maxCodePoints: 1024 })}` : "";
|
|
5272
|
+
}
|
|
5273
|
+
function crossPrefixDeniedWarning(s, error, stage = "probe") {
|
|
5274
|
+
if (stage === "registry" && isNotImplemented(unwrapCause(error))) return `Could not use the stack registry for stack ${subjectText(s)}: the state bucket's endpoint does not implement a conditional write${failedKeyText(error)} (${errorName$1(error, "NotImplemented")}), so the bucket's other state prefixes were scanned instead, which is slower. Continuing.`;
|
|
5275
|
+
if (stage === "registry") return `Could not use the stack registry for stack ${subjectText(s)}: S3 refused${failedKeyText(error)} (${errorName$1(error, "AccessDenied")}), so the bucket's other state prefixes were scanned instead, which is slower. Grant s3:GetObject, s3:PutObject and s3:DeleteObject on ${displayIdent(s.bucket)}/_cdkd-registry/* to use it. Continuing.`;
|
|
5276
|
+
const what = stage === "list" ? `S3 refused to list bucket ${displayIdent(s.bucket)}` : `S3 refused a read under another state prefix of bucket ${displayIdent(s.bucket)}`;
|
|
5277
|
+
return `Could not check the other state prefixes for stack ${subjectText(s)}: ${what} (${errorName$1(error, "AccessDenied")}). Continuing. Deploying one stack name under two state prefixes in one account and region is unsupported.`;
|
|
5278
|
+
}
|
|
5279
|
+
function inProgressUnderOtherPrefixMessage(s, action, prefix) {
|
|
5280
|
+
const account = recoveryCommandFlags({
|
|
5281
|
+
profile: s.recovery?.profile,
|
|
5282
|
+
stateBucket: s.recovery?.stateBucket ?? s.bucket
|
|
5283
|
+
});
|
|
5284
|
+
const unlock = pasteableCommand("cdkd force-unlock", [
|
|
5285
|
+
{
|
|
5286
|
+
value: s.stackName,
|
|
5287
|
+
hole: "stack"
|
|
5288
|
+
},
|
|
5289
|
+
{
|
|
5290
|
+
flag: "--stack-region",
|
|
5291
|
+
value: s.region,
|
|
5292
|
+
hole: "region"
|
|
5293
|
+
},
|
|
5294
|
+
{
|
|
5295
|
+
flag: "--state-prefix",
|
|
5296
|
+
value: prefix,
|
|
5297
|
+
hole: "prefix"
|
|
5298
|
+
}
|
|
5299
|
+
], account.flags).command;
|
|
5300
|
+
return `Refusing to ${VERB[action]} stack ${subjectText(s)}: bucket ${displayIdent(s.bucket)} assigns it to state prefix ${displayIdent(prefix)}, which records nothing for it yet but holds its lock, so a deploy there may be in progress. ${UNSUPPORTED_SENTENCE} ${NOTHING[action]} Re-run once that deploy has finished; if none is running, remove its lock with \`${unlock}\` and re-run.`;
|
|
5301
|
+
}
|
|
5302
|
+
function crossPrefixFailedMessage(s, action, error) {
|
|
5303
|
+
return `Refusing to ${VERB[action]} stack ${subjectText(s)}: cdkd could not check whether the bucket ${displayIdent(s.bucket)} records it under another state prefix (${errorName$1(error, "an unknown error")}${failedKeyText(error)}). ${UNSUPPORTED_SENTENCE} ${NOTHING[action]} Re-run once the check can succeed.`;
|
|
5304
|
+
}
|
|
5305
|
+
function applyCrossPrefixScan(result, s, action, warn, info) {
|
|
5306
|
+
if (result.kind !== "in-progress" && result.stale !== void 0 && result.stale.length > 0) info?.(staleRecordNotice(s, result.stale));
|
|
5307
|
+
switch (result.kind) {
|
|
5308
|
+
case "clear": return;
|
|
5309
|
+
case "denied":
|
|
5310
|
+
warn(crossPrefixDeniedWarning(s, result.error, result.stage));
|
|
5311
|
+
return;
|
|
5312
|
+
case "found": {
|
|
5313
|
+
const message = action === "deploy" ? deployUnderOtherPrefixMessage(s, result.prefixes) : action === "deploy-destructive" || action === "deploy-late-replace" ? destructiveDeployUnderOtherPrefixMessage(s, result.prefixes, action) : destroyUnderOtherPrefixMessage(s, result.prefixes, action);
|
|
5314
|
+
throw new CdkdError(message, STACK_UNDER_OTHER_PREFIX);
|
|
5315
|
+
}
|
|
5316
|
+
case "failed": throw new CdkdError(crossPrefixFailedMessage(s, action, result.error), STACK_UNDER_OTHER_PREFIX);
|
|
5317
|
+
case "in-progress": throw new CdkdError(inProgressUnderOtherPrefixMessage(s, action, result.prefix), STACK_UNDER_OTHER_PREFIX);
|
|
5318
|
+
}
|
|
5319
|
+
}
|
|
5320
|
+
|
|
5321
|
+
//#endregion
|
|
5322
|
+
//#region src/state/earlier-state-versions.ts
|
|
5323
|
+
async function readEarlierStateResources(s3, bucket, owner, key, max) {
|
|
5324
|
+
const versions = ((await s3.send(new ListObjectVersionsCommand({
|
|
5325
|
+
Bucket: bucket,
|
|
5326
|
+
...owner,
|
|
5327
|
+
Prefix: key,
|
|
5328
|
+
EncodingType: "url",
|
|
5329
|
+
MaxKeys: max + 1
|
|
5330
|
+
}))).Versions ?? []).filter((v) => decodeListingKey(v.Key) === key && typeof v.VersionId === "string").sort((a, b) => (b.LastModified?.getTime() ?? 0) - (a.LastModified?.getTime() ?? 0)).slice(0, max);
|
|
5331
|
+
return (await Promise.all(versions.map(async (v) => {
|
|
5332
|
+
const body = await (await s3.send(new GetObjectCommand({
|
|
5333
|
+
Bucket: bucket,
|
|
5334
|
+
...owner,
|
|
5335
|
+
Key: key,
|
|
5336
|
+
VersionId: v.VersionId
|
|
5337
|
+
}))).Body?.transformToString();
|
|
5338
|
+
if (body === void 0) return void 0;
|
|
5339
|
+
try {
|
|
5340
|
+
const parsed = JSON.parse(body);
|
|
5341
|
+
if (parsed === null || typeof parsed.resources !== "object" || parsed.resources === null) return;
|
|
5342
|
+
return {
|
|
5343
|
+
resources: parsed.resources,
|
|
5344
|
+
...v.LastModified instanceof Date && { writtenAt: v.LastModified.getTime() }
|
|
5345
|
+
};
|
|
5346
|
+
} catch {
|
|
5347
|
+
return;
|
|
5348
|
+
}
|
|
5349
|
+
}))).filter((r) => r !== void 0);
|
|
5350
|
+
}
|
|
5351
|
+
|
|
5352
|
+
//#endregion
|
|
5353
|
+
//#region src/state/retained-time.ts
|
|
5354
|
+
var RetainedTimeUnconfirmedError = class extends Error {
|
|
5355
|
+
cause;
|
|
5356
|
+
constructor(cause) {
|
|
5357
|
+
super("the kept-resource record was written, but its time could not be confirmed from S3");
|
|
5358
|
+
this.name = "RetainedTimeUnconfirmedError";
|
|
5359
|
+
this.cause = cause;
|
|
5360
|
+
}
|
|
5361
|
+
};
|
|
5362
|
+
|
|
4958
5363
|
//#endregion
|
|
4959
5364
|
//#region src/utils/bucket-region-client.ts
|
|
4960
5365
|
async function rebuildClientForBucketRegion(client, bucket, opts = {}) {
|
|
@@ -5014,12 +5419,14 @@ function legacyProbeBelongsTo(probe, region) {
|
|
|
5014
5419
|
case "unreadable": return false;
|
|
5015
5420
|
}
|
|
5016
5421
|
}
|
|
5017
|
-
|
|
5422
|
+
const KEPT_AT_CLOCK_TOLERANCE_MS = 2e3;
|
|
5423
|
+
var S3StateBackend = class S3StateBackend {
|
|
5018
5424
|
logger = getLogger().child("S3StateBackend");
|
|
5019
5425
|
s3Client;
|
|
5020
5426
|
config;
|
|
5021
5427
|
clientOpts;
|
|
5022
5428
|
clientResolved = false;
|
|
5429
|
+
scanClient;
|
|
5023
5430
|
resolveInFlight = null;
|
|
5024
5431
|
constructor(s3Client, config, clientOpts = {}) {
|
|
5025
5432
|
this.s3Client = s3Client;
|
|
@@ -5028,6 +5435,19 @@ var S3StateBackend = class {
|
|
|
5028
5435
|
}
|
|
5029
5436
|
destroyClient() {
|
|
5030
5437
|
this.s3Client.destroy();
|
|
5438
|
+
this.scanClient?.destroy();
|
|
5439
|
+
}
|
|
5440
|
+
async clientForScan() {
|
|
5441
|
+
await this.ensureClientForBucket();
|
|
5442
|
+
if (this.scanClient !== void 0) return this.scanClient;
|
|
5443
|
+
if (!(this.s3Client instanceof S3Client)) return this.s3Client;
|
|
5444
|
+
const region = await this.s3Client.config.region();
|
|
5445
|
+
this.scanClient = new S3Client({
|
|
5446
|
+
...awsClientDefaults(this.clientOpts.profile ? { profile: this.clientOpts.profile } : {}),
|
|
5447
|
+
region,
|
|
5448
|
+
credentials: this.s3Client.config.credentials
|
|
5449
|
+
});
|
|
5450
|
+
return this.scanClient;
|
|
5031
5451
|
}
|
|
5032
5452
|
get prefix() {
|
|
5033
5453
|
return this.config.prefix;
|
|
@@ -5295,6 +5715,87 @@ var S3StateBackend = class {
|
|
|
5295
5715
|
throw new StateError(`Failed to list stacks: ${normalized.message}`, normalized);
|
|
5296
5716
|
}
|
|
5297
5717
|
}
|
|
5718
|
+
async listTopLevelPrefixes() {
|
|
5719
|
+
const client = await this.clientForScan();
|
|
5720
|
+
const out = [];
|
|
5721
|
+
let continuationToken;
|
|
5722
|
+
do {
|
|
5723
|
+
const response = await client.send(new ListObjectsV2Command({
|
|
5724
|
+
Bucket: this.config.bucket,
|
|
5725
|
+
...await this.ownerParam(),
|
|
5726
|
+
Delimiter: "/",
|
|
5727
|
+
EncodingType: "url",
|
|
5728
|
+
...continuationToken && { ContinuationToken: continuationToken }
|
|
5729
|
+
}));
|
|
5730
|
+
for (const cp of response.CommonPrefixes ?? []) {
|
|
5731
|
+
let decoded;
|
|
5732
|
+
try {
|
|
5733
|
+
decoded = decodeListingKey(cp.Prefix);
|
|
5734
|
+
} catch {
|
|
5735
|
+
this.logger.debug(safeMsg`Skipping a top-level prefix of bucket ${this.config.bucket} that is not valid URL encoding: ${cp.Prefix ?? ""}`);
|
|
5736
|
+
continue;
|
|
5737
|
+
}
|
|
5738
|
+
if (decoded === void 0 || !decoded.endsWith("/")) continue;
|
|
5739
|
+
if (decoded === `${"_cdkd-registry"}/`) continue;
|
|
5740
|
+
out.push(decoded.slice(0, -1));
|
|
5741
|
+
}
|
|
5742
|
+
continuationToken = response.IsTruncated ? response.NextContinuationToken : void 0;
|
|
5743
|
+
} while (continuationToken);
|
|
5744
|
+
return out;
|
|
5745
|
+
}
|
|
5746
|
+
async recordUnderPrefix(prefix, stackName, region) {
|
|
5747
|
+
const client = await this.clientForScan();
|
|
5748
|
+
const sibling = new S3StateBackend(client, {
|
|
5749
|
+
...this.config,
|
|
5750
|
+
prefix
|
|
5751
|
+
}, this.clientOpts);
|
|
5752
|
+
sibling.clientResolved = true;
|
|
5753
|
+
const stackDir = `${prefix}/${stackName}/`;
|
|
5754
|
+
let listed;
|
|
5755
|
+
try {
|
|
5756
|
+
listed = await client.send(new ListObjectsV2Command({
|
|
5757
|
+
Bucket: this.config.bucket,
|
|
5758
|
+
...await this.ownerParam(),
|
|
5759
|
+
Prefix: stackDir,
|
|
5760
|
+
MaxKeys: 1,
|
|
5761
|
+
EncodingType: "url"
|
|
5762
|
+
}));
|
|
5763
|
+
} catch (error) {
|
|
5764
|
+
throw new CrossPrefixReadError(stackDir, error);
|
|
5765
|
+
}
|
|
5766
|
+
if ((listed.KeyCount ?? listed.Contents?.length ?? 0) === 0) return "absent";
|
|
5767
|
+
const read = async (key) => {
|
|
5768
|
+
let body;
|
|
5769
|
+
try {
|
|
5770
|
+
body = await sibling.getRawObject(key);
|
|
5771
|
+
} catch (error) {
|
|
5772
|
+
throw new CrossPrefixReadError(key, error);
|
|
5773
|
+
}
|
|
5774
|
+
if (body === null) return null;
|
|
5775
|
+
if (body.trim() === "") throw new CrossPrefixReadError(key, Object.assign(/* @__PURE__ */ new Error("empty object"), { name: "EmptyObject" }));
|
|
5776
|
+
let value;
|
|
5777
|
+
try {
|
|
5778
|
+
value = JSON.parse(body);
|
|
5779
|
+
} catch (error) {
|
|
5780
|
+
throw new CrossPrefixReadError(key, error);
|
|
5781
|
+
}
|
|
5782
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new CrossPrefixReadError(key, /* @__PURE__ */ new TypeError("not a JSON object"));
|
|
5783
|
+
return value;
|
|
5784
|
+
};
|
|
5785
|
+
const [scoped, legacy, journal] = await Promise.all([
|
|
5786
|
+
read(sibling.getStateKey(stackName, region)),
|
|
5787
|
+
read(sibling.getLegacyStateKey(stackName)),
|
|
5788
|
+
read(sibling.getRollbackJournalKey(stackName, region))
|
|
5789
|
+
]);
|
|
5790
|
+
let record;
|
|
5791
|
+
if (scoped !== null) record = scoped;
|
|
5792
|
+
else if (legacy !== null) {
|
|
5793
|
+
const bodyRegion = legacy["region"];
|
|
5794
|
+
if (!bodyRegion || bodyRegion === region) record = legacy;
|
|
5795
|
+
}
|
|
5796
|
+
if (record === void 0 && journal === null) return "absent";
|
|
5797
|
+
return recordCanOwnResources(record ?? { resources: {} }, journal) ? "holder" : "empty";
|
|
5798
|
+
}
|
|
5298
5799
|
async putRawObject(key, body, contentType = "application/json") {
|
|
5299
5800
|
await this.ensureClientForBucket();
|
|
5300
5801
|
await this.s3Client.send(new PutObjectCommand({
|
|
@@ -5306,6 +5807,176 @@ var S3StateBackend = class {
|
|
|
5306
5807
|
ContentType: contentType
|
|
5307
5808
|
}));
|
|
5308
5809
|
}
|
|
5810
|
+
async earlierStateResources(stackName, region, max = 10) {
|
|
5811
|
+
await this.ensureClientForBucket();
|
|
5812
|
+
return readEarlierStateResources(this.s3Client, this.config.bucket, await this.ownerParam(), this.getStateKey(stackName, region), max);
|
|
5813
|
+
}
|
|
5814
|
+
getRetainedKey(stackName, region) {
|
|
5815
|
+
return `${this.config.prefix}/${stackName}/${region}/retained.json`;
|
|
5816
|
+
}
|
|
5817
|
+
async loadRetainedResources(stackName, region) {
|
|
5818
|
+
return await this.loadRetainedRecord(stackName, region) ?? [];
|
|
5819
|
+
}
|
|
5820
|
+
async loadRetainedRecord(stackName, region) {
|
|
5821
|
+
const key = this.getRetainedKey(stackName, region);
|
|
5822
|
+
let body;
|
|
5823
|
+
try {
|
|
5824
|
+
body = await this.getRawObject(key);
|
|
5825
|
+
} catch (error) {
|
|
5826
|
+
throw new CrossPrefixReadError(key, error);
|
|
5827
|
+
}
|
|
5828
|
+
if (body === null) return null;
|
|
5829
|
+
const entries = parseRetainedResources(body);
|
|
5830
|
+
if (entries === void 0) throw new CrossPrefixReadError(key, Object.assign(/* @__PURE__ */ new Error("not a retained-resource record"), { name: "MalformedRecord" }));
|
|
5831
|
+
return entries;
|
|
5832
|
+
}
|
|
5833
|
+
async ensureRetainedTombstone(stackName, region) {
|
|
5834
|
+
await this.ensureClientForBucket();
|
|
5835
|
+
const key = this.getRetainedKey(stackName, region);
|
|
5836
|
+
const put = async () => this.s3Client.send(new PutObjectCommand({
|
|
5837
|
+
Bucket: this.config.bucket,
|
|
5838
|
+
...await this.ownerParam(),
|
|
5839
|
+
Key: key,
|
|
5840
|
+
Body: JSON.stringify({
|
|
5841
|
+
retainedVersion: 1,
|
|
5842
|
+
resources: []
|
|
5843
|
+
}),
|
|
5844
|
+
ContentType: "application/json",
|
|
5845
|
+
IfNoneMatch: "*"
|
|
5846
|
+
}));
|
|
5847
|
+
try {
|
|
5848
|
+
try {
|
|
5849
|
+
await put();
|
|
5850
|
+
} catch (error) {
|
|
5851
|
+
if (!isConditionalConflict(error)) throw error;
|
|
5852
|
+
await put();
|
|
5853
|
+
}
|
|
5854
|
+
} catch (error) {
|
|
5855
|
+
if (isConditionFailure(error) && !isConditionalConflict(error)) return;
|
|
5856
|
+
if (!isNotImplemented(error)) throw error;
|
|
5857
|
+
if (await this.loadRetainedRecord(stackName, region) === null) await this.saveRetainedResources(stackName, region, []);
|
|
5858
|
+
}
|
|
5859
|
+
}
|
|
5860
|
+
async saveRetainedResources(stackName, region, entries) {
|
|
5861
|
+
await this.ensureClientForBucket();
|
|
5862
|
+
const key = this.getRetainedKey(stackName, region);
|
|
5863
|
+
const put = (resources) => this.ownerParam().then((owner) => this.s3Client.send(new PutObjectCommand({
|
|
5864
|
+
Bucket: this.config.bucket,
|
|
5865
|
+
...owner,
|
|
5866
|
+
Key: key,
|
|
5867
|
+
Body: JSON.stringify({
|
|
5868
|
+
retainedVersion: 1,
|
|
5869
|
+
resources
|
|
5870
|
+
}),
|
|
5871
|
+
ContentType: "application/json"
|
|
5872
|
+
})));
|
|
5873
|
+
const fresh = new Set(entries.filter((e) => e.keptAt === void 0));
|
|
5874
|
+
const provisional = Date.now();
|
|
5875
|
+
await put(entries.map((e) => fresh.has(e) ? {
|
|
5876
|
+
...e,
|
|
5877
|
+
keptAt: provisional
|
|
5878
|
+
} : e));
|
|
5879
|
+
if (fresh.size === 0) return;
|
|
5880
|
+
try {
|
|
5881
|
+
const head = await this.s3Client.send(new HeadObjectCommand({
|
|
5882
|
+
Bucket: this.config.bucket,
|
|
5883
|
+
...await this.ownerParam(),
|
|
5884
|
+
Key: key
|
|
5885
|
+
}));
|
|
5886
|
+
const keptAt = head.LastModified instanceof Date ? head.LastModified.getTime() : void 0;
|
|
5887
|
+
if (keptAt === void 0) throw new Error("S3 reported no LastModified");
|
|
5888
|
+
if (Math.abs(keptAt - provisional) <= KEPT_AT_CLOCK_TOLERANCE_MS) return;
|
|
5889
|
+
await put(entries.map((e) => fresh.has(e) ? {
|
|
5890
|
+
...e,
|
|
5891
|
+
keptAt
|
|
5892
|
+
} : e));
|
|
5893
|
+
} catch (error) {
|
|
5894
|
+
throw new RetainedTimeUnconfirmedError(error);
|
|
5895
|
+
}
|
|
5896
|
+
}
|
|
5897
|
+
registryMarkerKey(stackName, region) {
|
|
5898
|
+
return `${REGISTRY_ROOT}/${region}/${stackName}.json`;
|
|
5899
|
+
}
|
|
5900
|
+
async getRegistryMarker(stackName, region) {
|
|
5901
|
+
await this.ensureClientForBucket();
|
|
5902
|
+
const key = this.registryMarkerKey(stackName, region);
|
|
5903
|
+
let body;
|
|
5904
|
+
let etag;
|
|
5905
|
+
try {
|
|
5906
|
+
const response = await this.s3Client.send(new GetObjectCommand({
|
|
5907
|
+
Bucket: this.config.bucket,
|
|
5908
|
+
...await this.ownerParam(),
|
|
5909
|
+
Key: key
|
|
5910
|
+
}));
|
|
5911
|
+
body = await response.Body?.transformToString();
|
|
5912
|
+
etag = response.ETag;
|
|
5913
|
+
} catch (error) {
|
|
5914
|
+
if (isNoSuchKey(error) || error.name === "NotFound") return null;
|
|
5915
|
+
throw new CrossPrefixReadError(key, error);
|
|
5916
|
+
}
|
|
5917
|
+
const prefix = registryMarkerPrefix(body);
|
|
5918
|
+
if (prefix === void 0 || etag === void 0) throw new CrossPrefixReadError(key, Object.assign(/* @__PURE__ */ new Error("not a registry marker"), { name: "MalformedMarker" }));
|
|
5919
|
+
return {
|
|
5920
|
+
prefix,
|
|
5921
|
+
etag
|
|
5922
|
+
};
|
|
5923
|
+
}
|
|
5924
|
+
async claimRegistryMarker(stackName, region, ifMatch) {
|
|
5925
|
+
await this.ensureClientForBucket();
|
|
5926
|
+
const key = this.registryMarkerKey(stackName, region);
|
|
5927
|
+
try {
|
|
5928
|
+
await this.s3Client.send(new PutObjectCommand({
|
|
5929
|
+
Bucket: this.config.bucket,
|
|
5930
|
+
...await this.ownerParam(),
|
|
5931
|
+
Key: key,
|
|
5932
|
+
Body: JSON.stringify({ prefix: this.config.prefix }),
|
|
5933
|
+
ContentType: "application/json",
|
|
5934
|
+
...ifMatch === void 0 ? { IfNoneMatch: "*" } : { IfMatch: ifMatch }
|
|
5935
|
+
}));
|
|
5936
|
+
return "claimed";
|
|
5937
|
+
} catch (error) {
|
|
5938
|
+
if (isConditionFailure(error)) return "conflict";
|
|
5939
|
+
throw new CrossPrefixReadError(key, error);
|
|
5940
|
+
}
|
|
5941
|
+
}
|
|
5942
|
+
async releaseRegistryMarker(stackName, region, known) {
|
|
5943
|
+
const marker = known !== void 0 && (known === null || known.etag !== "") ? known : await this.getRegistryMarker(stackName, region);
|
|
5944
|
+
if (marker === null) return "absent";
|
|
5945
|
+
if (marker.prefix !== this.config.prefix) return "elsewhere";
|
|
5946
|
+
const key = this.registryMarkerKey(stackName, region);
|
|
5947
|
+
try {
|
|
5948
|
+
await this.s3Client.send(new DeleteObjectCommand({
|
|
5949
|
+
Bucket: this.config.bucket,
|
|
5950
|
+
...await this.ownerParam(),
|
|
5951
|
+
Key: key,
|
|
5952
|
+
IfMatch: marker.etag
|
|
5953
|
+
}));
|
|
5954
|
+
return "released";
|
|
5955
|
+
} catch (error) {
|
|
5956
|
+
if (isConditionFailure(error)) return "elsewhere";
|
|
5957
|
+
if (!isNotImplemented(error)) throw error;
|
|
5958
|
+
}
|
|
5959
|
+
const again = await this.getRegistryMarker(stackName, region);
|
|
5960
|
+
if (again === null) return "absent";
|
|
5961
|
+
if (again.prefix !== this.config.prefix || again.etag !== marker.etag) return "elsewhere";
|
|
5962
|
+
await this.s3Client.send(new DeleteObjectCommand({
|
|
5963
|
+
Bucket: this.config.bucket,
|
|
5964
|
+
...await this.ownerParam(),
|
|
5965
|
+
Key: key
|
|
5966
|
+
}));
|
|
5967
|
+
return "released";
|
|
5968
|
+
}
|
|
5969
|
+
async lockUnderPrefix(prefix, stackName, region) {
|
|
5970
|
+
await this.ensureClientForBucket();
|
|
5971
|
+
const keys = [`${prefix}/${stackName}/${region}/lock.json`, `${prefix}/${stackName}/lock.json`];
|
|
5972
|
+
return (await Promise.all(keys.map(async (key) => {
|
|
5973
|
+
try {
|
|
5974
|
+
return await this.headObject(key);
|
|
5975
|
+
} catch (error) {
|
|
5976
|
+
throw new CrossPrefixReadError(key, error);
|
|
5977
|
+
}
|
|
5978
|
+
}))).some(Boolean);
|
|
5979
|
+
}
|
|
5309
5980
|
async getRawObject(key) {
|
|
5310
5981
|
await this.ensureClientForBucket();
|
|
5311
5982
|
try {
|
|
@@ -5750,6 +6421,53 @@ var S3StateBackend = class {
|
|
|
5750
6421
|
return parsed;
|
|
5751
6422
|
}
|
|
5752
6423
|
};
|
|
6424
|
+
function parseRetainedResources(body) {
|
|
6425
|
+
let value;
|
|
6426
|
+
try {
|
|
6427
|
+
value = JSON.parse(body);
|
|
6428
|
+
} catch {
|
|
6429
|
+
return;
|
|
6430
|
+
}
|
|
6431
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
6432
|
+
const doc = value;
|
|
6433
|
+
if (doc.retainedVersion !== 1 || !Array.isArray(doc.resources)) return void 0;
|
|
6434
|
+
const out = [];
|
|
6435
|
+
for (const entry of doc.resources) {
|
|
6436
|
+
const e = entry;
|
|
6437
|
+
if (typeof e?.logicalId === "string" && typeof e.resourceType === "string" && typeof e.physicalId === "string" && e.physicalId !== "") out.push({
|
|
6438
|
+
logicalId: e.logicalId,
|
|
6439
|
+
resourceType: e.resourceType,
|
|
6440
|
+
physicalId: e.physicalId,
|
|
6441
|
+
...typeof e.keptAt === "number" && Number.isFinite(e.keptAt) && { keptAt: e.keptAt }
|
|
6442
|
+
});
|
|
6443
|
+
}
|
|
6444
|
+
return out;
|
|
6445
|
+
}
|
|
6446
|
+
const REGISTRY_ROOT = "_cdkd-registry";
|
|
6447
|
+
function registryMarkerPrefix(body) {
|
|
6448
|
+
if (body === void 0) return void 0;
|
|
6449
|
+
let value;
|
|
6450
|
+
try {
|
|
6451
|
+
value = JSON.parse(body);
|
|
6452
|
+
} catch {
|
|
6453
|
+
return;
|
|
6454
|
+
}
|
|
6455
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
6456
|
+
const prefix = value.prefix;
|
|
6457
|
+
if (typeof prefix !== "string" || prefix.length > 1024) return void 0;
|
|
6458
|
+
if (/[\u0000-\u001f\u007f<>]/.test(prefix)) return void 0;
|
|
6459
|
+
return prefix;
|
|
6460
|
+
}
|
|
6461
|
+
function isConditionalConflict(error) {
|
|
6462
|
+
const name = error?.name;
|
|
6463
|
+
const status = error?.$metadata?.httpStatusCode;
|
|
6464
|
+
return name === "ConditionalRequestConflict" || status === 409 && name !== "PreconditionFailed";
|
|
6465
|
+
}
|
|
6466
|
+
function isConditionFailure(error) {
|
|
6467
|
+
const name = error?.name;
|
|
6468
|
+
const status = error?.$metadata?.httpStatusCode;
|
|
6469
|
+
return name === "PreconditionFailed" || name === "ConditionalRequestConflict" || status === 412 || status === 409;
|
|
6470
|
+
}
|
|
5753
6471
|
function isNoSuchKey(error) {
|
|
5754
6472
|
if (error instanceof NoSuchKey) return true;
|
|
5755
6473
|
return error?.name === "NoSuchKey";
|
|
@@ -5768,6 +6486,15 @@ function addSupersededIds(segment, ids) {
|
|
|
5768
6486
|
const MAX_RENEWAL_INTERVAL_MS = 12e4;
|
|
5769
6487
|
const RENEWAL_TTL_FRACTION = 4;
|
|
5770
6488
|
const MIN_RENEWAL_INTERVAL_MS = 1e3;
|
|
6489
|
+
function renewalIntervalFor(ttlMs) {
|
|
6490
|
+
return Math.max(MIN_RENEWAL_INTERVAL_MS, Math.min(MAX_RENEWAL_INTERVAL_MS, Math.floor(ttlMs / RENEWAL_TTL_FRACTION)));
|
|
6491
|
+
}
|
|
6492
|
+
function abandonedRunHorizon(record) {
|
|
6493
|
+
if (record.lastModified === void 0) return void 0;
|
|
6494
|
+
const ttlMs = Number(record.info.expiresAt) - record.lastModified;
|
|
6495
|
+
const interval = Number.isFinite(ttlMs) && ttlMs > 0 ? renewalIntervalFor(ttlMs) : MAX_RENEWAL_INTERVAL_MS;
|
|
6496
|
+
return record.lastModified + interval;
|
|
6497
|
+
}
|
|
5771
6498
|
const RELEASED_LOCK_REACQUIRE_LIMIT = 3;
|
|
5772
6499
|
const LOCK_OBJECT_DESCRIPTION = "a stack lock heartbeat, which records the lock owner, its acquisition timestamp, its deadline and the operation name — no secret, but one row per renewal";
|
|
5773
6500
|
function safeSegment(value) {
|
|
@@ -5791,6 +6518,7 @@ var LockManager = class {
|
|
|
5791
6518
|
renewalIntervalMs;
|
|
5792
6519
|
renewalDisabled;
|
|
5793
6520
|
heldLocks = /* @__PURE__ */ new Map();
|
|
6521
|
+
abandonedLocks = /* @__PURE__ */ new Map();
|
|
5794
6522
|
clientResolved = false;
|
|
5795
6523
|
resolveInFlight = null;
|
|
5796
6524
|
constructor(s3Client, config, options) {
|
|
@@ -5800,7 +6528,7 @@ var LockManager = class {
|
|
|
5800
6528
|
if (!(ttlMinutes > 0) || !Number.isFinite(ttlMinutes)) throw new LockError(`Lock TTL must be a positive number of minutes (got ${String(ttlMinutes)}). A non-positive TTL makes every lock born expired, and the renewal interval clamps to its floor, so the heartbeat would spin one request per second for the life of the process.`);
|
|
5801
6529
|
this.ttlMs = ttlMinutes * 60 * 1e3;
|
|
5802
6530
|
this.renewalDisabled = options?.disableRenewal === true;
|
|
5803
|
-
this.renewalIntervalMs =
|
|
6531
|
+
this.renewalIntervalMs = renewalIntervalFor(this.ttlMs);
|
|
5804
6532
|
}
|
|
5805
6533
|
async ownerParam() {
|
|
5806
6534
|
return expectedOwnerParam(this.s3Client);
|
|
@@ -5889,6 +6617,8 @@ var LockManager = class {
|
|
|
5889
6617
|
}
|
|
5890
6618
|
const why = Number.isFinite(existing.info.expiresAt) ? "A live cdkd process renews its lock well inside the TTL, so this normally means the previous owner crashed or was suspended" : `Its expiresAt is not a finite number, which cdkd treats as already expired`;
|
|
5891
6619
|
this.logger.warn(`Took over an EXPIRED lock for stack: ${safeSegment(stackName)} (${safeSegment(region)}, owner: ${formatLockOwner(existing.info.owner)}, ${formatLockExpiry(existing.info.expiresAt)}). ${why} -- if it is in fact still running, both processes are now writing to the same stack.`);
|
|
6620
|
+
const abandonedAt = abandonedRunHorizon(existing);
|
|
6621
|
+
if (abandonedAt !== void 0) this.abandonedLocks.set(key, abandonedAt);
|
|
5892
6622
|
try {
|
|
5893
6623
|
const retryEtag = await this.putLockObject(key, lockInfo);
|
|
5894
6624
|
this.logger.debug(`Lock acquired for stack: ${stackRef(stackName, region)} after expired lock cleanup, owner: ${displaySafe(lockOwner)}`);
|
|
@@ -5959,7 +6689,8 @@ var LockManager = class {
|
|
|
5959
6689
|
this.logger.debug(`Lock info for stack: ${shownStack}:`, lockInfo);
|
|
5960
6690
|
return {
|
|
5961
6691
|
info: lockInfo,
|
|
5962
|
-
etag: response.ETag
|
|
6692
|
+
etag: response.ETag,
|
|
6693
|
+
...response.LastModified instanceof Date && { lastModified: response.LastModified.getTime() }
|
|
5963
6694
|
};
|
|
5964
6695
|
} catch (error) {
|
|
5965
6696
|
if (error instanceof NoSuchKey) {
|
|
@@ -6042,9 +6773,13 @@ var LockManager = class {
|
|
|
6042
6773
|
sameLockIdentity(a, b) {
|
|
6043
6774
|
return displaySafe(a.owner) === displaySafe(b.owner) && a.timestamp === b.timestamp;
|
|
6044
6775
|
}
|
|
6045
|
-
|
|
6776
|
+
abandonedRunEndedBy(stackName, region) {
|
|
6777
|
+
return this.abandonedLocks.get(this.getLockKey(stackName, region));
|
|
6778
|
+
}
|
|
6779
|
+
async forceReleaseLock(stackName, region, beforeDelete) {
|
|
6046
6780
|
const where = stackRef(stackName, region);
|
|
6047
|
-
const
|
|
6781
|
+
const record = await this.getLockRecord(stackName, region).catch(() => null);
|
|
6782
|
+
const lockInfo = record?.info ?? null;
|
|
6048
6783
|
this.logger.warn(lockInfo ? `Force releasing lock for stack: ${where}, owner: ${formatLockOwner(lockInfo.owner)}${lockInfo.operation ? `, operation: ${formatLockOperation(lockInfo.operation)}` : ""}, expired: ${this.isLockExpired(lockInfo)}` : `Force releasing lock for stack: ${where} (no lock body read — absent or unparseable; deleting the object either way, since a lock cdkd cannot read is a lock nothing else can clear)`);
|
|
6049
6784
|
const key = this.getLockKey(stackName, region);
|
|
6050
6785
|
const held = this.heldLocks.get(key);
|
|
@@ -6052,11 +6787,14 @@ var LockManager = class {
|
|
|
6052
6787
|
this.stopRenewal(held);
|
|
6053
6788
|
held.releasing = Promise.resolve();
|
|
6054
6789
|
}
|
|
6790
|
+
const horizon = record ? abandonedRunHorizon(record) : void 0;
|
|
6791
|
+
if (horizon !== void 0 && beforeDelete !== void 0) await beforeDelete(horizon).catch(() => void 0);
|
|
6055
6792
|
try {
|
|
6056
6793
|
await this.deleteLock(stackName, region);
|
|
6057
6794
|
} finally {
|
|
6058
6795
|
await this.purgeLockVersions(key, "reap");
|
|
6059
6796
|
}
|
|
6797
|
+
return horizon;
|
|
6060
6798
|
}
|
|
6061
6799
|
async deleteLock(stackName, region, etag) {
|
|
6062
6800
|
await this.ensureClientForBucket();
|
|
@@ -11311,7 +12049,7 @@ const DERIVED_GENERATED_NAMES = {
|
|
|
11311
12049
|
},
|
|
11312
12050
|
"AWS::S3::Bucket": {
|
|
11313
12051
|
file: "s3-bucket-provider.ts",
|
|
11314
|
-
source: "generateResourceName(logicalId, {\n
|
|
12052
|
+
source: "generateResourceName(logicalId, {\n maxLength: 63,\n lowercase: true,\n allowedPattern: /[^a-z0-9.-]/g,\n })",
|
|
11315
12053
|
derive: (id) => generateResourceName(id, {
|
|
11316
12054
|
maxLength: 63,
|
|
11317
12055
|
lowercase: true,
|
|
@@ -22972,7 +23710,7 @@ var CloudControlProvider = class {
|
|
|
22972
23710
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
22973
23711
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
22974
23712
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
22975
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
23713
|
+
const { ASGProvider } = await import("./asg-provider-BaabA371.js");
|
|
22976
23714
|
const delegateRegion = ambientRegion();
|
|
22977
23715
|
let asgProvider = this.asgDeleteDelegates.get(delegateRegion);
|
|
22978
23716
|
if (asgProvider === void 0) {
|
|
@@ -23078,7 +23816,7 @@ var CloudControlProvider = class {
|
|
|
23078
23816
|
const family = SDK_DELETE_FAMILIES.get(resourceType);
|
|
23079
23817
|
const cached = this.sdkDeleteDelegates.get(family);
|
|
23080
23818
|
if (cached !== void 0 && cached.region === ccRegion) return cached.provider;
|
|
23081
|
-
const provider = family === "rds" ? new (await (import("./rds-provider-
|
|
23819
|
+
const provider = family === "rds" ? new (await (import("./rds-provider-IBY6fDvN.js"))).RDSProvider({ region: ccRegion }) : new (await (import("./neptune-provider-5yc_bOJ-.js"))).NeptuneProvider({ region: ccRegion });
|
|
23082
23820
|
this.sdkDeleteDelegates.set(family, {
|
|
23083
23821
|
region: ccRegion,
|
|
23084
23822
|
provider
|
|
@@ -25661,7 +26399,7 @@ const NESTED_REQUIRED = /* @__PURE__ */ new Map([
|
|
|
25661
26399
|
["ContainerDefinitions.LinuxParameters.Tmpfs", ["Size"]],
|
|
25662
26400
|
["ContainerDefinitions.LogConfiguration", ["LogDriver"]],
|
|
25663
26401
|
["ContainerDefinitions.LogConfiguration.SecretOptions", ["Name", "ValueFrom"]],
|
|
25664
|
-
["ContainerDefinitions.ResourceRequirements", ["Type"
|
|
26402
|
+
["ContainerDefinitions.ResourceRequirements", ["Type"]],
|
|
25665
26403
|
["ContainerDefinitions.Secrets", ["Name", "ValueFrom"]],
|
|
25666
26404
|
["ContainerDefinitions.Ulimits", [
|
|
25667
26405
|
"HardLimit",
|
|
@@ -27003,9 +27741,12 @@ var ProviderRegistry = class {
|
|
|
27003
27741
|
this.providers.delete(resourceType);
|
|
27004
27742
|
}
|
|
27005
27743
|
getProviderFor(input) {
|
|
27744
|
+
const debug = (message) => {
|
|
27745
|
+
if (input.quiet !== true) this.logger.debug(message);
|
|
27746
|
+
};
|
|
27006
27747
|
const { resourceType, properties, provisionedBy } = input;
|
|
27007
27748
|
if (isCustomResource(resourceType)) {
|
|
27008
|
-
|
|
27749
|
+
debug(`Using Custom Resource provider for ${resourceType}`);
|
|
27009
27750
|
return {
|
|
27010
27751
|
provider: this.customResourceProvider,
|
|
27011
27752
|
provisionedBy: "sdk"
|
|
@@ -27020,7 +27761,7 @@ var ProviderRegistry = class {
|
|
|
27020
27761
|
allowedUnsupportedProperties: this.allowedUnsupportedProperties,
|
|
27021
27762
|
forceCcApi: input.forceCcApi === true
|
|
27022
27763
|
})) {
|
|
27023
|
-
|
|
27764
|
+
debug(`Routing ${resourceType} via Cloud Control (state-recorded provisionedBy=cc-api)`);
|
|
27024
27765
|
return {
|
|
27025
27766
|
provider: this.cloudControlProvider,
|
|
27026
27767
|
provisionedBy: "cc-api"
|
|
@@ -27032,9 +27773,9 @@ var ProviderRegistry = class {
|
|
|
27032
27773
|
if (specificProvider) {
|
|
27033
27774
|
const actionableDrops = findActionableSilentDrops(resourceType, properties, this.allowedUnsupportedProperties, input.previousProperties);
|
|
27034
27775
|
if (actionableDrops.length === 0) {
|
|
27035
|
-
|
|
27776
|
+
debug(`Using specific SDK provider for ${resourceType}`);
|
|
27036
27777
|
if (returningToSdk) {
|
|
27037
|
-
|
|
27778
|
+
debug(`${resourceType} is returning to its SDK provider from a state-recorded cc-api route; physical id is preserved`);
|
|
27038
27779
|
return {
|
|
27039
27780
|
provider: specificProvider,
|
|
27040
27781
|
provisionedBy: "sdk",
|
|
@@ -27048,7 +27789,7 @@ var ProviderRegistry = class {
|
|
|
27048
27789
|
}
|
|
27049
27790
|
const unroutable = this.ccRouteUnavailableReason(resourceType);
|
|
27050
27791
|
if (unroutable !== void 0) throw new Error(this.buildUnroutableSilentDropMessage(resourceType, actionableDrops, unroutable));
|
|
27051
|
-
|
|
27792
|
+
debug(`Auto-routing ${resourceType} via Cloud Control (silent-drop properties: ${actionableDrops.map((d) => d.property).join(", ")})`);
|
|
27052
27793
|
return {
|
|
27053
27794
|
provider: this.cloudControlProvider,
|
|
27054
27795
|
provisionedBy: "cc-api",
|
|
@@ -27056,14 +27797,14 @@ var ProviderRegistry = class {
|
|
|
27056
27797
|
};
|
|
27057
27798
|
}
|
|
27058
27799
|
if (CloudControlProvider.isSupportedResourceType(resourceType)) {
|
|
27059
|
-
|
|
27800
|
+
debug(`Using Cloud Control API provider for ${resourceType}`);
|
|
27060
27801
|
return {
|
|
27061
27802
|
provider: this.cloudControlProvider,
|
|
27062
27803
|
provisionedBy: "cc-api"
|
|
27063
27804
|
};
|
|
27064
27805
|
}
|
|
27065
27806
|
if (this.allowedUnsupportedTypes.has(resourceType)) {
|
|
27066
|
-
|
|
27807
|
+
debug(`Routing escape-hatch-allowed type ${resourceType} through Cloud Control API`);
|
|
27067
27808
|
return {
|
|
27068
27809
|
provider: this.cloudControlProvider,
|
|
27069
27810
|
provisionedBy: "cc-api"
|
|
@@ -27336,7 +28077,7 @@ function recordedTagsUnreadableWarning(resourceType, id, applied) {
|
|
|
27336
28077
|
//#endregion
|
|
27337
28078
|
//#region src/provisioning/providers/idempotency-token.ts
|
|
27338
28079
|
const PROCESS_NONCE = randomUUID();
|
|
27339
|
-
const inFlight = /* @__PURE__ */ new Map();
|
|
28080
|
+
const inFlight$1 = /* @__PURE__ */ new Map();
|
|
27340
28081
|
const generations = /* @__PURE__ */ new Map();
|
|
27341
28082
|
const MAX_TRACKED_GENERATIONS = 1e5;
|
|
27342
28083
|
const tokenKey = (scope, logicalId, maxLength, charset) => injectiveKey(scope, ambientRegion() ?? "", getCurrentStackName() ?? "", logicalId, maxLength, charset);
|
|
@@ -27347,14 +28088,14 @@ const derive = (key, generation, maxLength, charset) => {
|
|
|
27347
28088
|
function acquireIdempotencyToken(options) {
|
|
27348
28089
|
const { scope, logicalId, maxLength = 64, charset = "default" } = options;
|
|
27349
28090
|
const key = tokenKey(scope, logicalId, maxLength, charset);
|
|
27350
|
-
const existing = inFlight.get(key);
|
|
28091
|
+
const existing = inFlight$1.get(key);
|
|
27351
28092
|
const value = existing ?? derive(key, generations.get(key) ?? 0, maxLength, charset);
|
|
27352
|
-
if (existing === void 0) inFlight.set(key, value);
|
|
28093
|
+
if (existing === void 0) inFlight$1.set(key, value);
|
|
27353
28094
|
return {
|
|
27354
28095
|
value,
|
|
27355
28096
|
release: () => {
|
|
27356
|
-
if (!inFlight.has(key)) return;
|
|
27357
|
-
inFlight.delete(key);
|
|
28097
|
+
if (!inFlight$1.has(key)) return;
|
|
28098
|
+
inFlight$1.delete(key);
|
|
27358
28099
|
const generation = (generations.get(key) ?? 0) + 1;
|
|
27359
28100
|
if (!generations.has(key) && generations.size >= MAX_TRACKED_GENERATIONS) {
|
|
27360
28101
|
const oldest = generations.keys().next();
|
|
@@ -27379,6 +28120,7 @@ function stackScopedCreateToken(options) {
|
|
|
27379
28120
|
//#region src/provisioning/providers/create-token-ledger.ts
|
|
27380
28121
|
var CreateTokenLedger = class {
|
|
27381
28122
|
doc;
|
|
28123
|
+
loadedAbsent = false;
|
|
27382
28124
|
stale = false;
|
|
27383
28125
|
startedThisDeploy = false;
|
|
27384
28126
|
replaceRecordedLedger = false;
|
|
@@ -27404,8 +28146,10 @@ var CreateTokenLedger = class {
|
|
|
27404
28146
|
}
|
|
27405
28147
|
async current() {
|
|
27406
28148
|
if (this.doc !== void 0 && !this.stale) return this.doc;
|
|
28149
|
+
if (this.loadedAbsent && !this.stale) return null;
|
|
27407
28150
|
const loaded = await this.store.load();
|
|
27408
28151
|
this.stale = false;
|
|
28152
|
+
this.loadedAbsent = loaded === null;
|
|
27409
28153
|
if (loaded !== null && this.replaceRecordedLedger && loaded.stateRecorded === true) {
|
|
27410
28154
|
this.logger.info("This stack has no state record but its create-token ledger outlived an earlier one; starting a fresh ledger, so this deploy does not take over resources that deployment left.");
|
|
27411
28155
|
this.doc = emptyCreateTokenLedger(randomUUID());
|
|
@@ -27492,6 +28236,85 @@ var CreateTokenLedger = class {
|
|
|
27492
28236
|
}
|
|
27493
28237
|
});
|
|
27494
28238
|
}
|
|
28239
|
+
sentEntries() {
|
|
28240
|
+
return this.serialized(async () => {
|
|
28241
|
+
try {
|
|
28242
|
+
return { ...(await this.current())?.sent ?? {} };
|
|
28243
|
+
} catch (error) {
|
|
28244
|
+
this.stale = true;
|
|
28245
|
+
throw error;
|
|
28246
|
+
}
|
|
28247
|
+
});
|
|
28248
|
+
}
|
|
28249
|
+
recordSent(entries, firstSentAt) {
|
|
28250
|
+
return this.serialized(async () => {
|
|
28251
|
+
try {
|
|
28252
|
+
let doc = await this.current();
|
|
28253
|
+
if (doc === null) {
|
|
28254
|
+
doc = emptyCreateTokenLedger(randomUUID());
|
|
28255
|
+
this.doc = doc;
|
|
28256
|
+
this.startedThisDeploy = true;
|
|
28257
|
+
}
|
|
28258
|
+
let changed = false;
|
|
28259
|
+
for (const entry of entries) {
|
|
28260
|
+
const recorded = doc.sent[entry.logicalId];
|
|
28261
|
+
if (recorded?.base === entry.base && recorded.token === entry.token) continue;
|
|
28262
|
+
doc.sent[entry.logicalId] = {
|
|
28263
|
+
base: entry.base,
|
|
28264
|
+
token: entry.token,
|
|
28265
|
+
firstSentAt
|
|
28266
|
+
};
|
|
28267
|
+
changed = true;
|
|
28268
|
+
}
|
|
28269
|
+
if (changed) await this.persist(doc);
|
|
28270
|
+
} catch (error) {
|
|
28271
|
+
this.stale = true;
|
|
28272
|
+
throw error;
|
|
28273
|
+
}
|
|
28274
|
+
});
|
|
28275
|
+
}
|
|
28276
|
+
abandonedAt() {
|
|
28277
|
+
return this.serialized(async () => (await this.current())?.abandonedAt);
|
|
28278
|
+
}
|
|
28279
|
+
noteAbandoned(at) {
|
|
28280
|
+
return this.serialized(async () => {
|
|
28281
|
+
try {
|
|
28282
|
+
const doc = await this.current();
|
|
28283
|
+
if (doc === null) return;
|
|
28284
|
+
if (!Object.values(doc.sent).some((e) => e.base.startsWith("adopt-by-name:")) || doc.abandonedAt !== void 0 && doc.abandonedAt >= at) return;
|
|
28285
|
+
doc.abandonedAt = at;
|
|
28286
|
+
await this.persist(doc);
|
|
28287
|
+
} catch (error) {
|
|
28288
|
+
this.stale = true;
|
|
28289
|
+
throw error;
|
|
28290
|
+
}
|
|
28291
|
+
});
|
|
28292
|
+
}
|
|
28293
|
+
settleAdoptingCreates(drop, failedAt) {
|
|
28294
|
+
return this.serialized(async () => {
|
|
28295
|
+
try {
|
|
28296
|
+
const doc = await this.current();
|
|
28297
|
+
if (doc === null) return;
|
|
28298
|
+
const adopting = (id) => doc.sent[id]?.base.startsWith(ADOPTING_CREATE_BASE) === true;
|
|
28299
|
+
let changed = false;
|
|
28300
|
+
for (const id of drop) {
|
|
28301
|
+
if (!adopting(id)) continue;
|
|
28302
|
+
delete doc.sent[id];
|
|
28303
|
+
changed = true;
|
|
28304
|
+
}
|
|
28305
|
+
for (const [id, at] of failedAt) {
|
|
28306
|
+
const entry = adopting(id) ? doc.sent[id] : void 0;
|
|
28307
|
+
if (entry === void 0 || entry.failedAt !== void 0) continue;
|
|
28308
|
+
entry.failedAt = at;
|
|
28309
|
+
changed = true;
|
|
28310
|
+
}
|
|
28311
|
+
if (changed) await this.persist(doc);
|
|
28312
|
+
} catch (error) {
|
|
28313
|
+
this.stale = true;
|
|
28314
|
+
throw error;
|
|
28315
|
+
}
|
|
28316
|
+
});
|
|
28317
|
+
}
|
|
27495
28318
|
forget(logicalIds) {
|
|
27496
28319
|
return this.serialized(async () => {
|
|
27497
28320
|
try {
|
|
@@ -27537,6 +28360,39 @@ function ledgerForStack(backend, stackName, region) {
|
|
|
27537
28360
|
});
|
|
27538
28361
|
}
|
|
27539
28362
|
const ledgerStore = new AsyncLocalStorage();
|
|
28363
|
+
const ADOPTING_CREATE_BASE = "adopt-by-name:";
|
|
28364
|
+
async function recordedAdoptingCreates() {
|
|
28365
|
+
const ledger = ledgerStore.getStore();
|
|
28366
|
+
if (ledger === void 0) return void 0;
|
|
28367
|
+
const out = /* @__PURE__ */ new Map();
|
|
28368
|
+
for (const [logicalId, entry] of Object.entries(await ledger.sentEntries())) if (entry.base.startsWith("adopt-by-name:")) out.set(logicalId, {
|
|
28369
|
+
resourceType: entry.base.slice(14),
|
|
28370
|
+
name: entry.token,
|
|
28371
|
+
firstSentAt: entry.firstSentAt,
|
|
28372
|
+
...entry.failedAt !== void 0 && { failedAt: entry.failedAt }
|
|
28373
|
+
});
|
|
28374
|
+
return out;
|
|
28375
|
+
}
|
|
28376
|
+
async function recordAdoptingCreates(creates) {
|
|
28377
|
+
const ledger = ledgerStore.getStore();
|
|
28378
|
+
if (ledger === void 0 || creates.length === 0) return;
|
|
28379
|
+
await ledger.recordSent(creates.map((c) => ({
|
|
28380
|
+
logicalId: c.logicalId,
|
|
28381
|
+
base: `${ADOPTING_CREATE_BASE}${c.resourceType}`,
|
|
28382
|
+
token: c.name
|
|
28383
|
+
})), Date.now());
|
|
28384
|
+
}
|
|
28385
|
+
async function noteAbandonedRun(at) {
|
|
28386
|
+
const ledger = ledgerStore.getStore();
|
|
28387
|
+
if (ledger === void 0) return true;
|
|
28388
|
+
return ledger.noteAbandoned(at).then(() => true, () => false);
|
|
28389
|
+
}
|
|
28390
|
+
async function ledgerAbandonedAt() {
|
|
28391
|
+
return ledgerStore.getStore()?.abandonedAt();
|
|
28392
|
+
}
|
|
28393
|
+
async function settleAdoptingCreates(drop, failedAt = /* @__PURE__ */ new Map()) {
|
|
28394
|
+
await ledgerStore.getStore()?.settleAdoptingCreates(drop, failedAt);
|
|
28395
|
+
}
|
|
27540
28396
|
function withCreateTokenLedger(ledger, fn) {
|
|
27541
28397
|
return ledgerStore.run(ledger, () => withStateSavedObserver((stackName, region) => ledger.noteStateSaved(stackName, region), fn));
|
|
27542
28398
|
}
|
|
@@ -28280,6 +29136,25 @@ function skipUnaddressableReplay(scope, logger, op, what, source = "record") {
|
|
|
28280
29136
|
logger.warn(safeMsg` Rollback: Cannot ${what} ${shownLogicalId(op.logicalId)} (${refusalResourceType(op.resourceType)}) ` + "— its state record has no non-empty string 'physicalId', so cdkd cannot address it in AWS and sent nothing for it. The resource and its state record are left exactly as they are. Repair the record's 'physicalId' (inspect it with `cdkd state show`), then re-converge it with `cdkd deploy`.");
|
|
28281
29137
|
recordRollbackSkip(scope, op, UNADDRESSABLE_SKIP_CAUSE);
|
|
28282
29138
|
}
|
|
29139
|
+
async function keptForAnotherHolder(scope, logger, op, physicalId) {
|
|
29140
|
+
const ask = scope.ctx.createdResourceHolder;
|
|
29141
|
+
if (ask === void 0) return false;
|
|
29142
|
+
let holding;
|
|
29143
|
+
try {
|
|
29144
|
+
holding = await ask(op.resourceType, physicalId);
|
|
29145
|
+
} catch {
|
|
29146
|
+
holding = {
|
|
29147
|
+
kind: "unreadable",
|
|
29148
|
+
what: "the other stacks' state records (the check failed)"
|
|
29149
|
+
};
|
|
29150
|
+
}
|
|
29151
|
+
if (holding === void 0) return false;
|
|
29152
|
+
const why = holding.kind === "held" ? `${holding.by} holds it` : `${holding.what}, so it is not known whether another deployment owns it`;
|
|
29153
|
+
const retry = holding.kind === "unreadable" && holding.retryable === true ? scope.ctx.createdResourceRetryCommand : void 0;
|
|
29154
|
+
logger.warn(safeMsg` Rollback: Keeping created resource ${shownLogicalId(op.logicalId)} (${refusalResourceType(op.resourceType)}) ` + safeMsg`\u2014 ${why}. The failed deploy may have adopted a resource that existed under its name, so the rollback does not delete it; the rollback journal keeps it.` + (retry === void 0 ? "" : safeMsg` Once S3 can be read, finish the rollback with: ${retry}`));
|
|
29155
|
+
recordRollbackSkip(scope, op, "Another state record holds this resource, or it could not be checked, so the rollback did not delete it.");
|
|
29156
|
+
return true;
|
|
29157
|
+
}
|
|
28283
29158
|
function effectiveProvisionedBy(record, fallbackProvisionedBy) {
|
|
28284
29159
|
return record?.provisionedBy ?? fallbackProvisionedBy;
|
|
28285
29160
|
}
|
|
@@ -30644,6 +31519,7 @@ async function replayDelete(s) {
|
|
|
30644
31519
|
skipUnaddressableReplay(s, logger, op, "delete created resource");
|
|
30645
31520
|
return;
|
|
30646
31521
|
}
|
|
31522
|
+
if (await keptForAnotherHolder(s, logger, op, op.physicalId)) return;
|
|
30647
31523
|
s.createRollbackRoute = deleteProvisionedBy;
|
|
30648
31524
|
const snapshotPolicy = action === "delete-with-final-snapshot";
|
|
30649
31525
|
const takeFinalSnapshot = snapshotPolicy && ctx.skipFinalSnapshot !== true;
|
|
@@ -31440,6 +32316,10 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
|
|
|
31440
32316
|
skipUnaddressableReplay(skipScope, logger, op, "delete partially-created resource", failedCreateRecord ? "record" : "journal");
|
|
31441
32317
|
break;
|
|
31442
32318
|
}
|
|
32319
|
+
if (await keptForAnotherHolder(skipScope, logger, op, op.physicalId)) {
|
|
32320
|
+
pending.add(op);
|
|
32321
|
+
break;
|
|
32322
|
+
}
|
|
31443
32323
|
const orphanDeleteChecked = op.physicalIdRecoveredFromError === true && ctx.foreignHolder !== void 0 && ctx.orphanDeleteProven?.has(op) !== true;
|
|
31444
32324
|
if (orphanDeleteChecked) {
|
|
31445
32325
|
const verdict = await journaledOrphanKeepReason({
|
|
@@ -32636,7 +33516,16 @@ async function performRollback(completedOperations, stateResources, stackName, p
|
|
|
32636
33516
|
operations: completedOperations,
|
|
32637
33517
|
failedOperations: orphanOps
|
|
32638
33518
|
}], priorOrphans);
|
|
32639
|
-
const
|
|
33519
|
+
const crossPrefixHolder = this.options.crossPrefixHolder;
|
|
33520
|
+
let crossPrefixAnswer;
|
|
33521
|
+
const ctx = {
|
|
33522
|
+
...this.rollbackExecutorContext(previousState, stackName),
|
|
33523
|
+
createdResourceRetryCommand: pasteableCommand("cdkd rollback", [{
|
|
33524
|
+
value: (this.options.parentStackInfo?.parentStack ?? stackName).split("~")[0],
|
|
33525
|
+
hole: "stack"
|
|
33526
|
+
}], recoveryCommandFlags(this.options.refusalRecovery).flags).command,
|
|
33527
|
+
createdResourceHolder: crossPrefixHolder === void 0 ? void 0 : () => crossPrefixAnswer ??= crossPrefixHolder(stackName)
|
|
33528
|
+
};
|
|
32640
33529
|
const inlinePolicyWriters = new RollbackInlinePolicyWriters();
|
|
32641
33530
|
const runId = this.options.eventRecorder?.runId;
|
|
32642
33531
|
const { result, failed, run } = await withNestedRevertRun(runId, async (scope) => {
|
|
@@ -32666,6 +33555,21 @@ async function performRollback(completedOperations, stateResources, stackName, p
|
|
|
32666
33555
|
remainingFailedOps: failedOperations.filter((_, i) => !handled.has(i))
|
|
32667
33556
|
};
|
|
32668
33557
|
}
|
|
33558
|
+
function foreignHolderResolver(engine) {
|
|
33559
|
+
const sameBucketHolderFor = makeForeignHolderScan(engine.stateBackend);
|
|
33560
|
+
const crossPrefixHolder = engine.options.crossPrefixHolder;
|
|
33561
|
+
const crossPrefixAnswers = /* @__PURE__ */ new Map();
|
|
33562
|
+
return (self) => async (resourceType, physicalId) => {
|
|
33563
|
+
const held = await sameBucketHolderFor(self)(resourceType, physicalId);
|
|
33564
|
+
if (held !== void 0 || crossPrefixHolder === void 0) return held;
|
|
33565
|
+
let answer = crossPrefixAnswers.get(self.stackName);
|
|
33566
|
+
if (answer === void 0) {
|
|
33567
|
+
answer = crossPrefixHolder(self.stackName);
|
|
33568
|
+
crossPrefixAnswers.set(self.stackName, answer);
|
|
33569
|
+
}
|
|
33570
|
+
return answer;
|
|
33571
|
+
};
|
|
33572
|
+
}
|
|
32669
33573
|
async function settleJournalAfterSuccess(stackName, completedOperations, previousState, finalResources, initialDeploy) {
|
|
32670
33574
|
if (this.options.parentStackInfo) {
|
|
32671
33575
|
await this.writeRollbackJournalSegment(stackName, completedOperations, [], NESTED_PENDING_PARENT_REASON, initialDeploy, nestedPendingSnapshot(previousState, (name) => {
|
|
@@ -32677,7 +33581,7 @@ async function settleJournalAfterSuccess(stackName, completedOperations, previou
|
|
|
32677
33581
|
return 0;
|
|
32678
33582
|
}
|
|
32679
33583
|
let nestedLeft = 0;
|
|
32680
|
-
const foreignHolderFor =
|
|
33584
|
+
const foreignHolderFor = foreignHolderResolver(this);
|
|
32681
33585
|
const deployRunId = this.options.eventRecorder?.runId;
|
|
32682
33586
|
const stripOnFailure = /* @__PURE__ */ new Map();
|
|
32683
33587
|
const [ownLeft] = await Promise.all([(async () => {
|
|
@@ -34197,6 +35101,13 @@ async function provisionCreate(logicalId, change, stateResources, stackName, tem
|
|
|
34197
35101
|
});
|
|
34198
35102
|
const createProvider = createDecision.provider;
|
|
34199
35103
|
const createProps = createDecision.provisionedBy === "cc-api" ? this.preparePropertiesForCcApi(resourceType, resolvedProps, logicalId) : resolvedProps;
|
|
35104
|
+
await refuseUnlicensedGeneratedName.call(this, {
|
|
35105
|
+
logicalId,
|
|
35106
|
+
resourceType,
|
|
35107
|
+
stackName,
|
|
35108
|
+
createdVia: createDecision.provisionedBy,
|
|
35109
|
+
resolvedProps
|
|
35110
|
+
});
|
|
34200
35111
|
await refuseTakenCreateName.call(this, {
|
|
34201
35112
|
logicalId,
|
|
34202
35113
|
resourceType,
|
|
@@ -34207,7 +35118,15 @@ async function provisionCreate(logicalId, change, stateResources, stackName, tem
|
|
|
34207
35118
|
secrets: createSecrets,
|
|
34208
35119
|
stateResources
|
|
34209
35120
|
});
|
|
34210
|
-
|
|
35121
|
+
this.generatedNameGuard?.noteSent(logicalId);
|
|
35122
|
+
let result;
|
|
35123
|
+
try {
|
|
35124
|
+
result = await this.withRetry(() => withCurrentResourceSecrets(createSecrets, () => createProvider.create(logicalId, resourceType, createProps, { maskSecrets: createSecretMasker(createSecrets) })), logicalId, void 0, void 0, createProvider);
|
|
35125
|
+
} catch (error) {
|
|
35126
|
+
this.generatedNameGuard?.noteFailed(logicalId, error);
|
|
35127
|
+
throw error;
|
|
35128
|
+
}
|
|
35129
|
+
this.generatedNameGuard?.noteReturned(logicalId);
|
|
34211
35130
|
this.registerNoEchoAttributes(logicalId, result, createSecrets, resolvedProps);
|
|
34212
35131
|
const dependencies = this.extractAllDependencies(template, logicalId);
|
|
34213
35132
|
const templateAttrs = this.extractTemplateAttributes(template, logicalId);
|
|
@@ -34235,6 +35154,46 @@ async function provisionCreate(logicalId, change, stateResources, stackName, tem
|
|
|
34235
35154
|
renderer.removeTask(logicalId);
|
|
34236
35155
|
this.logger.info(`${createPrefix}${formatResourceLine("created", logicalId, resourceType)}`);
|
|
34237
35156
|
}
|
|
35157
|
+
async function refuseUnlicensedGeneratedName(input) {
|
|
35158
|
+
const { logicalId, resourceType } = input;
|
|
35159
|
+
const asked = this.generatedNameGuard?.candidate(logicalId);
|
|
35160
|
+
if (asked === void 0 || input.createdVia === "cc-api") return;
|
|
35161
|
+
const verdict = await this.generatedNameGuard.admit(logicalId, input.resolvedProps);
|
|
35162
|
+
if (verdict === void 0) return;
|
|
35163
|
+
const subject = `${displaySafe(logicalId)} (${displaySafe(resourceType)})`;
|
|
35164
|
+
const named = `the cdkd-generated ${asked.property ?? "name"} ${displaySafe(asked.name)}`;
|
|
35165
|
+
const adoptsText = "its create API hands back or overwrites an existing resource of that name instead of refusing it";
|
|
35166
|
+
const refuse = (message) => {
|
|
35167
|
+
throw markNonRetryable(markRefusedBeforeApplying(new CdkdError(message, "GENERATED_NAME_HELD")));
|
|
35168
|
+
};
|
|
35169
|
+
switch (verdict.kind) {
|
|
35170
|
+
case "free":
|
|
35171
|
+
case "licensed": return;
|
|
35172
|
+
case "unchecked":
|
|
35173
|
+
this.logger.warn(safeMsg`${subject} is created with ${named} without checking whether another resource already ` + safeMsg`holds it: the lookup was refused (${describeAwsFailure(verdict.error).summary}). ` + "Grant this identity the type's Describe / List permission to have cdkd refuse taking over another deployment's resource.");
|
|
35174
|
+
return;
|
|
35175
|
+
case "failed":
|
|
35176
|
+
if (isInterruptedWaitError(verdict.error)) throw verdict.error;
|
|
35177
|
+
return refuse(`${subject} is created with ${named}, and ${adoptsText}, but cdkd could not check whether another resource already holds it (${describeAwsFailure(verdict.error).summary}). ${displaySafe(logicalId)} was not created. Re-run the deploy once the check can succeed.`);
|
|
35178
|
+
case "held": {
|
|
35179
|
+
const nested = input.stackName.includes("~");
|
|
35180
|
+
const top = input.stackName.split("~")[0];
|
|
35181
|
+
const importLine = nested ? `adopt it through the top-level stack ${quotedOrDescribed(top, "stack name")} with \`cdkd import\`` : `adopt it with \`${pasteableCommand("cdkd import", [{
|
|
35182
|
+
value: input.stackName,
|
|
35183
|
+
hole: "stack"
|
|
35184
|
+
}, {
|
|
35185
|
+
flag: "--resource",
|
|
35186
|
+
value: `${logicalId}=${verdict.holder}`,
|
|
35187
|
+
hole: "logicalId=physicalId"
|
|
35188
|
+
}], recoveryCommandFlags(this.options.refusalRecovery).flags).command}\``;
|
|
35189
|
+
if (verdict.ownIntent !== void 0) {
|
|
35190
|
+
const why = verdict.ownIntent === "abandoned" ? "a deploy of this stack that was killed or force-unlocked" : "a create of this stack that came back failed (a timeout or a server error)";
|
|
35191
|
+
return refuse(`${subject} would be created with ${named}, which an existing resource (${displaySafe(verdict.holder)}) already holds. This stack's create-token ledger records that ${why} sent this name, but this type reports no creation time, so cdkd cannot tell that resource is the one it made rather than another deployment's created since. Since ${adoptsText}, creating it would take that resource over. ${displaySafe(logicalId)} was not created. The likely cause: the resource is this stack's own, from that deploy. If it is, ${importLine} and re-run; if it is not, this stack is also deployed under another state backend (go-to-k/cdkd#4705).`);
|
|
35192
|
+
}
|
|
35193
|
+
return refuse(`${subject} would be created with ${named}, which an existing resource (${displaySafe(verdict.holder)}) already holds, and nothing this stack records names that resource: not its state, its rollback journal or create-token ledger, nor what a destroy of it under this state prefix kept. Since ${adoptsText}, creating it would take that resource over, as CloudFormation refuses with "already exists". ${displaySafe(logicalId)} was not created. The likely cause: this stack is also deployed under another state backend (another --state-prefix, --state-bucket or account), where cdkd generates the same names. One stack name per account and region is supported (go-to-k/cdkd#4705): deploy it under one backend only, or give this stack another name. If the resource is this stack's own, ${importLine} and re-run.`);
|
|
35194
|
+
}
|
|
35195
|
+
}
|
|
35196
|
+
}
|
|
34238
35197
|
async function refuseTakenCreateName(input) {
|
|
34239
35198
|
const { logicalId, resourceType, secrets } = input;
|
|
34240
35199
|
const question = createNameQuestion({
|
|
@@ -34517,6 +35476,9 @@ function isResourceNotFoundError(error) {
|
|
|
34517
35476
|
|
|
34518
35477
|
//#endregion
|
|
34519
35478
|
//#region src/provisioning/providers/dynamodb-globaltable-provider.ts
|
|
35479
|
+
function isTableName(id) {
|
|
35480
|
+
return /^[A-Za-z0-9_.-]{3,255}$/.test(id);
|
|
35481
|
+
}
|
|
34520
35482
|
const DRIFT_STRIPPED_INDEX_MEMBERS = [{
|
|
34521
35483
|
listKey: "GlobalSecondaryIndexes",
|
|
34522
35484
|
member: "WarmThroughput"
|
|
@@ -34757,8 +35719,9 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
34757
35719
|
toSdkReplicaThroughputOverrides(replica, billingMode, diagnostics, {}, maskSecrets);
|
|
34758
35720
|
}
|
|
34759
35721
|
this.reportThroughputDiagnostics(diagnostics, logicalId, tableName, maskSecrets);
|
|
35722
|
+
let createdTableId;
|
|
34760
35723
|
try {
|
|
34761
|
-
await (await this.getCreateClient()).send(new CreateTableCommand(createParams));
|
|
35724
|
+
createdTableId = (await (await this.getCreateClient()).send(new CreateTableCommand(createParams)))?.TableDescription?.TableId;
|
|
34762
35725
|
log.debug(`CreateTable initiated for ${log.value(tableName)}, waiting for ACTIVE`);
|
|
34763
35726
|
} catch (error) {
|
|
34764
35727
|
const cause = error instanceof Error ? error : void 0;
|
|
@@ -34834,7 +35797,7 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
34834
35797
|
markAuxiliaryFailure(wiringError, logicalId);
|
|
34835
35798
|
const cause = wiringError instanceof Error ? wiringError : void 0;
|
|
34836
35799
|
const thrown = this.wrapMaskedError(log, wiringError, (text) => new ProvisioningError(`Failed to create DynamoDB GlobalTable ${logicalId}: ${text}`, resourceType, logicalId, tableName, cause));
|
|
34837
|
-
if (tableLeftBehind) markCreatedBeforeFailure(thrown, logicalId, resourceType, tableName);
|
|
35800
|
+
if (tableLeftBehind) markCreatedBeforeFailure(thrown, logicalId, resourceType, tableName, createdTableId);
|
|
34838
35801
|
throw thrown;
|
|
34839
35802
|
}
|
|
34840
35803
|
}
|
|
@@ -35770,7 +36733,8 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
35770
36733
|
if (error instanceof ResourceNotFoundException$1) {
|
|
35771
36734
|
const clientRegion = await this.dynamoDBClient.config.region();
|
|
35772
36735
|
assertRegionMatch(clientRegion, context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
35773
|
-
this.logger.
|
|
36736
|
+
if (context?.failedCreateOrphan === true) this.logger.info(safeMsg` DynamoDB GlobalTable ${physicalId} (${logicalId}), which a failed deploy created, is already gone; nothing to delete`);
|
|
36737
|
+
else this.logger.debug(`DynamoDB GlobalTable ${physicalId} does not exist, skipping`);
|
|
35774
36738
|
this.deleteBudgets.release(deleteBudgetKey(physicalId, context?.expectedRegion));
|
|
35775
36739
|
this.protectionFlips.release(deleteBudgetKey(physicalId, context?.expectedRegion));
|
|
35776
36740
|
return;
|
|
@@ -35779,6 +36743,36 @@ var DynamoDBGlobalTableProvider = class {
|
|
|
35779
36743
|
throw new ProvisioningError(`Failed to delete DynamoDB GlobalTable ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
|
|
35780
36744
|
}
|
|
35781
36745
|
}
|
|
36746
|
+
async isSameResource(journaledPhysicalId, record, resourceType, context) {
|
|
36747
|
+
if (resourceType !== "AWS::DynamoDB::GlobalTable") return "unknown";
|
|
36748
|
+
if (!isTableName(journaledPhysicalId) || !isTableName(record.physicalId)) return "unknown";
|
|
36749
|
+
if (journaledPhysicalId === record.physicalId) return "same";
|
|
36750
|
+
if (await this.dynamoDBClient.config.region() !== context.expectedRegion) return "unknown";
|
|
36751
|
+
const recordTableId = await this.readTableIdIfExists(record.physicalId);
|
|
36752
|
+
if (recordTableId === void 0) return "unknown";
|
|
36753
|
+
const journaledTableId = await this.readTableIdIfExists(journaledPhysicalId);
|
|
36754
|
+
if (journaledTableId === void 0) return "different";
|
|
36755
|
+
return journaledTableId === recordTableId ? "same" : "different";
|
|
36756
|
+
}
|
|
36757
|
+
async resourceIdentity(physicalId, resourceType, context) {
|
|
36758
|
+
if (resourceType !== "AWS::DynamoDB::GlobalTable" || !isTableName(physicalId)) return;
|
|
36759
|
+
if (await this.dynamoDBClient.config.region() !== context.expectedRegion) return void 0;
|
|
36760
|
+
const live = await this.readTableIdIfExists(physicalId);
|
|
36761
|
+
return live === void 0 ? RESOURCE_NOT_FOUND : live;
|
|
36762
|
+
}
|
|
36763
|
+
async readTableIdIfExists(tableName) {
|
|
36764
|
+
let response;
|
|
36765
|
+
try {
|
|
36766
|
+
response = await this.dynamoDBClient.send(new DescribeTableCommand({ TableName: tableName }));
|
|
36767
|
+
} catch (error) {
|
|
36768
|
+
if (error instanceof ResourceNotFoundException$1) return void 0;
|
|
36769
|
+
throw error;
|
|
36770
|
+
}
|
|
36771
|
+
if (response.Table?.TableName !== tableName) throw new Error("DescribeTable answered for another table than the one asked for");
|
|
36772
|
+
const tableId = response.Table.TableId;
|
|
36773
|
+
if (typeof tableId !== "string" || tableId === "") throw new Error("DescribeTable returned no TableId");
|
|
36774
|
+
return tableId;
|
|
36775
|
+
}
|
|
35782
36776
|
async getAttribute(physicalId, _resourceType, attributeName) {
|
|
35783
36777
|
const cacheKey = injectiveKey(physicalId, attributeName);
|
|
35784
36778
|
if (this.attributeCache.has(cacheKey)) return this.attributeCache.get(cacheKey);
|
|
@@ -37450,11 +38444,11 @@ const NESTED_STACK_TYPE$1 = "AWS::CloudFormation::Stack";
|
|
|
37450
38444
|
async function requireDeploymentApproval(args) {
|
|
37451
38445
|
const level = args.options.requireApproval ?? "never";
|
|
37452
38446
|
const approve = args.options.approveDeployment;
|
|
37453
|
-
if (level === "never" || approve === void 0) return;
|
|
38447
|
+
if (level === "never" || approve === void 0) return false;
|
|
37454
38448
|
const changes = [...args.changes].filter((c) => c.changeType !== "NO_CHANGE" && !isNoEchoPromotionOnly(c));
|
|
37455
38449
|
const destructiveChanges = findDestructiveChanges(args.stackName, changes, args.records, args.template, new Set(args.recreateTargetIds ?? []));
|
|
37456
38450
|
const onlyNestedUpdates = changes.every((c) => c.changeType === "UPDATE" && c.resourceType === NESTED_STACK_TYPE$1);
|
|
37457
|
-
if (!(level === "destructive" ? destructiveChanges.length > 0 : changes.length > 0 && !onlyNestedUpdates)) return;
|
|
38451
|
+
if (!(level === "destructive" ? destructiveChanges.length > 0 : changes.length > 0 && !onlyNestedUpdates)) return false;
|
|
37458
38452
|
const count = (type) => changes.filter((c) => c.changeType === type).length;
|
|
37459
38453
|
if (enclosingDeadlineExpired()) throw approvalAfterTimeout(args.stackName);
|
|
37460
38454
|
let approved;
|
|
@@ -37473,6 +38467,16 @@ async function requireDeploymentApproval(args) {
|
|
|
37473
38467
|
throw error instanceof Error ? markNonRetryable(error) : error;
|
|
37474
38468
|
}
|
|
37475
38469
|
if (!approved) throw markNonRetryable(new CdkdError(safeMsg`Deployment of stack ${args.stackName} was not approved (--require-approval=${level}). Nothing was changed.`, "DEPLOY_NOT_APPROVED"));
|
|
38470
|
+
return approve.autoApproves !== true;
|
|
38471
|
+
}
|
|
38472
|
+
async function checkDestructivePlan(args) {
|
|
38473
|
+
const hook = args.options.onDestructivePlan;
|
|
38474
|
+
if (hook === void 0) return;
|
|
38475
|
+
const changes = [...args.changes].filter((c) => c.changeType !== "NO_CHANGE" && !isNoEchoPromotionOnly(c));
|
|
38476
|
+
const destroying = findDestructiveChanges(args.stackName, changes, args.records, args.template, new Set(args.recreateTargetIds ?? [])).filter((c) => c.impact !== "WILL_ORPHAN");
|
|
38477
|
+
const nestedRowChanges = changes.some((c) => c.resourceType === NESTED_STACK_TYPE$1 && (c.changeType === "CREATE" || c.changeType === "UPDATE"));
|
|
38478
|
+
if (destroying.length === 0 && !nestedRowChanges) return;
|
|
38479
|
+
await hook(args.stackName, destroying);
|
|
37476
38480
|
}
|
|
37477
38481
|
async function approveLateReplacement(args) {
|
|
37478
38482
|
const level = args.options.requireApproval ?? "never";
|
|
@@ -37480,7 +38484,7 @@ async function approveLateReplacement(args) {
|
|
|
37480
38484
|
if (level === "never" || approve === void 0) return true;
|
|
37481
38485
|
if (enclosingDeadlineExpired()) return false;
|
|
37482
38486
|
try {
|
|
37483
|
-
|
|
38487
|
+
const approved = await whileEnclosingDeadlinesPaused(() => approve({
|
|
37484
38488
|
stackName: args.stackName,
|
|
37485
38489
|
level,
|
|
37486
38490
|
counts: {
|
|
@@ -37490,6 +38494,8 @@ async function approveLateReplacement(args) {
|
|
|
37490
38494
|
},
|
|
37491
38495
|
destructiveChanges: findDestructiveChanges(args.stackName, [args.change], args.records, args.template)
|
|
37492
38496
|
}));
|
|
38497
|
+
if (approve.autoApproves !== true) args.onAsked?.();
|
|
38498
|
+
return approved;
|
|
37493
38499
|
} catch {
|
|
37494
38500
|
return false;
|
|
37495
38501
|
}
|
|
@@ -37836,27 +38842,42 @@ async function provisionUpdate(logicalId, change, stateResources, stackName, tem
|
|
|
37836
38842
|
};
|
|
37837
38843
|
let replacedAnyway = false;
|
|
37838
38844
|
let lateApproval;
|
|
38845
|
+
let lateCrossPrefixRefused = false;
|
|
37839
38846
|
const approveReplacementOf = (pc) => {
|
|
37840
38847
|
if (replacedAnyway) return Promise.resolve(true);
|
|
37841
38848
|
const { noEchoPromoted: _promoted, ...asReplacement } = pc;
|
|
37842
|
-
|
|
37843
|
-
|
|
37844
|
-
|
|
37845
|
-
|
|
37846
|
-
...
|
|
37847
|
-
|
|
37848
|
-
|
|
37849
|
-
|
|
37850
|
-
|
|
37851
|
-
|
|
37852
|
-
|
|
37853
|
-
|
|
37854
|
-
|
|
37855
|
-
|
|
38849
|
+
const asChange = {
|
|
38850
|
+
...change,
|
|
38851
|
+
changeType: "UPDATE",
|
|
38852
|
+
propertyChanges: [{
|
|
38853
|
+
...asReplacement,
|
|
38854
|
+
requiresReplacement: true
|
|
38855
|
+
}]
|
|
38856
|
+
};
|
|
38857
|
+
lateApproval ??= (async () => {
|
|
38858
|
+
const crossPrefixGate = this.options.onDestructivePlan;
|
|
38859
|
+
if (crossPrefixGate !== void 0) try {
|
|
38860
|
+
await crossPrefixGate(stackName, findDestructiveChanges(stackName, [asChange], stateResources, template), "late");
|
|
38861
|
+
} catch (error) {
|
|
38862
|
+
if (!(error instanceof CdkdError) || error.code !== "STACK_UNDER_OTHER_STATE_PREFIX") throw error;
|
|
38863
|
+
lateCrossPrefixRefused = true;
|
|
38864
|
+
if (counts) counts.crossPrefixKept = (counts.crossPrefixKept ?? 0) + 1;
|
|
38865
|
+
this.logger.warn(error.message);
|
|
38866
|
+
return false;
|
|
38867
|
+
}
|
|
38868
|
+
return approveLateReplacement({
|
|
38869
|
+
options: this.options,
|
|
38870
|
+
stackName,
|
|
38871
|
+
change: asChange,
|
|
38872
|
+
records: stateResources,
|
|
38873
|
+
template,
|
|
38874
|
+
onAsked: () => this.generatedNameGuard?.noteApprovalPrompted()
|
|
38875
|
+
});
|
|
38876
|
+
})();
|
|
37856
38877
|
return lateApproval;
|
|
37857
38878
|
};
|
|
37858
38879
|
const lateReplacementDeclined = /* @__PURE__ */ new Set();
|
|
37859
|
-
const differsWhy = (key) => lateReplacementDeclined.has(key) ?
|
|
38880
|
+
const differsWhy = (key) => !lateReplacementDeclined.has(key) ? "differs; the provider is not known to report this property exactly, so the difference may be its normalization" : lateCrossPrefixRefused ? "differs; the replacement was refused: see the state-prefix warning above" : `differs; the replacement was not approved (--require-approval=${this.options.requireApproval ?? "never"})`;
|
|
37860
38881
|
if (unmarkedRecord && !typeChanged && echoCandidates.length > 0 && hasAddressablePhysicalId(currentResource)) await readOnce();
|
|
37861
38882
|
if (!typeChanged && !suppliesFreshMaskOnlyValue && lostChild === void 0 && this.recreateDirectionFor(stackName, logicalId) === void 0 && recordMatchesDesired) {
|
|
37862
38883
|
if (change.attributeChanges && change.attributeChanges.length > 0) {
|
|
@@ -38649,8 +39670,83 @@ async function updateInPlace({ conditions, counts, currentProps, currentPropsAsW
|
|
|
38649
39670
|
this.logger.info(`${updatePrefix}${formatResourceLine("updated", logicalId, resourceType)}`);
|
|
38650
39671
|
}
|
|
38651
39672
|
|
|
39673
|
+
//#endregion
|
|
39674
|
+
//#region src/deployment/retained-readoption.ts
|
|
39675
|
+
function keptForReadoption(resource) {
|
|
39676
|
+
if (!replacementCreateAdoptsName(resource.resourceType, resource.provisionedBy)) return false;
|
|
39677
|
+
if (typeof resource.physicalId !== "string" || resource.physicalId === "") return false;
|
|
39678
|
+
const property = explicitNamePropertyFor(resource.resourceType);
|
|
39679
|
+
const properties = resource.properties;
|
|
39680
|
+
return property === void 0 || !properties?.[property];
|
|
39681
|
+
}
|
|
39682
|
+
async function recordRetainedForReadoption(backend, stackName, region, kept, logger, earlier) {
|
|
39683
|
+
if (kept.length === 0) {
|
|
39684
|
+
try {
|
|
39685
|
+
await backend.ensureRetainedTombstone(stackName, region);
|
|
39686
|
+
} catch (error) {
|
|
39687
|
+
logger.warn(safeMsg`Could not write the empty kept-resource record of ${displayStackName(stackName)} ` + safeMsg`(${describeAwsFailure(error).summary}). Until one exists, a later deploy here may ` + `take back a resource an older cdkd kept; 'cdkd state orphan' writes it.`);
|
|
39688
|
+
}
|
|
39689
|
+
return;
|
|
39690
|
+
}
|
|
39691
|
+
try {
|
|
39692
|
+
const keptIds = new Set(kept.map((k) => k.logicalId));
|
|
39693
|
+
const before = await (earlier ?? backend.loadRetainedRecord(stackName, region)) ?? [];
|
|
39694
|
+
await backend.saveRetainedResources(stackName, region, [...before.filter((e) => !keptIds.has(e.logicalId)), ...kept]);
|
|
39695
|
+
} catch (error) {
|
|
39696
|
+
if (error instanceof RetainedTimeUnconfirmedError) {
|
|
39697
|
+
logger.warn(safeMsg`Recorded the ${String(kept.length)} kept resource(s) of ${displayStackName(stackName)} ` + safeMsg`a later deploy takes back by name, but their time could not be confirmed from S3 ` + safeMsg`(${describeAwsFailure(error.cause).summary}); they carry this machine's clock instead.`);
|
|
39698
|
+
return;
|
|
39699
|
+
}
|
|
39700
|
+
logger.warn(safeMsg`Could not record the ${String(kept.length)} kept resource(s) of ${displayStackName(stackName)} ` + safeMsg`a later deploy takes back by name (${describeAwsFailure(error).summary}). That deploy ` + `refuses to create them over the kept ones; adopt them with 'cdkd import' then.`);
|
|
39701
|
+
}
|
|
39702
|
+
}
|
|
39703
|
+
var KeptForReadoption = class {
|
|
39704
|
+
entries = [];
|
|
39705
|
+
earlier;
|
|
39706
|
+
inFlight;
|
|
39707
|
+
backend;
|
|
39708
|
+
stackName;
|
|
39709
|
+
region;
|
|
39710
|
+
logger;
|
|
39711
|
+
constructor(backend, stackName, region, logger) {
|
|
39712
|
+
this.backend = backend;
|
|
39713
|
+
this.stackName = stackName;
|
|
39714
|
+
this.region = region;
|
|
39715
|
+
this.logger = logger;
|
|
39716
|
+
}
|
|
39717
|
+
get size() {
|
|
39718
|
+
return this.entries.length;
|
|
39719
|
+
}
|
|
39720
|
+
add(entry) {
|
|
39721
|
+
this.entries.push(entry);
|
|
39722
|
+
if (this.earlier === void 0) {
|
|
39723
|
+
this.earlier = Promise.resolve().then(() => this.backend.loadRetainedRecord(this.stackName, this.region));
|
|
39724
|
+
this.earlier.catch(() => void 0);
|
|
39725
|
+
}
|
|
39726
|
+
}
|
|
39727
|
+
async flush() {
|
|
39728
|
+
if (this.inFlight !== void 0) await this.inFlight;
|
|
39729
|
+
if (this.entries.length === 0) return;
|
|
39730
|
+
const kept = this.entries;
|
|
39731
|
+
const earlier = this.earlier;
|
|
39732
|
+
this.entries = [];
|
|
39733
|
+
this.earlier = void 0;
|
|
39734
|
+
const write = recordRetainedForReadoption(this.backend, this.stackName, this.region, kept, this.logger, earlier);
|
|
39735
|
+
this.inFlight = write;
|
|
39736
|
+
try {
|
|
39737
|
+
await write;
|
|
39738
|
+
} finally {
|
|
39739
|
+
if (this.inFlight === write) this.inFlight = void 0;
|
|
39740
|
+
}
|
|
39741
|
+
}
|
|
39742
|
+
};
|
|
39743
|
+
|
|
38652
39744
|
//#endregion
|
|
38653
39745
|
//#region src/deployment/deploy-engine/delete.ts
|
|
39746
|
+
const keptByEngine = /* @__PURE__ */ new WeakMap();
|
|
39747
|
+
async function flushKeptForReadoption(engine) {
|
|
39748
|
+
await keptByEngine.get(engine)?.flush();
|
|
39749
|
+
}
|
|
38654
39750
|
async function provisionDelete(logicalId, change, stateResources, stackName, template, counts, progress) {
|
|
38655
39751
|
const resourceType = change.resourceType;
|
|
38656
39752
|
const existingState = stateResources[logicalId];
|
|
@@ -38662,6 +39758,18 @@ async function provisionDelete(logicalId, change, stateResources, stackName, tem
|
|
|
38662
39758
|
this.logger.info(`Retaining ${logicalId} (${resourceType}) - DeletionPolicy: ${deletionPolicy}`);
|
|
38663
39759
|
delete stateResources[logicalId];
|
|
38664
39760
|
await noteRetainedResource(resourceType, logicalId);
|
|
39761
|
+
if (keptForReadoption(currentResource)) {
|
|
39762
|
+
let kept = keptByEngine.get(this);
|
|
39763
|
+
if (kept === void 0) {
|
|
39764
|
+
kept = new KeptForReadoption(this.stateBackend, stackName, this.stackRegion, this.logger);
|
|
39765
|
+
keptByEngine.set(this, kept);
|
|
39766
|
+
}
|
|
39767
|
+
kept.add({
|
|
39768
|
+
logicalId,
|
|
39769
|
+
resourceType,
|
|
39770
|
+
physicalId: currentResource.physicalId
|
|
39771
|
+
});
|
|
39772
|
+
}
|
|
38665
39773
|
return;
|
|
38666
39774
|
}
|
|
38667
39775
|
if (!(currentResource.resourceType === "AWS::CloudFormation::Stack" && currentResource.provisionedBy !== "cc-api") && !hasAddressablePhysicalId(currentResource)) {
|
|
@@ -39548,6 +40656,707 @@ function journaledOrphanPolicy(policy) {
|
|
|
39548
40656
|
return policy === "Delete" || policy === "Retain" || policy === "Snapshot" || policy === "RetainExceptOnCreate" ? policy : "Retain";
|
|
39549
40657
|
}
|
|
39550
40658
|
|
|
40659
|
+
//#endregion
|
|
40660
|
+
//#region src/provisioning/name-lookup.ts
|
|
40661
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
40662
|
+
const waiting = /* @__PURE__ */ new Map();
|
|
40663
|
+
async function withApiLimit(api, max, fn) {
|
|
40664
|
+
if ((inFlight.get(api) ?? 0) >= max) await new Promise((resolve) => {
|
|
40665
|
+
const queue = waiting.get(api) ?? [];
|
|
40666
|
+
queue.push(resolve);
|
|
40667
|
+
waiting.set(api, queue);
|
|
40668
|
+
});
|
|
40669
|
+
else inFlight.set(api, (inFlight.get(api) ?? 0) + 1);
|
|
40670
|
+
try {
|
|
40671
|
+
return await fn();
|
|
40672
|
+
} finally {
|
|
40673
|
+
const next = waiting.get(api)?.shift();
|
|
40674
|
+
if (next) next();
|
|
40675
|
+
else inFlight.set(api, (inFlight.get(api) ?? 1) - 1);
|
|
40676
|
+
}
|
|
40677
|
+
}
|
|
40678
|
+
function chunks(items, size) {
|
|
40679
|
+
const out = [];
|
|
40680
|
+
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
40681
|
+
return out;
|
|
40682
|
+
}
|
|
40683
|
+
async function lookupEachName(names, api, concurrency, lookupOne) {
|
|
40684
|
+
const found = /* @__PURE__ */ new Map();
|
|
40685
|
+
await Promise.all(names.map((name) => withApiLimit(api, concurrency, async () => {
|
|
40686
|
+
const holder = await lookupOne(name);
|
|
40687
|
+
if (holder !== void 0) found.set(name, holder);
|
|
40688
|
+
})));
|
|
40689
|
+
return found;
|
|
40690
|
+
}
|
|
40691
|
+
function isAccessDeniedError(error) {
|
|
40692
|
+
const name = error?.name;
|
|
40693
|
+
const status = error?.$metadata?.httpStatusCode;
|
|
40694
|
+
return name === "AccessDenied" || name === "AccessDeniedException" || name === "AuthorizationError" || name === "UnauthorizedOperation" || status === 403;
|
|
40695
|
+
}
|
|
40696
|
+
|
|
40697
|
+
//#endregion
|
|
40698
|
+
//#region src/deployment/generated-name-guard.ts
|
|
40699
|
+
const GROUP_WINDOW_MS = 15;
|
|
40700
|
+
const DEFAULT_TIMING = {
|
|
40701
|
+
now: () => Date.now(),
|
|
40702
|
+
sleep: (ms) => new Promise((resolve) => {
|
|
40703
|
+
setTimeout(resolve, ms).unref?.();
|
|
40704
|
+
}),
|
|
40705
|
+
cooldownMs: 65e3,
|
|
40706
|
+
cooldownStepMs: 1e4
|
|
40707
|
+
};
|
|
40708
|
+
const STILL_LISTED_WHILE_DELETING = /* @__PURE__ */ new Set(["AWS::SQS::Queue", "AWS::S3::Bucket"]);
|
|
40709
|
+
const PER_NAME_CONCURRENCY = { "AWS::SNS::Topic": 4 };
|
|
40710
|
+
const DEFAULT_PER_NAME_CONCURRENCY = 10;
|
|
40711
|
+
const KEPT_AT_SKEW_MS = 6e4;
|
|
40712
|
+
function provenNothingCreated(error, logicalId, resourceType) {
|
|
40713
|
+
if (createdBeforeFailure(error, logicalId, resourceType) !== void 0) return false;
|
|
40714
|
+
if (isAmbiguousOutcomeError(error) || isThrottlingError(error)) return false;
|
|
40715
|
+
let current = error;
|
|
40716
|
+
for (let depth = 0; depth < 8 && current !== null && typeof current === "object"; depth++) {
|
|
40717
|
+
const status = current.$metadata?.httpStatusCode;
|
|
40718
|
+
if (typeof status === "number") return status >= 400 && status < 500 && status !== 408 && status !== 429;
|
|
40719
|
+
current = current.cause;
|
|
40720
|
+
}
|
|
40721
|
+
return false;
|
|
40722
|
+
}
|
|
40723
|
+
var GeneratedNameGuard = class GeneratedNameGuard {
|
|
40724
|
+
verdicts = /* @__PURE__ */ new Map();
|
|
40725
|
+
decidedAt = /* @__PURE__ */ new Map();
|
|
40726
|
+
candidates = /* @__PURE__ */ new Map();
|
|
40727
|
+
recorded = /* @__PURE__ */ new Set();
|
|
40728
|
+
sent = /* @__PURE__ */ new Set();
|
|
40729
|
+
returned = /* @__PURE__ */ new Set();
|
|
40730
|
+
rejected = /* @__PURE__ */ new Set();
|
|
40731
|
+
failedAt = /* @__PURE__ */ new Map();
|
|
40732
|
+
firstVerdicts = /* @__PURE__ */ new Map();
|
|
40733
|
+
plannedWrites;
|
|
40734
|
+
plannedIds = /* @__PURE__ */ new Set();
|
|
40735
|
+
groupPending = [];
|
|
40736
|
+
groupNext;
|
|
40737
|
+
groupInFlight = Promise.resolve();
|
|
40738
|
+
queued = [];
|
|
40739
|
+
flushing;
|
|
40740
|
+
writes = /* @__PURE__ */ new Set();
|
|
40741
|
+
evidenceRead;
|
|
40742
|
+
abandonedRead;
|
|
40743
|
+
historyRead;
|
|
40744
|
+
recordedRead;
|
|
40745
|
+
input;
|
|
40746
|
+
timing;
|
|
40747
|
+
constructor(input) {
|
|
40748
|
+
this.input = input;
|
|
40749
|
+
this.timing = {
|
|
40750
|
+
...DEFAULT_TIMING,
|
|
40751
|
+
...input.timing
|
|
40752
|
+
};
|
|
40753
|
+
}
|
|
40754
|
+
static start(input) {
|
|
40755
|
+
const candidates = [];
|
|
40756
|
+
for (const change of input.changes) {
|
|
40757
|
+
if (change.changeType !== "CREATE") continue;
|
|
40758
|
+
const properties = change.desiredProperties ?? {};
|
|
40759
|
+
let decision;
|
|
40760
|
+
let name;
|
|
40761
|
+
let deferred = false;
|
|
40762
|
+
try {
|
|
40763
|
+
decision = input.providerFor({
|
|
40764
|
+
resourceType: change.resourceType,
|
|
40765
|
+
properties
|
|
40766
|
+
});
|
|
40767
|
+
if (!replacementCreateAdoptsName(change.resourceType, decision.provisionedBy)) continue;
|
|
40768
|
+
name = decision.provider.generatedCreateName?.(change.resourceType, change.logicalId, properties);
|
|
40769
|
+
deferred = decision.provider.lookupNeedsResolvedProperties?.(change.resourceType, properties) === true;
|
|
40770
|
+
} catch {
|
|
40771
|
+
continue;
|
|
40772
|
+
}
|
|
40773
|
+
if (typeof name !== "string" || name === "") continue;
|
|
40774
|
+
candidates.push({
|
|
40775
|
+
logicalId: change.logicalId,
|
|
40776
|
+
resourceType: change.resourceType,
|
|
40777
|
+
name,
|
|
40778
|
+
property: explicitNamePropertyFor(change.resourceType),
|
|
40779
|
+
provider: decision.provider,
|
|
40780
|
+
properties,
|
|
40781
|
+
deferred
|
|
40782
|
+
});
|
|
40783
|
+
}
|
|
40784
|
+
if (candidates.length === 0) return void 0;
|
|
40785
|
+
const guard = new GeneratedNameGuard(input);
|
|
40786
|
+
for (const c of candidates) guard.candidates.set(c.logicalId, c);
|
|
40787
|
+
guard.ask(candidates.filter((c) => !c.deferred));
|
|
40788
|
+
return guard;
|
|
40789
|
+
}
|
|
40790
|
+
ask(candidates) {
|
|
40791
|
+
const { verdicts, first } = this.resolve(candidates);
|
|
40792
|
+
for (const [logicalId, verdict] of verdicts) this.verdicts.set(logicalId, verdict);
|
|
40793
|
+
for (const [logicalId, verdict] of first) this.firstVerdicts.set(logicalId, verdict);
|
|
40794
|
+
}
|
|
40795
|
+
resolve(candidates) {
|
|
40796
|
+
const out = /* @__PURE__ */ new Map();
|
|
40797
|
+
const first = /* @__PURE__ */ new Map();
|
|
40798
|
+
const byType = /* @__PURE__ */ new Map();
|
|
40799
|
+
for (const c of candidates) byType.set(c.resourceType, [...byType.get(c.resourceType) ?? [], c]);
|
|
40800
|
+
for (const group of byType.values()) {
|
|
40801
|
+
const decided = this.run(group).catch((error) => ({
|
|
40802
|
+
verdicts: new Map(group.map((c) => [c.logicalId, {
|
|
40803
|
+
kind: "failed",
|
|
40804
|
+
error
|
|
40805
|
+
}])),
|
|
40806
|
+
deleting: []
|
|
40807
|
+
}));
|
|
40808
|
+
let waited;
|
|
40809
|
+
for (const c of group) {
|
|
40810
|
+
first.set(c.logicalId, decided.then(({ verdicts }) => verdicts.get(c.logicalId) ?? { kind: "free" }));
|
|
40811
|
+
out.set(c.logicalId, decided.then(async ({ verdicts, deleting }) => {
|
|
40812
|
+
let verdict = verdicts.get(c.logicalId) ?? { kind: "free" };
|
|
40813
|
+
if (deleting.includes(c)) {
|
|
40814
|
+
waited ??= this.waitOutDeletion(deleting);
|
|
40815
|
+
verdict = (await waited).get(c.logicalId) ?? verdict;
|
|
40816
|
+
}
|
|
40817
|
+
this.decidedAt.set(c.logicalId, this.timing.now());
|
|
40818
|
+
return verdict;
|
|
40819
|
+
}));
|
|
40820
|
+
}
|
|
40821
|
+
}
|
|
40822
|
+
return {
|
|
40823
|
+
verdicts: out,
|
|
40824
|
+
first
|
|
40825
|
+
};
|
|
40826
|
+
}
|
|
40827
|
+
noteApprovalPrompted() {
|
|
40828
|
+
const stale = [...this.decidedAt.keys()].map((id) => this.candidates.get(id)).filter((c) => !c.deferred && !this.sent.has(c.logicalId));
|
|
40829
|
+
if (stale.length === 0) return;
|
|
40830
|
+
const previous = new Map(stale.map((c) => [c.logicalId, this.verdicts.get(c.logicalId)]));
|
|
40831
|
+
const { verdicts, first } = this.resolve(stale);
|
|
40832
|
+
const keepEarlier = (id, fresh, warn) => fresh.then(async (v) => {
|
|
40833
|
+
if (v.kind !== "failed" && v.kind !== "unchecked") return v;
|
|
40834
|
+
const earlier = await previous.get(id);
|
|
40835
|
+
if (earlier.kind !== "free" && earlier.kind !== "licensed") return v;
|
|
40836
|
+
if (warn) this.input.warn?.(`${displaySafe(id)}: could not re-read whether a resource holds its generated name after the approval prompt; acting on the earlier lookup.`);
|
|
40837
|
+
return earlier;
|
|
40838
|
+
});
|
|
40839
|
+
for (const [id, fresh] of verdicts) this.verdicts.set(id, keepEarlier(id, fresh, true));
|
|
40840
|
+
for (const [id, fresh] of first) this.firstVerdicts.set(id, keepEarlier(id, fresh, false));
|
|
40841
|
+
}
|
|
40842
|
+
recordPlannedIntents() {
|
|
40843
|
+
if (this.plannedWrites !== void 0) return;
|
|
40844
|
+
this.plannedWrites = /* @__PURE__ */ new Map();
|
|
40845
|
+
const byType = /* @__PURE__ */ new Map();
|
|
40846
|
+
for (const c of this.candidates.values()) {
|
|
40847
|
+
if (c.deferred) continue;
|
|
40848
|
+
byType.set(c.resourceType, [...byType.get(c.resourceType) ?? [], c]);
|
|
40849
|
+
}
|
|
40850
|
+
let typesWaiting = byType.size;
|
|
40851
|
+
let allArrived;
|
|
40852
|
+
const arrived = new Promise((resolve) => allArrived = resolve);
|
|
40853
|
+
const window = new Promise((resolve) => {
|
|
40854
|
+
setTimeout(resolve, GROUP_WINDOW_MS).unref?.();
|
|
40855
|
+
});
|
|
40856
|
+
this.groupInFlight = Promise.race([arrived, window]);
|
|
40857
|
+
const arrive = () => {
|
|
40858
|
+
typesWaiting--;
|
|
40859
|
+
if (typesWaiting === 0) allArrived();
|
|
40860
|
+
};
|
|
40861
|
+
for (const [resourceType, group] of byType) {
|
|
40862
|
+
const write = (async () => {
|
|
40863
|
+
let verdicts;
|
|
40864
|
+
try {
|
|
40865
|
+
verdicts = await Promise.all(group.map((c) => this.firstVerdicts.get(c.logicalId) ?? this.verdicts.get(c.logicalId)));
|
|
40866
|
+
} finally {
|
|
40867
|
+
arrive();
|
|
40868
|
+
}
|
|
40869
|
+
const batch = group.filter((_c, i) => verdicts[i].kind === "free" || verdicts[i].kind === "licensed");
|
|
40870
|
+
if (batch.length === 0) return;
|
|
40871
|
+
for (const c of batch) this.plannedIds.add(c.logicalId);
|
|
40872
|
+
await this.commitPlanned(batch);
|
|
40873
|
+
})();
|
|
40874
|
+
this.plannedWrites.set(resourceType, write);
|
|
40875
|
+
this.writes.add(write);
|
|
40876
|
+
write.catch(() => void 0);
|
|
40877
|
+
}
|
|
40878
|
+
}
|
|
40879
|
+
get size() {
|
|
40880
|
+
return this.candidates.size;
|
|
40881
|
+
}
|
|
40882
|
+
verdict(logicalId) {
|
|
40883
|
+
return this.verdicts.get(logicalId);
|
|
40884
|
+
}
|
|
40885
|
+
candidate(logicalId) {
|
|
40886
|
+
const c = this.candidates.get(logicalId);
|
|
40887
|
+
return c === void 0 ? void 0 : {
|
|
40888
|
+
name: c.name,
|
|
40889
|
+
property: c.property
|
|
40890
|
+
};
|
|
40891
|
+
}
|
|
40892
|
+
async admit(logicalId, resolvedProperties) {
|
|
40893
|
+
const c = this.candidates.get(logicalId);
|
|
40894
|
+
if (c === void 0) return void 0;
|
|
40895
|
+
if (c.deferred && !this.verdicts.has(logicalId)) this.ask([{
|
|
40896
|
+
...c,
|
|
40897
|
+
properties: resolvedProperties,
|
|
40898
|
+
deferred: false
|
|
40899
|
+
}]);
|
|
40900
|
+
const verdict = await this.verdicts.get(logicalId);
|
|
40901
|
+
if (verdict.kind !== "free" && verdict.kind !== "licensed") return verdict;
|
|
40902
|
+
try {
|
|
40903
|
+
const planned = this.plannedWrites?.get(c.resourceType);
|
|
40904
|
+
if (planned !== void 0) try {
|
|
40905
|
+
await planned;
|
|
40906
|
+
} catch (error) {
|
|
40907
|
+
if (this.plannedIds.has(logicalId)) throw error;
|
|
40908
|
+
}
|
|
40909
|
+
await this.recordIntent(c);
|
|
40910
|
+
} catch (error) {
|
|
40911
|
+
return {
|
|
40912
|
+
kind: "failed",
|
|
40913
|
+
error
|
|
40914
|
+
};
|
|
40915
|
+
}
|
|
40916
|
+
return verdict;
|
|
40917
|
+
}
|
|
40918
|
+
noteSent(logicalId) {
|
|
40919
|
+
if (this.candidates.has(logicalId)) this.sent.add(logicalId);
|
|
40920
|
+
}
|
|
40921
|
+
noteReturned(logicalId) {
|
|
40922
|
+
if (this.candidates.has(logicalId)) this.returned.add(logicalId);
|
|
40923
|
+
}
|
|
40924
|
+
noteFailed(logicalId, error) {
|
|
40925
|
+
const c = this.candidates.get(logicalId);
|
|
40926
|
+
if (c === void 0) return;
|
|
40927
|
+
if (provenNothingCreated(error, logicalId, c.resourceType)) this.rejected.add(logicalId);
|
|
40928
|
+
else if (!this.failedAt.has(logicalId)) this.failedAt.set(logicalId, this.timing.now());
|
|
40929
|
+
}
|
|
40930
|
+
async settle() {
|
|
40931
|
+
await Promise.all([...this.writes].map((w) => w.catch(() => void 0)));
|
|
40932
|
+
const drop = [...this.recorded].filter((id) => !this.sent.has(id) || this.returned.has(id) || this.rejected.has(id));
|
|
40933
|
+
const failedAt = new Map([...this.failedAt].filter(([id]) => this.recorded.has(id) && !drop.includes(id)));
|
|
40934
|
+
if (drop.length === 0 && failedAt.size === 0) return;
|
|
40935
|
+
let lastError;
|
|
40936
|
+
for (let attempt = 0; attempt < 2; attempt++) try {
|
|
40937
|
+
await settleAdoptingCreates(drop, failedAt);
|
|
40938
|
+
return;
|
|
40939
|
+
} catch (error) {
|
|
40940
|
+
lastError = error;
|
|
40941
|
+
}
|
|
40942
|
+
const bounded = await noteAbandonedRun(this.timing.now());
|
|
40943
|
+
this.input.warn?.(safeMsg`Could not clear this deploy's unsent name-adopting creates from the stack's create-token ledger (${describeAwsFailure(lastError).summary}). ` + (bounded ? "They license only what existed by now; a later deploy may refuse a resource of those names, which `cdkd import` adopts." : "Nor could the ledger record when this deploy ended: a later deploy may take back a resource of those names that another deployment creates. They are cleared by the next successful deploy."));
|
|
40944
|
+
}
|
|
40945
|
+
async readoptedFromRetained() {
|
|
40946
|
+
const out = [];
|
|
40947
|
+
for (const [logicalId, verdict] of this.verdicts) {
|
|
40948
|
+
if (!this.returned.has(logicalId)) continue;
|
|
40949
|
+
const v = await verdict;
|
|
40950
|
+
if (v.kind === "licensed" && (v.via === "retained" || v.via === "history")) out.push(logicalId);
|
|
40951
|
+
}
|
|
40952
|
+
return out;
|
|
40953
|
+
}
|
|
40954
|
+
recordIntent(c) {
|
|
40955
|
+
if (this.recorded.has(c.logicalId)) return Promise.resolve();
|
|
40956
|
+
this.queued.push(c);
|
|
40957
|
+
if (this.flushing === void 0) {
|
|
40958
|
+
const flush = new Promise((resolve) => setImmediate(resolve)).then(async () => {
|
|
40959
|
+
const batch = this.queued;
|
|
40960
|
+
this.queued = [];
|
|
40961
|
+
if (this.flushing === flush) this.flushing = void 0;
|
|
40962
|
+
await this.writeIntents(batch);
|
|
40963
|
+
});
|
|
40964
|
+
this.flushing = flush;
|
|
40965
|
+
this.writes.add(flush);
|
|
40966
|
+
}
|
|
40967
|
+
return this.flushing;
|
|
40968
|
+
}
|
|
40969
|
+
commitPlanned(batch) {
|
|
40970
|
+
this.groupPending.push(...batch);
|
|
40971
|
+
if (this.groupNext === void 0) {
|
|
40972
|
+
const next = this.groupInFlight.then(() => void 0, () => void 0).then(async () => {
|
|
40973
|
+
const carried = this.groupPending;
|
|
40974
|
+
this.groupPending = [];
|
|
40975
|
+
this.groupNext = void 0;
|
|
40976
|
+
await this.writeIntents(carried);
|
|
40977
|
+
});
|
|
40978
|
+
this.groupNext = next;
|
|
40979
|
+
this.groupInFlight = next;
|
|
40980
|
+
}
|
|
40981
|
+
return this.groupNext;
|
|
40982
|
+
}
|
|
40983
|
+
async writeIntents(batch) {
|
|
40984
|
+
await recordAdoptingCreates(batch.map((b) => ({
|
|
40985
|
+
logicalId: b.logicalId,
|
|
40986
|
+
resourceType: b.resourceType,
|
|
40987
|
+
name: b.name
|
|
40988
|
+
})));
|
|
40989
|
+
for (const b of batch) this.recorded.add(b.logicalId);
|
|
40990
|
+
}
|
|
40991
|
+
recordedIntents() {
|
|
40992
|
+
this.recordedRead ??= recordedAdoptingCreates();
|
|
40993
|
+
return this.recordedRead;
|
|
40994
|
+
}
|
|
40995
|
+
async run(candidates) {
|
|
40996
|
+
const input = this.input;
|
|
40997
|
+
const out = /* @__PURE__ */ new Map();
|
|
40998
|
+
const recorded = this.recordedIntents().then((r) => ({
|
|
40999
|
+
ok: true,
|
|
41000
|
+
value: r
|
|
41001
|
+
}), (error) => ({
|
|
41002
|
+
ok: false,
|
|
41003
|
+
error
|
|
41004
|
+
}));
|
|
41005
|
+
const { found, typeFailures } = await lookupAll(input, candidates);
|
|
41006
|
+
let evidence;
|
|
41007
|
+
if (found.size > 0) {
|
|
41008
|
+
this.evidenceRead ??= loadEvidence(input);
|
|
41009
|
+
evidence = await this.evidenceRead.catch((error) => ({ error }));
|
|
41010
|
+
}
|
|
41011
|
+
const recordedResult = await recorded;
|
|
41012
|
+
const unlicensed = [];
|
|
41013
|
+
for (const c of candidates) {
|
|
41014
|
+
const failure = typeFailures.get(c.resourceType);
|
|
41015
|
+
if (failure !== void 0) {
|
|
41016
|
+
out.set(c.logicalId, isAccessDeniedError(failure) ? {
|
|
41017
|
+
kind: "unchecked",
|
|
41018
|
+
error: failure
|
|
41019
|
+
} : {
|
|
41020
|
+
kind: "failed",
|
|
41021
|
+
error: failure
|
|
41022
|
+
});
|
|
41023
|
+
continue;
|
|
41024
|
+
}
|
|
41025
|
+
const holder = found.get(c.logicalId);
|
|
41026
|
+
if (holder === void 0) {
|
|
41027
|
+
out.set(c.logicalId, { kind: "free" });
|
|
41028
|
+
continue;
|
|
41029
|
+
}
|
|
41030
|
+
if (!recordedResult.ok) {
|
|
41031
|
+
out.set(c.logicalId, {
|
|
41032
|
+
kind: "failed",
|
|
41033
|
+
error: recordedResult.error
|
|
41034
|
+
});
|
|
41035
|
+
continue;
|
|
41036
|
+
}
|
|
41037
|
+
const intent = recordedResult.value?.get(c.logicalId);
|
|
41038
|
+
let ownIntent;
|
|
41039
|
+
if (intent !== void 0 && intent.resourceType === c.resourceType && intent.name === c.name) {
|
|
41040
|
+
const abandonedAt = await this.abandonedRunAt();
|
|
41041
|
+
const byIntent = await this.licenseIfCreatedAfterIntent(c, holder, intent, abandonedAt !== void 0 && intent.firstSentAt <= abandonedAt ? abandonedAt : void 0);
|
|
41042
|
+
if (byIntent.kind !== "held") {
|
|
41043
|
+
out.set(c.logicalId, byIntent);
|
|
41044
|
+
continue;
|
|
41045
|
+
}
|
|
41046
|
+
ownIntent = byIntent.ownIntent;
|
|
41047
|
+
}
|
|
41048
|
+
if (evidence === void 0 || "error" in evidence) {
|
|
41049
|
+
out.set(c.logicalId, {
|
|
41050
|
+
kind: "failed",
|
|
41051
|
+
error: evidence !== void 0 && "error" in evidence ? evidence.error : /* @__PURE__ */ new Error("no evidence")
|
|
41052
|
+
});
|
|
41053
|
+
continue;
|
|
41054
|
+
}
|
|
41055
|
+
const named = evidence.namedBy(c.resourceType, holder);
|
|
41056
|
+
if (named !== void 0) {
|
|
41057
|
+
out.set(c.logicalId, await this.licenseIfKeptBefore(c, holder, named));
|
|
41058
|
+
if (out.get(c.logicalId).kind === "held") unlicensed.push(c);
|
|
41059
|
+
continue;
|
|
41060
|
+
}
|
|
41061
|
+
out.set(c.logicalId, {
|
|
41062
|
+
kind: "held",
|
|
41063
|
+
holder,
|
|
41064
|
+
...ownIntent && { ownIntent }
|
|
41065
|
+
});
|
|
41066
|
+
unlicensed.push(c);
|
|
41067
|
+
}
|
|
41068
|
+
if (unlicensed.length > 0 && input.loadKeptInHistory !== void 0 && evidence !== void 0 && !("error" in evidence) && evidence.retainedAbsent) {
|
|
41069
|
+
let kept;
|
|
41070
|
+
try {
|
|
41071
|
+
this.historyRead ??= input.loadKeptInHistory();
|
|
41072
|
+
kept = await this.historyRead;
|
|
41073
|
+
} catch {
|
|
41074
|
+
kept = [];
|
|
41075
|
+
}
|
|
41076
|
+
for (const c of [...unlicensed]) {
|
|
41077
|
+
const holder = found.get(c.logicalId);
|
|
41078
|
+
const match = kept.find((k) => k.logicalId === c.logicalId && k.resourceType === c.resourceType && probeFoundSameId(c.resourceType, k.physicalId, holder));
|
|
41079
|
+
if (match === void 0) continue;
|
|
41080
|
+
const verdict = await this.licenseIfKeptBefore(c, holder, {
|
|
41081
|
+
via: "history",
|
|
41082
|
+
...match.keptAt !== void 0 && { keptAt: match.keptAt }
|
|
41083
|
+
});
|
|
41084
|
+
out.set(c.logicalId, verdict);
|
|
41085
|
+
if (verdict.kind !== "held") unlicensed.splice(unlicensed.indexOf(c), 1);
|
|
41086
|
+
}
|
|
41087
|
+
}
|
|
41088
|
+
return {
|
|
41089
|
+
verdicts: out,
|
|
41090
|
+
deleting: unlicensed.filter((c) => STILL_LISTED_WHILE_DELETING.has(c.resourceType))
|
|
41091
|
+
};
|
|
41092
|
+
}
|
|
41093
|
+
async licenseIfKeptBefore(c, holder, named) {
|
|
41094
|
+
const licensed = {
|
|
41095
|
+
kind: "licensed",
|
|
41096
|
+
holder,
|
|
41097
|
+
via: named.via
|
|
41098
|
+
};
|
|
41099
|
+
if (named.via !== "retained" && named.via !== "history" || named.keptAt === void 0) return licensed;
|
|
41100
|
+
if (c.provider.holderCreatedAt === void 0) return licensed;
|
|
41101
|
+
let createdAt;
|
|
41102
|
+
try {
|
|
41103
|
+
createdAt = await withSkipPrefix(true, () => c.provider.holderCreatedAt(c.resourceType, holder));
|
|
41104
|
+
} catch (error) {
|
|
41105
|
+
if (!isAccessDeniedError(error)) return {
|
|
41106
|
+
kind: "failed",
|
|
41107
|
+
error
|
|
41108
|
+
};
|
|
41109
|
+
this.creationTimeDenied(c, holder, error);
|
|
41110
|
+
return licensed;
|
|
41111
|
+
}
|
|
41112
|
+
if (createdAt === void 0 || createdAt <= named.keptAt + KEPT_AT_SKEW_MS) return licensed;
|
|
41113
|
+
return {
|
|
41114
|
+
kind: "held",
|
|
41115
|
+
holder
|
|
41116
|
+
};
|
|
41117
|
+
}
|
|
41118
|
+
async licenseIfCreatedAfterIntent(c, holder, intent, abandonedAt) {
|
|
41119
|
+
const licensed = {
|
|
41120
|
+
kind: "licensed",
|
|
41121
|
+
holder,
|
|
41122
|
+
via: "ledger"
|
|
41123
|
+
};
|
|
41124
|
+
const held = {
|
|
41125
|
+
kind: "held",
|
|
41126
|
+
holder
|
|
41127
|
+
};
|
|
41128
|
+
const byName = abandonedAt !== void 0 ? {
|
|
41129
|
+
kind: "held",
|
|
41130
|
+
holder,
|
|
41131
|
+
ownIntent: "abandoned"
|
|
41132
|
+
} : intent.failedAt !== void 0 ? {
|
|
41133
|
+
kind: "held",
|
|
41134
|
+
holder,
|
|
41135
|
+
ownIntent: "failed"
|
|
41136
|
+
} : licensed;
|
|
41137
|
+
if (c.provider.holderCreatedAt === void 0) return byName;
|
|
41138
|
+
let createdAt;
|
|
41139
|
+
try {
|
|
41140
|
+
createdAt = await withSkipPrefix(true, () => c.provider.holderCreatedAt(c.resourceType, holder));
|
|
41141
|
+
} catch (error) {
|
|
41142
|
+
if (!isAccessDeniedError(error)) return {
|
|
41143
|
+
kind: "failed",
|
|
41144
|
+
error
|
|
41145
|
+
};
|
|
41146
|
+
this.creationTimeDenied(c, holder, error);
|
|
41147
|
+
return byName;
|
|
41148
|
+
}
|
|
41149
|
+
if (createdAt === void 0) return byName;
|
|
41150
|
+
const { firstSentAt, failedAt } = intent;
|
|
41151
|
+
if (Number.isFinite(firstSentAt) && createdAt < firstSentAt - KEPT_AT_SKEW_MS) return held;
|
|
41152
|
+
for (const until of [abandonedAt, failedAt]) if (until !== void 0 && createdAt > until + KEPT_AT_SKEW_MS) return held;
|
|
41153
|
+
return licensed;
|
|
41154
|
+
}
|
|
41155
|
+
creationTimeDenied(c, holder, error) {
|
|
41156
|
+
this.input.warn?.(safeMsg`${c.logicalId}: could not read when ${holder} was created (${describeAwsFailure(error).summary}); ` + "deciding by its name alone, as for a type that reports no creation time. Grant the type's Describe permission to narrow that.");
|
|
41157
|
+
}
|
|
41158
|
+
async abandonedRunAt() {
|
|
41159
|
+
this.abandonedRead ??= ledgerAbandonedAt().catch(() => void 0).then((fromLedger) => {
|
|
41160
|
+
const at = [fromLedger, this.input.abandonedRunAt].filter((v) => typeof v === "number" && Number.isFinite(v));
|
|
41161
|
+
return at.length > 0 ? Math.max(...at) : void 0;
|
|
41162
|
+
});
|
|
41163
|
+
return this.abandonedRead;
|
|
41164
|
+
}
|
|
41165
|
+
async waitOutDeletion(candidates) {
|
|
41166
|
+
const out = /* @__PURE__ */ new Map();
|
|
41167
|
+
let pending = [...candidates];
|
|
41168
|
+
const until = this.timing.now() + this.timing.cooldownMs;
|
|
41169
|
+
const watch = startInterruptWatch("generated-name deletion cooldown");
|
|
41170
|
+
try {
|
|
41171
|
+
while (pending.length > 0 && this.timing.now() < until) {
|
|
41172
|
+
await this.sleepUnlessInterrupted(this.timing.cooldownStepMs, watch);
|
|
41173
|
+
if (watch.isInterrupted()) {
|
|
41174
|
+
for (const c of pending) out.set(c.logicalId, {
|
|
41175
|
+
kind: "failed",
|
|
41176
|
+
error: watch.onInterrupted()
|
|
41177
|
+
});
|
|
41178
|
+
return out;
|
|
41179
|
+
}
|
|
41180
|
+
const { found, typeFailures } = await lookupAll(this.input, pending);
|
|
41181
|
+
pending = pending.filter((c) => {
|
|
41182
|
+
if (typeFailures.has(c.resourceType)) return true;
|
|
41183
|
+
if (found.has(c.logicalId)) return true;
|
|
41184
|
+
out.set(c.logicalId, { kind: "free" });
|
|
41185
|
+
return false;
|
|
41186
|
+
});
|
|
41187
|
+
}
|
|
41188
|
+
return out;
|
|
41189
|
+
} finally {
|
|
41190
|
+
watch.dispose();
|
|
41191
|
+
}
|
|
41192
|
+
}
|
|
41193
|
+
sleepUnlessInterrupted(ms, watch) {
|
|
41194
|
+
if (watch.isInterrupted()) return Promise.resolve();
|
|
41195
|
+
let timer;
|
|
41196
|
+
const interrupted = new Promise((resolve) => {
|
|
41197
|
+
timer = setInterval(() => {
|
|
41198
|
+
if (watch.isInterrupted()) resolve();
|
|
41199
|
+
}, 200);
|
|
41200
|
+
timer.unref?.();
|
|
41201
|
+
});
|
|
41202
|
+
return Promise.race([this.timing.sleep(ms), interrupted]).finally(() => clearInterval(timer));
|
|
41203
|
+
}
|
|
41204
|
+
};
|
|
41205
|
+
function isPermanentRedirect(error) {
|
|
41206
|
+
const e = error;
|
|
41207
|
+
return e?.name === "PermanentRedirect" || e?.$metadata?.httpStatusCode === 301 || e?.$response?.statusCode === 301;
|
|
41208
|
+
}
|
|
41209
|
+
async function lookupAll(input, candidates) {
|
|
41210
|
+
const byType = /* @__PURE__ */ new Map();
|
|
41211
|
+
for (const c of candidates) byType.set(c.resourceType, [...byType.get(c.resourceType) ?? [], c]);
|
|
41212
|
+
const found = /* @__PURE__ */ new Map();
|
|
41213
|
+
const typeFailures = /* @__PURE__ */ new Map();
|
|
41214
|
+
await Promise.all([...byType].map(async ([resourceType, group]) => {
|
|
41215
|
+
try {
|
|
41216
|
+
const holders = await lookupType(input, resourceType, group);
|
|
41217
|
+
for (const c of group) {
|
|
41218
|
+
const holder = holders.get(c.name);
|
|
41219
|
+
if (holder !== void 0) found.set(c.logicalId, holder);
|
|
41220
|
+
}
|
|
41221
|
+
} catch (error) {
|
|
41222
|
+
typeFailures.set(resourceType, error);
|
|
41223
|
+
}
|
|
41224
|
+
}));
|
|
41225
|
+
return {
|
|
41226
|
+
found,
|
|
41227
|
+
typeFailures
|
|
41228
|
+
};
|
|
41229
|
+
}
|
|
41230
|
+
async function loadEvidence(input) {
|
|
41231
|
+
const [journal, retained] = await Promise.all([input.loadJournal(), input.loadRetained()]);
|
|
41232
|
+
const sources = [];
|
|
41233
|
+
for (const record of Object.values(input.records)) if (typeof record?.physicalId === "string") sources.push({
|
|
41234
|
+
via: "record",
|
|
41235
|
+
type: record.resourceType,
|
|
41236
|
+
id: record.physicalId
|
|
41237
|
+
});
|
|
41238
|
+
for (const orphan of input.orphans ?? []) {
|
|
41239
|
+
const s = orphan.state;
|
|
41240
|
+
if (typeof s?.physicalId === "string") sources.push({
|
|
41241
|
+
via: "orphan",
|
|
41242
|
+
type: s.resourceType,
|
|
41243
|
+
id: s.physicalId
|
|
41244
|
+
});
|
|
41245
|
+
}
|
|
41246
|
+
for (const segment of journal?.segments ?? []) for (const op of [...segment.operations ?? [], ...segment.failedOperations ?? []]) if (typeof op.physicalId === "string" && op.physicalId !== "") sources.push({
|
|
41247
|
+
via: "journal",
|
|
41248
|
+
type: op.resourceType,
|
|
41249
|
+
id: op.physicalId
|
|
41250
|
+
});
|
|
41251
|
+
for (const r of retained ?? []) sources.push({
|
|
41252
|
+
via: "retained",
|
|
41253
|
+
type: r.resourceType,
|
|
41254
|
+
id: r.physicalId,
|
|
41255
|
+
...r.keptAt !== void 0 && { keptAt: r.keptAt }
|
|
41256
|
+
});
|
|
41257
|
+
return {
|
|
41258
|
+
retainedAbsent: retained === null,
|
|
41259
|
+
namedBy: (resourceType, holder) => {
|
|
41260
|
+
const hit = sources.find((s) => s.type === resourceType && probeFoundSameId(resourceType, s.id, holder));
|
|
41261
|
+
return hit === void 0 ? void 0 : {
|
|
41262
|
+
via: hit.via,
|
|
41263
|
+
...hit.keptAt !== void 0 && { keptAt: hit.keptAt }
|
|
41264
|
+
};
|
|
41265
|
+
}
|
|
41266
|
+
};
|
|
41267
|
+
}
|
|
41268
|
+
async function lookupType(input, resourceType, group) {
|
|
41269
|
+
const provider = group[0].provider;
|
|
41270
|
+
const names = group.map((c) => c.name);
|
|
41271
|
+
return withSkipPrefix(true, async () => {
|
|
41272
|
+
if (provider.lookupNames !== void 0) return provider.lookupNames(resourceType, names, {
|
|
41273
|
+
region: input.region,
|
|
41274
|
+
stackName: input.stackName,
|
|
41275
|
+
propertiesByName: new Map(group.map((c) => [c.name, c.properties]))
|
|
41276
|
+
});
|
|
41277
|
+
const lookup = provider.import?.bind(provider);
|
|
41278
|
+
if (lookup === void 0) return /* @__PURE__ */ new Map();
|
|
41279
|
+
const byName = new Map(group.map((c) => [c.name, c]));
|
|
41280
|
+
const account = ["AWS::SNS::Topic", "AWS::StepFunctions::StateMachine"].includes(resourceType) ? await input.accountInfo() : void 0;
|
|
41281
|
+
const concurrency = PER_NAME_CONCURRENCY[resourceType] ?? DEFAULT_PER_NAME_CONCURRENCY;
|
|
41282
|
+
return lookupEachName(names, `${resourceType}:import`, concurrency, async (name) => {
|
|
41283
|
+
const c = byName.get(name);
|
|
41284
|
+
const arn = account !== void 0 ? createLookupArn(resourceType, name, account) : void 0;
|
|
41285
|
+
if (arn !== void 0 && "unbuildable" in arn) throw new Error(`cannot build the ARN ${resourceType} name ${name} would take`);
|
|
41286
|
+
let found;
|
|
41287
|
+
try {
|
|
41288
|
+
found = await lookup({
|
|
41289
|
+
logicalId: c.logicalId,
|
|
41290
|
+
resourceType,
|
|
41291
|
+
stackName: input.stackName,
|
|
41292
|
+
region: input.region,
|
|
41293
|
+
properties: c.property === void 0 ? c.properties : {
|
|
41294
|
+
...c.properties,
|
|
41295
|
+
[c.property]: name
|
|
41296
|
+
},
|
|
41297
|
+
...arn !== void 0 && { knownPhysicalId: arn.arn }
|
|
41298
|
+
});
|
|
41299
|
+
} catch (error) {
|
|
41300
|
+
if (resourceType === "AWS::S3::Bucket" && isPermanentRedirect(error)) return name;
|
|
41301
|
+
throw error;
|
|
41302
|
+
}
|
|
41303
|
+
return found?.physicalId;
|
|
41304
|
+
});
|
|
41305
|
+
});
|
|
41306
|
+
}
|
|
41307
|
+
|
|
41308
|
+
//#endregion
|
|
41309
|
+
//#region src/deployment/kept-in-history.ts
|
|
41310
|
+
function keptInEarlierRecords(records) {
|
|
41311
|
+
const out = [];
|
|
41312
|
+
for (const record of records) for (const [logicalId, raw] of Object.entries(record.resources)) {
|
|
41313
|
+
if (raw === null || typeof raw !== "object") continue;
|
|
41314
|
+
const r = raw;
|
|
41315
|
+
if (typeof r.resourceType !== "string" || typeof r.physicalId !== "string") continue;
|
|
41316
|
+
if (r.physicalId === "") continue;
|
|
41317
|
+
if (!shouldRetainResource(r.deletionPolicy)) continue;
|
|
41318
|
+
out.push({
|
|
41319
|
+
logicalId,
|
|
41320
|
+
resourceType: r.resourceType,
|
|
41321
|
+
physicalId: r.physicalId,
|
|
41322
|
+
...record.writtenAt !== void 0 && { keptAt: record.writtenAt }
|
|
41323
|
+
});
|
|
41324
|
+
}
|
|
41325
|
+
return out;
|
|
41326
|
+
}
|
|
41327
|
+
function keptInEventHistory(runs, stackName) {
|
|
41328
|
+
const made = /* @__PURE__ */ new Map();
|
|
41329
|
+
const out = [];
|
|
41330
|
+
for (const events of runs) for (const e of events) {
|
|
41331
|
+
if (e.stackName !== stackName || typeof e.logicalId !== "string") continue;
|
|
41332
|
+
if (typeof e.resourceType !== "string") continue;
|
|
41333
|
+
if (e.eventType === "RESOURCE_SUCCEEDED" && (e.operation === "CREATE" || e.operation === "UPDATE") && typeof e.physicalId === "string" && e.physicalId !== "") made.set(e.logicalId, {
|
|
41334
|
+
resourceType: e.resourceType,
|
|
41335
|
+
physicalId: e.physicalId
|
|
41336
|
+
});
|
|
41337
|
+
else if (e.eventType === "RESOURCE_RETAINED") {
|
|
41338
|
+
const m = made.get(e.logicalId);
|
|
41339
|
+
if (m !== void 0 && m.resourceType === e.resourceType) {
|
|
41340
|
+
const keptAt = Date.parse(e.timestamp);
|
|
41341
|
+
out.push({
|
|
41342
|
+
logicalId: e.logicalId,
|
|
41343
|
+
...m,
|
|
41344
|
+
...Number.isFinite(keptAt) && { keptAt }
|
|
41345
|
+
});
|
|
41346
|
+
}
|
|
41347
|
+
}
|
|
41348
|
+
}
|
|
41349
|
+
return out;
|
|
41350
|
+
}
|
|
41351
|
+
async function loadKeptInHistory(sources, stackName, region) {
|
|
41352
|
+
const runsOf = stackName.split("~")[0];
|
|
41353
|
+
const [records, runs] = await Promise.all([sources.earlierStateResources(stackName, region).catch(() => []), (async () => {
|
|
41354
|
+
const listed = (await sources.listRuns(runsOf, region)).slice(0, 20);
|
|
41355
|
+
return (await Promise.all(listed.map((r) => sources.readRunEvents(runsOf, region, r.runId).catch(() => null)))).filter((e) => e !== null).reverse();
|
|
41356
|
+
})().catch(() => [])]);
|
|
41357
|
+
return [...keptInEarlierRecords(records), ...keptInEventHistory(runs, stackName)];
|
|
41358
|
+
}
|
|
41359
|
+
|
|
39551
41360
|
//#endregion
|
|
39552
41361
|
//#region src/provisioning/canonicalize-properties.ts
|
|
39553
41362
|
function makeCanonicalizePropertiesFn(registry) {
|
|
@@ -40108,6 +41917,9 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
|
|
|
40108
41917
|
process.removeListener("SIGINT", sigintHandler);
|
|
40109
41918
|
throw error;
|
|
40110
41919
|
}
|
|
41920
|
+
let firstDeployStateGate;
|
|
41921
|
+
let firstDeploy = false;
|
|
41922
|
+
let failed = false;
|
|
40111
41923
|
try {
|
|
40112
41924
|
renderer.start();
|
|
40113
41925
|
const currentStateData = await this.stateBackend.getState(stackName, this.stackRegion);
|
|
@@ -40131,7 +41943,14 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
|
|
|
40131
41943
|
refuseMalformedOrphanRecords(currentState, stackName, this.stackRegion, this.options.refusalRecovery);
|
|
40132
41944
|
const migrationPending = currentStateData?.migrationPending ?? false;
|
|
40133
41945
|
this.logger.debug(`Loaded current state: ${Object.keys(currentState.resources).length} resources`);
|
|
40134
|
-
if (this.options.onCurrentStateLoaded)
|
|
41946
|
+
if (this.options.onCurrentStateLoaded) {
|
|
41947
|
+
const gate = this.options.onCurrentStateLoaded(stackName, currentStateData?.state);
|
|
41948
|
+
if (currentStateData === null || currentStateData === void 0) {
|
|
41949
|
+
firstDeployStateGate = gate;
|
|
41950
|
+
firstDeploy = true;
|
|
41951
|
+
gate.catch(() => void 0);
|
|
41952
|
+
} else await gate;
|
|
41953
|
+
}
|
|
40135
41954
|
try {
|
|
40136
41955
|
const loaded = await this.stateBackend.loadRollbackJournal(stackName, this.stackRegion);
|
|
40137
41956
|
const journal = loaded && {
|
|
@@ -40262,6 +42081,11 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
|
|
|
40262
42081
|
conditions,
|
|
40263
42082
|
forceStatefulRecreation: this.options.forceStatefulRecreation === true
|
|
40264
42083
|
});
|
|
42084
|
+
if (firstDeployStateGate !== void 0) {
|
|
42085
|
+
const gate = firstDeployStateGate;
|
|
42086
|
+
firstDeployStateGate = void 0;
|
|
42087
|
+
await gate;
|
|
42088
|
+
}
|
|
40265
42089
|
if (!this.diffCalculator.hasChanges(changes)) {
|
|
40266
42090
|
this.logger.info("No changes detected. Stack is up to date.");
|
|
40267
42091
|
let persistedOutputs = currentState.outputs ?? {};
|
|
@@ -40369,7 +42193,34 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
|
|
|
40369
42193
|
const readers = [...changes.values()].filter(isNoEchoPromotionOnly).length;
|
|
40370
42194
|
this.logger.info(safeMsg`No changes detected in the template. Comparing ${String(readers)} resource(s) that read a NoEcho parameter with AWS.`);
|
|
40371
42195
|
}
|
|
40372
|
-
|
|
42196
|
+
const abandonedRunAt = this.lockManager.abandonedRunEndedBy?.(stackName, this.stackRegion);
|
|
42197
|
+
if (abandonedRunAt !== void 0) await noteAbandonedRun(abandonedRunAt);
|
|
42198
|
+
this.generatedNameGuard = GeneratedNameGuard.start({
|
|
42199
|
+
stackName,
|
|
42200
|
+
region: this.stackRegion,
|
|
42201
|
+
changes: changes.values(),
|
|
42202
|
+
providerFor: (input) => this.providerRegistry.getProviderFor({
|
|
42203
|
+
...input,
|
|
42204
|
+
quiet: true
|
|
42205
|
+
}),
|
|
42206
|
+
records: currentState.resources,
|
|
42207
|
+
orphans: currentState.orphans,
|
|
42208
|
+
loadJournal: () => this.stateBackend.loadRollbackJournal(stackName, this.stackRegion),
|
|
42209
|
+
loadRetained: () => this.stateBackend.loadRetainedRecord(stackName, this.stackRegion),
|
|
42210
|
+
loadKeptInHistory: () => {
|
|
42211
|
+
const reader = new DeploymentEventsReader(this.stateBackend);
|
|
42212
|
+
return loadKeptInHistory({
|
|
42213
|
+
earlierStateResources: (s, r) => this.stateBackend.earlierStateResources(s, r),
|
|
42214
|
+
listRuns: (s, r) => reader.listRuns(s, r),
|
|
42215
|
+
readRunEvents: (s, r, id) => reader.readRunEvents(s, r, id)
|
|
42216
|
+
}, stackName, this.stackRegion);
|
|
42217
|
+
},
|
|
42218
|
+
accountInfo: () => getAccountInfo(this.stackRegion),
|
|
42219
|
+
warn: (message) => this.logger.warn(message),
|
|
42220
|
+
...abandonedRunAt !== void 0 && { abandonedRunAt }
|
|
42221
|
+
});
|
|
42222
|
+
if (this.generatedNameGuard !== void 0) this.logger.debug(safeMsg`Generated-name check: looking up ${String(this.generatedNameGuard.size)} planned create(s)`);
|
|
42223
|
+
await checkDestructivePlan({
|
|
40373
42224
|
options: this.options,
|
|
40374
42225
|
stackName,
|
|
40375
42226
|
changes: changes.values(),
|
|
@@ -40377,6 +42228,15 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
|
|
|
40377
42228
|
template: effectiveTemplate,
|
|
40378
42229
|
recreateTargetIds: recreateTargetIdsFor(this.options.recreateTargets, stackName)
|
|
40379
42230
|
});
|
|
42231
|
+
if (await requireDeploymentApproval({
|
|
42232
|
+
options: this.options,
|
|
42233
|
+
stackName,
|
|
42234
|
+
changes: changes.values(),
|
|
42235
|
+
records: currentState.resources,
|
|
42236
|
+
template: effectiveTemplate,
|
|
42237
|
+
recreateTargetIds: recreateTargetIdsFor(this.options.recreateTargets, stackName)
|
|
42238
|
+
})) this.generatedNameGuard?.noteApprovalPrompted();
|
|
42239
|
+
this.generatedNameGuard?.recordPlannedIntents();
|
|
40380
42240
|
this.resolver.resetPhysicalIdFallbackCount();
|
|
40381
42241
|
const progress = {
|
|
40382
42242
|
current: 0,
|
|
@@ -40384,16 +42244,29 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
|
|
|
40384
42244
|
};
|
|
40385
42245
|
const { state: newState, actualCounts, completedOperations } = await withProducerRegions(() => this.producerRegionEvidence(currentState), () => this.executeDeployment(effectiveTemplate, currentState, changes, dag, executionLevels, stackName, outputsDigestSource, parameterValues, conditions, currentEtag, progress, migrationPending));
|
|
40386
42246
|
await this.drainObservedCaptures(newState.resources);
|
|
40387
|
-
const newEtag = await this.stateBackend.saveState(stackName, this.stackRegion, this.withParentInfo(conditionVerdicts ? {
|
|
42247
|
+
const [newEtag] = await Promise.all([this.stateBackend.saveState(stackName, this.stackRegion, this.withParentInfo(conditionVerdicts ? {
|
|
40388
42248
|
...newState,
|
|
40389
42249
|
conditionVerdicts
|
|
40390
|
-
} : newState));
|
|
42250
|
+
} : newState)), flushKeptForReadoption(this)]);
|
|
40391
42251
|
this.logger.debug(`State saved (ETag: ${newEtag})`);
|
|
40392
|
-
|
|
40393
|
-
|
|
40394
|
-
|
|
40395
|
-
|
|
40396
|
-
|
|
42252
|
+
const ledgerAndKeptCleanup = (async () => {
|
|
42253
|
+
await forgetRecordedCreateTokens(Object.keys(newState.resources));
|
|
42254
|
+
const readopted = await this.generatedNameGuard?.readoptedFromRetained() ?? [];
|
|
42255
|
+
if (readopted.length > 0) try {
|
|
42256
|
+
const kept = await this.stateBackend.loadRetainedResources(stackName, this.stackRegion);
|
|
42257
|
+
await this.stateBackend.saveRetainedResources(stackName, this.stackRegion, kept.filter((entry) => !readopted.includes(entry.logicalId)));
|
|
42258
|
+
} catch (error) {
|
|
42259
|
+
this.logger.debug(safeMsg`Could not clear re-adopted resources from the kept-resource record: ${describeAwsFailure(error).summary}`);
|
|
42260
|
+
}
|
|
42261
|
+
})();
|
|
42262
|
+
const [journaledOrphansLeft] = await Promise.all([
|
|
42263
|
+
this.settleJournalAfterSuccess(stackName, completedOperations, currentState, newState.resources, currentEtag === void 0),
|
|
42264
|
+
this.exportIndexStore ? this.exportIndexStore.updateForStack(stackName, this.stackRegion, importableOutputs({
|
|
42265
|
+
...newState,
|
|
42266
|
+
outputs: this.redactOutputs(newState.outputs)
|
|
42267
|
+
})) : Promise.resolve(),
|
|
42268
|
+
ledgerAndKeptCleanup
|
|
42269
|
+
]);
|
|
40397
42270
|
const durationMs = Date.now() - startTime;
|
|
40398
42271
|
const unchangedCount = this.diffCalculator.filterByType(changes, "NO_CHANGE").length + actualCounts.skipped;
|
|
40399
42272
|
return {
|
|
@@ -40404,17 +42277,33 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
|
|
|
40404
42277
|
deleteSkipped: actualCounts.deleteSkipped + journaledOrphansLeft,
|
|
40405
42278
|
updatePartial: actualCounts.updatePartial,
|
|
40406
42279
|
nestedUpdatePartial: actualCounts.nestedUpdatePartial,
|
|
42280
|
+
...(actualCounts.crossPrefixKept ?? 0) > 0 && { crossPrefixKept: actualCounts.crossPrefixKept },
|
|
40407
42281
|
unchanged: unchangedCount,
|
|
40408
42282
|
durationMs,
|
|
40409
42283
|
outputs: this.buildDisplayOutputs(template, this.redactOutputs(newState.outputs ?? {})),
|
|
40410
42284
|
attributeFallbackCount: this.resolver.getPhysicalIdFallbackCount()
|
|
40411
42285
|
};
|
|
42286
|
+
} catch (error) {
|
|
42287
|
+
failed = true;
|
|
42288
|
+
if (firstDeployStateGate !== void 0) await firstDeployStateGate;
|
|
42289
|
+
throw error;
|
|
40412
42290
|
} finally {
|
|
40413
42291
|
try {
|
|
40414
42292
|
renderer.stop();
|
|
40415
42293
|
} catch {}
|
|
40416
42294
|
process.removeListener("SIGINT", sigintHandler);
|
|
40417
42295
|
this.observedCaptureTasks.clear();
|
|
42296
|
+
try {
|
|
42297
|
+
await flushKeptForReadoption(this);
|
|
42298
|
+
await this.generatedNameGuard?.settle();
|
|
42299
|
+
} catch {}
|
|
42300
|
+
if (firstDeploy && failed && this.options.onFirstDeployLeftNoRecord && !this.options.dryRun) try {
|
|
42301
|
+
const left = await this.stateBackend.getState(stackName, this.stackRegion);
|
|
42302
|
+
const journal = left === null ? await this.stateBackend.loadRollbackJournal(stackName, this.stackRegion) : void 0;
|
|
42303
|
+
if (left === null && !journalCanOwnResources(journal ?? null)) await this.options.onFirstDeployLeftNoRecord(stackName);
|
|
42304
|
+
} catch (error) {
|
|
42305
|
+
this.logger.debug(safeMsg`Could not check whether the failed first deploy left a record: ${describeAwsFailure(error).summary}`);
|
|
42306
|
+
}
|
|
40418
42307
|
try {
|
|
40419
42308
|
await this.lockManager.releaseLock(stackName, this.stackRegion);
|
|
40420
42309
|
this.logger.debug("Lock released");
|
|
@@ -40668,6 +42557,7 @@ var DeployEngine = class {
|
|
|
40668
42557
|
constructPathTemplate;
|
|
40669
42558
|
outputsSourceUsable = true;
|
|
40670
42559
|
attemptedResolvedProps = /* @__PURE__ */ new Map();
|
|
42560
|
+
generatedNameGuard;
|
|
40671
42561
|
liveTaskLabels = /* @__PURE__ */ new Map();
|
|
40672
42562
|
retainedOldOnReplacement = /* @__PURE__ */ new Set();
|
|
40673
42563
|
recreatedUnderSameId = /* @__PURE__ */ new Set();
|
|
@@ -40725,6 +42615,7 @@ var DeployEngine = class {
|
|
|
40725
42615
|
this.fingerprintChildTemplates = void 0;
|
|
40726
42616
|
this.perResourceInputFingerprints = /* @__PURE__ */ new Map();
|
|
40727
42617
|
this.attemptedResolvedProps = /* @__PURE__ */ new Map();
|
|
42618
|
+
this.generatedNameGuard = void 0;
|
|
40728
42619
|
this.liveTaskLabels = /* @__PURE__ */ new Map();
|
|
40729
42620
|
this.outputSecrets = /* @__PURE__ */ new Map();
|
|
40730
42621
|
this.outputsPassSecretMaps = [];
|
|
@@ -40955,5 +42846,5 @@ DeployEngine.prototype.replacementNameOrigin = replacementNameOrigin;
|
|
|
40955
42846
|
DeployEngine.prototype.orphanedNameCollisionAdvice = orphanedNameCollisionAdvice;
|
|
40956
42847
|
|
|
40957
42848
|
//#endregion
|
|
40958
|
-
export { gray as $, prefetchCreateOnlyPropertyPaths as $a, refuseMalformedOutputs as $i, tagPlanWarning as $n, wholeStringLeavesOf as $o, getAccountInfo as $r, partitionSensitiveEnv as $s, redactedDeleteAddressSkip as $t, perType as A, NO_CC_HANDLERS_REASON as Aa, decodeListingKey as Ac, malformedLocalResourceEntriesWarning as Ai, buildFinalSnapshotIdentifier as An, shareLogOnlyValues as Ao, poisonRenderedSpellingsCollidingIn as Ar, rewriteTemplateAssetReferences as As, awaitInFlightSibling as At, INDEX_SETTLE_POLL_INTERVAL_MS as B, isWarmThroughputDecrease as Ba, hasPlainPrototype$1 as Bc, malformedResourceEntriesWarning as Bi, formatDestructiveChange as Bn, isUncertifiedBaselineMaskPosition as Bo, AWS_CREDENTIAL_ENV_KEYS as Br, validateContainerRepoName as Bs, planRollback as Bt, noEchoComparisonForTemplate as C, unreadableOrphanRecords as Ca, ssmResolvedValueType as Cc, hasReadableResources as Ci, maskEventTextWithBoundBags as Cn, createUnionSecretMasker as Co, isTerminationProtectionPropagationError as Cr, AssetPublisher as Cs, isHandledOrphan as Ct, recordedProtectionNote as D, findSilentDropProperties as Da, findLargeInlineResources as Dc, malformedExportNamesWarning as Di, extractDeploymentEventError as Dn, printingCorpusOf as Do, DiffCalculator as Dr, buildAssetRedirectMap as Ds, sameJournaledOrphans as Dt, recordedProtectionEvidence as E, findActionableSilentDrops as Ea, MIGRATE_TMP_PREFIX as Ec, malformedDriftResourcePropertiesWarning as Ei, stateSecretNameNeedles as En, logOnlyValueCount as Eo, reEnableInstanceApiTermination as Er, WorkGraph as Es, makeForeignHolderScan as Et, compensateRemovedDeletionProtection as F, isStatefulRecreateTargetForReplace as Fa, AssemblyReader as Fc, malformedOrphansRefusalMessage as Fi, refusesFinalSnapshot as Fn, markSameGenerationBag as Fo, assertFlagRoleArn as Fr, getBootstrapMarkerKey as Fs, recordAfterRollbackUpdate as Ft, hasCdkAutoDeleteTag as G, configBooleanRefusal as Ga, displayAssemblyPath as Gc, previewableOrphanRecords as Gi, withCreateTokenLedger as Gn, recoverMaskedOutput as Go, s3BucketArn as Gr, describeDockerExecFailure as Gs, seedsDeleteFirstGuard as Gt, waitForIndexesSettled as H, toFiniteNumber as Ha, ownValue as Hc, malformedResourcesWarning as Hi, earliestDefined as Hn, identityKeyFor as Ho, callerEnvCredentials as Hr, buildDockerImage as Hs, recordUnderIdIsNotOwn as Ht, deleteBudgetKey as I, isStatefulRecreateTargetSync as Ia, StageLoadError as Ic, malformedOrphansWarning as Ii, unsupportedFinalSnapshotError as In, redactSecretsForState as Io, assertSendableRoleArn as Ir, isCrossRegionRedirect as Is, demoteSupersededOrphans as It, markRefusedBeforeApplying as J, readConfigString as Ja, clearBucketRegionCache as Jc, refuseMalformedOrphanRecords as Ji, DBPROXY_TAG_OPTIONS as Jn, recordMaskOnlyValue as Jo, s3BucketRegionalDomainName as Jr, finchSecretArgvRefusal as Js, getCurrentProducerRegions as Jt, isTruthyCfnBoolean as K, configIntegerRefusal as Ka, renderAssemblyPathEscape as Kc, refuseDivergentRecordRegionForDestroy as Ki, acquireIdempotencyToken as Kn, carriesSecretMask as Ko, s3BucketDomainName as Kr, describeDockerFailure as Ks, orphanDeleteNeedsIdentity as Kt, resolveDynamoDbDeleteBudgetClock as L, renderStatefulReason as La, processStackMessages as Lc, malformedOutputsRefusalMessage as Li, maskingRetryLogger as Ln, scrubResourceRecord as Lo, explicitRoleArnOrThrow as Lr, parseBootstrapMarker as Ls, isJournaledOrphan as Lt, DynamoDBGlobalTableProvider as M, findReplacedReadersOfRecreateTargets as Ma, PARTITION_TABLE as Mc, malformedOrphanRecordsRefusalMessage as Mi, createPreDeleteFinalSnapshot as Mn, errorCauseChain as Mo, producerRegionsFromState as Mr, BOOTSTRAP_MARKER_PREFIX as Ms, getStackRecords as Mt, DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS as N, shownType as Na, canonicalizeRegion as Nc, malformedOrphanRecordsWarning as Ni, effectiveDeletionPolicy as Nn, maskSecretsInError as No, regionLessSecretName as Nr, assertAssetBucketRegion as Ns, withStackRecords as Nt, PROTECTION_PROPERTY_BY_TYPE as O, unwrittenCreateOnlyReplacement as Oa, uploadCfnTemplate as Oc, malformedExportSourceWarning as Oi, ATOMIC_FINAL_SNAPSHOT_TYPES as On, recordLogOnlyParameterValue as Oo, INTRINSIC_KEYS as Or, createAssetRedirectResolver as Os, replayFailedOperations as Ot, beginDeleteWait as P, MULTI_REGION_RECREATE_BLOCKED_TYPES as Pa, derivePartitionAndUrlSuffix as Pc, malformedOrphanRowsKeptWarning as Pi, isFinalSnapshotError as Pn, maskSecretsInText as Po, applyRoleArnIfSet as Pr, ensureAssetStorage as Ps, removeProtectionTypeList as Pt, cyan as Q, requireConfigString as Qa, refuseMalformedOrphansForOrphan as Qi, tagMapAsList as Qn, recordProvenPublicExpression as Qo, parameterTypeMayLoseSecretIdentity as Qr, isMalformedEnvKey as Qs, redactedDeleteAddressFields as Qt, resolveDynamoDbDeleteBudgetMs as R, WARM_THROUGHPUT_MEMBERS as Ra, defineOwnKey as Rc, malformedOutputsWarning as Ri, constructPathOf as Rn, DYNAMIC_REFERENCE_TOKEN_SCAN as Ro, isIamRoleArn as Rr, readBootstrapMarkerBody as Rs, isReplacementOrphan as Rt, noEchoAttributeNamesOf as S, unpreviewableOrphanRecords as Sa, warnDeprecatedNoPrefixCliFlag as Sc, hasReadableOutputs as Si, journaledOrphanPrintingBag as Sn, carryLogOnlyValues as So, ec2InstanceProtectionSite as Sr, shouldRetainResource as Ss, dropFailedHint as St, noEchoParameterNamesOf as T, withheldAccountClause as Ta, CFN_TEMPLATE_URL_LIMIT as Tc, isReadableResourceEntry as Ti, secretNamePrintingBag as Tn, literalSplitDelimitersOf as To, reDisableInstanceApiTermination as Tr, stringifyValue as Ts, loadJournaledOrphans as Tt, ECR_AUTO_DELETE_IMAGES_TAG as U, coerceCfnBoolean as Ua, absoluteAssemblyPathEscape as Uc, malformedScrubResourceEntriesRefusalMessage as Ui, ledgerForStack as Un, inheritNestedStackParameterAssociations as Uo, envCredentialsAreAssumedRole as Ur, resolveDockerContextDirectory as Us, replacementNeverSwapped as Ut, deleteTableWithIndexBusyRetry as V, toCfnInteger as Va, nullPrototypeRecord as Vc, malformedResourcePropertiesWarning as Vi, RollbackInlinePolicyWriters as Vn, pathCrossesDottedKey as Vo, applyCallerIdentityCredentials as Vr, buildDenyExternalAccessPolicy as Vs, recheckFailedPlan as Vt, S3_AUTO_DELETE_OBJECTS_TAG as W, coerceCfnInteger as Wa, describeFileReadFailure as Wc, malformedStateRefusalMessage as Wi, reserveStackCreateToken as Wn, recordNestedStackParameterExpressions as Wo, isSettledInstanceState as Wr, describeDockerCapturedOutput as Ws, resolveReplacementOldType as Wt, createReadOnlyAttributeHealerFactory as X, requireConfigArray as Xa, refuseMalformedOrphans as Xi, readTagList as Xn, recordNoEchoAttributeValues as Xo, coerceParameterTypedValue as Xr, getDockerCmd as Xs, REDACTED_DELETE_ADDRESS_SKIP_REASON as Xt, formatResourceLine as Y, replayWarn as Ya, resolveBucketRegion as Yc, refuseMalformedOrphanRecordsForDestroy as Yi, planTagDiff as Yn, recordMaskOnlyValuesIn as Yo, s3BucketWebsiteUrl as Yr, formatDockerLoginError as Ys, inheritProducerRegions as Yt, bold as Z, requireConfigObject as Za, refuseMalformedOrphansForDestroy as Zi, refuseMalformedDesiredTags as Zn, recordNoEchoParameterFreshValue as Zo, isUnboundTemplateParameter as Zr, isDockerClientEnvKey as Zs, isRedactedRecordedValue as Zt, makeCanonicalizePropertiesFn as _, repairMalformedOrphansForReadOnly as _a, resolveSkipPrefix as _c, UNREADABLE_RESOURCES_MAP_ROW as _i, isExportAliasCollision as _n, passedNoEchoParametersOf as _o, deleteSkipReason as _r, importableOutputKeys as _s, revertedNestedRowIds as _t, findNestedTemplateTreeDefect as a, refuseMalformedResourceEntriesForImport as aa, escapeRegExp$3 as ac, AWS_NO_VALUE as ai, displayTextOrWithheld as an, sameLambdaFunctionAddress as ao, isCustomResource as ar, isSingleDynamicReferenceToken as as, reachableDivergentParameters as at, serviceTokenBlockingReason as b, repairMalformedResourcePropertiesForReadOnly as ba, resolveUseCdkBootstrapAssets as bc, displayLogicalId as bi, DEFAULT_RESOURCE_WARN_AFTER_MS as bn, valueAtCoordinate as bo, markWaitAbandoned as br, orphansAfterRollback as bs, withNestedStackContext as bt, renderNestedTemplateTreeDefect as c, refuseMalformedResourcePropertiesForDestroy as ca, getDockerImageBySourceHash as cc, refStateLookupFromResource as ci, secretBearingStateKeyWarning as cn, canonicalCoordinates as co, readsNoEchoDeleteValues as cr, S3StateBackend as cs, planOrphanAdoption as ct, conditionInputsFrom as d, refuseMalformedResourcePropertiesForOrphan as da, getDefaultStateBucketName as dc, parameterInputsFor as di, exportNameNoEchoParameters as dn, maskReadbackAtCoordinates as do, withPrintingSecrets as dr, acceptedCreateOnlyDropsOf as ds, displacedPhysicalIdShown as dt, refuseMalformedOutputsForDestroy as ea, redactDockerArgvValues as ec, isImpossibleEmptyStoredAttribute as ei, updatePartialMessage as en, templateResourceTypes as eo, definedAttributes as er, STATE_SOURCED_BASELINE_RULES as es, green as et, parentSuppliedValues as f, refuseMalformedResourcesForDeploy as fa, getLegacyStateBucketName as fc, passedParameterClassesOf as fi, exportNameSecretExposure as fn, maskWholeValue as fo, CloudControlProvider as fr, describeRegionValueKind as fs, dropSettledNestedJournals as ft, mergeNoChangeOutputs as g, repairMalformedOrphanRecordsForReadOnly as ga, resolveCaptureObservedState as gc, UNREADABLE_ORPHANS_CONTAINER_ROW as gi, collectPublishedOutputNames as gn, noEchoOutputsComparison as go, deleteIndeterminateGuards as gr, hasUnverifiableParameterRefusal as gs, revertNestedChildFromJournal as gt, keptWholeReasonText as h, refuseMalformedState as ha, resolveAutoAssetStorage as hc, STATE_RESOURCES_MALFORMED as hi, collectDeclaredOutputNames as hn, noEchoLeavesOf as ho, UNSPECIFIED_SKIP_REASON as hr, hasReasonlessBaselineRefusal as hs, recordDisplacedSkips as ht, childTemplateLoader as i, refuseMalformedResourceEntriesForDestroy as ia, warnFinchArgvExposure as ic, keepsSecretReferenceToken as ii, WITHHELD_NAME_DISPLAY as in, lambdaFunctionNameForMask as io, ccBrokenReason as ir, WHOLE_DYNAMIC_REFERENCE_PATTERN as is, namesAnyDeclaredParameter as it, readProtection as j, hasNoCloudControlHandlers as ja, expectedOwnerParam as jc, malformedLocalResourcesWarning as ji, ccRoutedFinalSnapshotError as jn, createSecretMasker as jo, classifyReplaySecretRegion as jr, AssetModeResolver as js, destroyStackRecordsView as jt, isProtectionValueActive as k, withoutSilentDropProperties as ka, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as kc, malformedLocalOutputsWarning as ki, PRE_DELETE_SNAPSHOT_TYPES as kn, recordLogOnlyValue as ko, IntrinsicFunctionResolver as kr, loadPublishableAssetManifest as ks, replayRollback as kt, templateIdentity as l, refuseMalformedResourcePropertiesForDrift as la, Synthesizer as lc, parseWebACLArn as li, secretSafeKeyDisplay as ln, isMarkedCoordinate as lo, isCustomResourceType as lr, rebuildClientForBucketRegion as ls, NESTED_PENDING_PARENT_REASON as lt, isSecretBearingReferenceString as m, refuseMalformedResourcesForExport as ma, resolveApp as mc, STATE_REGION_DIVERGED as mi, noEchoParameterValueSeed as mn, noEchoCoordinatesOf as mo, unchangedBehindSecretReference as mr, hasReadableExportSet as ms, nestedChildStackName as mt, IMPLICIT_DELETE_DEPENDENCIES as n, refuseMalformedResourceEntries as na, runDockerForeground as nc, carriesFetchableDynamicReference as ni, DeploymentEventsReader as nn, withRetry as no, CC_BROKEN_REASON as nr, STATE_SOURCED_READBACK_RULES as ns, yellow as nt, isAbsoluteAssetPath as o, refuseMalformedResourceEntriesForImportSave as oa, stripControlChars as oc, cfnRefValueFromPhysicalId as oi, exportAliasCollisionScrubWarning as on, DagBuilder as oo, wouldReturnToSdkProvider as or, LockManager as os, resourcesNamingDeclaredParameter as ot, readRecordedConditionVerdicts as p, refuseMalformedResourcesForDestroy as pa, loadCdkJson as pc, JSON_LISTING_HOLE_VALUE as pi, isWholeDynamicReferenceValue as pn, maskedLeafCoordinatesOf as po, slowCcOperationTimeoutMs as pr, exportNamesCarriedFrom as ps, getNestedRevertRun as pt, getPriorAttempts as q, configStringRefusal as qa, resolveAssemblyPath as qc, refuseMalformedNestedChildOutputs as qi, DBPROXY_TAGS_WHAT as qn, provenPublicValue as qo, s3BucketDualStackDomainName as qr, dockerSpawnEnvWithSensitive as qs, findNestedStackTypeChanges as qt, computeImplicitDeleteEdges as r, refuseMalformedResourceEntriesForDeploy as ra, runDockerStreaming as rc, isNamelessDynamicReferenceError as ri, DeploymentEventsStore as rn, canonicalLambdaFunctionName as ro, ProviderRegistry as rr, TEMPLATE_SOURCED_RULES as rs, computeParameterTaint as rt, listNestedTemplateRows as s, refuseMalformedResourceEntriesForOrphan as sa, AssetManifestLoader as sc, isStalePlaceholderArnAttribute as si, secretBearing as sn, TemplateParser as so, TemplateNoEchoReresolver as sr, isLockInfoExpired as ss, makeSiblingClaimReader as st, DeployEngine as t, refuseMalformedResourceAttributesForOrphan as ta, redactedDockerCause as tc, carriesDynamicReference as ti, updatePartialReason as tn, describeTypeWithThrottleRetry as to, stringifyIfAssigned as tr, STATE_SOURCED_CROSS_GENERATION_RULES as ts, red as tt, conditionFingerprint as u, refuseMalformedResourcePropertiesForExport as ua, synthesisStatusMessage as uc, withSharedDrainBudget as ui, carriedExportAliasExposure as un, maskAtCoordinates as uo, getCurrentResourceSecrets as ur, splitImportedOps as us, displacedOpClause as ut, deferredServiceTokenWarning as v, repairMalformedOutputsForReadOnly as va, resolveStateBucketWithDefault as vc, accountArgs as vi, bindingSkippedOutputs as vn, readbackPathFor as vo, withIndeterminateGuard as vr, importableOutputs as vs, withNestedRevertRun as vt, noEchoExactEchoLeavesOf as w, unreadableResourceEntries as wa, CFN_TEMPLATE_BODY_LIMIT as wc, isReadableOrphanRecord as wi, orphanRecordsPrintingBag as wn, hasMaskableValues as wo, observeThenDisableInstanceApiTermination as wr, isSensitiveAttributeName as ws, journaledOrphanLines as wt, withResourceDeadline as x, repairMalformedResourcesForReadOnly as xa, stateBucketExistenceConfirmed as xc, hasReadableOrphans as xi, completedReplayEntries as xn, liveMatchesUnresolvedTokenFrame as xo, disableInstanceApiTermination as xr, orphansCarriedFrom as xs, deleteJournaledOrphans as xt, findServiceTokenRefusals as y, repairMalformedResourceEntriesForReadOnly as ya, resolveStateBucketWithDefaultAndSource as yc, deployRefusesOrphanRowsReason as yi, DEFAULT_RESOURCE_TIMEOUT_MS as yn, readsNoEchoSource as yo, isWaitAbandonedError as yr, isReadableBag as ys, getCurrentNestedStackContext as yt, DELETE_INDEX_WAIT_PROCEED_NOTE as z, coerceWarmThroughput as za, hasOwnKey as zc, malformedRenderedContainersWarning as zi, findDestructiveChanges as zn, dynamicReferenceTokens as zo, sendableAssumeRoleCommand as zr, validateAssetBucketName as zs, planFailedOps as zt };
|
|
40959
|
-
//# sourceMappingURL=deploy-engine-
|
|
42849
|
+
export { getPriorAttempts as $, configBooleanRefusal as $a, hasOwnKey as $c, previewableOrphanRecords as $i, acquireIdempotencyToken as $n, recoverMaskedOutput as $o, s3BucketArn as $r, validateAssetBucketName as $s, orphanDeleteNeedsIdentity as $t, noEchoExactEchoLeavesOf as A, unpreviewableOrphanRecords as Aa, resolveCaptureObservedState as Ac, hasReadableOutputs as Ai, maskEventTextWithBoundBags as An, carryLogOnlyValues as Ao, ec2InstanceProtectionSite as Ar, hasUnverifiableParameterRefusal as As, isHandledOrphan as At, beginDeleteWait as B, findReplacedReadersOfRecreateTargets as Ba, MIGRATE_TMP_PREFIX as Bc, malformedOrphanRecordsRefusalMessage as Bi, effectiveDeletionPolicy as Bn, errorCauseChain as Bo, producerRegionsFromState as Br, WorkGraph as Bs, withStackRecords as Bt, recordRetainedForReadoption as C, refuseMalformedState as Ca, Synthesizer as Cc, STATE_RESOURCES_MALFORMED as Ci, collectPublishedOutputNames as Cn, noEchoLeavesOf as Co, deleteIndeterminateGuards as Cr, isNotImplemented as Cs, revertedNestedRowIds as Ct, withResourceDeadline as D, repairMalformedResourceEntriesForReadOnly as Da, loadCdkJson as Dc, deployRefusesOrphanRowsReason as Di, DEFAULT_RESOURCE_WARN_AFTER_MS as Dn, readsNoEchoSource as Do, isWaitAbandonedError as Dr, exportNamesCarriedFrom as Ds, withNestedStackContext as Dt, serviceTokenBlockingReason as E, repairMalformedOutputsForReadOnly as Ea, getLegacyStateBucketName as Ec, accountArgs as Ei, DEFAULT_RESOURCE_TIMEOUT_MS as En, readbackPathFor as Eo, withIndeterminateGuard as Er, describeRegionValueKind as Es, getCurrentNestedStackContext as Et, isProtectionValueActive as F, findSilentDropProperties as Fa, stateBucketExistenceConfirmed as Fc, malformedExportNamesWarning as Fi, ATOMIC_FINAL_SNAPSHOT_TYPES as Fn, printingCorpusOf as Fo, DiffCalculator as Fr, orphansCarriedFrom as Fs, replayFailedOperations as Ft, DELETE_INDEX_WAIT_PROCEED_NOTE as G, renderStatefulReason as Ga, expectedOwnerParam as Gc, malformedOutputsRefusalMessage as Gi, constructPathOf as Gn, scrubResourceRecord as Go, explicitRoleArnOrThrow as Gr, AssetModeResolver as Gs, isReplacementOrphan as Gt, deleteBudgetKey as H, MULTI_REGION_RECREATE_BLOCKED_TYPES as Ha, uploadCfnTemplate as Hc, malformedOrphanRowsKeptWarning as Hi, refusesFinalSnapshot as Hn, maskSecretsInText as Ho, applyRoleArnIfSet as Hr, createAssetRedirectResolver as Hs, recordAfterRollbackUpdate as Ht, perType as I, unwrittenCreateOnlyReplacement as Ia, warnDeprecatedNoPrefixCliFlag as Ic, malformedExportSourceWarning as Ii, PRE_DELETE_SNAPSHOT_TYPES as In, recordLogOnlyParameterValue as Io, INTRINSIC_KEYS as Ir, shouldRetainResource as Is, replayRollback as It, waitForIndexesSettled as J, isWarmThroughputDecrease as Ja, derivePartitionAndUrlSuffix as Jc, malformedResourceEntriesWarning as Ji, RollbackInlinePolicyWriters as Jn, isUncertifiedBaselineMaskPosition as Jo, AWS_CREDENTIAL_ENV_KEYS as Jr, ensureAssetStorage as Js, recheckFailedPlan as Jt, INDEX_SETTLE_POLL_INTERVAL_MS as K, WARM_THROUGHPUT_MEMBERS as Ka, PARTITION_TABLE as Kc, malformedOutputsWarning as Ki, findDestructiveChanges as Kn, DYNAMIC_REFERENCE_TOKEN_SCAN as Ko, isIamRoleArn as Kr, BOOTSTRAP_MARKER_PREFIX as Ks, planFailedOps as Kt, readProtection as L, withoutSilentDropProperties as La, ssmResolvedValueType as Lc, malformedLocalOutputsWarning as Li, buildFinalSnapshotIdentifier as Ln, recordLogOnlyValue as Lo, IntrinsicFunctionResolver as Lr, AssetPublisher as Ls, awaitInFlightSibling as Lt, recordedProtectionEvidence as M, unreadableResourceEntries as Ma, resolveStateBucketWithDefault as Mc, isReadableOrphanRecord as Mi, secretNamePrintingBag as Mn, hasMaskableValues as Mo, observeThenDisableInstanceApiTermination as Mr, importableOutputs as Ms, loadJournaledOrphans as Mt, recordedProtectionNote as N, withheldAccountClause as Na, resolveStateBucketWithDefaultAndSource as Nc, isReadableResourceEntry as Ni, stateSecretNameNeedles as Nn, literalSplitDelimitersOf as No, reDisableInstanceApiTermination as Nr, isReadableBag as Ns, makeForeignHolderScan as Nt, noEchoAttributeNamesOf as O, repairMalformedResourcePropertiesForReadOnly as Oa, resolveApp as Oc, displayLogicalId as Oi, completedReplayEntries as On, valueAtCoordinate as Oo, markWaitAbandoned as Or, hasReadableExportSet as Os, deleteJournaledOrphans as Ot, PROTECTION_PROPERTY_BY_TYPE as P, findActionableSilentDrops as Pa, resolveUseCdkBootstrapAssets as Pc, malformedDriftResourcePropertiesWarning as Pi, extractDeploymentEventError as Pn, logOnlyValueCount as Po, reEnableInstanceApiTermination as Pr, orphansAfterRollback as Ps, sameJournaledOrphans as Pt, isTruthyCfnBoolean as Q, coerceCfnInteger as Qa, defineOwnKey as Qc, malformedStateRefusalMessage as Qi, withCreateTokenLedger as Qn, recordNestedStackParameterExpressions as Qo, isSettledInstanceState as Qr, readBootstrapMarkerBody as Qs, seedsDeleteFirstGuard as Qt, DynamoDBGlobalTableProvider as R, NO_CC_HANDLERS_REASON as Ra, CFN_TEMPLATE_BODY_LIMIT as Rc, malformedLocalResourceEntriesWarning as Ri, ccRoutedFinalSnapshotError as Rn, shareLogOnlyValues as Ro, poisonRenderedSpellingsCollidingIn as Rr, isSensitiveAttributeName as Rs, destroyStackRecordsView as Rt, keptForReadoption as S, refuseMalformedResourcesForExport as Sa, getDockerImageBySourceHash as Sc, STATE_REGION_DIVERGED as Si, collectDeclaredOutputNames as Sn, noEchoCoordinatesOf as So, UNSPECIFIED_SKIP_REASON as Sr, isAccessDenied as Ss, revertNestedChildFromJournal as St, findServiceTokenRefusals as T, repairMalformedOrphansForReadOnly as Ta, getDefaultStateBucketName as Tc, UNREADABLE_RESOURCES_MAP_ROW as Ti, bindingSkippedOutputs as Tn, passedNoEchoParametersOf as To, deleteSkipReason as Tr, acceptedCreateOnlyDropsOf as Ts, collectNestedChildUnaddressed as Tt, resolveDynamoDbDeleteBudgetClock as U, isStatefulRecreateTargetForReplace as Ua, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Uc, malformedOrphansRefusalMessage as Ui, unsupportedFinalSnapshotError as Un, markSameGenerationBag as Uo, assertFlagRoleArn as Ur, loadPublishableAssetManifest as Us, demoteSupersededOrphans as Ut, compensateRemovedDeletionProtection as V, shownType as Va, findLargeInlineResources as Vc, malformedOrphanRecordsWarning as Vi, isFinalSnapshotError as Vn, maskSecretsInError as Vo, regionLessSecretName as Vr, buildAssetRedirectMap as Vs, removeProtectionTypeList as Vt, resolveDynamoDbDeleteBudgetMs as W, isStatefulRecreateTargetSync as Wa, decodeListingKey as Wc, malformedOrphansWarning as Wi, maskingRetryLogger as Wn, redactSecretsForState as Wo, assertSendableRoleArn as Wr, rewriteTemplateAssetReferences as Ws, isJournaledOrphan as Wt, S3_AUTO_DELETE_OBJECTS_TAG as X, toFiniteNumber as Xa, StageLoadError as Xc, malformedResourcesWarning as Xi, ledgerForStack as Xn, identityKeyFor as Xo, callerEnvCredentials as Xr, isCrossRegionRedirect as Xs, replacementNeverSwapped as Xt, ECR_AUTO_DELETE_IMAGES_TAG as Y, toCfnInteger as Ya, AssemblyReader as Yc, malformedResourcePropertiesWarning as Yi, earliestDefined as Yn, pathCrossesDottedKey as Yo, applyCallerIdentityCredentials as Yr, getBootstrapMarkerKey as Ys, recordUnderIdIsNotOwn as Yt, hasCdkAutoDeleteTag as Z, coerceCfnBoolean as Za, processStackMessages as Zc, malformedScrubResourceEntriesRefusalMessage as Zi, reserveStackCreateToken as Zn, inheritNestedStackParameterAssociations as Zo, envCredentialsAreAssumedRole as Zr, parseBootstrapMarker as Zs, resolveReplacementOldType as Zt, makeCanonicalizePropertiesFn as _, refuseMalformedResourcePropertiesForDrift as _a, runDockerStreaming as _c, parseWebACLArn as _i, carriedExportAliasExposure as _n, isMarkedCoordinate as _o, getCurrentResourceSecrets as _r, rebuildClientForBucketRegion as _s, displacedPhysicalIdShown as _t, findNestedTemplateTreeDefect as a, refuseMalformedOrphansForDestroy as aa, describeDockerExecFailure as ac, isUnboundTemplateParameter as ai, displayAssemblyPath as al, redactedDeleteAddressFields as an, requireConfigObject as ao, tagMapAsList as ar, recordNoEchoParameterFreshValue as as, gray as at, lookupEachName as b, refuseMalformedResourcesForDeploy as ba, stripControlChars as bc, passedParameterClassesOf as bi, isWholeDynamicReferenceValue as bn, maskWholeValue as bo, slowCcOperationTimeoutMs as br, applyCrossPrefixScan as bs, nestedChildStackName as bt, renderNestedTemplateTreeDefect as c, refuseMalformedOutputsForDestroy as ca, finchSecretArgvRefusal as cc, isImpossibleEmptyStoredAttribute as ci, clearBucketRegionCache as cl, updatePartialReason as cn, templateResourceTypes as co, stringifyIfAssigned as cr, STATE_SOURCED_BASELINE_RULES as cs, yellow as ct, conditionInputsFrom as d, refuseMalformedResourceEntriesForDeploy as da, isDockerClientEnvKey as dc, isNamelessDynamicReferenceError as di, WITHHELD_NAME_DISPLAY as dn, canonicalLambdaFunctionName as do, ccBrokenReason as dr, TEMPLATE_SOURCED_RULES as ds, reachableDivergentParameters as dt, refuseDivergentRecordRegionForDestroy as ea, validateContainerRepoName as ec, s3BucketDomainName as ei, hasPlainPrototype$1 as el, findNestedStackTypeChanges as en, configIntegerRefusal as eo, DBPROXY_TAGS_WHAT as er, carriesSecretMask as es, markRefusedBeforeApplying as et, parentSuppliedValues as f, refuseMalformedResourceEntriesForDestroy as fa, isMalformedEnvKey as fc, keepsSecretReferenceToken as fi, displayTextOrWithheld as fn, lambdaFunctionNameForMask as fo, isCustomResource as fr, WHOLE_DYNAMIC_REFERENCE_PATTERN as fs, resourcesNamingDeclaredParameter as ft, mergeNoChangeOutputs as g, refuseMalformedResourcePropertiesForDestroy as ga, runDockerForeground as gc, refStateLookupFromResource as gi, secretSafeKeyDisplay as gn, canonicalCoordinates as go, isCustomResourceType as gr, S3StateBackend as gs, displacedOpClause as gt, keptWholeReasonText as h, refuseMalformedResourceEntriesForOrphan as ha, redactedDockerCause as hc, isStalePlaceholderArnAttribute as hi, secretBearingStateKeyWarning as hn, TemplateParser as ho, readsNoEchoDeleteValues as hr, isLockInfoExpired as hs, NESTED_PENDING_PARENT_REASON as ht, childTemplateLoader as i, refuseMalformedOrphans as ia, describeDockerCapturedOutput as ic, coerceParameterTypedValue as ii, describeFileReadFailure as il, isRedactedRecordedValue as in, requireConfigArray as io, refuseMalformedDesiredTags as ir, recordNoEchoAttributeValues as is, cyan as it, noEchoParameterNamesOf as j, unreadableOrphanRecords as ja, resolveSkipPrefix as jc, hasReadableResources as ji, orphanRecordsPrintingBag as jn, createUnionSecretMasker as jo, isTerminationProtectionPropagationError as jr, importableOutputKeys as js, journaledOrphanLines as jt, noEchoComparisonForTemplate as k, repairMalformedResourcesForReadOnly as ka, resolveAutoAssetStorage as kc, hasReadableOrphans as ki, journaledOrphanPrintingBag as kn, liveMatchesUnresolvedTokenFrame as ko, disableInstanceApiTermination as kr, hasReasonlessBaselineRefusal as ks, dropFailedHint as kt, templateIdentity as l, refuseMalformedResourceAttributesForOrphan as la, formatDockerLoginError as lc, carriesDynamicReference as li, resolveBucketRegion as ll, DeploymentEventsReader as ln, describeTypeWithThrottleRetry as lo, CC_BROKEN_REASON as lr, STATE_SOURCED_CROSS_GENERATION_RULES as ls, computeParameterTaint as lt, isSecretBearingReferenceString as m, refuseMalformedResourceEntriesForImportSave as ma, redactDockerArgvValues as mc, cfnRefValueFromPhysicalId as mi, secretBearing as mn, DagBuilder as mo, TemplateNoEchoReresolver as mr, LockManager as ms, planOrphanAdoption as mt, IMPLICIT_DELETE_DEPENDENCIES as n, refuseMalformedOrphanRecords as na, buildDockerImage as nc, s3BucketRegionalDomainName as ni, ownValue as nl, inheritProducerRegions as nn, readConfigString as no, planTagDiff as nr, recordMaskOnlyValue as ns, createReadOnlyAttributeHealerFactory as nt, isAbsoluteAssetPath as o, refuseMalformedOrphansForOrphan as oa, describeDockerFailure as oc, parameterTypeMayLoseSecretIdentity as oi, renderAssemblyPathEscape as ol, redactedDeleteAddressSkip as on, requireConfigString as oo, tagPlanWarning as or, recordProvenPublicExpression as os, green as ot, readRecordedConditionVerdicts as p, refuseMalformedResourceEntriesForImport as pa, partitionSensitiveEnv as pc, AWS_NO_VALUE as pi, exportAliasCollisionScrubWarning as pn, sameLambdaFunctionAddress as po, wouldReturnToSdkProvider as pr, isSingleDynamicReferenceToken as ps, makeSiblingClaimReader as pt, deleteTableWithIndexBusyRetry as q, coerceWarmThroughput as qa, canonicalizeRegion as qc, malformedRenderedContainersWarning as qi, formatDestructiveChange as qn, dynamicReferenceTokens as qo, sendableAssumeRoleCommand as qr, assertAssetBucketRegion as qs, planRollback as qt, computeImplicitDeleteEdges as r, refuseMalformedOrphanRecordsForDestroy as ra, resolveDockerContextDirectory as rc, s3BucketWebsiteUrl as ri, absoluteAssemblyPathEscape as rl, REDACTED_DELETE_ADDRESS_SKIP_REASON as rn, replayWarn as ro, readTagList as rr, recordMaskOnlyValuesIn as rs, bold as rt, listNestedTemplateRows as s, refuseMalformedOutputs as sa, dockerSpawnEnvWithSensitive as sc, getAccountInfo as si, resolveAssemblyPath as sl, updatePartialMessage as sn, prefetchCreateOnlyPropertyPaths as so, definedAttributes as sr, wholeStringLeavesOf as ss, red as st, DeployEngine as t, refuseMalformedNestedChildOutputs as ta, buildDenyExternalAccessPolicy as tc, s3BucketDualStackDomainName as ti, nullPrototypeRecord as tl, getCurrentProducerRegions as tn, configStringRefusal as to, DBPROXY_TAG_OPTIONS as tr, provenPublicValue as ts, formatResourceLine as tt, conditionFingerprint as u, refuseMalformedResourceEntries as ua, getDockerCmd as uc, carriesFetchableDynamicReference as ui, DeploymentEventsStore as un, withRetry as uo, ProviderRegistry as ur, STATE_SOURCED_READBACK_RULES as us, namesAnyDeclaredParameter as ut, chunks as v, refuseMalformedResourcePropertiesForExport as va, warnFinchArgvExposure as vc, withSharedDrainBudget as vi, exportNameNoEchoParameters as vn, maskAtCoordinates as vo, withPrintingSecrets as vr, CrossPrefixReadError as vs, dropSettledNestedJournals as vt, deferredServiceTokenWarning as w, repairMalformedOrphanRecordsForReadOnly as wa, synthesisStatusMessage as wc, UNREADABLE_ORPHANS_CONTAINER_ROW as wi, isExportAliasCollision as wn, noEchoOutputsComparison as wo, deleteLeftInPlace as wr, splitImportedOps as ws, withNestedRevertRun as wt, withApiLimit as x, refuseMalformedResourcesForDestroy as xa, AssetManifestLoader as xc, JSON_LISTING_HOLE_VALUE as xi, noEchoParameterValueSeed as xn, maskedLeafCoordinatesOf as xo, unchangedBehindSecretReference as xr, crossPrefixDeniedWarning as xs, recordDisplacedSkips as xt, isAccessDeniedError as y, refuseMalformedResourcePropertiesForOrphan as ya, escapeRegExp$3 as yc, parameterInputsFor as yi, exportNameSecretExposure as yn, maskReadbackAtCoordinates as yo, CloudControlProvider as yr, CrossPrefixScanCache as ys, getNestedRevertRun as yt, DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS as z, hasNoCloudControlHandlers as za, CFN_TEMPLATE_URL_LIMIT as zc, malformedLocalResourcesWarning as zi, createPreDeleteFinalSnapshot as zn, createSecretMasker as zo, classifyReplaySecretRegion as zr, stringifyValue as zs, getStackRecords as zt };
|
|
42850
|
+
//# sourceMappingURL=deploy-engine-BuPc2Dfc.js.map
|