@go-to-k/cdkd 0.294.8 → 0.294.10
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-CsajFK4_.js → asg-provider-BFlmarbF.js} +2 -2
- package/dist/{asg-provider-CsajFK4_.js.map → asg-provider-BFlmarbF.js.map} +1 -1
- package/dist/cli.js +35 -4
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-C5kAhscq.js → deploy-engine-RCOtmm4V.js} +1214 -702
- package/dist/deploy-engine-RCOtmm4V.js.map +1 -0
- package/dist/iam-policy-targets-CMSQGQnm.js.map +1 -1
- package/dist/{iam-role-provider-CPn7nqoS.js → iam-role-provider-2w-Hr9_h.js} +2 -2
- package/dist/{iam-role-provider-CPn7nqoS.js.map → iam-role-provider-2w-Hr9_h.js.map} +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{neptune-provider-DlFQdgwe.js → neptune-provider-CHnIs1hG.js} +2 -2
- package/dist/{neptune-provider-DlFQdgwe.js.map → neptune-provider-CHnIs1hG.js.map} +1 -1
- package/dist/{program-CVOLJJ2z.js → program-Eed63bId.js} +255 -17
- package/dist/program-Eed63bId.js.map +1 -0
- package/dist/{provider-classes-DlVxBF89.js → provider-classes-R7yvwymx.js} +287 -67
- package/dist/provider-classes-R7yvwymx.js.map +1 -0
- package/dist/{rds-provider-a-vZsR9L.js → rds-provider-DqHELwjV.js} +2 -2
- package/dist/{rds-provider-a-vZsR9L.js.map → rds-provider-DqHELwjV.js.map} +1 -1
- package/dist/{version-DGxZ-zFN.js → version-BNo3JdVW.js} +2 -2
- package/dist/{version-DGxZ-zFN.js.map → version-BNo3JdVW.js.map} +1 -1
- package/package.json +2 -2
- package/dist/deploy-engine-C5kAhscq.js.map +0 -1
- package/dist/program-CVOLJJ2z.js.map +0 -1
- package/dist/provider-classes-DlVxBF89.js.map +0 -1
|
@@ -2,7 +2,7 @@ import { r as getAwsClients, s as __exportAll } from "./aws-clients-CnmtbrxD.js"
|
|
|
2
2
|
import { A as displaySafe, E as UNRENDERABLE, F as safeMsg, L as truncateCodePoints, M as isPasteableIdent, O as displayAwsMessage, P as plainIdentOr, S as withoutGeneratedFallbackName, T as STACK_REF_MAX_CODE_POINTS, _ as getCurrentSkipPrefix, b as withSkipPrefix, c as getLiveRenderer, f as applyDefaultNameForFallback, g as generateResourceNameWithFallback, h as generateResourceName, j as displayStackName, k as displayIdent, m as explicitNamePropertyFor, n as getLogger, r as isStdoutReservedForPayload, s as installLogLineMaskerSource, v as getCurrentStackName, w as SECRET_REF_MAX_CODE_POINTS, x as withStackName, y as looksLikeCdkdGeneratedName } from "./logger-B46YMG0h.js";
|
|
3
3
|
import { $t as StateError, A as isTerminalDeleteFailure, An as isAmbiguousCcHandlerErrorCode, At as clientDefaultsFor, Bn as isMarkedNonRetryable, Bt as IntrinsicResolutionRefusalError, C as refusalTypeShown, Cn as isNameCollisionErrorFrom, D as ProtectionFlipRegistry, Dt as withPasteableAwsProfile, En as isReplayedNameCollisionFrom, Fn as retryClassificationText, Gt as MalformedProducerRecordRefusalError, H as pasteableAwsCommand, Hn as markNonRetryable, In as describeAwsFailure, It as CrossAccountSecretRefusalError, J as isInterruptedWaitError, Jt as ProvisioningError, Lt as DependencyError, M as observeThenDisableProtection, Mn as isIamPropagationError, Mt as AccountIdUnavailableError, N as protectionFlipKey, Nn as isRetryableTransientError, Nt as AssetError, O as compensateProtectionFlip, On as markReplayMayCollide, Ot as ambientClientDefaults, Pn as isTransientServerError, Pt as CdkdError, Q as canonicalizeRoute53QueryName, R as DELETION_PROTECTION_DOC_POINTER, Rn as safeStringify$1, S as quotedRemedyLogicalId, Sn as hasReplayMayCollide, St as injectiveKey, T as resolveExplicitPhysicalId, Tn as isRecreateRetryableError, Tt as producerRecordKey, U as protectedReplacementAdvice, Un as markRedactedCause, Ut as LockError, Vn as isThrottlingError, Wn as wrapMaskedAwsError, Wt as MacroExpansionError, X as ElapsedBudgetRegistry, Xt as ResourceUpdateNotSupportedError, Y as startInterruptWatch, Yt as ResourceTimeoutError, Z as monotonicNowMs, _n as shellQuote, _t as formatLockOperation, at as segmentAfterAnchor, b as isQuotableImportValue, bt as CUSTOM_RESOURCE_RESPONSE_PREFIX, cn as UNSHOWABLE_VALUE, ct as isSecretDerivedValue, d as prepareRemovalForUpdate, dn as isInertUnquoted, dt as withDerivedNameMasks, en as SynthesisError, et as compositeIdSeparatorRefusal, f as removalWarning, fn as pasteableCommand, ft as UNREPRODUCIBLE_LOCK_CLAUSE, g as assertRegionMatch, gn as shellBoundedDisplay, gt as formatLockExpiry, h as RESOURCE_NOT_FOUND, hn as quotedOrDescribed, i as holdsSecretDerivedEntry, it as resourceTypeShown, jn as isAmbiguousOutcomeError, jt as credentialFingerprint, k as deleteWithProtectionCompensation, kn as formatRetryClassificationSignals, kt as ambientCredentialConfig, l as withUnchangedSecretPrincipalLists, ln as commandHole, lt as maskDeep, m as withoutInjectedRemovals, mn as plainOrDescribed, nt as logicalIdShown, on as SHELL_ACTIVE, ot as createMaskedLogSinks, pn as physicalIdShownBesideCommand, pt as buildForceUnlockCommand, rn as normalizeAwsError, sn as SHELL_ACTIVE_WHY, st as createMaskedRetryLogger, ut as maskerOrIdentity, vn as withheldTargetClause, vt as formatLockOwner, wn as isNameCooldownError, x as normalizeAwsTagsToCfn, xn as isUpdateUnsupportedError, xt as DEFAULT_STATE_PREFIX, yt as sanitizeRecoveryValue, zt as DynamicReferenceRegionAmbiguousError } from "./iam-policy-targets-CMSQGQnm.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-BNo3JdVW.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";
|
|
@@ -4645,6 +4645,12 @@ function importableOutputKeys(state) {
|
|
|
4645
4645
|
if (state.exportNames === void 0) return Object.keys(outputs);
|
|
4646
4646
|
return state.exportNames.filter((name) => typeof name === "string" && Object.hasOwn(outputs, name));
|
|
4647
4647
|
}
|
|
4648
|
+
function acceptedCreateOnlyDropsOf(record) {
|
|
4649
|
+
if (record === null || typeof record !== "object") return /* @__PURE__ */ new Set();
|
|
4650
|
+
const value = record.acceptedCreateOnlyDrops;
|
|
4651
|
+
if (!Array.isArray(value)) return /* @__PURE__ */ new Set();
|
|
4652
|
+
return new Set(value.filter((entry) => typeof entry === "string"));
|
|
4653
|
+
}
|
|
4648
4654
|
function hasReadableExportSet(state) {
|
|
4649
4655
|
if (!isReadableBag(state.outputs)) return false;
|
|
4650
4656
|
if (state.exportNames === void 0) return true;
|
|
@@ -6077,18 +6083,24 @@ function isPlainObject$3(value) {
|
|
|
6077
6083
|
|
|
6078
6084
|
//#endregion
|
|
6079
6085
|
//#region src/deployment/secret-redaction/mask-only.ts
|
|
6080
|
-
const recordedSecretExpressions
|
|
6081
|
-
|
|
6082
|
-
|
|
6086
|
+
const recordedSecretExpressions = /* @__PURE__ */ new Set();
|
|
6087
|
+
const secretVerdictScopes = /* @__PURE__ */ new Map();
|
|
6088
|
+
function pinScopedSecretVerdict(scope, expression, secret) {
|
|
6089
|
+
const scopes = secretVerdictScopes.get(expression);
|
|
6090
|
+
if (secret) {
|
|
6091
|
+
if (scopes) scopes.add(scope);
|
|
6092
|
+
else secretVerdictScopes.set(expression, /* @__PURE__ */ new Set([scope]));
|
|
6093
|
+
recordedSecretExpressions.add(expression);
|
|
6094
|
+
return;
|
|
6095
|
+
}
|
|
6096
|
+
if (scopes?.delete(scope) && scopes.size === 0) secretVerdictScopes.delete(expression);
|
|
6097
|
+
recordedSecretExpressions.delete(expression);
|
|
6083
6098
|
}
|
|
6084
|
-
function
|
|
6085
|
-
|
|
6099
|
+
function isScopedSecretVerdict(scope, expression) {
|
|
6100
|
+
return secretVerdictScopes.get(expression)?.has(scope) === true;
|
|
6086
6101
|
}
|
|
6087
6102
|
function isRecordedSecretExpression(expression) {
|
|
6088
|
-
return recordedSecretExpressions
|
|
6089
|
-
}
|
|
6090
|
-
function clearRecordedSecretExpressions() {
|
|
6091
|
-
recordedSecretExpressions$1.clear();
|
|
6103
|
+
return recordedSecretExpressions.has(expression);
|
|
6092
6104
|
}
|
|
6093
6105
|
function recordMaskOnlyValue(secrets, plaintext) {
|
|
6094
6106
|
if (plaintext.length < 4) return;
|
|
@@ -6542,12 +6554,11 @@ function positionByRecordedParameterSpans(bag, source, secrets, associations) {
|
|
|
6542
6554
|
let persisted = "";
|
|
6543
6555
|
const gaps = [];
|
|
6544
6556
|
const certified = [];
|
|
6545
|
-
for (const { start, length,
|
|
6557
|
+
for (const { start, length, key } of recorded) {
|
|
6546
6558
|
if (!(start >= previousEnd && length > 0 && start + length <= bag.length)) return void 0;
|
|
6547
6559
|
previousEnd = start + length;
|
|
6548
6560
|
const text = bag.slice(start, start + length);
|
|
6549
|
-
const
|
|
6550
|
-
const association = key === void 0 ? void 0 : associations.get(key);
|
|
6561
|
+
const association = associations.get(key);
|
|
6551
6562
|
if (association === void 0 || typeof association === "symbol") continue;
|
|
6552
6563
|
const expression = certifiedExpressionForLeaf(secrets, association, text);
|
|
6553
6564
|
if (expression === void 0) continue;
|
|
@@ -7201,7 +7212,7 @@ function positionByIntrinsicSkeleton(bag, source, secrets, secretExpressions) {
|
|
|
7201
7212
|
if (!pattern) return void 0;
|
|
7202
7213
|
const plaintextOf = plaintextIndexOf(secrets);
|
|
7203
7214
|
let matched;
|
|
7204
|
-
for (const candidate of /* @__PURE__ */ new Set([...secretExpressions, ...recordedSecretExpressions
|
|
7215
|
+
for (const candidate of /* @__PURE__ */ new Set([...secretExpressions, ...recordedSecretExpressions])) {
|
|
7205
7216
|
if (candidate.length > MAX_SKELETON_CANDIDATE_LENGTH) return void 0;
|
|
7206
7217
|
if (!pattern.test(candidate)) continue;
|
|
7207
7218
|
const recordedPlaintext = plaintextOf.get(candidate);
|
|
@@ -7259,7 +7270,7 @@ function positionByIntrinsicFrame(bag, source, secrets, secretExpressions, bagIs
|
|
|
7259
7270
|
let matched;
|
|
7260
7271
|
for (const candidate of /* @__PURE__ */ new Set([
|
|
7261
7272
|
...secretExpressions,
|
|
7262
|
-
...recordedSecretExpressions
|
|
7273
|
+
...recordedSecretExpressions,
|
|
7263
7274
|
...resolvedExpressionsOf(secrets)
|
|
7264
7275
|
])) {
|
|
7265
7276
|
if (candidate.length > MAX_SKELETON_CANDIDATE_LENGTH) return void 0;
|
|
@@ -7327,10 +7338,10 @@ function recordIntrinsicLeafResolution(secrets, source, resolution) {
|
|
|
7327
7338
|
secret
|
|
7328
7339
|
})),
|
|
7329
7340
|
complete: resolution.complete,
|
|
7330
|
-
...resolution.parameterSpans === void 0 ? {} : { parameterSpans: resolution.parameterSpans.map(({ start, length,
|
|
7341
|
+
...resolution.parameterSpans === void 0 ? {} : { parameterSpans: resolution.parameterSpans.map(({ start, length, key }) => ({
|
|
7331
7342
|
start,
|
|
7332
7343
|
length,
|
|
7333
|
-
|
|
7344
|
+
key
|
|
7334
7345
|
})) }
|
|
7335
7346
|
};
|
|
7336
7347
|
const previous = leaves.get(source);
|
|
@@ -7359,7 +7370,7 @@ function sameLeafResolution(a, b) {
|
|
|
7359
7370
|
}
|
|
7360
7371
|
function sameParameterSpans(a, b) {
|
|
7361
7372
|
if (a === void 0 || b === void 0) return a === b;
|
|
7362
|
-
return a.length === b.length && a.every((s, i) => s.start === b[i].start && s.length === b[i].length && s.
|
|
7373
|
+
return a.length === b.length && a.every((s, i) => s.start === b[i].start && s.length === b[i].length && s.key === b[i].key);
|
|
7363
7374
|
}
|
|
7364
7375
|
function substitutedSpellingOf(secrets, source, resolvedValue) {
|
|
7365
7376
|
const resolution = intrinsicLeafResolutionsOf.get(secrets)?.get(source);
|
|
@@ -10063,8 +10074,8 @@ const REVERSE_REPLACEMENT_NAME_KEYS = {
|
|
|
10063
10074
|
"AWS::Glue::Job": flat("Name"),
|
|
10064
10075
|
"AWS::Glue::SecurityConfiguration": flat("Name"),
|
|
10065
10076
|
"AWS::Glue::Table": {
|
|
10066
|
-
name: [["TableInput", "Name"]],
|
|
10067
|
-
scope: [["DatabaseName"]]
|
|
10077
|
+
name: [["TableInput", "Name"], ["Name"]],
|
|
10078
|
+
scope: [["DatabaseName"], ["CatalogId"]]
|
|
10068
10079
|
},
|
|
10069
10080
|
"AWS::Glue::Trigger": flat("Name"),
|
|
10070
10081
|
"AWS::Glue::Workflow": flat("Name"),
|
|
@@ -10137,6 +10148,8 @@ const CASE_INSENSITIVE_NAME_TYPES = /* @__PURE__ */ new Set([
|
|
|
10137
10148
|
"AWS::RDS::DBInstance",
|
|
10138
10149
|
"AWS::RDS::DBSubnetGroup"
|
|
10139
10150
|
]);
|
|
10151
|
+
const ASCII_CASE_INSENSITIVE_NAME_TYPES = /* @__PURE__ */ new Set(["AWS::Glue::Table"]);
|
|
10152
|
+
const NO_PHYSICAL_ID_NAME_PROOF = /* @__PURE__ */ new Set(["AWS::Glue::Table"]);
|
|
10140
10153
|
const GENERATED_NAME_VERBATIM = /* @__PURE__ */ new Set([
|
|
10141
10154
|
"AWS::CloudWatch::Alarm",
|
|
10142
10155
|
"AWS::DocDB::DBCluster",
|
|
@@ -10566,16 +10579,18 @@ function holderVerdict(input, v) {
|
|
|
10566
10579
|
}
|
|
10567
10580
|
if (namePath === void 0 || wantName === void 0) return unproven(`${v.create} named no ${labels} (its provider picks one), so cdkd cannot show that ${newResource} holds it`);
|
|
10568
10581
|
const wanted = generatedName ? `${v.create} named no ${labels}, and the cdkd naming rule generates ${r.shown(namePath.join("."))} ${r.quoted(wantName)} for it` : `${v.create} asked for ${r.shown(namePath.join("."))} ${r.quoted(wantName)}`;
|
|
10569
|
-
const
|
|
10570
|
-
const inCase = CASE_INSENSITIVE_NAME_TYPES.has(oldResourceType) ? (value) => value.toLowerCase() : (value) => value;
|
|
10571
|
-
const
|
|
10572
|
-
|
|
10573
|
-
const
|
|
10574
|
-
return
|
|
10582
|
+
const asciiFold = ASCII_CASE_INSENSITIVE_NAME_TYPES.has(oldResourceType);
|
|
10583
|
+
const inCase = CASE_INSENSITIVE_NAME_TYPES.has(oldResourceType) ? (value) => value.toLowerCase() : asciiFold ? (value) => value.replace(/[A-Z]/g, (c) => c.toLowerCase()) : (value) => value;
|
|
10584
|
+
const same = (a, b) => inCase(a) === inCase(b);
|
|
10585
|
+
const namePathHeld = newKey.name.find((path) => {
|
|
10586
|
+
const h = heldAt(recorded, observed, path);
|
|
10587
|
+
return h.value !== void 0 || h.unreadable;
|
|
10575
10588
|
});
|
|
10576
|
-
|
|
10577
|
-
const
|
|
10578
|
-
if (
|
|
10589
|
+
const recordedName = namePathHeld && valueAt(recorded, namePathHeld);
|
|
10590
|
+
const observedName = namePathHeld && valueAt(observed, namePathHeld);
|
|
10591
|
+
if (namePathHeld !== void 0 && recordedName !== void 0 && observedName !== void 0 && !same(recordedName, observedName)) return unproven(`${wanted}, and the records of ${newResource} disagree on its ${r.shown(namePathHeld.join("."))} (recorded ${r.quoted(recordedName)}, read back ${r.quoted(observedName)}), so cdkd cannot show which name it holds`);
|
|
10592
|
+
const haveName = namePathHeld === void 0 ? void 0 : heldAt(recorded, observed, namePathHeld).value;
|
|
10593
|
+
if (!(haveName !== void 0 && same(haveName, wantName) || physicalId !== "" && !NO_PHYSICAL_ID_NAME_PROOF.has(newResourceType) && holderIdNames(inCase(physicalId), inCase(wantName)))) return haveName !== void 0 && !generatedName ? elsewhere(`${wanted}, while ${newResource} holds ${r.quoted(haveName)}`) : unproven(`${wanted}, and cdkd cannot show that ${newResource} holds that name`);
|
|
10579
10594
|
for (const [i, path] of (oldKey.scope ?? []).entries()) {
|
|
10580
10595
|
const label = r.shown(path.join("."));
|
|
10581
10596
|
const wantRaw = valueAt(requested, path);
|
|
@@ -14841,6 +14856,29 @@ function excludeCreateOnly(resourceType, names) {
|
|
|
14841
14856
|
if (createOnly === void 0 || createOnly.size === 0) return names;
|
|
14842
14857
|
return names.filter((property) => !createOnly.has(property));
|
|
14843
14858
|
}
|
|
14859
|
+
function withoutUnwrittenSilentDropProperties(resourceType, recordedProperties, desiredProperties, allowedKeys, evidence) {
|
|
14860
|
+
const drops = findSilentDropProperties(resourceType, recordedProperties);
|
|
14861
|
+
if (drops.length === 0) return recordedProperties;
|
|
14862
|
+
const createOnly = getPropertyCoverage(resourceType)?.createOnlyDrops;
|
|
14863
|
+
const accepted = new Set(findAcceptedSilentDrops(resourceType, desiredProperties, allowedKeys, recordedProperties));
|
|
14864
|
+
const removable = drops.map(({ property }) => property).filter((property) => createOnly?.has(property) !== true || evidence.has(property) && !accepted.has(property));
|
|
14865
|
+
if (removable.length === 0) return recordedProperties;
|
|
14866
|
+
const narrowed = { ...recordedProperties };
|
|
14867
|
+
for (const property of removable) delete narrowed[property];
|
|
14868
|
+
return narrowed;
|
|
14869
|
+
}
|
|
14870
|
+
function findUnwrittenCreateOnlyDrops(resourceType, recordedProperties, desiredProperties, allowedKeys, evidence) {
|
|
14871
|
+
const createOnly = getPropertyCoverage(resourceType)?.createOnlyDrops;
|
|
14872
|
+
if (createOnly === void 0 || createOnly.size === 0 || evidence.size === 0) return [];
|
|
14873
|
+
const accepted = new Set(findAcceptedSilentDrops(resourceType, desiredProperties, allowedKeys, recordedProperties));
|
|
14874
|
+
return findSilentDropProperties(resourceType, recordedProperties).map(({ property }) => property).filter((property) => createOnly.has(property) && evidence.has(property) && !accepted.has(property) && Object.hasOwn(desiredProperties, property) && sameJsonValue(recordedProperties[property], desiredProperties[property], "unchanged"));
|
|
14875
|
+
}
|
|
14876
|
+
function unwrittenCreateOnlyReplacement(resourceType, recordedProperties, desiredProperties, allowedKeys, evidence, replacingPaths) {
|
|
14877
|
+
if (replacingPaths.length === 0) return [];
|
|
14878
|
+
const unwritten = findUnwrittenCreateOnlyDrops(resourceType, recordedProperties, desiredProperties, allowedKeys, evidence);
|
|
14879
|
+
if (unwritten.length === 0) return [];
|
|
14880
|
+
return replacingPaths.every((path) => unwritten.includes(path)) ? unwritten : [];
|
|
14881
|
+
}
|
|
14844
14882
|
function unsupportedPropertyIssueUrl(resourceType, property) {
|
|
14845
14883
|
return `https://github.com/go-to-k/cdkd/issues/new?title=${encodeURIComponent(`Support property ${resourceType}.${property}`)}&labels=resource-support`;
|
|
14846
14884
|
}
|
|
@@ -15645,6 +15683,120 @@ function malformedOrphansForOrphanRefusalMessage(rawStackName, rawRegion, logica
|
|
|
15645
15683
|
return orphanRefusal(stackName, region, recovery, `${stackClause(stackName, region)} holds ${logicalIds.length} rollback-orphan record(s) in 'orphans' that cannot be read — ${namedOrphanRows(logicalIds)} — because the record has no string 'logicalId' or shares it with another row, or its 'state' is not an object, names no resource type, has no non-empty string 'physicalId', or holds a 'properties' or 'attributes' map that is not an object. The record is malformed or truncated. ${verbatim} Those records are not inert: they are what the next 'cdkd deploy' may re-adopt into 'resources' and what 'cdkd diff' previews for adoption, so continuing would report success over damage the next command meets.`, "'cdkd orphan' cannot remove such a record itself: it is not in 'resources', so it has no construct path. Repair the record by hand rather than deleting it — it is the only record that an earlier failed deploy left its resource live in AWS.", ORPHANS_DROP_CAVEAT);
|
|
15646
15684
|
}
|
|
15647
15685
|
|
|
15686
|
+
//#endregion
|
|
15687
|
+
//#region src/deployment/masked-property-fingerprints.ts
|
|
15688
|
+
const FINGERPRINT_LAYOUT$1 = 1;
|
|
15689
|
+
function canonicalJson$2(value) {
|
|
15690
|
+
if (value === void 0) return "null";
|
|
15691
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson$2).join(",")}]`;
|
|
15692
|
+
if (value !== null && typeof value === "object") {
|
|
15693
|
+
const record = value;
|
|
15694
|
+
return `{${Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson$2(record[key])}`).join(",")}}`;
|
|
15695
|
+
}
|
|
15696
|
+
return JSON.stringify(value) ?? "null";
|
|
15697
|
+
}
|
|
15698
|
+
const REFUSED_FINGERPRINT = "refused:secret-in-template";
|
|
15699
|
+
const FINGERPRINT_PREFIX = "sha256:";
|
|
15700
|
+
function templateCarriesNeedle(templateValue, secrets) {
|
|
15701
|
+
const text = canonicalJson$2(templateValue);
|
|
15702
|
+
for (const needle of printingCorpusOf(secrets).keys()) {
|
|
15703
|
+
if (needle.length < 4) continue;
|
|
15704
|
+
if (text.includes(needle) || text.includes(JSON.stringify(needle).slice(1, -1))) return true;
|
|
15705
|
+
}
|
|
15706
|
+
return false;
|
|
15707
|
+
}
|
|
15708
|
+
function maskedPropertyFingerprint(templateValue) {
|
|
15709
|
+
const digest = createHash("sha256").update(canonicalJson$2({
|
|
15710
|
+
layout: FINGERPRINT_LAYOUT$1,
|
|
15711
|
+
value: templateValue
|
|
15712
|
+
})).digest("hex");
|
|
15713
|
+
return `${FINGERPRINT_PREFIX}${digest}`;
|
|
15714
|
+
}
|
|
15715
|
+
function maskedPropertyFingerprintsFor(recordedProperties, templateProps) {
|
|
15716
|
+
const entries = [];
|
|
15717
|
+
for (const key of Object.keys(recordedProperties)) {
|
|
15718
|
+
if (!Object.hasOwn(templateProps, key)) continue;
|
|
15719
|
+
if (!carriesSecretMask(recordedProperties[key])) continue;
|
|
15720
|
+
entries.push([key, maskedPropertyFingerprint(templateProps[key])]);
|
|
15721
|
+
}
|
|
15722
|
+
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
|
|
15723
|
+
}
|
|
15724
|
+
function maskedPropertyFingerprintsOf(record) {
|
|
15725
|
+
const read = /* @__PURE__ */ new Map();
|
|
15726
|
+
if (record === null || typeof record !== "object") return read;
|
|
15727
|
+
const field = record.maskedPropertyFingerprints;
|
|
15728
|
+
if (field === null || typeof field !== "object" || Array.isArray(field)) return read;
|
|
15729
|
+
for (const [key, value] of Object.entries(field)) if (typeof value === "string" && value.startsWith(FINGERPRINT_PREFIX)) read.set(key, value);
|
|
15730
|
+
return read;
|
|
15731
|
+
}
|
|
15732
|
+
function movedMaskedProperties(record, templateProps) {
|
|
15733
|
+
const recorded = maskedPropertyFingerprintsOf(record);
|
|
15734
|
+
if (recorded.size === 0) return [];
|
|
15735
|
+
const properties = record.properties;
|
|
15736
|
+
if (properties === null || typeof properties !== "object") return [];
|
|
15737
|
+
const moved = [];
|
|
15738
|
+
for (const [key, fingerprint] of recorded) {
|
|
15739
|
+
if (!Object.hasOwn(templateProps, key) || !Object.hasOwn(properties, key)) continue;
|
|
15740
|
+
if (!carriesSecretMask(properties[key])) continue;
|
|
15741
|
+
if (maskedPropertyFingerprint(templateProps[key]) !== fingerprint) moved.push(key);
|
|
15742
|
+
}
|
|
15743
|
+
return moved;
|
|
15744
|
+
}
|
|
15745
|
+
const writtenFromDeployedTemplate = /* @__PURE__ */ new WeakSet();
|
|
15746
|
+
function markWrittenFromDeployedTemplate(bag) {
|
|
15747
|
+
writtenFromDeployedTemplate.add(bag);
|
|
15748
|
+
return bag;
|
|
15749
|
+
}
|
|
15750
|
+
function withMaskedPropertyFingerprints(scrubbed, writtenBag, templateProps, secrets, noEchoParameterValues) {
|
|
15751
|
+
if (templateProps === void 0) return scrubbed;
|
|
15752
|
+
const corpora = [secrets, noEchoParameterValues].filter((corpus) => corpus !== void 0);
|
|
15753
|
+
const written = writtenBag !== null && typeof writtenBag === "object" && writtenFromDeployedTemplate.has(writtenBag);
|
|
15754
|
+
const { maskedPropertyFingerprints: previous, ...rest } = scrubbed;
|
|
15755
|
+
let fingerprints;
|
|
15756
|
+
if (written) fingerprints = maskedPropertyFingerprintsFor(scrubbed.properties, templateProps);
|
|
15757
|
+
else {
|
|
15758
|
+
if (previous === void 0 || previous === null || typeof previous !== "object") return scrubbed;
|
|
15759
|
+
if (corpora.length === 0) return scrubbed;
|
|
15760
|
+
fingerprints = { ...previous };
|
|
15761
|
+
}
|
|
15762
|
+
if (fingerprints !== void 0 && corpora.length > 0) {
|
|
15763
|
+
const refused = Object.keys(fingerprints).filter((key) => Object.hasOwn(templateProps, key) && corpora.some((corpus) => templateCarriesNeedle(templateProps[key], corpus)));
|
|
15764
|
+
if (refused.length > 0) fingerprints = Object.fromEntries(Object.entries(fingerprints).map(([key, value]) => [key, refused.includes(key) ? REFUSED_FINGERPRINT : value]));
|
|
15765
|
+
}
|
|
15766
|
+
if (!written && JSON.stringify(fingerprints) === JSON.stringify(previous)) return scrubbed;
|
|
15767
|
+
return fingerprints === void 0 ? rest : {
|
|
15768
|
+
...rest,
|
|
15769
|
+
maskedPropertyFingerprints: fingerprints
|
|
15770
|
+
};
|
|
15771
|
+
}
|
|
15772
|
+
function backfillMaskedPropertyFingerprints(resources, template, noEchoParameterValues) {
|
|
15773
|
+
const declared = template?.Resources;
|
|
15774
|
+
if (declared === void 0 || declared === null) return 0;
|
|
15775
|
+
let stamped = 0;
|
|
15776
|
+
for (const [logicalId, record] of Object.entries(resources)) {
|
|
15777
|
+
if (record === null || typeof record !== "object") continue;
|
|
15778
|
+
const field = record.maskedPropertyFingerprints;
|
|
15779
|
+
if (field !== void 0 && (field === null || typeof field !== "object" || Array.isArray(field))) continue;
|
|
15780
|
+
const existing = field ?? {};
|
|
15781
|
+
if (!Object.hasOwn(declared, logicalId)) continue;
|
|
15782
|
+
const definition = declared[logicalId];
|
|
15783
|
+
if (definition === void 0 || definition.Type !== record.resourceType) continue;
|
|
15784
|
+
const properties = record.properties;
|
|
15785
|
+
if (properties === null || typeof properties !== "object" || Array.isArray(properties)) continue;
|
|
15786
|
+
const fingerprints = maskedPropertyFingerprintsFor(properties, definition.Properties ?? {});
|
|
15787
|
+
if (fingerprints === void 0) continue;
|
|
15788
|
+
const templateProps = definition.Properties ?? {};
|
|
15789
|
+
const added = Object.entries(fingerprints).filter(([key]) => !Object.hasOwn(existing, key)).map(([key, value]) => noEchoParameterValues !== void 0 && templateCarriesNeedle(templateProps[key], noEchoParameterValues) ? [key, REFUSED_FINGERPRINT] : [key, value]);
|
|
15790
|
+
if (added.length === 0) continue;
|
|
15791
|
+
resources[logicalId] = {
|
|
15792
|
+
...record,
|
|
15793
|
+
maskedPropertyFingerprints: Object.fromEntries([...Object.entries(existing), ...added])
|
|
15794
|
+
};
|
|
15795
|
+
stamped++;
|
|
15796
|
+
}
|
|
15797
|
+
return stamped;
|
|
15798
|
+
}
|
|
15799
|
+
|
|
15648
15800
|
//#endregion
|
|
15649
15801
|
//#region src/deployment/drain-budget.ts
|
|
15650
15802
|
const drainDeadlines = new AsyncLocalStorage();
|
|
@@ -15975,12 +16127,6 @@ function isDeliberateResolutionRefusal(err) {
|
|
|
15975
16127
|
}
|
|
15976
16128
|
const cachedAccountIdentities = /* @__PURE__ */ new Map();
|
|
15977
16129
|
const cachedAvailabilityZones = /* @__PURE__ */ new Map();
|
|
15978
|
-
const recordedSecretExpressions = {
|
|
15979
|
-
has: (expression) => isRecordedSecretExpression(expression),
|
|
15980
|
-
add: (expression) => recordSecretExpression(expression),
|
|
15981
|
-
delete: (expression) => forgetSecretExpression(expression),
|
|
15982
|
-
clear: () => clearRecordedSecretExpressions()
|
|
15983
|
-
};
|
|
15984
16130
|
const cachedEc2InstanceAttributes = Object.create(null);
|
|
15985
16131
|
const cachedVpcDefaultSecurityGroups = Object.create(null);
|
|
15986
16132
|
const cachedCloudFrontDomainNames = Object.create(null);
|
|
@@ -17109,10 +17255,9 @@ async function reresolveCrossStackValue(value, producerRegion, context, origin,
|
|
|
17109
17255
|
if (sourceKey !== void 0 && typeof value === "string" && isSingleDynamicReferenceToken(value) && typeof reresolved === "string" && context.recordedSecretValues?.has(reresolved) === true && isSecretExpressionByVerdictOrSpelling(value)) recordCrossStackExpression(context.recordedSecretValues, sourceKey, value, reresolved);
|
|
17110
17256
|
return reresolved;
|
|
17111
17257
|
}
|
|
17112
|
-
function pinSecretVerdict(expression, secret) {
|
|
17258
|
+
function pinSecretVerdict(expression, secret, scope) {
|
|
17113
17259
|
if (this.producerRegionGuest) return;
|
|
17114
|
-
|
|
17115
|
-
else recordedSecretExpressions.delete(expression);
|
|
17260
|
+
pinScopedSecretVerdict(scope, expression, secret);
|
|
17116
17261
|
}
|
|
17117
17262
|
async function resolveImportValue(importValueArg, context) {
|
|
17118
17263
|
const sourceKey = crossStackSourceKey({ "Fn::ImportValue": importValueArg });
|
|
@@ -17725,6 +17870,9 @@ function classifyReplaySecretRegion(expression, consumerRegion, importedProducer
|
|
|
17725
17870
|
|
|
17726
17871
|
//#endregion
|
|
17727
17872
|
//#region src/deployment/intrinsic-resolver/dynamic-refs.ts
|
|
17873
|
+
function secretVerdictScope(resolver) {
|
|
17874
|
+
return injectiveKey(credentialFingerprint(ambientCredentialConfig()), canonicalizeRegion(resolver.explicitRegion) ?? "");
|
|
17875
|
+
}
|
|
17728
17876
|
async function resolveDynamicReferences(value, context) {
|
|
17729
17877
|
return (await this.resolveDynamicReferencesWithLogTwin(value, value, context, void 0, true)).result;
|
|
17730
17878
|
}
|
|
@@ -17751,7 +17899,8 @@ async function resolveDynamicReferencesWithLogTwin(value, logTwin, context, inhe
|
|
|
17751
17899
|
const tokenLogText = inherited?.tokenLogText ?? (pairedTwin === void 0 ? "***" : this.straddleSafeTwin(fullMatch, pairedTwin, context));
|
|
17752
17900
|
const nameLogText = inherited?.nameLogText ?? (pairedTwin === void 0 ? () => "***" : this.dynamicReferenceNameLogText(inner, pairedTwin, context));
|
|
17753
17901
|
try {
|
|
17754
|
-
const
|
|
17902
|
+
const verdictScope = secretVerdictScope(this);
|
|
17903
|
+
const isKnownSecret = service === "secretsmanager" || service === "ssm-secure" || isScopedSecretVerdict(verdictScope, fullMatch);
|
|
17755
17904
|
if (isKnownSecret && context?.skipDynamicReferences) {
|
|
17756
17905
|
complete = false;
|
|
17757
17906
|
continue;
|
|
@@ -17804,8 +17953,8 @@ async function resolveDynamicReferencesWithLogTwin(value, logTwin, context, inhe
|
|
|
17804
17953
|
const param = await this.resolveSSMReference(parts, decrypt, "ssm", context, nameLogText);
|
|
17805
17954
|
if (param.type === "SecureString") {
|
|
17806
17955
|
if (decrypt) pinSecureVerdict = true;
|
|
17807
|
-
else if (persistedText || !this.tokenAssembledForRecording(fullMatch, tokenLogText, context)) this.pinSecretVerdict(fullMatch, true);
|
|
17808
|
-
} else if (!param.secure) this.pinSecretVerdict(fullMatch, false);
|
|
17956
|
+
else if (persistedText || !this.tokenAssembledForRecording(fullMatch, tokenLogText, context)) this.pinSecretVerdict(fullMatch, true, verdictScope);
|
|
17957
|
+
} else if (!param.secure) this.pinSecretVerdict(fullMatch, false, verdictScope);
|
|
17809
17958
|
else cacheable = false;
|
|
17810
17959
|
isSecret = param.secure;
|
|
17811
17960
|
if (param.secure) {
|
|
@@ -17827,7 +17976,7 @@ async function resolveDynamicReferencesWithLogTwin(value, logTwin, context, inhe
|
|
|
17827
17976
|
continue;
|
|
17828
17977
|
}
|
|
17829
17978
|
if (isSecret && resolved && !persistedText) this.refuseSecretAssembledReference(fullMatch, tokenLogText, context);
|
|
17830
|
-
if (pinSecureVerdict) this.pinSecretVerdict(fullMatch, true);
|
|
17979
|
+
if (pinSecureVerdict) this.pinSecretVerdict(fullMatch, true, verdictScope);
|
|
17831
17980
|
if (cacheable) this.cachedDynamicReferences.set(dynamicReferenceCacheKey, {
|
|
17832
17981
|
value: resolved,
|
|
17833
17982
|
secret: isSecret
|
|
@@ -17838,7 +17987,7 @@ async function resolveDynamicReferencesWithLogTwin(value, logTwin, context, inhe
|
|
|
17838
17987
|
recorded.set(resolved, fullMatch);
|
|
17839
17988
|
recordResolvedPair(recorded, fullMatch, resolved);
|
|
17840
17989
|
}
|
|
17841
|
-
if (service === "secretsmanager" || service === "ssm-secure") this.pinSecretVerdict(fullMatch, true);
|
|
17990
|
+
if (service === "secretsmanager" || service === "ssm-secure") this.pinSecretVerdict(fullMatch, true, verdictScope);
|
|
17842
17991
|
}
|
|
17843
17992
|
result = result.replace(fullMatch, () => resolved);
|
|
17844
17993
|
twin = twin.replace(fullMatch, () => isSecret && resolved ? "***" : resolved);
|
|
@@ -17977,11 +18126,20 @@ function partParameterSpans(context, part, resolved) {
|
|
|
17977
18126
|
if (own === void 0 || own.output !== resolved) return [];
|
|
17978
18127
|
return own.parameterSpans ?? [];
|
|
17979
18128
|
}
|
|
18129
|
+
function wholeReferenceSpans(source, value) {
|
|
18130
|
+
if (typeof value !== "string" || value === "") return [];
|
|
18131
|
+
const key = crossStackSourceKey(source);
|
|
18132
|
+
return key === void 0 ? [] : [{
|
|
18133
|
+
start: 0,
|
|
18134
|
+
length: value.length,
|
|
18135
|
+
key
|
|
18136
|
+
}];
|
|
18137
|
+
}
|
|
17980
18138
|
function appendShiftedSpans(out, spans, by) {
|
|
17981
18139
|
for (const span of spans) out.push({
|
|
17982
18140
|
start: span.start + by,
|
|
17983
18141
|
length: span.length,
|
|
17984
|
-
|
|
18142
|
+
key: span.key
|
|
17985
18143
|
});
|
|
17986
18144
|
}
|
|
17987
18145
|
async function resolveJoin(joinArgs, context, source) {
|
|
@@ -18276,11 +18434,7 @@ async function resolveSub(subArgs, context, source) {
|
|
|
18276
18434
|
listRefusal ??= refusal;
|
|
18277
18435
|
replacement = refusal ? match[0] : String(value);
|
|
18278
18436
|
if (!refusal) twinReplacement = this.productLogTwin(value, context);
|
|
18279
|
-
if (!refusal && fromParameter
|
|
18280
|
-
start: 0,
|
|
18281
|
-
length: value.length,
|
|
18282
|
-
parameter: varNameStr
|
|
18283
|
-
}];
|
|
18437
|
+
if (!refusal && fromParameter) parameterSpans = wholeReferenceSpans({ Ref: varNameStr }, value);
|
|
18284
18438
|
} catch (refError) {
|
|
18285
18439
|
if (refError instanceof IntrinsicResolutionRefusalError) throw refError;
|
|
18286
18440
|
if (varNameStr.includes(".")) try {
|
|
@@ -18288,7 +18442,10 @@ async function resolveSub(subArgs, context, source) {
|
|
|
18288
18442
|
const refusal = this.subListRefusal(`the variable \${${varNameStr}}`, value, context);
|
|
18289
18443
|
listRefusal ??= refusal;
|
|
18290
18444
|
replacement = refusal ? match[0] : String(value);
|
|
18291
|
-
if (!refusal)
|
|
18445
|
+
if (!refusal) {
|
|
18446
|
+
twinReplacement = this.productLogTwin(value, context);
|
|
18447
|
+
parameterSpans = wholeReferenceSpans({ "Fn::GetAtt": varNameStr }, value);
|
|
18448
|
+
}
|
|
18292
18449
|
} catch (getAttError) {
|
|
18293
18450
|
if (getAttError instanceof IntrinsicResolutionRefusalError) throw getAttError;
|
|
18294
18451
|
this.rethrowStructuralSubFailure(varNameStr, getAttError, context);
|
|
@@ -19118,6 +19275,16 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
19118
19275
|
const parts = pending === 1 ? "1 concurrent part was" : `${pending} concurrent parts were`;
|
|
19119
19276
|
this.logger.warn(`A resolution failed while ${parts} still running, and cdkd stopped waiting because the wait after a failure (capped at ${capSeconds}s) was used up. A secret such a part resolves from now on is still recorded, but only what runs after it arrives can mask it: output already printed or saved is not revisited.`);
|
|
19120
19277
|
}
|
|
19278
|
+
recordCrossStackReadSpan(context, source, value) {
|
|
19279
|
+
if (typeof value === "string") this.recordLeafResolution(context, source, {
|
|
19280
|
+
input: value,
|
|
19281
|
+
output: value,
|
|
19282
|
+
substitutions: [],
|
|
19283
|
+
complete: true,
|
|
19284
|
+
parameterSpans: wholeReferenceSpans(source, value)
|
|
19285
|
+
});
|
|
19286
|
+
return value;
|
|
19287
|
+
}
|
|
19121
19288
|
async resolveValue(value, context) {
|
|
19122
19289
|
if (typeof value !== "object" || value === null) {
|
|
19123
19290
|
if (typeof value === "string" && value.includes("{{resolve:")) return await this.resolveTemplateLeafReferences(value, context);
|
|
@@ -19136,15 +19303,11 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
19136
19303
|
output: value,
|
|
19137
19304
|
substitutions: [],
|
|
19138
19305
|
complete: true,
|
|
19139
|
-
parameterSpans:
|
|
19140
|
-
start: 0,
|
|
19141
|
-
length: value.length,
|
|
19142
|
-
parameter: logicalId
|
|
19143
|
-
}]
|
|
19306
|
+
parameterSpans: wholeReferenceSpans({ Ref: logicalId }, value)
|
|
19144
19307
|
});
|
|
19145
19308
|
return value;
|
|
19146
19309
|
}
|
|
19147
|
-
if ("Fn::GetAtt" in obj) return await this.resolveGetAtt(obj["Fn::GetAtt"], context);
|
|
19310
|
+
if ("Fn::GetAtt" in obj) return this.recordCrossStackReadSpan(context, obj, await this.resolveGetAtt(obj["Fn::GetAtt"], context));
|
|
19148
19311
|
if ("Fn::Join" in obj) return await this.resolveJoin(obj["Fn::Join"], context, obj);
|
|
19149
19312
|
if ("Fn::Sub" in obj) return await this.resolveSub(obj["Fn::Sub"], context, obj);
|
|
19150
19313
|
if ("Fn::Select" in obj) return await this.resolveSelect(obj["Fn::Select"], context);
|
|
@@ -19155,8 +19318,8 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
19155
19318
|
if ("Fn::And" in obj) return await this.resolveAnd(obj["Fn::And"], context);
|
|
19156
19319
|
if ("Fn::Or" in obj) return await this.resolveOr(obj["Fn::Or"], context);
|
|
19157
19320
|
if ("Fn::Not" in obj) return await this.resolveNot(obj["Fn::Not"], context);
|
|
19158
|
-
if ("Fn::ImportValue" in obj) return await this.resolveImportValue(obj["Fn::ImportValue"], context);
|
|
19159
|
-
if ("Fn::GetStackOutput" in obj) return await this.resolveGetStackOutput(obj["Fn::GetStackOutput"], context);
|
|
19321
|
+
if ("Fn::ImportValue" in obj) return this.recordCrossStackReadSpan(context, obj, await this.resolveImportValue(obj["Fn::ImportValue"], context));
|
|
19322
|
+
if ("Fn::GetStackOutput" in obj) return this.recordCrossStackReadSpan(context, obj, await this.resolveGetStackOutput(obj["Fn::GetStackOutput"], context));
|
|
19160
19323
|
if ("Fn::FindInMap" in obj) return await this.resolveFindInMap(obj["Fn::FindInMap"], context);
|
|
19161
19324
|
if ("Fn::Base64" in obj) return await this.resolveBase64(obj["Fn::Base64"], context);
|
|
19162
19325
|
if ("Fn::GetAZs" in obj) return await this.resolveGetAZs(obj["Fn::GetAZs"], context);
|
|
@@ -19417,13 +19580,29 @@ var DiffCalculator = class DiffCalculator {
|
|
|
19417
19580
|
const resolvedDesiredProps = resolveFn ? await this.resolveBestEffort(rawDesiredProps, resolveFn) : rawDesiredProps;
|
|
19418
19581
|
const sdkRouted = currentResource.provisionedBy !== "cc-api";
|
|
19419
19582
|
const desiredAfterDrops = sdkRouted && allowedUnsupportedProperties ? withoutAcceptedSilentDropProperties(desiredResource.Type, resolvedDesiredProps, allowedUnsupportedProperties, currentResource.properties) : resolvedDesiredProps;
|
|
19420
|
-
const currentAfterDrops = sdkRouted ?
|
|
19583
|
+
const currentAfterDrops = sdkRouted ? withoutUnwrittenSilentDropProperties(desiredResource.Type, currentResource.properties, resolvedDesiredProps, allowedUnsupportedProperties ?? /* @__PURE__ */ new Set(), acceptedCreateOnlyDropsOf(currentResource)) : currentResource.properties;
|
|
19421
19584
|
const desiredPropsForCompare = canonicalizeProperties ? canonicalizeProperties(desiredResource.Type, desiredAfterDrops) : desiredAfterDrops;
|
|
19422
19585
|
const currentPropsForCompare = canonicalizeProperties && sdkRouted ? canonicalizeProperties(desiredResource.Type, currentAfterDrops) : currentAfterDrops;
|
|
19423
19586
|
const droppedKeys = canonicalizeProperties ? Object.keys(desiredAfterDrops).filter((key) => !Object.hasOwn(desiredPropsForCompare, key)) : [];
|
|
19424
19587
|
if (droppedKeys.length > 0) this.logger.warn(`${logicalId} (${desiredResource.Type}): part of the declared properties cannot be sent as declared and is ignored when comparing against deployed state — the provider narrows them (${droppedKeys.join(", ")}). Fix the template to declare only what the resource supports; until then changes to the ignored keys have no effect.`);
|
|
19425
19588
|
else if (canonicalizeProperties && !this.valuesEqual(desiredPropsForCompare, desiredAfterDrops)) this.logger.debug(`${logicalId} (${desiredResource.Type}): the provider normalizes part of the declared properties before sending them; the comparison uses the normalized form. Nothing is dropped.`);
|
|
19426
19589
|
const propertyChanges = await this.compareProperties(desiredResource.Type, currentPropsForCompare, desiredPropsForCompare, maskForLog);
|
|
19590
|
+
const reported = new Set(propertyChanges.map((pc) => pc.path));
|
|
19591
|
+
for (const key of movedMaskedProperties(currentResource, rawDesiredProps)) {
|
|
19592
|
+
if (reported.has(key)) continue;
|
|
19593
|
+
if (!Object.hasOwn(currentPropsForCompare, key)) continue;
|
|
19594
|
+
if (!Object.hasOwn(desiredPropsForCompare, key)) continue;
|
|
19595
|
+
const oldValue = currentPropsForCompare[key];
|
|
19596
|
+
const newValue = desiredPropsForCompare[key];
|
|
19597
|
+
propertyChanges.push({
|
|
19598
|
+
path: key,
|
|
19599
|
+
oldValue,
|
|
19600
|
+
newValue,
|
|
19601
|
+
requiresReplacement: await this.maskedEditRequiresReplacement(desiredResource.Type, key, oldValue, newValue),
|
|
19602
|
+
maskedExpressionChanged: true
|
|
19603
|
+
});
|
|
19604
|
+
this.logger.debug(safeMsg`${logicalId}: ${key} is recorded masked and its template expression changed (go-to-k/cdkd#4451)`);
|
|
19605
|
+
}
|
|
19427
19606
|
const attributeChanges = this.compareAttributes(currentResource, desiredResource);
|
|
19428
19607
|
if (propertyChanges.length > 0 || attributeChanges.length > 0) {
|
|
19429
19608
|
changes.set(logicalId, {
|
|
@@ -19790,6 +19969,11 @@ var DiffCalculator = class DiffCalculator {
|
|
|
19790
19969
|
}
|
|
19791
19970
|
return changes;
|
|
19792
19971
|
}
|
|
19972
|
+
async maskedEditRequiresReplacement(resourceType, key, oldValue, newValue) {
|
|
19973
|
+
if (this.replacementRules.requiresReplacement(resourceType, key, oldValue, newValue)) return true;
|
|
19974
|
+
if (this.replacementRules.isClassified(resourceType, key)) return false;
|
|
19975
|
+
return (await getCreateOnlyPropertyPaths(resourceType)).some((path) => path.length === 1 && path[0] === key);
|
|
19976
|
+
}
|
|
19793
19977
|
static INTRINSIC_KEYS = INTRINSIC_KEYS;
|
|
19794
19978
|
static isIntrinsic(value) {
|
|
19795
19979
|
if (value === null || value === void 0 || typeof value !== "object" || Array.isArray(value)) return false;
|
|
@@ -20649,7 +20833,8 @@ var CloudControlProvider = class {
|
|
|
20649
20833
|
}
|
|
20650
20834
|
}
|
|
20651
20835
|
async update(logicalId, physicalId, resourceType, properties, previousProperties, context) {
|
|
20652
|
-
this.logger
|
|
20836
|
+
const idLog = updateIdLogSink(this.logger, physicalId, properties, previousProperties, context?.maskSecrets);
|
|
20837
|
+
idLog.debug(`Updating resource ${logicalId} (${resourceType}), physical ID: ${physicalId}`);
|
|
20653
20838
|
await this.assertRecordedRegionAgainstClient("pre-update", context?.expectedRegion, resourceType, logicalId, physicalId);
|
|
20654
20839
|
try {
|
|
20655
20840
|
const cleanPreviousProperties = stringifyJsonProperties(resourceType, stripNullValues(previousProperties));
|
|
@@ -20714,9 +20899,9 @@ var CloudControlProvider = class {
|
|
|
20714
20899
|
physicalId,
|
|
20715
20900
|
wasReplaced: false
|
|
20716
20901
|
};
|
|
20717
|
-
if (progressEvent.ResourceModel) result.attributes = this.parseResourceModel(progressEvent.ResourceModel);
|
|
20718
|
-
result.attributes = await this.mergeSparseModelReadback(resourceType, physicalId, result.attributes || {});
|
|
20719
|
-
result.attributes = await this.enrichResourceAttributes(resourceType, physicalId, result.attributes);
|
|
20902
|
+
if (progressEvent.ResourceModel) result.attributes = this.parseResourceModel(progressEvent.ResourceModel, idLog);
|
|
20903
|
+
result.attributes = await this.mergeSparseModelReadback(resourceType, physicalId, result.attributes || {}, idLog);
|
|
20904
|
+
result.attributes = await this.enrichResourceAttributes(resourceType, physicalId, result.attributes, idLog);
|
|
20720
20905
|
return result;
|
|
20721
20906
|
} catch (error) {
|
|
20722
20907
|
this.handleError(error, "UPDATE", resourceType, logicalId, physicalId);
|
|
@@ -20729,7 +20914,7 @@ var CloudControlProvider = class {
|
|
|
20729
20914
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
20730
20915
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
20731
20916
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
20732
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
20917
|
+
const { ASGProvider } = await import("./asg-provider-BFlmarbF.js");
|
|
20733
20918
|
const delegateRegion = ambientRegion();
|
|
20734
20919
|
let asgProvider = this.asgDeleteDelegates.get(delegateRegion);
|
|
20735
20920
|
if (asgProvider === void 0) {
|
|
@@ -20835,7 +21020,7 @@ var CloudControlProvider = class {
|
|
|
20835
21020
|
const family = SDK_DELETE_FAMILIES.get(resourceType);
|
|
20836
21021
|
const cached = this.sdkDeleteDelegates.get(family);
|
|
20837
21022
|
if (cached !== void 0 && cached.region === ccRegion) return cached.provider;
|
|
20838
|
-
const provider = family === "rds" ? new (await (import("./rds-provider-
|
|
21023
|
+
const provider = family === "rds" ? new (await (import("./rds-provider-DqHELwjV.js"))).RDSProvider({ region: ccRegion }) : new (await (import("./neptune-provider-CHnIs1hG.js"))).NeptuneProvider({ region: ccRegion });
|
|
20839
21024
|
this.sdkDeleteDelegates.set(family, {
|
|
20840
21025
|
region: ccRegion,
|
|
20841
21026
|
provider
|
|
@@ -21080,34 +21265,34 @@ var CloudControlProvider = class {
|
|
|
21080
21265
|
const regionFlag = safeRegion === void 0 ? "" : ` --region ${shellQuote(safeRegion)}`;
|
|
21081
21266
|
return `${withPasteableAwsProfile("aws cloudcontrol get-resource-request-status")} --request-token ${shellQuote(safeToken)}${regionFlag}`;
|
|
21082
21267
|
}
|
|
21083
|
-
parseResourceModel(resourceModel) {
|
|
21268
|
+
parseResourceModel(resourceModel, log = this.logger) {
|
|
21084
21269
|
try {
|
|
21085
21270
|
return JSON.parse(resourceModel);
|
|
21086
21271
|
} catch (error) {
|
|
21087
21272
|
const described = describeAwsFailure(error);
|
|
21088
|
-
|
|
21089
|
-
|
|
21273
|
+
log.warn(`Failed to parse resource model: ${described.redacted ? described.summary : error instanceof Error ? error.name : "Error"}\nModel shape: ${resourceModel.length} chars, ${describeJsonKeys(resourceModel)}`);
|
|
21274
|
+
if (log.withheld !== true) log.debug(`Resource model parse failure detail: ${displaySafe(described.detail)}`);
|
|
21090
21275
|
return {};
|
|
21091
21276
|
}
|
|
21092
21277
|
}
|
|
21093
|
-
async accountInfoForSynthesizedArn(resourceType, attributeName, physicalId) {
|
|
21278
|
+
async accountInfoForSynthesizedArn(resourceType, attributeName, physicalId, log = this.logger) {
|
|
21094
21279
|
const region = await this.cloudControlClient.config.region();
|
|
21095
21280
|
try {
|
|
21096
21281
|
return await getAccountInfo(region);
|
|
21097
21282
|
} catch (error) {
|
|
21098
21283
|
if (!(error instanceof AccountIdUnavailableError)) throw error;
|
|
21099
|
-
|
|
21284
|
+
log.warn(`Not enriching ${resourceType} ${attributeName} for ${physicalId}: ${error.message} The resource itself is unaffected; the record heals on its next update.`);
|
|
21100
21285
|
return;
|
|
21101
21286
|
}
|
|
21102
21287
|
}
|
|
21103
|
-
async enrichResourceAttributes(resourceType, physicalId, attributes) {
|
|
21288
|
+
async enrichResourceAttributes(resourceType, physicalId, attributes, log = this.logger) {
|
|
21104
21289
|
const enriched = { ...attributes };
|
|
21105
21290
|
switch (resourceType) {
|
|
21106
21291
|
case "AWS::S3::Bucket":
|
|
21107
21292
|
if (!enriched["Arn"]) try {
|
|
21108
21293
|
enriched["Arn"] = s3BucketArn(physicalId, await this.cloudControlClient.config.region());
|
|
21109
21294
|
} catch (error) {
|
|
21110
|
-
|
|
21295
|
+
log.debug(`Failed to construct S3 Bucket Arn for ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21111
21296
|
}
|
|
21112
21297
|
break;
|
|
21113
21298
|
case "AWS::RDS::DBCluster":
|
|
@@ -21119,10 +21304,10 @@ var CloudControlProvider = class {
|
|
|
21119
21304
|
if (cluster.ReaderEndpoint) enriched["ReadEndpoint.Address"] = cluster.ReaderEndpoint;
|
|
21120
21305
|
if (cluster.DBClusterArn) enriched["Arn"] = cluster.DBClusterArn;
|
|
21121
21306
|
if (cluster.DbClusterResourceId) enriched["DBClusterResourceId"] = cluster.DbClusterResourceId;
|
|
21122
|
-
|
|
21307
|
+
log.debug(`Enriched RDS DBCluster ${physicalId} with Endpoint/Port/Arn from DescribeDBClusters`);
|
|
21123
21308
|
}
|
|
21124
21309
|
} catch (error) {
|
|
21125
|
-
|
|
21310
|
+
log.debug(`Failed to enrich RDS DBCluster ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21126
21311
|
}
|
|
21127
21312
|
break;
|
|
21128
21313
|
case "AWS::RDS::DBInstance":
|
|
@@ -21133,10 +21318,10 @@ var CloudControlProvider = class {
|
|
|
21133
21318
|
if (inst.Endpoint?.Port !== void 0) enriched["Endpoint.Port"] = String(inst.Endpoint.Port);
|
|
21134
21319
|
if (inst.Endpoint?.HostedZoneId) enriched["Endpoint.HostedZoneId"] = inst.Endpoint.HostedZoneId;
|
|
21135
21320
|
if (inst.DBInstanceArn) enriched["Arn"] = inst.DBInstanceArn;
|
|
21136
|
-
|
|
21321
|
+
log.debug(`Enriched RDS DBInstance ${physicalId} with Endpoint/Port/Arn from DescribeDBInstances`);
|
|
21137
21322
|
}
|
|
21138
21323
|
} catch (error) {
|
|
21139
|
-
|
|
21324
|
+
log.debug(`Failed to enrich RDS DBInstance ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21140
21325
|
}
|
|
21141
21326
|
break;
|
|
21142
21327
|
case "AWS::DynamoDB::Table":
|
|
@@ -21144,10 +21329,10 @@ var CloudControlProvider = class {
|
|
|
21144
21329
|
const latestStreamArn = (await getAwsClients().dynamoDB.send(new DescribeTableCommand({ TableName: physicalId }))).Table?.LatestStreamArn;
|
|
21145
21330
|
if (latestStreamArn) {
|
|
21146
21331
|
enriched["StreamArn"] = latestStreamArn;
|
|
21147
|
-
|
|
21332
|
+
log.debug(`Enriched DynamoDB StreamArn for ${physicalId}: ${latestStreamArn}`);
|
|
21148
21333
|
}
|
|
21149
21334
|
} catch (error) {
|
|
21150
|
-
|
|
21335
|
+
log.debug(`Failed to get DynamoDB StreamArn for ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21151
21336
|
}
|
|
21152
21337
|
break;
|
|
21153
21338
|
case "AWS::ApiGateway::RestApi":
|
|
@@ -21155,10 +21340,10 @@ var CloudControlProvider = class {
|
|
|
21155
21340
|
const getRestApiResponse = await getAwsClients().apiGateway.send(new GetRestApiCommand({ restApiId: physicalId }));
|
|
21156
21341
|
if (getRestApiResponse.rootResourceId) {
|
|
21157
21342
|
enriched["RootResourceId"] = getRestApiResponse.rootResourceId;
|
|
21158
|
-
|
|
21343
|
+
log.debug(`Enriched RestApi RootResourceId for ${physicalId}: ${getRestApiResponse.rootResourceId}`);
|
|
21159
21344
|
}
|
|
21160
21345
|
} catch (error) {
|
|
21161
|
-
|
|
21346
|
+
log.debug(`Failed to get RestApi RootResourceId for ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21162
21347
|
}
|
|
21163
21348
|
if (!enriched["RestApiId"]) enriched["RestApiId"] = physicalId;
|
|
21164
21349
|
break;
|
|
@@ -21167,21 +21352,21 @@ var CloudControlProvider = class {
|
|
|
21167
21352
|
const s3CanonicalUserId = (await getAwsClients().cloudFront.send(new GetCloudFrontOriginAccessIdentityCommand({ Id: physicalId }))).CloudFrontOriginAccessIdentity?.S3CanonicalUserId;
|
|
21168
21353
|
if (s3CanonicalUserId) {
|
|
21169
21354
|
enriched["S3CanonicalUserId"] = s3CanonicalUserId;
|
|
21170
|
-
|
|
21355
|
+
log.debug(`Enriched CloudFront OAI S3CanonicalUserId for ${physicalId}: ${s3CanonicalUserId}`);
|
|
21171
21356
|
}
|
|
21172
21357
|
} catch (error) {
|
|
21173
|
-
|
|
21358
|
+
log.debug(`Failed to get CloudFront OAI S3CanonicalUserId for ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21174
21359
|
}
|
|
21175
21360
|
break;
|
|
21176
21361
|
case "AWS::KMS::Key":
|
|
21177
21362
|
if (!enriched["Arn"]) try {
|
|
21178
|
-
const kmsAccountInfo = await this.accountInfoForSynthesizedArn(resourceType, "Arn", physicalId);
|
|
21363
|
+
const kmsAccountInfo = await this.accountInfoForSynthesizedArn(resourceType, "Arn", physicalId, log);
|
|
21179
21364
|
if (kmsAccountInfo) {
|
|
21180
21365
|
enriched["Arn"] = `arn:${kmsAccountInfo.partition}:kms:${canonicalizeRegion(kmsAccountInfo.region)}:${kmsAccountInfo.accountId}:key/${physicalId}`;
|
|
21181
|
-
|
|
21366
|
+
log.debug(`Enriched KMS Key Arn for ${physicalId}: ${String(enriched["Arn"])}`);
|
|
21182
21367
|
}
|
|
21183
21368
|
} catch (error) {
|
|
21184
|
-
|
|
21369
|
+
log.debug(`Failed to construct KMS Key Arn for ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21185
21370
|
}
|
|
21186
21371
|
if (!enriched["KeyId"]) enriched["KeyId"] = physicalId;
|
|
21187
21372
|
break;
|
|
@@ -21193,16 +21378,16 @@ var CloudControlProvider = class {
|
|
|
21193
21378
|
break;
|
|
21194
21379
|
case "AWS::ECR::Repository":
|
|
21195
21380
|
if (!enriched["Arn"]) try {
|
|
21196
|
-
const ecrAccountInfo = await this.accountInfoForSynthesizedArn(resourceType, "Arn", physicalId);
|
|
21381
|
+
const ecrAccountInfo = await this.accountInfoForSynthesizedArn(resourceType, "Arn", physicalId, log);
|
|
21197
21382
|
if (ecrAccountInfo) {
|
|
21198
21383
|
enriched["Arn"] = `arn:${ecrAccountInfo.partition}:ecr:${canonicalizeRegion(ecrAccountInfo.region)}:${ecrAccountInfo.accountId}:repository/${physicalId}`;
|
|
21199
|
-
|
|
21384
|
+
log.debug(`Enriched ECR Repository Arn for ${physicalId}: ${String(enriched["Arn"])}`);
|
|
21200
21385
|
}
|
|
21201
21386
|
} catch (error) {
|
|
21202
|
-
|
|
21387
|
+
log.debug(`Failed to construct ECR Repository Arn: ${describeAwsFailure(error).detail}`);
|
|
21203
21388
|
}
|
|
21204
21389
|
if (!enriched["RepositoryUri"]) try {
|
|
21205
|
-
const ecrAccountInfo = await this.accountInfoForSynthesizedArn(resourceType, "RepositoryUri", physicalId);
|
|
21390
|
+
const ecrAccountInfo = await this.accountInfoForSynthesizedArn(resourceType, "RepositoryUri", physicalId, log);
|
|
21206
21391
|
if (ecrAccountInfo) {
|
|
21207
21392
|
const { urlSuffix } = derivePartitionAndUrlSuffix(ecrAccountInfo.region);
|
|
21208
21393
|
enriched["RepositoryUri"] = `${ecrAccountInfo.accountId}.dkr.ecr.${canonicalizeRegion(ecrAccountInfo.region)}.${urlSuffix}/${physicalId}`;
|
|
@@ -21214,7 +21399,7 @@ var CloudControlProvider = class {
|
|
|
21214
21399
|
const [publicIp, allocationId] = physicalId.split("|");
|
|
21215
21400
|
if (!enriched["AllocationId"]) enriched["AllocationId"] = allocationId;
|
|
21216
21401
|
if (!enriched["PublicIp"]) enriched["PublicIp"] = publicIp;
|
|
21217
|
-
|
|
21402
|
+
log.debug(`Enriched EIP attributes: AllocationId=${allocationId}, PublicIp=${publicIp}`);
|
|
21218
21403
|
}
|
|
21219
21404
|
break;
|
|
21220
21405
|
case "AWS::Lambda::Version":
|
|
@@ -21222,18 +21407,18 @@ var CloudControlProvider = class {
|
|
|
21222
21407
|
const versionSegments = physicalId.split(":");
|
|
21223
21408
|
const versionNumber = versionSegments[versionSegments.length - 1];
|
|
21224
21409
|
enriched["Version"] = versionNumber;
|
|
21225
|
-
|
|
21410
|
+
log.debug(`Enriched Lambda Version for ${physicalId}: ${versionNumber}`);
|
|
21226
21411
|
}
|
|
21227
21412
|
break;
|
|
21228
21413
|
case "AWS::Kinesis::Stream":
|
|
21229
21414
|
if (!enriched["Arn"]) try {
|
|
21230
|
-
const kinesisAccountInfo = await this.accountInfoForSynthesizedArn(resourceType, "Arn", physicalId);
|
|
21415
|
+
const kinesisAccountInfo = await this.accountInfoForSynthesizedArn(resourceType, "Arn", physicalId, log);
|
|
21231
21416
|
if (kinesisAccountInfo) {
|
|
21232
21417
|
enriched["Arn"] = `arn:${kinesisAccountInfo.partition}:kinesis:${canonicalizeRegion(kinesisAccountInfo.region)}:${kinesisAccountInfo.accountId}:stream/${physicalId}`;
|
|
21233
|
-
|
|
21418
|
+
log.debug(`Enriched Kinesis Stream Arn for ${physicalId}: ${String(enriched["Arn"])}`);
|
|
21234
21419
|
}
|
|
21235
21420
|
} catch (error) {
|
|
21236
|
-
|
|
21421
|
+
log.debug(`Failed to construct Kinesis Stream Arn for ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21237
21422
|
}
|
|
21238
21423
|
break;
|
|
21239
21424
|
case "AWS::Lambda::Url":
|
|
@@ -21241,11 +21426,11 @@ var CloudControlProvider = class {
|
|
|
21241
21426
|
const urlConfig = await getAwsClients().lambda.send(new GetFunctionUrlConfigCommand({ FunctionName: physicalId }));
|
|
21242
21427
|
if (urlConfig.FunctionUrl) {
|
|
21243
21428
|
enriched["FunctionUrl"] = urlConfig.FunctionUrl;
|
|
21244
|
-
|
|
21429
|
+
log.debug(`Enriched Lambda URL FunctionUrl for ${physicalId}: ${urlConfig.FunctionUrl}`);
|
|
21245
21430
|
}
|
|
21246
21431
|
if (urlConfig.FunctionArn) enriched["FunctionArn"] = urlConfig.FunctionArn;
|
|
21247
21432
|
} catch (error) {
|
|
21248
|
-
|
|
21433
|
+
log.debug(`Failed to get Lambda URL config for ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21249
21434
|
}
|
|
21250
21435
|
break;
|
|
21251
21436
|
case "AWS::Events::Connection":
|
|
@@ -21259,9 +21444,9 @@ var CloudControlProvider = class {
|
|
|
21259
21444
|
}
|
|
21260
21445
|
}
|
|
21261
21446
|
if (conn.SecretArn && !enriched["SecretArn"]) enriched["SecretArn"] = conn.SecretArn;
|
|
21262
|
-
|
|
21447
|
+
log.debug(`Enriched Events Connection ${physicalId} with Arn/SecretArn/ArnForPolicy from DescribeConnection`);
|
|
21263
21448
|
} catch (error) {
|
|
21264
|
-
|
|
21449
|
+
log.debug(`Failed to enrich Events Connection ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21265
21450
|
}
|
|
21266
21451
|
break;
|
|
21267
21452
|
case "AWS::Events::ApiDestination":
|
|
@@ -21274,9 +21459,9 @@ var CloudControlProvider = class {
|
|
|
21274
21459
|
if (lastSlash > 0) enriched["ArnForPolicy"] = dest.ApiDestinationArn.slice(0, lastSlash);
|
|
21275
21460
|
}
|
|
21276
21461
|
}
|
|
21277
|
-
|
|
21462
|
+
log.debug(`Enriched Events ApiDestination ${physicalId} with Arn/ArnForPolicy from DescribeApiDestination`);
|
|
21278
21463
|
} catch (error) {
|
|
21279
|
-
|
|
21464
|
+
log.debug(`Failed to enrich Events ApiDestination ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21280
21465
|
}
|
|
21281
21466
|
break;
|
|
21282
21467
|
case "AWS::ElastiCache::ReplicationGroup":
|
|
@@ -21295,10 +21480,10 @@ var CloudControlProvider = class {
|
|
|
21295
21480
|
const readPorts = readEndpoints.map((ep) => ep?.Port).filter((p) => p !== void 0);
|
|
21296
21481
|
if (readAddrs.length > 0) enriched["ReadEndPoint.Addresses"] = readAddrs.join(",");
|
|
21297
21482
|
if (readPorts.length > 0) enriched["ReadEndPoint.Ports"] = readPorts.map(String).join(",");
|
|
21298
|
-
|
|
21483
|
+
log.debug(`Enriched ElastiCache ReplicationGroup ${physicalId} with endpoint attributes from DescribeReplicationGroups`);
|
|
21299
21484
|
}
|
|
21300
21485
|
} catch (error) {
|
|
21301
|
-
|
|
21486
|
+
log.debug(`Failed to enrich ElastiCache ReplicationGroup ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21302
21487
|
}
|
|
21303
21488
|
break;
|
|
21304
21489
|
case "AWS::Redshift::Cluster":
|
|
@@ -21307,10 +21492,10 @@ var CloudControlProvider = class {
|
|
|
21307
21492
|
if (cluster?.Endpoint) {
|
|
21308
21493
|
if (cluster.Endpoint.Address) enriched["Endpoint.Address"] = cluster.Endpoint.Address;
|
|
21309
21494
|
if (cluster.Endpoint.Port !== void 0) enriched["Endpoint.Port"] = String(cluster.Endpoint.Port);
|
|
21310
|
-
|
|
21495
|
+
log.debug(`Enriched Redshift Cluster ${physicalId} with Endpoint.Address/Port from DescribeClusters`);
|
|
21311
21496
|
}
|
|
21312
21497
|
} catch (error) {
|
|
21313
|
-
|
|
21498
|
+
log.debug(`Failed to enrich Redshift Cluster ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21314
21499
|
}
|
|
21315
21500
|
break;
|
|
21316
21501
|
case "AWS::OpenSearchService::Domain":
|
|
@@ -21324,31 +21509,31 @@ var CloudControlProvider = class {
|
|
|
21324
21509
|
enriched["DomainArn"] = domain.ARN;
|
|
21325
21510
|
}
|
|
21326
21511
|
if (domain.DomainId) enriched["Id"] = domain.DomainId;
|
|
21327
|
-
|
|
21512
|
+
log.debug(`Enriched OpenSearch Domain ${physicalId} with DomainEndpoint/Arn from DescribeDomain`);
|
|
21328
21513
|
}
|
|
21329
21514
|
} catch (error) {
|
|
21330
|
-
|
|
21515
|
+
log.debug(`Failed to enrich OpenSearch Domain ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21331
21516
|
}
|
|
21332
21517
|
break;
|
|
21333
21518
|
case "AWS::Backup::BackupVault":
|
|
21334
21519
|
if (!enriched["BackupVaultArn"]) {
|
|
21335
|
-
const model = await this.readCcResourceModel(resourceType, physicalId);
|
|
21520
|
+
const model = await this.readCcResourceModel(resourceType, physicalId, log);
|
|
21336
21521
|
if (model) {
|
|
21337
21522
|
if (typeof model["BackupVaultArn"] === "string") enriched["BackupVaultArn"] = model["BackupVaultArn"];
|
|
21338
21523
|
if (!enriched["BackupVaultName"] && typeof model["BackupVaultName"] === "string") enriched["BackupVaultName"] = model["BackupVaultName"];
|
|
21339
|
-
|
|
21524
|
+
log.debug(`Enriched Backup BackupVault ${physicalId} with BackupVaultArn from CC GetResource`);
|
|
21340
21525
|
}
|
|
21341
21526
|
}
|
|
21342
21527
|
if (!enriched["BackupVaultName"]) enriched["BackupVaultName"] = physicalId;
|
|
21343
21528
|
break;
|
|
21344
21529
|
case "AWS::Backup::BackupPlan":
|
|
21345
21530
|
if (!enriched["BackupPlanArn"] || !enriched["VersionId"]) {
|
|
21346
|
-
const model = await this.readCcResourceModel(resourceType, physicalId);
|
|
21531
|
+
const model = await this.readCcResourceModel(resourceType, physicalId, log);
|
|
21347
21532
|
if (model) {
|
|
21348
21533
|
if (!enriched["BackupPlanArn"] && typeof model["BackupPlanArn"] === "string") enriched["BackupPlanArn"] = model["BackupPlanArn"];
|
|
21349
21534
|
if (!enriched["VersionId"] && typeof model["VersionId"] === "string") enriched["VersionId"] = model["VersionId"];
|
|
21350
21535
|
if (!enriched["BackupPlanId"] && typeof model["BackupPlanId"] === "string") enriched["BackupPlanId"] = model["BackupPlanId"];
|
|
21351
|
-
|
|
21536
|
+
log.debug(`Enriched Backup BackupPlan ${physicalId} with BackupPlanArn/VersionId from CC GetResource`);
|
|
21352
21537
|
}
|
|
21353
21538
|
}
|
|
21354
21539
|
if (!enriched["BackupPlanId"]) enriched["BackupPlanId"] = physicalId;
|
|
@@ -21360,53 +21545,53 @@ var CloudControlProvider = class {
|
|
|
21360
21545
|
if (!enriched["SelectionId"]) enriched["SelectionId"] = physicalId.substring(0, firstUnderscore);
|
|
21361
21546
|
if (!enriched["BackupPlanId"]) enriched["BackupPlanId"] = physicalId.substring(firstUnderscore + 1);
|
|
21362
21547
|
}
|
|
21363
|
-
const model = await this.readCcResourceModel(resourceType, physicalId);
|
|
21548
|
+
const model = await this.readCcResourceModel(resourceType, physicalId, log);
|
|
21364
21549
|
if (model) {
|
|
21365
21550
|
if (typeof model["SelectionId"] === "string") enriched["SelectionId"] = model["SelectionId"];
|
|
21366
21551
|
if (typeof model["BackupPlanId"] === "string") enriched["BackupPlanId"] = model["BackupPlanId"];
|
|
21367
|
-
|
|
21552
|
+
log.debug(`Enriched Backup BackupSelection ${physicalId} with SelectionId from CC GetResource`);
|
|
21368
21553
|
}
|
|
21369
21554
|
}
|
|
21370
21555
|
break;
|
|
21371
21556
|
case "AWS::Pipes::Pipe":
|
|
21372
21557
|
if (!enriched["Arn"]) {
|
|
21373
|
-
const model = await this.readCcResourceModel(resourceType, physicalId);
|
|
21558
|
+
const model = await this.readCcResourceModel(resourceType, physicalId, log);
|
|
21374
21559
|
if (model) {
|
|
21375
21560
|
if (typeof model["Arn"] === "string") enriched["Arn"] = model["Arn"];
|
|
21376
21561
|
if (!enriched["CurrentState"] && typeof model["CurrentState"] === "string") enriched["CurrentState"] = model["CurrentState"];
|
|
21377
21562
|
if (!enriched["StateReason"] && typeof model["StateReason"] === "string") enriched["StateReason"] = model["StateReason"];
|
|
21378
21563
|
if (!enriched["CreationTime"] && typeof model["CreationTime"] === "string") enriched["CreationTime"] = model["CreationTime"];
|
|
21379
21564
|
if (!enriched["LastModifiedTime"] && typeof model["LastModifiedTime"] === "string") enriched["LastModifiedTime"] = model["LastModifiedTime"];
|
|
21380
|
-
|
|
21565
|
+
log.debug(`Enriched Pipes Pipe ${physicalId} with Arn from CC GetResource`);
|
|
21381
21566
|
}
|
|
21382
21567
|
}
|
|
21383
21568
|
break;
|
|
21384
21569
|
case "AWS::S3::AccessPoint":
|
|
21385
21570
|
if (!enriched["Arn"] || !enriched["Alias"]) {
|
|
21386
|
-
const model = await this.readCcResourceModel(resourceType, physicalId);
|
|
21571
|
+
const model = await this.readCcResourceModel(resourceType, physicalId, log);
|
|
21387
21572
|
if (model) {
|
|
21388
21573
|
if (!enriched["Arn"] && typeof model["Arn"] === "string") enriched["Arn"] = model["Arn"];
|
|
21389
21574
|
if (!enriched["Alias"] && typeof model["Alias"] === "string") enriched["Alias"] = model["Alias"];
|
|
21390
21575
|
if (!enriched["NetworkOrigin"] && typeof model["NetworkOrigin"] === "string") enriched["NetworkOrigin"] = model["NetworkOrigin"];
|
|
21391
|
-
|
|
21576
|
+
log.debug(`Enriched S3 AccessPoint ${physicalId} with Arn/Alias from CC GetResource`);
|
|
21392
21577
|
}
|
|
21393
21578
|
}
|
|
21394
21579
|
break;
|
|
21395
21580
|
case "AWS::ResourceGroups::Group": if (!enriched["Arn"]) {
|
|
21396
|
-
const model = await this.readCcResourceModel(resourceType, physicalId);
|
|
21581
|
+
const model = await this.readCcResourceModel(resourceType, physicalId, log);
|
|
21397
21582
|
if (model) {
|
|
21398
21583
|
if (typeof model["Arn"] === "string") enriched["Arn"] = model["Arn"];
|
|
21399
|
-
|
|
21584
|
+
log.debug(`Enriched ResourceGroups Group ${physicalId} with Arn from CC GetResource`);
|
|
21400
21585
|
}
|
|
21401
21586
|
}
|
|
21402
21587
|
}
|
|
21403
21588
|
return enriched;
|
|
21404
21589
|
}
|
|
21405
|
-
async mergeSparseModelReadback(resourceType, physicalId, attributes) {
|
|
21590
|
+
async mergeSparseModelReadback(resourceType, physicalId, attributes, log = this.logger) {
|
|
21406
21591
|
if (!this.isSparseAttributeMap(attributes, physicalId)) return attributes;
|
|
21407
|
-
const model = await this.readCcResourceModel(resourceType, physicalId);
|
|
21592
|
+
const model = await this.readCcResourceModel(resourceType, physicalId, log);
|
|
21408
21593
|
if (!model) return attributes;
|
|
21409
|
-
|
|
21594
|
+
log.debug(`Merged CC GetResource read-back over sparse ${resourceType} attributes for ${physicalId}`);
|
|
21410
21595
|
return {
|
|
21411
21596
|
...attributes,
|
|
21412
21597
|
...model
|
|
@@ -21419,7 +21604,7 @@ var CloudControlProvider = class {
|
|
|
21419
21604
|
identifierEchoes.add(physicalId);
|
|
21420
21605
|
return values.every((value) => typeof value === "string" && identifierEchoes.has(value));
|
|
21421
21606
|
}
|
|
21422
|
-
async readCcResourceModel(resourceType, physicalId) {
|
|
21607
|
+
async readCcResourceModel(resourceType, physicalId, log = this.logger) {
|
|
21423
21608
|
try {
|
|
21424
21609
|
const raw = (await this.cloudControlClient.send(new GetResourceCommand({
|
|
21425
21610
|
TypeName: resourceType,
|
|
@@ -21430,7 +21615,7 @@ var CloudControlProvider = class {
|
|
|
21430
21615
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return;
|
|
21431
21616
|
return parsed;
|
|
21432
21617
|
} catch (error) {
|
|
21433
|
-
|
|
21618
|
+
log.debug(`Failed to read CC model for ${resourceType} ${physicalId}: ${describeAwsFailure(error).detail}`);
|
|
21434
21619
|
return;
|
|
21435
21620
|
}
|
|
21436
21621
|
}
|
|
@@ -21538,6 +21723,28 @@ var CloudControlProvider = class {
|
|
|
21538
21723
|
return masked;
|
|
21539
21724
|
}
|
|
21540
21725
|
};
|
|
21726
|
+
function updateIdLogSink(logger, physicalId, properties, previousProperties, maskSecrets) {
|
|
21727
|
+
const mask = maskerOrIdentity(maskSecrets);
|
|
21728
|
+
const raw = updateIdWithheld(properties, previousProperties, mask) ? [physicalId, ...physicalId.split("|")].filter((needle) => needle !== "") : [];
|
|
21729
|
+
const masked = raw.map((needle) => mask(needle));
|
|
21730
|
+
const needles = [.../* @__PURE__ */ new Set([...raw, ...masked.filter((m, i) => m !== "" && m !== raw[i] && m !== "***")])].sort((a, b) => b.length - a.length);
|
|
21731
|
+
const scrub = (message) => needles.reduce((text, needle) => text.split(needle).join("***"), mask(message));
|
|
21732
|
+
return {
|
|
21733
|
+
debug: (message) => logger.debug(scrub(message)),
|
|
21734
|
+
warn: (message) => logger.warn(scrub(message)),
|
|
21735
|
+
withheld: needles.length > 0
|
|
21736
|
+
};
|
|
21737
|
+
}
|
|
21738
|
+
function updateIdWithheld(properties, previousProperties, mask) {
|
|
21739
|
+
return holdsSecretDerivedLeaf(properties, mask) || holdsSecretDerivedLeaf(previousProperties, mask);
|
|
21740
|
+
}
|
|
21741
|
+
function holdsSecretDerivedLeaf(value, mask, depth = 0) {
|
|
21742
|
+
if (typeof value === "string") return isSecretDerivedValue(value, mask);
|
|
21743
|
+
if (value === null || typeof value !== "object") return false;
|
|
21744
|
+
if (depth > 64) return true;
|
|
21745
|
+
if (Array.isArray(value)) return value.some((entry) => holdsSecretDerivedLeaf(entry, mask, depth + 1));
|
|
21746
|
+
return Object.entries(value).some(([key, entry]) => isSecretDerivedValue(key, mask) || holdsSecretDerivedLeaf(entry, mask, depth + 1));
|
|
21747
|
+
}
|
|
21541
21748
|
const IMPORT_YIELDED_NO_ATTRIBUTES_CONSEQUENCE = "Attributes the stack's state already records for this physical id are kept; without them, an Fn::GetAtt against this resource falls back to a value constructed from its physical id.";
|
|
21542
21749
|
function maskLeavesDeep(value) {
|
|
21543
21750
|
if (Array.isArray(value)) return value.map((element) => maskLeavesDeep(element));
|
|
@@ -24458,7 +24665,7 @@ var ProviderRegistry = class {
|
|
|
24458
24665
|
if (reroutable.length > 0) remedies.push(`Remove the override for ${reroutable.join(", ")} to route this resource via Cloud Control API instead.`);
|
|
24459
24666
|
if (needsRecreate.length > 0) {
|
|
24460
24667
|
const one = needsRecreate.length === 1;
|
|
24461
|
-
remedies.push(`${needsRecreate.join(", ")} ${one ? "is" : "are"} create-only, so removing the override does not apply ${one ? "it" : "them"}
|
|
24668
|
+
remedies.push(`${needsRecreate.join(", ")} ${one ? "is" : "are"} create-only, so removing the override does not apply ${one ? "it" : "them"} in place -- a create-only property can only be applied by recreating the resource. Where cdkd created the resource without ${one ? "it" : "them"}, a deploy without the override refuses unless something else already forces a replacement or the recreate is opted into.`);
|
|
24462
24669
|
}
|
|
24463
24670
|
this.logger.warn(`${logicalId} (${resourceType}): ${overridden.join(", ")} will be silently dropped (--prefer-sdk-route override accepted). ${remedies.join(" ")}`);
|
|
24464
24671
|
}
|
|
@@ -24866,30 +25073,47 @@ async function reserveStackCreateToken(options) {
|
|
|
24866
25073
|
//#region src/deployment/inline-policy-claims.ts
|
|
24867
25074
|
function isInlinePolicyClaimedByCompletedWriter(args, kind, principal, policyName) {
|
|
24868
25075
|
const { selfLogicalId, writers, changes, stateResources } = args;
|
|
24869
|
-
const same = (a, b) => typeof a === "string" && IAM_NAME.test(a) && a.toLowerCase() === b.toLowerCase();
|
|
24870
25076
|
for (const [lid, write] of writers) {
|
|
24871
25077
|
if (lid === selfLogicalId || !Object.hasOwn(stateResources, lid)) continue;
|
|
24872
25078
|
const record = stateResources[lid];
|
|
24873
25079
|
if (record.provisionedBy === "cc-api") continue;
|
|
24874
25080
|
const props = record.properties ?? {};
|
|
24875
25081
|
if (record.resourceType === "AWS::IAM::Policy") {
|
|
24876
|
-
if (!
|
|
25082
|
+
if (!sameIamName(record.physicalId, policyName)) continue;
|
|
24877
25083
|
const listed = props[POLICY_LIST_FIELDS[kind]];
|
|
24878
|
-
if (Array.isArray(listed) && listed.some((p) =>
|
|
25084
|
+
if (Array.isArray(listed) && listed.some((p) => sameIamName(p, principal))) return true;
|
|
24879
25085
|
continue;
|
|
24880
25086
|
}
|
|
24881
25087
|
if (!Object.hasOwn(PRINCIPAL_KINDS, record.resourceType)) continue;
|
|
24882
25088
|
if (PRINCIPAL_KINDS[record.resourceType] !== kind) continue;
|
|
24883
|
-
if (!
|
|
25089
|
+
if (!sameIamName(record.physicalId, principal)) continue;
|
|
24884
25090
|
if (write !== "create" && !ownPoliciesChange(changes.get(lid))) continue;
|
|
24885
25091
|
const policies = props["Policies"];
|
|
24886
25092
|
if (!Array.isArray(policies)) continue;
|
|
24887
|
-
if (policies.some((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry) &&
|
|
25093
|
+
if (policies.some((entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry) && sameIamName(entry["PolicyName"], policyName))) return true;
|
|
24888
25094
|
}
|
|
24889
25095
|
return false;
|
|
24890
25096
|
}
|
|
24891
25097
|
var RollbackInlinePolicyWriters = class {
|
|
24892
25098
|
entries = /* @__PURE__ */ new Map();
|
|
25099
|
+
removals = /* @__PURE__ */ new Map();
|
|
25100
|
+
unsettled = /* @__PURE__ */ new Map();
|
|
25101
|
+
notePending(ops) {
|
|
25102
|
+
for (const op of ops) if (!this.unsettled.has(op)) this.unsettled.set(op, {
|
|
25103
|
+
logicalId: op.logicalId,
|
|
25104
|
+
state: "pending"
|
|
25105
|
+
});
|
|
25106
|
+
}
|
|
25107
|
+
noteOutcome(op, completed) {
|
|
25108
|
+
if (!completed) {
|
|
25109
|
+
this.unsettled.set(op, {
|
|
25110
|
+
logicalId: op.logicalId,
|
|
25111
|
+
state: "failed"
|
|
25112
|
+
});
|
|
25113
|
+
return;
|
|
25114
|
+
}
|
|
25115
|
+
for (const [key, entry] of this.unsettled) if (key === op || entry.logicalId === op.logicalId) this.unsettled.delete(key);
|
|
25116
|
+
}
|
|
24893
25117
|
record(logicalId, write, record, policiesChanged, via) {
|
|
24894
25118
|
if (via === "cc-api") {
|
|
24895
25119
|
this.entries.delete(logicalId);
|
|
@@ -24921,15 +25145,100 @@ var RollbackInlinePolicyWriters = class {
|
|
|
24921
25145
|
}]
|
|
24922
25146
|
});
|
|
24923
25147
|
}
|
|
24924
|
-
|
|
25148
|
+
const claimed = isInlinePolicyClaimedByCompletedWriter({
|
|
24925
25149
|
selfLogicalId: logicalId,
|
|
24926
25150
|
writers,
|
|
24927
25151
|
changes,
|
|
24928
25152
|
stateResources
|
|
24929
25153
|
}, kind, principal, policyName);
|
|
25154
|
+
if (!claimed) {
|
|
25155
|
+
const key = removalKey(kind, principal, policyName);
|
|
25156
|
+
const noted = this.removals.get(key) ?? {
|
|
25157
|
+
kind,
|
|
25158
|
+
principal,
|
|
25159
|
+
policyName,
|
|
25160
|
+
removers: /* @__PURE__ */ new Map()
|
|
25161
|
+
};
|
|
25162
|
+
noted.removers.set(logicalId, stateResources[logicalId]);
|
|
25163
|
+
this.removals.set(key, noted);
|
|
25164
|
+
}
|
|
25165
|
+
return claimed;
|
|
24930
25166
|
};
|
|
24931
25167
|
}
|
|
25168
|
+
takeHeldRemovals(stateResources) {
|
|
25169
|
+
const held = [];
|
|
25170
|
+
for (const [key, { kind, principal, policyName, removers }] of this.removals) {
|
|
25171
|
+
const { holders, unreadable } = inlinePolicyHolders(stateResources, kind, principal, policyName);
|
|
25172
|
+
const others = holders.filter((h) => !removers.has(h.logicalId) || stateResources[h.logicalId] !== removers.get(h.logicalId));
|
|
25173
|
+
if (others.length === 0) continue;
|
|
25174
|
+
this.removals.delete(key);
|
|
25175
|
+
const open = new Set([...this.unsettled.values()].map((u) => u.logicalId));
|
|
25176
|
+
const unsettled = [...new Set(others.map((h) => h.logicalId))].filter((id) => open.has(id));
|
|
25177
|
+
held.push({
|
|
25178
|
+
kind,
|
|
25179
|
+
holders: others,
|
|
25180
|
+
unreadable,
|
|
25181
|
+
unsettled
|
|
25182
|
+
});
|
|
25183
|
+
}
|
|
25184
|
+
return held;
|
|
25185
|
+
}
|
|
24932
25186
|
};
|
|
25187
|
+
function inlinePolicyHolders(stateResources, kind, principal, policyName) {
|
|
25188
|
+
const holders = [];
|
|
25189
|
+
const unreadable = [];
|
|
25190
|
+
const unnamed = (v) => typeof v !== "string" || !IAM_NAME.test(v);
|
|
25191
|
+
for (const [logicalId, record] of Object.entries(stateResources)) {
|
|
25192
|
+
if (record === null || typeof record !== "object") continue;
|
|
25193
|
+
const props = record.properties ?? {};
|
|
25194
|
+
if (record.resourceType === "AWS::IAM::Policy") {
|
|
25195
|
+
const nameMatches = sameIamName(record.physicalId, policyName);
|
|
25196
|
+
if (!nameMatches && !unnamed(record.physicalId)) continue;
|
|
25197
|
+
const listed = props[POLICY_LIST_FIELDS[kind]];
|
|
25198
|
+
const entries = Array.isArray(listed) ? listed : [];
|
|
25199
|
+
const named = entries.find((p) => sameIamName(p, principal));
|
|
25200
|
+
if (nameMatches && typeof named === "string") holders.push({
|
|
25201
|
+
logicalId,
|
|
25202
|
+
principal: named,
|
|
25203
|
+
policyName: record.physicalId,
|
|
25204
|
+
document: props["PolicyDocument"]
|
|
25205
|
+
});
|
|
25206
|
+
else if (named !== void 0 || entries.some(unnamed)) unreadable.push(logicalId);
|
|
25207
|
+
continue;
|
|
25208
|
+
}
|
|
25209
|
+
if (!Object.hasOwn(PRINCIPAL_KINDS, record.resourceType)) continue;
|
|
25210
|
+
if (PRINCIPAL_KINDS[record.resourceType] !== kind) continue;
|
|
25211
|
+
const principalMatches = sameIamName(record.physicalId, principal);
|
|
25212
|
+
if (!principalMatches && !unnamed(record.physicalId)) continue;
|
|
25213
|
+
const policies = props["Policies"];
|
|
25214
|
+
if (!Array.isArray(policies)) continue;
|
|
25215
|
+
for (const entry of policies) {
|
|
25216
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) continue;
|
|
25217
|
+
const name = entry["PolicyName"];
|
|
25218
|
+
if (principalMatches && sameIamName(name, policyName)) holders.push({
|
|
25219
|
+
logicalId,
|
|
25220
|
+
principal: record.physicalId,
|
|
25221
|
+
policyName: name,
|
|
25222
|
+
document: entry["PolicyDocument"]
|
|
25223
|
+
});
|
|
25224
|
+
else if (unnamed(name) || sameIamName(name, policyName)) unreadable.push(logicalId);
|
|
25225
|
+
}
|
|
25226
|
+
}
|
|
25227
|
+
return {
|
|
25228
|
+
holders,
|
|
25229
|
+
unreadable: [...new Set(unreadable)]
|
|
25230
|
+
};
|
|
25231
|
+
}
|
|
25232
|
+
function sameIamName(a, b) {
|
|
25233
|
+
return typeof a === "string" && IAM_NAME.test(a) && a.toLowerCase() === b.toLowerCase();
|
|
25234
|
+
}
|
|
25235
|
+
function removalKey(kind, principal, policyName) {
|
|
25236
|
+
return JSON.stringify([
|
|
25237
|
+
kind,
|
|
25238
|
+
principal.toLowerCase(),
|
|
25239
|
+
policyName.toLowerCase()
|
|
25240
|
+
]);
|
|
25241
|
+
}
|
|
24933
25242
|
const IAM_NAME = /^[\w+=,.@-]+$/;
|
|
24934
25243
|
const POLICY_LIST_FIELDS = {
|
|
24935
25244
|
role: "Roles",
|
|
@@ -25805,6 +26114,31 @@ function updatePartialMessage(reason) {
|
|
|
25805
26114
|
return `partial (${reason})`;
|
|
25806
26115
|
}
|
|
25807
26116
|
|
|
26117
|
+
//#endregion
|
|
26118
|
+
//#region src/provisioning/redacted-delete-address.ts
|
|
26119
|
+
function isRedactedRecordedValue(value) {
|
|
26120
|
+
if (carriesSecretMask(value)) return true;
|
|
26121
|
+
let text;
|
|
26122
|
+
try {
|
|
26123
|
+
text = typeof value === "string" ? value : JSON.stringify(value);
|
|
26124
|
+
} catch {
|
|
26125
|
+
return false;
|
|
26126
|
+
}
|
|
26127
|
+
return text !== void 0 && dynamicReferenceTokens(text).length > 0;
|
|
26128
|
+
}
|
|
26129
|
+
const REDACTED_DELETE_ADDRESS_SKIP_REASON = "redacted address property in state — no delete issued";
|
|
26130
|
+
function redactedDeleteAddressFields(fields) {
|
|
26131
|
+
return Object.keys(fields).filter((key) => isRedactedRecordedValue(fields[key]));
|
|
26132
|
+
}
|
|
26133
|
+
function redactedDeleteAddressSkip(logger, logicalId, what, redactedFields) {
|
|
26134
|
+
if (redactedFields.length === 0) return void 0;
|
|
26135
|
+
logger.warn(safeMsg`${what} ${logicalId} is recorded in state with ${redactedFields.join(", ")} ` + "redacted (the '***' mask of a NoEcho value it read, or a secret '{{resolve:...}}' reference), which names nothing in AWS; skipping deletion — no AWS call is issued, so the resource is LEFT IN PLACE unless a parent resource this run also deletes removes it (then only the record is stale). Re-deploying records the same redaction again. The state record is KEPT and the run reports the skip: on 'cdkd destroy' / 'cdkd state destroy' (which exits non-zero) and, since issue 1762, on the plain DELETE of a resource removed from the template during cdkd deploy. Remove the resource by hand, then clear the stack's records with 'cdkd state orphan <stack>' — that command drops EVERY record for the stack, not just this one. NOTE a deploy-side REPLACEMENT or rollback delete instead FAILS the resource (https://github.com/go-to-k/cdkd/issues/1762) — the old resource is left untracked there, so remove it by hand.");
|
|
26136
|
+
return {
|
|
26137
|
+
outcome: "skipped",
|
|
26138
|
+
reason: REDACTED_DELETE_ADDRESS_SKIP_REASON
|
|
26139
|
+
};
|
|
26140
|
+
}
|
|
26141
|
+
|
|
25808
26142
|
//#endregion
|
|
25809
26143
|
//#region src/provisioning/final-snapshot.ts
|
|
25810
26144
|
const ATOMIC_FINAL_SNAPSHOT_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -26132,30 +26466,6 @@ async function createReplicationGroupFinalSnapshot(client, replicationGroupId, l
|
|
|
26132
26466
|
const NESTED_STACK_RESOURCE_TYPE$1 = "AWS::CloudFormation::Stack";
|
|
26133
26467
|
const nonEmptyString = (v) => typeof v === "string" && v.length > 0;
|
|
26134
26468
|
|
|
26135
|
-
//#endregion
|
|
26136
|
-
//#region src/deployment/producer-regions-scope.ts
|
|
26137
|
-
const producerRegionsStore = new AsyncLocalStorage();
|
|
26138
|
-
function withProducerRegions(evidence, fn) {
|
|
26139
|
-
return producerRegionsStore.run(evidence, fn);
|
|
26140
|
-
}
|
|
26141
|
-
function getCurrentProducerRegions() {
|
|
26142
|
-
return producerRegionsStore.getStore();
|
|
26143
|
-
}
|
|
26144
|
-
function inheritProducerRegions(own, inherited) {
|
|
26145
|
-
const seen = /* @__PURE__ */ new Set();
|
|
26146
|
-
const regions = [];
|
|
26147
|
-
for (const region of [...own, ...inherited?.regions ?? []]) {
|
|
26148
|
-
const canonical = canonicalizeRegion(region);
|
|
26149
|
-
if (!canonical || seen.has(canonical)) continue;
|
|
26150
|
-
seen.add(canonical);
|
|
26151
|
-
regions.push(region);
|
|
26152
|
-
}
|
|
26153
|
-
return {
|
|
26154
|
-
regions,
|
|
26155
|
-
complete: inherited?.complete === true
|
|
26156
|
-
};
|
|
26157
|
-
}
|
|
26158
|
-
|
|
26159
26469
|
//#endregion
|
|
26160
26470
|
//#region src/deployment/rollback-executor/messages.ts
|
|
26161
26471
|
function throwIfDeleteSkipped(result, logicalId, physicalId, duringClause) {
|
|
@@ -26183,6 +26493,19 @@ function maskedRollbackEventError(error, mask) {
|
|
|
26183
26493
|
message
|
|
26184
26494
|
};
|
|
26185
26495
|
}
|
|
26496
|
+
function recordRollbackSkip(scope, op, reason) {
|
|
26497
|
+
scope.result.warnings++;
|
|
26498
|
+
scope.result.skipped++;
|
|
26499
|
+
scope.ctx.recordEvent?.({
|
|
26500
|
+
eventType: "ROLLBACK_RESOURCE_SKIPPED",
|
|
26501
|
+
stackName: scope.stackName,
|
|
26502
|
+
operation: op.changeType,
|
|
26503
|
+
logicalId: op.logicalId,
|
|
26504
|
+
resourceType: op.resourceType,
|
|
26505
|
+
...op.provisionedBy && { provisionedBy: op.provisionedBy },
|
|
26506
|
+
reason: scope.mask(reason)
|
|
26507
|
+
});
|
|
26508
|
+
}
|
|
26186
26509
|
function effectiveProvisionedBy(record, fallbackProvisionedBy) {
|
|
26187
26510
|
return record?.provisionedBy ?? fallbackProvisionedBy;
|
|
26188
26511
|
}
|
|
@@ -26286,6 +26609,271 @@ function retainedSurvivorMessages(logicalId, resourceType, survivorPhysicalId, s
|
|
|
26286
26609
|
};
|
|
26287
26610
|
}
|
|
26288
26611
|
|
|
26612
|
+
//#endregion
|
|
26613
|
+
//#region src/deployment/rollback-executor/names.ts
|
|
26614
|
+
function replayPrefixScope(input, logger, mask, restoring) {
|
|
26615
|
+
const choice = replayPrefixChoice(input);
|
|
26616
|
+
if (choice.kind === "not-applicable") return (fn) => fn();
|
|
26617
|
+
const setting = (skip) => skip ? "SKIPPED" : "KEPT";
|
|
26618
|
+
const subject = `${safe(input.logicalId)} (${safe(input.resourceType)})`;
|
|
26619
|
+
const declared = choice.declared;
|
|
26620
|
+
const secretDerived = declared !== void 0 && mask(declared) !== declared;
|
|
26621
|
+
const withheld = secretDerived || choice.kind === "unreproduced" && declared === void 0;
|
|
26622
|
+
const shown = (value) => displayIdent(mask(String(value)));
|
|
26623
|
+
const quoted = (value) => {
|
|
26624
|
+
const masked = mask(value);
|
|
26625
|
+
const rendered = displayIdent(masked);
|
|
26626
|
+
return rendered === masked ? `"${masked}"` : rendered;
|
|
26627
|
+
};
|
|
26628
|
+
if (choice.kind === "reproduced" && choice.skipPrefix !== choice.recorded) logger.info(mask(` Rollback: ${restoring ? "re-creating" : "reverting"} ${subject} under the user-supplied-name prefix setting that created ` + (withheld ? `it (its physical id is withheld, as its name is secret-derived): ` : `${shown(input.physicalId)} (`) + `stack-name prefix ${setting(choice.skipPrefix)}; the failed deploy ran with it ${setting(choice.recorded)}${withheld ? "" : ")"}`));
|
|
26629
|
+
else if (choice.kind === "unreproduced" && restoring) {
|
|
26630
|
+
const idUnreadable = typeof input.physicalId !== "string" || input.physicalId === "";
|
|
26631
|
+
const prop = safe(choice.property);
|
|
26632
|
+
const unreadable = declared === void 0 ? idUnreadable ? `its ${prop} or its physical id` : `its ${prop}` : idUnreadable ? `its physical id` : void 0;
|
|
26633
|
+
logger.warn(mask(` ⚠ ${subject}: cdkd cannot tell which user-supplied-name prefix setting created the old resource (` + (withheld ? `its physical id is withheld, as its name is secret-derived or unreadable` : shown(input.physicalId)) + `) — ` + (choice.names === void 0 ? unreadable !== void 0 ? `cdkd cannot read ${unreadable} to compare` : `cdkd cannot derive the names its ${prop} takes to compare` : secretDerived ? `its ${prop} is secret-derived, and neither name it derives matches` : `its ${prop} derives to ${quoted(choice.names.skipped)} (prefix skipped) or ${quoted(choice.names.kept)} (prefix kept), and it is neither`) + `; re-creating it with the stack-name prefix ${setting(choice.skipPrefix)}, as the failed deploy ran, so it may come back under a different physical name.`));
|
|
26634
|
+
}
|
|
26635
|
+
const skip = choice.skipPrefix;
|
|
26636
|
+
return (fn) => withSkipPrefix(skip, fn);
|
|
26637
|
+
}
|
|
26638
|
+
function createOpMasker(logger, secrets) {
|
|
26639
|
+
const base = createMaskedLogSinks(logger, (text) => maskSecretsInText(text, secrets));
|
|
26640
|
+
const pairs = [];
|
|
26641
|
+
let sinks = base;
|
|
26642
|
+
return {
|
|
26643
|
+
mask: (text) => typeof text === "string" ? sinks.mask(text) : text,
|
|
26644
|
+
addNamed: (record) => {
|
|
26645
|
+
pairs.push(...secretDerivedNamePairs(record));
|
|
26646
|
+
sinks = withDerivedNameMasks(logger, base, pairs);
|
|
26647
|
+
}
|
|
26648
|
+
};
|
|
26649
|
+
}
|
|
26650
|
+
const OTHER_SPELLED_NAME_KEYS = {
|
|
26651
|
+
"AWS::Cognito::UserPoolDomain": ["Domain"],
|
|
26652
|
+
"AWS::Cognito::UserPoolUser": ["Username"],
|
|
26653
|
+
"AWS::ECS::TaskDefinition": ["Family"],
|
|
26654
|
+
"AWS::ElastiCache::ReplicationGroup": ["ReplicationGroupId"],
|
|
26655
|
+
"AWS::ElastiCache::User": ["UserId"],
|
|
26656
|
+
"AWS::ElastiCache::UserGroup": ["UserGroupId"],
|
|
26657
|
+
"AWS::SES::EmailIdentity": ["EmailIdentity"]
|
|
26658
|
+
};
|
|
26659
|
+
const COMPOSITE_SCOPE_LAST = /^(?:REGIONAL|CLOUDFRONT|GLOBAL)$/;
|
|
26660
|
+
const COMPOSITE_POOL_FIRST = /^[a-z]{2}(?:-gov)?-[a-z]+-\d+_[A-Za-z0-9]+$/;
|
|
26661
|
+
function idNameSegments(id) {
|
|
26662
|
+
const segments = /* @__PURE__ */ new Set();
|
|
26663
|
+
const arn = /^arn:[^:]*:([^:]*):[^:]*:[^:]*:(.+)$/.exec(id);
|
|
26664
|
+
if (arn !== null) {
|
|
26665
|
+
const service = arn[1];
|
|
26666
|
+
const resource = arn[2];
|
|
26667
|
+
const lambda = service === "lambda" ? /^(?:function|layer):([^:]+)(?::([^:]+))?/.exec(resource) : null;
|
|
26668
|
+
if (lambda !== null) {
|
|
26669
|
+
segments.add(lambda[1]);
|
|
26670
|
+
const qualifier = lambda[2];
|
|
26671
|
+
if (qualifier !== void 0 && !/^\d+$/.test(qualifier)) segments.add(qualifier);
|
|
26672
|
+
} else {
|
|
26673
|
+
const parts = resource.split(/[/:]/).filter((part) => part !== "");
|
|
26674
|
+
let body = parts.length > 1 ? parts.slice(1) : parts;
|
|
26675
|
+
if (body.length > 1 && /^\d+$/.test(body[body.length - 1])) body = body.slice(0, -1);
|
|
26676
|
+
for (const part of body) {
|
|
26677
|
+
segments.add(part);
|
|
26678
|
+
if (service === "secretsmanager") segments.add(part.replace(/-[A-Za-z0-9]{6}$/, ""));
|
|
26679
|
+
}
|
|
26680
|
+
}
|
|
26681
|
+
}
|
|
26682
|
+
if (id.includes("/")) {
|
|
26683
|
+
const afterSlash = id.slice(id.lastIndexOf("/") + 1);
|
|
26684
|
+
segments.add(afterSlash);
|
|
26685
|
+
segments.add(afterSlash.replace(/:\d+$/, ""));
|
|
26686
|
+
}
|
|
26687
|
+
if (id.includes("|")) {
|
|
26688
|
+
const parts = id.split("|");
|
|
26689
|
+
parts.forEach((part, index) => {
|
|
26690
|
+
if (part === "") return;
|
|
26691
|
+
if (index === parts.length - 1 && parts.length > 1 && COMPOSITE_SCOPE_LAST.test(part)) return;
|
|
26692
|
+
if (index === 0 && parts.length > 1 && COMPOSITE_POOL_FIRST.test(part)) return;
|
|
26693
|
+
segments.add(part);
|
|
26694
|
+
});
|
|
26695
|
+
}
|
|
26696
|
+
return [...segments].filter((segment) => segment.length >= 4 && segment !== id);
|
|
26697
|
+
}
|
|
26698
|
+
function ownEntry(table, key) {
|
|
26699
|
+
return Object.hasOwn(table, key) ? table[key] : void 0;
|
|
26700
|
+
}
|
|
26701
|
+
function secretDerivedNamePairs(record) {
|
|
26702
|
+
const { resourceType, properties, logicalId } = record;
|
|
26703
|
+
if (typeof resourceType !== "string" || !isRestorableBag(properties)) return [];
|
|
26704
|
+
const rewritten = reverseReplacementRewrittenNameTypes();
|
|
26705
|
+
const named = /* @__PURE__ */ new Set();
|
|
26706
|
+
const primary = Object.hasOwn(rewritten, resourceType) ? rewritten[resourceType].property : explicitNamePropertyFor(resourceType);
|
|
26707
|
+
if (typeof primary === "string") named.add(primary);
|
|
26708
|
+
for (const key of Object.keys(properties)) if (/(?:Name|Identifier)$/.test(key)) named.add(key);
|
|
26709
|
+
for (const key of ownEntry(OTHER_SPELLED_NAME_KEYS, resourceType) ?? []) named.add(key);
|
|
26710
|
+
const idSpellings = record.physicalIds.filter((id) => typeof id === "string" && id !== "").flatMap((id) => [
|
|
26711
|
+
id,
|
|
26712
|
+
...id.toLowerCase() === id || id.length < 4 ? [] : [id.toLowerCase()],
|
|
26713
|
+
finalSnapshotNamePrefix(id, resourceType),
|
|
26714
|
+
...idNameSegments(id)
|
|
26715
|
+
]);
|
|
26716
|
+
const pairs = [];
|
|
26717
|
+
for (const key of named) {
|
|
26718
|
+
if (!Object.hasOwn(properties, key)) continue;
|
|
26719
|
+
const value = properties[key];
|
|
26720
|
+
if (typeof value !== "string" || value === "") continue;
|
|
26721
|
+
const derived = key === primary && !value.includes("{{resolve:") && value !== "***" ? rewrittenNameSpellings(resourceType, value, logicalId) : [];
|
|
26722
|
+
for (const spelling of [...idSpellings, ...derived]) pairs.push([value, spelling]);
|
|
26723
|
+
}
|
|
26724
|
+
return pairs;
|
|
26725
|
+
}
|
|
26726
|
+
function addRecordNames(opMasker, op, current) {
|
|
26727
|
+
const { logicalId } = op;
|
|
26728
|
+
for (const properties of [op.properties, op.attemptedProperties]) opMasker.addNamed({
|
|
26729
|
+
resourceType: op.resourceType,
|
|
26730
|
+
properties,
|
|
26731
|
+
logicalId,
|
|
26732
|
+
physicalIds: [op.physicalId]
|
|
26733
|
+
});
|
|
26734
|
+
for (const record of [op.previousState, current]) {
|
|
26735
|
+
if (record === null || typeof record !== "object") continue;
|
|
26736
|
+
opMasker.addNamed({
|
|
26737
|
+
resourceType: record.resourceType ?? op.resourceType,
|
|
26738
|
+
properties: record.properties,
|
|
26739
|
+
logicalId,
|
|
26740
|
+
physicalIds: [record.physicalId]
|
|
26741
|
+
});
|
|
26742
|
+
}
|
|
26743
|
+
}
|
|
26744
|
+
const ABSENT_BASELINE_SKIP_CAUSE = "Its recorded previous state has no `properties` bag, so there is nothing to restore it to; the rollback left the resource exactly as it is.";
|
|
26745
|
+
function requireRestorableBaseline(bag, logger, { logicalId, consequence, remedy, retry }) {
|
|
26746
|
+
if (isRestorableBag(bag)) return true;
|
|
26747
|
+
if (bag === void 0) {
|
|
26748
|
+
logger.warn(` Rollback: Cannot restore ${shownLogicalId(logicalId)} \u2014 its recorded previous state has no \`properties\` bag, so there is nothing to restore it to. An empty desired state would ${consequence}. The resource is therefore left exactly as it is. ${remedy}`);
|
|
26749
|
+
return false;
|
|
26750
|
+
}
|
|
26751
|
+
const shape = bag === null ? "null" : Array.isArray(bag) ? "an array" : `a ${typeof bag}`;
|
|
26752
|
+
throw new CdkdError(`Cannot roll ${shownLogicalId(logicalId)} back: its recorded previous state has a \`properties\` field that is not a property bag (${shape}), so cdkd cannot tell what to restore it to. Replaying it would do one of two things, and cdkd does neither: send the malformed value to the provider as-is, or send an empty desired state (which would ${consequence}). The rollback JOURNAL record is kept: once that record holds a property bag again, ${retry}. Otherwise fix forward with \`cdkd deploy\`, or remove the stack with \`cdkd destroy\`.`, "ROLLBACK_UNUSABLE_BASELINE");
|
|
26753
|
+
}
|
|
26754
|
+
function isRestorableBag(bag) {
|
|
26755
|
+
return typeof bag === "object" && bag !== null && !Array.isArray(bag);
|
|
26756
|
+
}
|
|
26757
|
+
async function prepareCreateRollbackFinalSnapshot(op, provisionedBy, ctx, mask) {
|
|
26758
|
+
const { logicalId, resourceType } = op;
|
|
26759
|
+
const physicalId = op.physicalId;
|
|
26760
|
+
switch (finalSnapshotMechanism(resourceType, provisionedBy)) {
|
|
26761
|
+
case "atomic-delete-parameter": return buildFinalSnapshotIdentifier(physicalId, resourceType);
|
|
26762
|
+
case "pre-delete-snapshot":
|
|
26763
|
+
await createPreDeleteFinalSnapshot(resourceType, physicalId, logicalId, ctx.finalSnapshotClients ?? getAwsClients(), {
|
|
26764
|
+
info: (message) => ctx.logger.info(mask(message)),
|
|
26765
|
+
debug: (message) => ctx.logger.debug(mask(message))
|
|
26766
|
+
});
|
|
26767
|
+
return;
|
|
26768
|
+
case "refuse-cc-routed": throw ccRoutedFinalSnapshotError(logicalId, resourceType, SKIP_FINAL_SNAPSHOT_FLAG);
|
|
26769
|
+
case "refuse-unsupported-type": throw unsupportedFinalSnapshotError(logicalId, resourceType, SKIP_FINAL_SNAPSHOT_FLAG);
|
|
26770
|
+
}
|
|
26771
|
+
}
|
|
26772
|
+
|
|
26773
|
+
//#endregion
|
|
26774
|
+
//#region src/deployment/rollback-executor/replay-inline-policy-restore.ts
|
|
26775
|
+
async function restoreHeldInlinePolicies(writers, stateResources, stackName, ctx, result, options = {}) {
|
|
26776
|
+
for (const removal of writers.takeHeldRemovals(stateResources)) await restoreOne(removal, stateResources, stackName, ctx, result, options.refuseAll === true);
|
|
26777
|
+
}
|
|
26778
|
+
async function restoreOne({ kind, holders, unreadable, unsettled }, stateResources, stackName, ctx, result, refuseAll) {
|
|
26779
|
+
const { logger } = ctx;
|
|
26780
|
+
const ids = [...new Set(holders.map((h) => h.logicalId))].map((id) => safe(id)).join(", ");
|
|
26781
|
+
const lost = safeMsg`the ${kind} it is on lacks that inline policy until the resource is next updated, or ` + `'cdkd drift <stack> --revert' restores it`;
|
|
26782
|
+
if (refuseAll || unsettled.length > 0) {
|
|
26783
|
+
const pending = refuseAll ? ids : unsettled.map((id) => safe(id)).join(", ");
|
|
26784
|
+
logger.warn(safeMsg` Rollback: an inline policy this rollback removed is recorded by ${ids}, but the ` + safeMsg`rollback of ${pending} has not completed, so that record may be the failed ` + safeMsg`deploy's and cdkd did not put it back; ${lost}.`);
|
|
26785
|
+
result.warnings++;
|
|
26786
|
+
return;
|
|
26787
|
+
}
|
|
26788
|
+
if (unreadable.length > 0) {
|
|
26789
|
+
logger.warn(safeMsg` Rollback: an inline policy this rollback removed is recorded by ${ids}, and ` + safeMsg`${unreadable.map((id) => safe(id)).join(", ")} may record it too under a redacted ` + safeMsg`name, so cdkd did not put it back; ${lost}.`);
|
|
26790
|
+
result.warnings++;
|
|
26791
|
+
return;
|
|
26792
|
+
}
|
|
26793
|
+
const documents = holders.map((h) => serializedDocument(h.document));
|
|
26794
|
+
if (documents.some((d) => d === void 0)) {
|
|
26795
|
+
logger.warn(safeMsg` Rollback: an inline policy this rollback removed is still recorded by ${ids}, but its ` + safeMsg`recorded document is absent or redacted, so cdkd did not put it back; ${lost}.`);
|
|
26796
|
+
result.warnings++;
|
|
26797
|
+
return;
|
|
26798
|
+
}
|
|
26799
|
+
if (new Set(documents).size > 1) {
|
|
26800
|
+
logger.warn(safeMsg` Rollback: an inline policy this rollback removed is recorded by ${ids} with different ` + safeMsg`documents under one name on one ${kind}, so cdkd put neither back; ${lost}.`);
|
|
26801
|
+
result.warnings++;
|
|
26802
|
+
return;
|
|
26803
|
+
}
|
|
26804
|
+
const holder = holders[0];
|
|
26805
|
+
const secrets = /* @__PURE__ */ new Map();
|
|
26806
|
+
for (const h of holders) {
|
|
26807
|
+
const bag = ctx.logOnlyNeedlesFor?.(h.logicalId);
|
|
26808
|
+
if (bag) carryLogOnlyValues(bag, secrets);
|
|
26809
|
+
}
|
|
26810
|
+
const masker = createOpMasker(logger, secrets);
|
|
26811
|
+
for (const h of holders) {
|
|
26812
|
+
const record = stateResources[h.logicalId];
|
|
26813
|
+
addRecordNames(masker, {
|
|
26814
|
+
logicalId: h.logicalId,
|
|
26815
|
+
resourceType: record?.resourceType,
|
|
26816
|
+
physicalId: record?.physicalId
|
|
26817
|
+
}, record);
|
|
26818
|
+
}
|
|
26819
|
+
try {
|
|
26820
|
+
const { provider, provisionedBy } = ctx.providerRegistry.getProviderFor({
|
|
26821
|
+
resourceType: "AWS::IAM::Policy",
|
|
26822
|
+
provisionedBy: "sdk"
|
|
26823
|
+
});
|
|
26824
|
+
if (provisionedBy !== "sdk") throw new Error("AWS::IAM::Policy is not routed to its SDK provider");
|
|
26825
|
+
await provider.create(holder.logicalId, "AWS::IAM::Policy", {
|
|
26826
|
+
PolicyName: holder.policyName,
|
|
26827
|
+
PolicyDocument: holder.document,
|
|
26828
|
+
[POLICY_LIST_FIELDS[kind]]: [holder.principal]
|
|
26829
|
+
}, { maskSecrets: masker.mask });
|
|
26830
|
+
} catch (error) {
|
|
26831
|
+
logger.warn(maskedFailureText(safeMsg` Rollback: could not put back the inline policy ${ids} records on its ${kind} ` + safeMsg`(${lost}): `, error, masker.mask));
|
|
26832
|
+
result.warnings++;
|
|
26833
|
+
return;
|
|
26834
|
+
}
|
|
26835
|
+
logger.info(safeMsg` Rollback: put back the inline policy ${ids} records on its ${kind}`);
|
|
26836
|
+
const holderType = stateResources[holder.logicalId]?.resourceType;
|
|
26837
|
+
ctx.recordEvent?.({
|
|
26838
|
+
eventType: "ROLLBACK_RESOURCE_SUCCEEDED",
|
|
26839
|
+
stackName,
|
|
26840
|
+
operation: "UPDATE",
|
|
26841
|
+
logicalId: holder.logicalId,
|
|
26842
|
+
...typeof holderType === "string" && { resourceType: holderType },
|
|
26843
|
+
provisionedBy: "sdk",
|
|
26844
|
+
reason: `Put back the inline policy this resource records on its ${kind}, which the rollback had removed from it.`
|
|
26845
|
+
});
|
|
26846
|
+
}
|
|
26847
|
+
function serializedDocument(document) {
|
|
26848
|
+
if (typeof document === "string") return document.length > 0 && !isRedactedRecordedValue(document) ? document : void 0;
|
|
26849
|
+
if (document === null || typeof document !== "object" || Array.isArray(document)) return;
|
|
26850
|
+
return isRedactedRecordedValue(document) ? void 0 : JSON.stringify(document);
|
|
26851
|
+
}
|
|
26852
|
+
|
|
26853
|
+
//#endregion
|
|
26854
|
+
//#region src/deployment/producer-regions-scope.ts
|
|
26855
|
+
const producerRegionsStore = new AsyncLocalStorage();
|
|
26856
|
+
function withProducerRegions(evidence, fn) {
|
|
26857
|
+
return producerRegionsStore.run(evidence, fn);
|
|
26858
|
+
}
|
|
26859
|
+
function getCurrentProducerRegions() {
|
|
26860
|
+
return producerRegionsStore.getStore();
|
|
26861
|
+
}
|
|
26862
|
+
function inheritProducerRegions(own, inherited) {
|
|
26863
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26864
|
+
const regions = [];
|
|
26865
|
+
for (const region of [...own, ...inherited?.regions ?? []]) {
|
|
26866
|
+
const canonical = canonicalizeRegion(region);
|
|
26867
|
+
if (!canonical || seen.has(canonical)) continue;
|
|
26868
|
+
seen.add(canonical);
|
|
26869
|
+
regions.push(region);
|
|
26870
|
+
}
|
|
26871
|
+
return {
|
|
26872
|
+
regions,
|
|
26873
|
+
complete: inherited?.complete === true
|
|
26874
|
+
};
|
|
26875
|
+
}
|
|
26876
|
+
|
|
26289
26877
|
//#endregion
|
|
26290
26878
|
//#region src/deployment/rollback-executor/replay-secrets.ts
|
|
26291
26879
|
function replayProducerRegionEvidence(ctx) {
|
|
@@ -26589,166 +27177,6 @@ function sortRollbackCreates(createOps, stateResources, logger) {
|
|
|
26589
27177
|
return sorted;
|
|
26590
27178
|
}
|
|
26591
27179
|
|
|
26592
|
-
//#endregion
|
|
26593
|
-
//#region src/deployment/rollback-executor/names.ts
|
|
26594
|
-
function replayPrefixScope(input, logger, mask, restoring) {
|
|
26595
|
-
const choice = replayPrefixChoice(input);
|
|
26596
|
-
if (choice.kind === "not-applicable") return (fn) => fn();
|
|
26597
|
-
const setting = (skip) => skip ? "SKIPPED" : "KEPT";
|
|
26598
|
-
const subject = `${safe(input.logicalId)} (${safe(input.resourceType)})`;
|
|
26599
|
-
const declared = choice.declared;
|
|
26600
|
-
const secretDerived = declared !== void 0 && mask(declared) !== declared;
|
|
26601
|
-
const withheld = secretDerived || choice.kind === "unreproduced" && declared === void 0;
|
|
26602
|
-
const shown = (value) => displayIdent(mask(String(value)));
|
|
26603
|
-
const quoted = (value) => {
|
|
26604
|
-
const masked = mask(value);
|
|
26605
|
-
const rendered = displayIdent(masked);
|
|
26606
|
-
return rendered === masked ? `"${masked}"` : rendered;
|
|
26607
|
-
};
|
|
26608
|
-
if (choice.kind === "reproduced" && choice.skipPrefix !== choice.recorded) logger.info(mask(` Rollback: ${restoring ? "re-creating" : "reverting"} ${subject} under the user-supplied-name prefix setting that created ` + (withheld ? `it (its physical id is withheld, as its name is secret-derived): ` : `${shown(input.physicalId)} (`) + `stack-name prefix ${setting(choice.skipPrefix)}; the failed deploy ran with it ${setting(choice.recorded)}${withheld ? "" : ")"}`));
|
|
26609
|
-
else if (choice.kind === "unreproduced" && restoring) {
|
|
26610
|
-
const idUnreadable = typeof input.physicalId !== "string" || input.physicalId === "";
|
|
26611
|
-
const prop = safe(choice.property);
|
|
26612
|
-
const unreadable = declared === void 0 ? idUnreadable ? `its ${prop} or its physical id` : `its ${prop}` : idUnreadable ? `its physical id` : void 0;
|
|
26613
|
-
logger.warn(mask(` ⚠ ${subject}: cdkd cannot tell which user-supplied-name prefix setting created the old resource (` + (withheld ? `its physical id is withheld, as its name is secret-derived or unreadable` : shown(input.physicalId)) + `) — ` + (choice.names === void 0 ? unreadable !== void 0 ? `cdkd cannot read ${unreadable} to compare` : `cdkd cannot derive the names its ${prop} takes to compare` : secretDerived ? `its ${prop} is secret-derived, and neither name it derives matches` : `its ${prop} derives to ${quoted(choice.names.skipped)} (prefix skipped) or ${quoted(choice.names.kept)} (prefix kept), and it is neither`) + `; re-creating it with the stack-name prefix ${setting(choice.skipPrefix)}, as the failed deploy ran, so it may come back under a different physical name.`));
|
|
26614
|
-
}
|
|
26615
|
-
const skip = choice.skipPrefix;
|
|
26616
|
-
return (fn) => withSkipPrefix(skip, fn);
|
|
26617
|
-
}
|
|
26618
|
-
function createOpMasker(logger, secrets) {
|
|
26619
|
-
const base = createMaskedLogSinks(logger, (text) => maskSecretsInText(text, secrets));
|
|
26620
|
-
const pairs = [];
|
|
26621
|
-
let sinks = base;
|
|
26622
|
-
return {
|
|
26623
|
-
mask: (text) => typeof text === "string" ? sinks.mask(text) : text,
|
|
26624
|
-
addNamed: (record) => {
|
|
26625
|
-
pairs.push(...secretDerivedNamePairs(record));
|
|
26626
|
-
sinks = withDerivedNameMasks(logger, base, pairs);
|
|
26627
|
-
}
|
|
26628
|
-
};
|
|
26629
|
-
}
|
|
26630
|
-
const OTHER_SPELLED_NAME_KEYS = {
|
|
26631
|
-
"AWS::Cognito::UserPoolDomain": ["Domain"],
|
|
26632
|
-
"AWS::Cognito::UserPoolUser": ["Username"],
|
|
26633
|
-
"AWS::ECS::TaskDefinition": ["Family"],
|
|
26634
|
-
"AWS::ElastiCache::ReplicationGroup": ["ReplicationGroupId"],
|
|
26635
|
-
"AWS::ElastiCache::User": ["UserId"],
|
|
26636
|
-
"AWS::ElastiCache::UserGroup": ["UserGroupId"],
|
|
26637
|
-
"AWS::SES::EmailIdentity": ["EmailIdentity"]
|
|
26638
|
-
};
|
|
26639
|
-
const COMPOSITE_SCOPE_LAST = /^(?:REGIONAL|CLOUDFRONT|GLOBAL)$/;
|
|
26640
|
-
const COMPOSITE_POOL_FIRST = /^[a-z]{2}(?:-gov)?-[a-z]+-\d+_[A-Za-z0-9]+$/;
|
|
26641
|
-
function idNameSegments(id) {
|
|
26642
|
-
const segments = /* @__PURE__ */ new Set();
|
|
26643
|
-
const arn = /^arn:[^:]*:([^:]*):[^:]*:[^:]*:(.+)$/.exec(id);
|
|
26644
|
-
if (arn !== null) {
|
|
26645
|
-
const service = arn[1];
|
|
26646
|
-
const resource = arn[2];
|
|
26647
|
-
const lambda = service === "lambda" ? /^(?:function|layer):([^:]+)(?::([^:]+))?/.exec(resource) : null;
|
|
26648
|
-
if (lambda !== null) {
|
|
26649
|
-
segments.add(lambda[1]);
|
|
26650
|
-
const qualifier = lambda[2];
|
|
26651
|
-
if (qualifier !== void 0 && !/^\d+$/.test(qualifier)) segments.add(qualifier);
|
|
26652
|
-
} else {
|
|
26653
|
-
const parts = resource.split(/[/:]/).filter((part) => part !== "");
|
|
26654
|
-
let body = parts.length > 1 ? parts.slice(1) : parts;
|
|
26655
|
-
if (body.length > 1 && /^\d+$/.test(body[body.length - 1])) body = body.slice(0, -1);
|
|
26656
|
-
for (const part of body) {
|
|
26657
|
-
segments.add(part);
|
|
26658
|
-
if (service === "secretsmanager") segments.add(part.replace(/-[A-Za-z0-9]{6}$/, ""));
|
|
26659
|
-
}
|
|
26660
|
-
}
|
|
26661
|
-
}
|
|
26662
|
-
if (id.includes("/")) {
|
|
26663
|
-
const afterSlash = id.slice(id.lastIndexOf("/") + 1);
|
|
26664
|
-
segments.add(afterSlash);
|
|
26665
|
-
segments.add(afterSlash.replace(/:\d+$/, ""));
|
|
26666
|
-
}
|
|
26667
|
-
if (id.includes("|")) {
|
|
26668
|
-
const parts = id.split("|");
|
|
26669
|
-
parts.forEach((part, index) => {
|
|
26670
|
-
if (part === "") return;
|
|
26671
|
-
if (index === parts.length - 1 && parts.length > 1 && COMPOSITE_SCOPE_LAST.test(part)) return;
|
|
26672
|
-
if (index === 0 && parts.length > 1 && COMPOSITE_POOL_FIRST.test(part)) return;
|
|
26673
|
-
segments.add(part);
|
|
26674
|
-
});
|
|
26675
|
-
}
|
|
26676
|
-
return [...segments].filter((segment) => segment.length >= 4 && segment !== id);
|
|
26677
|
-
}
|
|
26678
|
-
function ownEntry(table, key) {
|
|
26679
|
-
return Object.hasOwn(table, key) ? table[key] : void 0;
|
|
26680
|
-
}
|
|
26681
|
-
function secretDerivedNamePairs(record) {
|
|
26682
|
-
const { resourceType, properties, logicalId } = record;
|
|
26683
|
-
if (typeof resourceType !== "string" || !isRestorableBag(properties)) return [];
|
|
26684
|
-
const rewritten = reverseReplacementRewrittenNameTypes();
|
|
26685
|
-
const named = /* @__PURE__ */ new Set();
|
|
26686
|
-
const primary = Object.hasOwn(rewritten, resourceType) ? rewritten[resourceType].property : explicitNamePropertyFor(resourceType);
|
|
26687
|
-
if (typeof primary === "string") named.add(primary);
|
|
26688
|
-
for (const key of Object.keys(properties)) if (/(?:Name|Identifier)$/.test(key)) named.add(key);
|
|
26689
|
-
for (const key of ownEntry(OTHER_SPELLED_NAME_KEYS, resourceType) ?? []) named.add(key);
|
|
26690
|
-
const idSpellings = record.physicalIds.filter((id) => typeof id === "string" && id !== "").flatMap((id) => [
|
|
26691
|
-
id,
|
|
26692
|
-
...id.toLowerCase() === id || id.length < 4 ? [] : [id.toLowerCase()],
|
|
26693
|
-
finalSnapshotNamePrefix(id, resourceType),
|
|
26694
|
-
...idNameSegments(id)
|
|
26695
|
-
]);
|
|
26696
|
-
const pairs = [];
|
|
26697
|
-
for (const key of named) {
|
|
26698
|
-
if (!Object.hasOwn(properties, key)) continue;
|
|
26699
|
-
const value = properties[key];
|
|
26700
|
-
if (typeof value !== "string" || value === "") continue;
|
|
26701
|
-
const derived = key === primary && !value.includes("{{resolve:") && value !== "***" ? rewrittenNameSpellings(resourceType, value, logicalId) : [];
|
|
26702
|
-
for (const spelling of [...idSpellings, ...derived]) pairs.push([value, spelling]);
|
|
26703
|
-
}
|
|
26704
|
-
return pairs;
|
|
26705
|
-
}
|
|
26706
|
-
function addRecordNames(opMasker, op, current) {
|
|
26707
|
-
const { logicalId } = op;
|
|
26708
|
-
for (const properties of [op.properties, op.attemptedProperties]) opMasker.addNamed({
|
|
26709
|
-
resourceType: op.resourceType,
|
|
26710
|
-
properties,
|
|
26711
|
-
logicalId,
|
|
26712
|
-
physicalIds: [op.physicalId]
|
|
26713
|
-
});
|
|
26714
|
-
for (const record of [op.previousState, current]) {
|
|
26715
|
-
if (record === null || typeof record !== "object") continue;
|
|
26716
|
-
opMasker.addNamed({
|
|
26717
|
-
resourceType: record.resourceType ?? op.resourceType,
|
|
26718
|
-
properties: record.properties,
|
|
26719
|
-
logicalId,
|
|
26720
|
-
physicalIds: [record.physicalId]
|
|
26721
|
-
});
|
|
26722
|
-
}
|
|
26723
|
-
}
|
|
26724
|
-
function requireRestorableBaseline(bag, logger, { logicalId, consequence, remedy, retry }) {
|
|
26725
|
-
if (isRestorableBag(bag)) return true;
|
|
26726
|
-
if (bag === void 0) {
|
|
26727
|
-
logger.warn(` Rollback: Cannot restore ${shownLogicalId(logicalId)} \u2014 its recorded previous state has no \`properties\` bag, so there is nothing to restore it to. An empty desired state would ${consequence}. The resource is therefore left exactly as it is. ${remedy}`);
|
|
26728
|
-
return false;
|
|
26729
|
-
}
|
|
26730
|
-
const shape = bag === null ? "null" : Array.isArray(bag) ? "an array" : `a ${typeof bag}`;
|
|
26731
|
-
throw new CdkdError(`Cannot roll ${shownLogicalId(logicalId)} back: its recorded previous state has a \`properties\` field that is not a property bag (${shape}), so cdkd cannot tell what to restore it to. Replaying it would do one of two things, and cdkd does neither: send the malformed value to the provider as-is, or send an empty desired state (which would ${consequence}). The rollback JOURNAL record is kept: once that record holds a property bag again, ${retry}. Otherwise fix forward with \`cdkd deploy\`, or remove the stack with \`cdkd destroy\`.`, "ROLLBACK_UNUSABLE_BASELINE");
|
|
26732
|
-
}
|
|
26733
|
-
function isRestorableBag(bag) {
|
|
26734
|
-
return typeof bag === "object" && bag !== null && !Array.isArray(bag);
|
|
26735
|
-
}
|
|
26736
|
-
async function prepareCreateRollbackFinalSnapshot(op, provisionedBy, ctx, mask) {
|
|
26737
|
-
const { logicalId, resourceType } = op;
|
|
26738
|
-
const physicalId = op.physicalId;
|
|
26739
|
-
switch (finalSnapshotMechanism(resourceType, provisionedBy)) {
|
|
26740
|
-
case "atomic-delete-parameter": return buildFinalSnapshotIdentifier(physicalId, resourceType);
|
|
26741
|
-
case "pre-delete-snapshot":
|
|
26742
|
-
await createPreDeleteFinalSnapshot(resourceType, physicalId, logicalId, ctx.finalSnapshotClients ?? getAwsClients(), {
|
|
26743
|
-
info: (message) => ctx.logger.info(mask(message)),
|
|
26744
|
-
debug: (message) => ctx.logger.debug(mask(message))
|
|
26745
|
-
});
|
|
26746
|
-
return;
|
|
26747
|
-
case "refuse-cc-routed": throw ccRoutedFinalSnapshotError(logicalId, resourceType, SKIP_FINAL_SNAPSHOT_FLAG);
|
|
26748
|
-
case "refuse-unsupported-type": throw unsupportedFinalSnapshotError(logicalId, resourceType, SKIP_FINAL_SNAPSHOT_FLAG);
|
|
26749
|
-
}
|
|
26750
|
-
}
|
|
26751
|
-
|
|
26752
27180
|
//#endregion
|
|
26753
27181
|
//#region src/deployment/rollback-executor/replay-props.ts
|
|
26754
27182
|
async function resolveReplayProps(props, resolvers, secrets, execCtx, logicalId) {
|
|
@@ -26916,10 +27344,10 @@ async function replayOrphanRetain(s) {
|
|
|
26916
27344
|
});
|
|
26917
27345
|
}
|
|
26918
27346
|
async function replayDelete(s) {
|
|
26919
|
-
const { op, stateResources, stackName, ctx,
|
|
27347
|
+
const { op, stateResources, stackName, ctx, inlinePolicyWriters, afterOp, action, logger, mask } = s;
|
|
26920
27348
|
if (!op.physicalId) {
|
|
26921
27349
|
logger.warn(` Rollback: Cannot delete ${safe(op.logicalId)} — no physical ID recorded`);
|
|
26922
|
-
|
|
27350
|
+
recordRollbackSkip(s, op, "No physical id is recorded for the rolled-back CREATE, so the rollback cannot address it to delete it.");
|
|
26923
27351
|
return;
|
|
26924
27352
|
}
|
|
26925
27353
|
const deleteProvisionedBy = effectiveProvisionedBy(stateResources[op.logicalId], op.provisionedBy);
|
|
@@ -27004,17 +27432,17 @@ async function replayReadopt(s) {
|
|
|
27004
27432
|
});
|
|
27005
27433
|
}
|
|
27006
27434
|
async function replayRevert(s) {
|
|
27007
|
-
const { op, stateResources, stackName, ctx, resolver,
|
|
27435
|
+
const { op, stateResources, stackName, ctx, resolver, inlinePolicyWriters, afterOp, isInterrupted, logger, secrets, opMasker, mask } = s;
|
|
27008
27436
|
if (!op.previousState) {
|
|
27009
27437
|
logger.warn(` Rollback: Cannot restore ${safe(op.logicalId)} — no previous state available`);
|
|
27010
|
-
|
|
27438
|
+
recordRollbackSkip(s, op, "No previous state is recorded for it, so there is nothing to restore it to.");
|
|
27011
27439
|
return;
|
|
27012
27440
|
}
|
|
27013
27441
|
const previousState = op.previousState;
|
|
27014
27442
|
const current = stateResources[op.logicalId];
|
|
27015
27443
|
if (!current) {
|
|
27016
27444
|
logger.warn(` Rollback: Cannot restore ${safe(op.logicalId)} — resource not found in current state`);
|
|
27017
|
-
|
|
27445
|
+
recordRollbackSkip(s, op, "The resource is not in the current state, so the rollback had nothing to restore.");
|
|
27018
27446
|
return;
|
|
27019
27447
|
}
|
|
27020
27448
|
if (!requireRestorableBaseline(previousState.properties, logger, {
|
|
@@ -27023,7 +27451,7 @@ async function replayRevert(s) {
|
|
|
27023
27451
|
remedy: "Re-run `cdkd deploy` to re-converge it.",
|
|
27024
27452
|
retry: `re-running ${rerunRollbackPhrase(ctx, "`cdkd rollback`")} retries this op`
|
|
27025
27453
|
})) {
|
|
27026
|
-
|
|
27454
|
+
recordRollbackSkip(s, op, ABSENT_BASELINE_SKIP_CAUSE);
|
|
27027
27455
|
return;
|
|
27028
27456
|
}
|
|
27029
27457
|
logger.info(` Rollback: Restoring ${safe(op.logicalId)} (${safe(op.resourceType)}) to previous state`);
|
|
@@ -27102,7 +27530,7 @@ async function replayReverseReplacement(s) {
|
|
|
27102
27530
|
remedy: "Re-run `cdkd deploy` to re-converge it.",
|
|
27103
27531
|
retry: `re-running ${rerunRollbackPhrase(ctx, "`cdkd rollback`")} retries this op`
|
|
27104
27532
|
})) {
|
|
27105
|
-
|
|
27533
|
+
recordRollbackSkip(s, op, ABSENT_BASELINE_SKIP_CAUSE);
|
|
27106
27534
|
return;
|
|
27107
27535
|
}
|
|
27108
27536
|
const resolvedPrevProps = await resolveReplayProps(prev.properties, resolver, secrets, ctx, op.logicalId) ?? {};
|
|
@@ -27261,7 +27689,8 @@ async function replayReverseReplacement(s) {
|
|
|
27261
27689
|
physicalId: current.physicalId,
|
|
27262
27690
|
reason: mask(survivorReason),
|
|
27263
27691
|
...survivorProvisionedBy && { provisionedBy: survivorProvisionedBy }
|
|
27264
|
-
}
|
|
27692
|
+
},
|
|
27693
|
+
...adoptedLiveNewResource && { reason: mask("The re-create returned the live new resource instead of re-creating the old one (its Create API is name-idempotent), so the replacement was NOT fully reversed: the old resource's original properties may not have been re-applied. Inspect it with `cdkd drift` and run `cdkd deploy` to reconcile.") }
|
|
27265
27694
|
});
|
|
27266
27695
|
}
|
|
27267
27696
|
|
|
@@ -27317,11 +27746,13 @@ async function replayRollbackUnbound(operations, stateResources, stackName, ctx,
|
|
|
27317
27746
|
const result = {
|
|
27318
27747
|
failures: 0,
|
|
27319
27748
|
warnings: 0,
|
|
27749
|
+
skipped: 0,
|
|
27320
27750
|
interrupted: false,
|
|
27321
27751
|
orphaned: []
|
|
27322
27752
|
};
|
|
27323
27753
|
if (operations.length === 0) {
|
|
27324
27754
|
ctx.logger.info("No completed operations to roll back.");
|
|
27755
|
+
await restoreHeldInlinePolicies(inlinePolicyWriters, stateResources, stackName, ctx, result);
|
|
27325
27756
|
return result;
|
|
27326
27757
|
}
|
|
27327
27758
|
ctx.logger.info(`Rolling back ${operations.length} completed operation(s)...`);
|
|
@@ -27330,6 +27761,7 @@ async function replayRollbackUnbound(operations, stateResources, stackName, ctx,
|
|
|
27330
27761
|
stackName
|
|
27331
27762
|
});
|
|
27332
27763
|
const resolver = new ReplayResolvers(ctx.region);
|
|
27764
|
+
inlinePolicyWriters.notePending(operations);
|
|
27333
27765
|
const { createOps, otherOps } = partitionOps(operations);
|
|
27334
27766
|
for (let i = otherOps.length - 1; i >= 0; i--) {
|
|
27335
27767
|
if (options.isInterrupted?.()) {
|
|
@@ -27348,6 +27780,7 @@ async function replayRollbackUnbound(operations, stateResources, stackName, ctx,
|
|
|
27348
27780
|
await replaySingle(op, stateResources, stackName, ctx, resolver, orphanLogicalIds, result, options.onOrphan, inlinePolicyWriters, options.afterOp, options.isInterrupted);
|
|
27349
27781
|
}
|
|
27350
27782
|
}
|
|
27783
|
+
await restoreHeldInlinePolicies(inlinePolicyWriters, stateResources, stackName, ctx, result);
|
|
27351
27784
|
ctx.logger.info("Rollback completed. Some resources may remain if deletion failed.");
|
|
27352
27785
|
ctx.recordEvent?.({
|
|
27353
27786
|
eventType: "ROLLBACK_FINISHED",
|
|
@@ -27382,23 +27815,24 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
27382
27815
|
mask,
|
|
27383
27816
|
createRollbackRoute: void 0
|
|
27384
27817
|
};
|
|
27818
|
+
const recordBefore = ownRecord(stateResources, op.logicalId);
|
|
27385
27819
|
try {
|
|
27386
27820
|
addRecordNames(opMasker, op, stateResources[op.logicalId]);
|
|
27387
27821
|
switch (action) {
|
|
27388
27822
|
case "unrecoverable-delete":
|
|
27389
27823
|
logger.warn(` Rollback: Cannot restore deleted resource ${safe(op.logicalId)} (${safe(op.resourceType)}) — resource has already been deleted`);
|
|
27390
|
-
|
|
27824
|
+
recordRollbackSkip(scope, op, "The failed deploy deleted this resource, and a rollback cannot re-create a deleted resource.");
|
|
27391
27825
|
return;
|
|
27392
27826
|
case "skip-already-done":
|
|
27393
27827
|
logger.debug(` Rollback: ${safe(op.logicalId)} already reverted, skipping`);
|
|
27394
27828
|
return;
|
|
27395
27829
|
case "skip-mismatch":
|
|
27396
27830
|
logger.warn(` Rollback: Skipping ${safe(op.logicalId)} — its physical id changed since the failed deploy (replaced by a later attempt); manual attention may be required`);
|
|
27397
|
-
|
|
27831
|
+
recordRollbackSkip(scope, op, "Its physical id changed since the failed deploy (replaced by a later attempt), so the rollback left it as it is; manual attention may be required.");
|
|
27398
27832
|
return;
|
|
27399
27833
|
case "skip-absent":
|
|
27400
27834
|
logger.warn(` Rollback: Cannot restore ${safe(op.logicalId)} — resource no longer in state, skipping`);
|
|
27401
|
-
|
|
27835
|
+
recordRollbackSkip(scope, op, "The resource is no longer in state, so the rollback had nothing to restore.");
|
|
27402
27836
|
return;
|
|
27403
27837
|
case "refuse-replacement-routing": {
|
|
27404
27838
|
const routing = resolveReplacementOldType(op);
|
|
@@ -27426,13 +27860,19 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
27426
27860
|
...failedRoute && { provisionedBy: failedRoute },
|
|
27427
27861
|
error: maskedRollbackEventError(rollbackError, mask)
|
|
27428
27862
|
});
|
|
27863
|
+
} finally {
|
|
27864
|
+
inlinePolicyWriters.noteOutcome(op, action === "skip-already-done" || ownRecord(stateResources, op.logicalId) !== recordBefore);
|
|
27429
27865
|
}
|
|
27430
27866
|
}
|
|
27867
|
+
function ownRecord(stateResources, logicalId) {
|
|
27868
|
+
return Object.hasOwn(stateResources, logicalId) ? stateResources[logicalId] : void 0;
|
|
27869
|
+
}
|
|
27431
27870
|
async function replayFailedOperationsUnbound(failedOps, stateResources, stackName, ctx, options = {}) {
|
|
27432
27871
|
const inlinePolicyWriters = options.inlinePolicyWriters ?? new RollbackInlinePolicyWriters();
|
|
27433
27872
|
const result = {
|
|
27434
27873
|
failures: 0,
|
|
27435
27874
|
warnings: 0,
|
|
27875
|
+
skipped: 0,
|
|
27436
27876
|
interrupted: false,
|
|
27437
27877
|
remainingFailedOps: [],
|
|
27438
27878
|
orphaned: []
|
|
@@ -27456,7 +27896,14 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
|
|
|
27456
27896
|
const secrets = /* @__PURE__ */ new Map();
|
|
27457
27897
|
const opMasker = createOpMasker(logger, secrets);
|
|
27458
27898
|
const mask = opMasker.mask;
|
|
27899
|
+
const skipScope = {
|
|
27900
|
+
ctx,
|
|
27901
|
+
stackName,
|
|
27902
|
+
result,
|
|
27903
|
+
mask
|
|
27904
|
+
};
|
|
27459
27905
|
let createRollbackRoute;
|
|
27906
|
+
const recordBefore = ownRecord(stateResources, op.logicalId);
|
|
27460
27907
|
try {
|
|
27461
27908
|
addRecordNames(opMasker, op, stateResources[op.logicalId]);
|
|
27462
27909
|
switch (action) {
|
|
@@ -27465,15 +27912,15 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
|
|
|
27465
27912
|
break;
|
|
27466
27913
|
case "skip-failed-unknown":
|
|
27467
27914
|
logger.warn(` Rollback: failed CREATE of ${safe(op.logicalId)} (${safe(op.resourceType)}) recorded no physical id — if it was partially created in AWS, delete it manually`);
|
|
27468
|
-
|
|
27915
|
+
recordRollbackSkip(skipScope, op, "The failed CREATE recorded no physical id, so the rollback cannot address it; if it was partially created in AWS, delete it manually.");
|
|
27469
27916
|
break;
|
|
27470
27917
|
case "skip-failed-absent":
|
|
27471
27918
|
logger.warn(` Rollback: cannot revert failed UPDATE of ${safe(op.logicalId)} — no previous state available, skipping`);
|
|
27472
|
-
|
|
27919
|
+
recordRollbackSkip(skipScope, op, "No previous state is available for the failed UPDATE, so there is nothing to revert it to.");
|
|
27473
27920
|
break;
|
|
27474
27921
|
case "skip-failed-type-change":
|
|
27475
27922
|
logger.warn(` Rollback: cannot revert failed UPDATE of ${shownLogicalId(op.logicalId)} in place — it was a Type change (${refusalResourceType(op.previousState?.resourceType)} -> ${refusalResourceType(op.resourceType)}), which is a replacement, and its remote state is unknown. Inspect it with \`cdkd drift\` and re-converge with \`cdkd deploy\`. Skipping.`);
|
|
27476
|
-
|
|
27923
|
+
recordRollbackSkip(skipScope, op, `The failed UPDATE was a Type change (${String(op.previousState?.resourceType)} -> ${String(op.resourceType)}), which is a replacement, and its remote state is unknown, so it cannot be reverted in place.`);
|
|
27477
27924
|
break;
|
|
27478
27925
|
case "orphan-failed-create-retain": {
|
|
27479
27926
|
const failedCreateRecord = stateResources[op.logicalId];
|
|
@@ -27546,7 +27993,7 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
|
|
|
27546
27993
|
remedy: "Inspect it with `cdkd drift` (this op died mid-flight, so its remote state is unknown) and re-converge with `cdkd drift --revert` or `cdkd deploy`.",
|
|
27547
27994
|
retry: "re-running `cdkd rollback --revert-failed` retries this op (a plain `cdkd rollback` replays only the COMPLETED ops and then pops the whole segment, discarding this record)"
|
|
27548
27995
|
})) {
|
|
27549
|
-
|
|
27996
|
+
recordRollbackSkip(skipScope, op, ABSENT_BASELINE_SKIP_CAUSE);
|
|
27550
27997
|
break;
|
|
27551
27998
|
}
|
|
27552
27999
|
logger.info(` Rollback: force-reverting failed UPDATE of ${safe(op.logicalId)} (${safe(op.resourceType)}) to its pre-deploy properties (--revert-failed; remote state is unknown)`);
|
|
@@ -27625,7 +28072,9 @@ async function replayFailedOperationsUnbound(failedOps, stateResources, stackNam
|
|
|
27625
28072
|
error: maskedRollbackEventError(revertError, mask)
|
|
27626
28073
|
});
|
|
27627
28074
|
}
|
|
28075
|
+
inlinePolicyWriters.noteOutcome(op, action === "skip-failed-noop" || ownRecord(stateResources, op.logicalId) !== recordBefore);
|
|
27628
28076
|
}
|
|
28077
|
+
if (result.interrupted) await restoreHeldInlinePolicies(inlinePolicyWriters, stateResources, stackName, ctx, result, { refuseAll: true });
|
|
27629
28078
|
if (emitEnvelope) ctx.recordEvent?.({
|
|
27630
28079
|
eventType: "ROLLBACK_FINISHED",
|
|
27631
28080
|
stackName
|
|
@@ -28096,6 +28545,7 @@ async function performRollback(completedOperations, stateResources, stackName, p
|
|
|
28096
28545
|
return {
|
|
28097
28546
|
failures: result.failures,
|
|
28098
28547
|
warnings: result.warnings + run.warnings,
|
|
28548
|
+
skipped: result.skipped,
|
|
28099
28549
|
orphaned: result.orphaned,
|
|
28100
28550
|
settledNested: run.settled
|
|
28101
28551
|
};
|
|
@@ -28145,7 +28595,7 @@ async function settleJournalAfterCleanRollback(stackName, failedOperations, init
|
|
|
28145
28595
|
this.logger.warn(safeMsg`Failed to settle the rollback journal after the clean rollback: ${err instanceof Error ? err.message : String(err)}. ` + `The journal keeps the full segment; a later rollback replay is idempotent.`);
|
|
28146
28596
|
return false;
|
|
28147
28597
|
}
|
|
28148
|
-
await this.writeRollbackJournalSegment(stackName, [], failedOperations, "auto-rollback-clean", initialDeploy);
|
|
28598
|
+
if (!await this.writeRollbackJournalSegment(stackName, [], failedOperations, "auto-rollback-clean", initialDeploy)) return true;
|
|
28149
28599
|
this.logger.info(`The automatic rollback restored the pre-deploy state. The failed resource's pre-failure record was kept — if it was left partially applied, revert it.\nRevert it with: ${pasteableCommand("cdkd rollback", [{
|
|
28150
28600
|
value: stackName,
|
|
28151
28601
|
hole: "stack"
|
|
@@ -28193,7 +28643,7 @@ function producerRegionEvidence(previousState) {
|
|
|
28193
28643
|
return inheritProducerRegions(own, this.options.inheritedProducerRegions?.());
|
|
28194
28644
|
}
|
|
28195
28645
|
async function writeRollbackJournalSegment(stackName, completedOperations, failedOperations, reason, initialDeploy, nestedPending) {
|
|
28196
|
-
if (!nestedPending && completedOperations.length === 0 && failedOperations.length === 0) return;
|
|
28646
|
+
if (!nestedPending && completedOperations.length === 0 && failedOperations.length === 0) return false;
|
|
28197
28647
|
const redactedCompleted = this.redactOperationsForJournal(completedOperations);
|
|
28198
28648
|
const redactedFailed = this.redactOperationsForJournal(failedOperations);
|
|
28199
28649
|
try {
|
|
@@ -28212,8 +28662,10 @@ async function writeRollbackJournalSegment(stackName, completedOperations, faile
|
|
|
28212
28662
|
};
|
|
28213
28663
|
await this.stateBackend.appendRollbackJournalSegment(stackName, this.stackRegion, segment);
|
|
28214
28664
|
this.logger.debug(`Rollback journal segment written (${reason})`);
|
|
28665
|
+
return true;
|
|
28215
28666
|
} catch (journalError) {
|
|
28216
28667
|
this.logger.warn(`Failed to write rollback journal: ${journalError instanceof Error ? journalError.message : String(journalError)}. 'cdkd rollback' will NOT be able to revert this deploy — use 'cdkd deploy' to resume or 'cdkd destroy' to clean up.`);
|
|
28668
|
+
return false;
|
|
28217
28669
|
}
|
|
28218
28670
|
}
|
|
28219
28671
|
|
|
@@ -29147,6 +29599,45 @@ function hasUnpersistedHeals(resources) {
|
|
|
29147
29599
|
return false;
|
|
29148
29600
|
}
|
|
29149
29601
|
|
|
29602
|
+
//#endregion
|
|
29603
|
+
//#region src/deployment/deploy-engine/record-shape.ts
|
|
29604
|
+
function auditResolvedAssetReferences(logicalId, resourceType, resolvedProps) {
|
|
29605
|
+
const redirect = this.options.assetRedirect;
|
|
29606
|
+
if (!redirect) return;
|
|
29607
|
+
const findings = findUnrewrittenAssetReferences(resolvedProps, redirect);
|
|
29608
|
+
if (findings.length === 0) return;
|
|
29609
|
+
const detail = findings.map((f) => ` - ${f.path}: still references '${f.source}'`).join("\n");
|
|
29610
|
+
throw new ProvisioningError(`Unrewritten asset reference on '${logicalId}' (${resourceType}): this region uses cdkd-owned asset storage, but the following resolved properties still point at the CDK bootstrap storage that 'cdk gc' may garbage-collect:\n${detail}\nThis is a template shape cdkd's asset-reference rewrite did not cover — deploying it would split-brain the stack (assets in cdkd storage, properties reading the CDK bucket). Please report this at https://github.com/go-to-k/cdkd/issues with the property shape. Workaround: deploy with --use-cdk-bootstrap-assets to pin the legacy destinations for this app.`, resourceType, logicalId);
|
|
29611
|
+
}
|
|
29612
|
+
function extractAllDependencies(template, logicalId) {
|
|
29613
|
+
const resource = template?.Resources?.[logicalId];
|
|
29614
|
+
if (!resource) return void 0;
|
|
29615
|
+
const parser = new TemplateParser();
|
|
29616
|
+
const parameterNames = new Set(Object.keys(template?.Parameters ?? {}));
|
|
29617
|
+
const deps = [...parser.extractDependencies(resource)].filter((dep) => !parameterNames.has(dep));
|
|
29618
|
+
return deps.length > 0 ? deps : void 0;
|
|
29619
|
+
}
|
|
29620
|
+
function propertiesToRecord(desiredProperties, result, resourceType, provisionedBy) {
|
|
29621
|
+
if (result.effectiveProperties) return markWrittenFromDeployedTemplate(provisionedBy === "sdk" ? withoutSilentDropProperties(resourceType, result.effectiveProperties) : result.effectiveProperties);
|
|
29622
|
+
const written = provisionedBy === "sdk" ? withoutSilentDropProperties(resourceType, desiredProperties) : desiredProperties;
|
|
29623
|
+
return markWrittenFromDeployedTemplate(markSameGenerationBag(written));
|
|
29624
|
+
}
|
|
29625
|
+
function extractTemplateAttributes(template, logicalId) {
|
|
29626
|
+
const resource = template?.Resources?.[logicalId];
|
|
29627
|
+
return {
|
|
29628
|
+
deletionPolicy: resource?.DeletionPolicy,
|
|
29629
|
+
updateReplacePolicy: resource?.UpdateReplacePolicy
|
|
29630
|
+
};
|
|
29631
|
+
}
|
|
29632
|
+
function acceptedCreateOnlyDropsField(recordedProperties, resourceType, provisionedBy, mode, previous) {
|
|
29633
|
+
if (provisionedBy !== "sdk") return { acceptedCreateOnlyDrops: void 0 };
|
|
29634
|
+
const createOnly = getPropertyCoverage(resourceType)?.createOnlyDrops;
|
|
29635
|
+
if (createOnly === void 0 || createOnly.size === 0) return { acceptedCreateOnlyDrops: void 0 };
|
|
29636
|
+
const carried = mode === "in-place" ? acceptedCreateOnlyDropsOf(previous) : void 0;
|
|
29637
|
+
const names = findSilentDropProperties(resourceType, recordedProperties).map(({ property }) => property).filter((property) => createOnly.has(property) && (carried === void 0 || carried.has(property)));
|
|
29638
|
+
return { acceptedCreateOnlyDrops: names.length > 0 ? names : void 0 };
|
|
29639
|
+
}
|
|
29640
|
+
|
|
29150
29641
|
//#endregion
|
|
29151
29642
|
//#region src/utils/resource-line.ts
|
|
29152
29643
|
function formatResourceLine(op, logicalId, resourceType, verbOverride) {
|
|
@@ -29160,6 +29651,75 @@ function formatResourceLine(op, logicalId, resourceType, verbOverride) {
|
|
|
29160
29651
|
}
|
|
29161
29652
|
}
|
|
29162
29653
|
|
|
29654
|
+
//#endregion
|
|
29655
|
+
//#region src/deployment/prior-attempt-scope.ts
|
|
29656
|
+
const priorAttemptStore = new AsyncLocalStorage();
|
|
29657
|
+
function withPriorAttempts(lookup, fn) {
|
|
29658
|
+
return priorAttemptStore.run(lookup, fn);
|
|
29659
|
+
}
|
|
29660
|
+
function priorAttemptLookup(logicalId, attempts) {
|
|
29661
|
+
let landed = false;
|
|
29662
|
+
return {
|
|
29663
|
+
logicalId,
|
|
29664
|
+
attempts,
|
|
29665
|
+
notePossiblyLanded: () => {
|
|
29666
|
+
landed = true;
|
|
29667
|
+
},
|
|
29668
|
+
possiblyLanded: () => landed
|
|
29669
|
+
};
|
|
29670
|
+
}
|
|
29671
|
+
function getPriorAttempts(logicalId) {
|
|
29672
|
+
const bound = priorAttemptStore.getStore();
|
|
29673
|
+
return bound?.logicalId === logicalId ? bound : void 0;
|
|
29674
|
+
}
|
|
29675
|
+
function priorAttemptsInJournal(journal, logicalId, resourceType) {
|
|
29676
|
+
let bags = [];
|
|
29677
|
+
for (const segment of journal?.segments ?? []) {
|
|
29678
|
+
if (segment.operations.some((op) => op.logicalId === logicalId)) bags = [];
|
|
29679
|
+
for (const op of segment.failedOperations ?? []) {
|
|
29680
|
+
if (op.logicalId !== logicalId || op.resourceType !== resourceType) continue;
|
|
29681
|
+
if (op.changeType === "DELETE" || !isBag(op.attemptedProperties)) continue;
|
|
29682
|
+
if (recordedAsNewResource(op)) continue;
|
|
29683
|
+
bags.push(op.attemptedProperties);
|
|
29684
|
+
}
|
|
29685
|
+
if (segment.supersededLogicalIds?.includes(logicalId)) bags = [];
|
|
29686
|
+
}
|
|
29687
|
+
return bags;
|
|
29688
|
+
}
|
|
29689
|
+
function recordedAsNewResource(op) {
|
|
29690
|
+
if (typeof op.physicalId !== "string" || op.physicalId === "") return false;
|
|
29691
|
+
if (op.changeType === "CREATE") return true;
|
|
29692
|
+
const previous = op.previousState?.physicalId;
|
|
29693
|
+
return typeof previous === "string" && previous !== op.physicalId;
|
|
29694
|
+
}
|
|
29695
|
+
function isBag(value) {
|
|
29696
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29697
|
+
}
|
|
29698
|
+
const REFUSED_BEFORE_APPLYING = Symbol.for("cdkd.refusedBeforeApplying");
|
|
29699
|
+
const MAX_CAUSE_DEPTH = 10;
|
|
29700
|
+
function markRefusedBeforeApplying(error) {
|
|
29701
|
+
if (!Object.isExtensible(error)) return error;
|
|
29702
|
+
Object.defineProperty(error, REFUSED_BEFORE_APPLYING, {
|
|
29703
|
+
value: true,
|
|
29704
|
+
enumerable: false,
|
|
29705
|
+
configurable: true,
|
|
29706
|
+
writable: false
|
|
29707
|
+
});
|
|
29708
|
+
return error;
|
|
29709
|
+
}
|
|
29710
|
+
function isRefusedBeforeApplying(error, logicalId) {
|
|
29711
|
+
let current = error;
|
|
29712
|
+
for (let depth = 0; depth < MAX_CAUSE_DEPTH && current != null; depth++) {
|
|
29713
|
+
if (typeof current === "object" || typeof current === "function") {
|
|
29714
|
+
const linkId = current.logicalId;
|
|
29715
|
+
if (typeof linkId === "string" && linkId !== logicalId) return false;
|
|
29716
|
+
if (current[REFUSED_BEFORE_APPLYING] === true) return true;
|
|
29717
|
+
}
|
|
29718
|
+
current = current.cause;
|
|
29719
|
+
}
|
|
29720
|
+
return false;
|
|
29721
|
+
}
|
|
29722
|
+
|
|
29163
29723
|
//#endregion
|
|
29164
29724
|
//#region src/deployment/deploy-engine/create.ts
|
|
29165
29725
|
async function provisionCreate(logicalId, change, stateResources, stackName, template, parameterValues, conditions, counts, progress) {
|
|
@@ -29202,10 +29762,12 @@ async function provisionCreate(logicalId, change, stateResources, stackName, tem
|
|
|
29202
29762
|
this.registerNoEchoAttributes(logicalId, result, createSecrets, resolvedProps);
|
|
29203
29763
|
const dependencies = this.extractAllDependencies(template, logicalId);
|
|
29204
29764
|
const templateAttrs = this.extractTemplateAttributes(template, logicalId);
|
|
29765
|
+
const recordedProperties = this.propertiesToRecord(resolvedProps, result, resourceType, createDecision.provisionedBy);
|
|
29205
29766
|
stateResources[logicalId] = {
|
|
29206
29767
|
physicalId: result.physicalId,
|
|
29207
29768
|
resourceType,
|
|
29208
|
-
properties:
|
|
29769
|
+
properties: recordedProperties,
|
|
29770
|
+
...acceptedCreateOnlyDropsField(recordedProperties, resourceType, createDecision.provisionedBy, "new-resource"),
|
|
29209
29771
|
...result.attributes && { attributes: result.attributes },
|
|
29210
29772
|
...dependencies && dependencies.length > 0 && { dependencies },
|
|
29211
29773
|
...templateAttrs,
|
|
@@ -29239,7 +29801,7 @@ async function refuseTakenCreateName(input) {
|
|
|
29239
29801
|
const named = `${question.property} ${shown(question.desiredName)}`;
|
|
29240
29802
|
const adoptsText = "its create API hands back or overwrites an existing resource of that name instead of refusing it";
|
|
29241
29803
|
const refuse = (message, cause) => {
|
|
29242
|
-
throw markNonRetryable(new CdkdError(maskName(message), "NAMED_CREATE_COLLISION", cause instanceof Error ? cause : void 0));
|
|
29804
|
+
throw markNonRetryable(markRefusedBeforeApplying(new CdkdError(maskName(message), "NAMED_CREATE_COLLISION", cause instanceof Error ? cause : void 0)));
|
|
29243
29805
|
};
|
|
29244
29806
|
let knownPhysicalId;
|
|
29245
29807
|
const byArn = ["AWS::SNS::Topic", "AWS::StepFunctions::StateMachine"].includes(resourceType) ? createLookupArn(resourceType, question.desiredName, await getAccountInfo(this.stackRegion)) : void 0;
|
|
@@ -29272,262 +29834,6 @@ async function refuseTakenCreateName(input) {
|
|
|
29272
29834
|
return refuse(`${subject} is created with ${named}, and an existing resource (${shown(holderId)}) already holds that name. Since ${adoptsText}, creating it would take that resource over and record it as this stack's, for a later \`cdkd destroy\` to delete. Nothing was created. Choose a name no other resource holds; ` + ownRemedy);
|
|
29273
29835
|
}
|
|
29274
29836
|
|
|
29275
|
-
//#endregion
|
|
29276
|
-
//#region src/deployment/deploy-value-equality.ts
|
|
29277
|
-
function isReplacementCeiling(pc) {
|
|
29278
|
-
return pc.requiresReplacement && (pc.inPlacePropagated === true || pc.replacementPropagated === true);
|
|
29279
|
-
}
|
|
29280
|
-
function keyOrderFreeJson(value) {
|
|
29281
|
-
return JSON.stringify(value, (_key, node) => {
|
|
29282
|
-
if (node === null || typeof node !== "object" || Array.isArray(node)) return node;
|
|
29283
|
-
const sorted = Object.create(null);
|
|
29284
|
-
for (const key of Object.keys(node).sort()) sorted[key] = node[key];
|
|
29285
|
-
return sorted;
|
|
29286
|
-
});
|
|
29287
|
-
}
|
|
29288
|
-
function liveHoldsFreshLeaves(live, leaves) {
|
|
29289
|
-
for (const leaf of leaves) {
|
|
29290
|
-
let node = live;
|
|
29291
|
-
for (const segment of leaf.path) {
|
|
29292
|
-
if (typeof segment === "number") {
|
|
29293
|
-
if (!Array.isArray(node) || segment >= node.length) return false;
|
|
29294
|
-
node = node[segment];
|
|
29295
|
-
continue;
|
|
29296
|
-
}
|
|
29297
|
-
if (node === null || typeof node !== "object" || Array.isArray(node)) return false;
|
|
29298
|
-
if (!Object.prototype.hasOwnProperty.call(node, segment)) return false;
|
|
29299
|
-
node = node[segment];
|
|
29300
|
-
}
|
|
29301
|
-
if (typeof node !== "string" || node !== leaf.plaintext) return false;
|
|
29302
|
-
}
|
|
29303
|
-
return true;
|
|
29304
|
-
}
|
|
29305
|
-
function outputMapsEqual(a, b) {
|
|
29306
|
-
return deepEqualValue(a, b);
|
|
29307
|
-
}
|
|
29308
|
-
function deepEqualValue(a, b) {
|
|
29309
|
-
if (a === b) return true;
|
|
29310
|
-
if (a == null || b == null) return a === b;
|
|
29311
|
-
if (typeof a !== typeof b) return false;
|
|
29312
|
-
if (typeof a !== "object") return false;
|
|
29313
|
-
if (Array.isArray(a) || Array.isArray(b)) {
|
|
29314
|
-
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
|
|
29315
|
-
return a.every((v, i) => deepEqualValue(v, b[i]));
|
|
29316
|
-
}
|
|
29317
|
-
const ao = a;
|
|
29318
|
-
const bo = b;
|
|
29319
|
-
const ak = Object.keys(ao);
|
|
29320
|
-
if (ak.length !== Object.keys(bo).length) return false;
|
|
29321
|
-
for (const k of ak) {
|
|
29322
|
-
if (!Object.prototype.hasOwnProperty.call(bo, k)) return false;
|
|
29323
|
-
if (!deepEqualValue(ao[k], bo[k])) return false;
|
|
29324
|
-
}
|
|
29325
|
-
return true;
|
|
29326
|
-
}
|
|
29327
|
-
|
|
29328
|
-
//#endregion
|
|
29329
|
-
//#region src/deployment/deploy-engine/update.ts
|
|
29330
|
-
async function provisionUpdate(logicalId, change, stateResources, stackName, template, parameterValues, conditions, counts, progress) {
|
|
29331
|
-
const resourceType = change.resourceType;
|
|
29332
|
-
const existingState = stateResources[logicalId];
|
|
29333
|
-
const renderer = getLiveRenderer();
|
|
29334
|
-
const currentResource = existingState;
|
|
29335
|
-
if (!currentResource) throw new Error(`Cannot update ${logicalId}: resource not found in state`);
|
|
29336
|
-
const oldResourceType = currentResource.resourceType;
|
|
29337
|
-
const typeChanged = oldResourceType !== resourceType;
|
|
29338
|
-
const desiredProps = change.desiredProperties || {};
|
|
29339
|
-
const currentProps = change.currentProperties || {};
|
|
29340
|
-
const currentPropsAsWritten = currentResource.provisionedBy === "cc-api" ? currentProps : withoutSilentDropProperties(resourceType, currentProps);
|
|
29341
|
-
const context = this.buildResolverContext({
|
|
29342
|
-
template,
|
|
29343
|
-
resources: stateResources,
|
|
29344
|
-
...parameterValues && { parameters: parameterValues },
|
|
29345
|
-
...conditions && { conditions },
|
|
29346
|
-
redactedAttributeReads: []
|
|
29347
|
-
}, stackName);
|
|
29348
|
-
const updateSecrets = context.recordedSecretValues ?? /* @__PURE__ */ new Map();
|
|
29349
|
-
this.perResourceSecrets.set(logicalId, updateSecrets);
|
|
29350
|
-
const resolvedProps = await this.resolver.resolve(desiredProps, context);
|
|
29351
|
-
this.perResourceTemplateProps.set(logicalId, desiredProps);
|
|
29352
|
-
this.perResourceResolvedType.set(logicalId, resourceType);
|
|
29353
|
-
recordNestedStackParameterExpressions(updateSecrets, resourceType, resolvedProps, desiredProps);
|
|
29354
|
-
this.auditResolvedAssetReferences(logicalId, resourceType, resolvedProps);
|
|
29355
|
-
const lostCandidate = childLostWithRecreatedParent({
|
|
29356
|
-
resourceType,
|
|
29357
|
-
templateProperties: desiredProps,
|
|
29358
|
-
recreatedUnderSameId: this.recreatedUnderSameId,
|
|
29359
|
-
recordedTypeOf: (id) => Object.hasOwn(stateResources, id) ? stateResources[id]?.resourceType : void 0,
|
|
29360
|
-
conditions
|
|
29361
|
-
});
|
|
29362
|
-
const lostChild = lostCandidate?.mode === "reattach" || lostCandidate !== void 0 && Object.hasOwn(resolvedProps, lostCandidate.property) && Object.hasOwn(currentProps, lostCandidate.property) && keyOrderFreeJson(resolvedProps[lostCandidate.property]) === keyOrderFreeJson(currentProps[lostCandidate.property]) ? lostCandidate : void 0;
|
|
29363
|
-
const lostWithParent = lostChild?.mode === "recreate" ? lostChild.parent : void 0;
|
|
29364
|
-
const suppliesFreshMaskOnlyValue = carriesFreshNoEchoValue(resolvedProps, updateSecrets);
|
|
29365
|
-
const desiredForSkipCheck = redactSecretsForState(markSameGenerationBag({ ...resolvedProps }), updateSecrets, desiredProps);
|
|
29366
|
-
const allowedSilentDrops = this.providerRegistry.getAllowedUnsupportedProperties?.();
|
|
29367
|
-
const desiredForSkipCheckAsWritten = currentResource.provisionedBy !== "cc-api" && allowedSilentDrops ? withoutAcceptedSilentDropProperties(resourceType, desiredForSkipCheck, allowedSilentDrops, currentResource.properties) : desiredForSkipCheck;
|
|
29368
|
-
const applyAttributeOnlyUpdate = (attributeChanges) => {
|
|
29369
|
-
const attrSummary = attributeChanges.map((a) => `${a.attribute}: ${a.oldValue ?? "(unset)"} → ${a.newValue ?? "(unset)"}`).join(", ");
|
|
29370
|
-
this.logger.info(safeMsg` ↻ ${logicalId} (${resourceType}) attribute update: ${attrSummary}`);
|
|
29371
|
-
stateResources[logicalId] = {
|
|
29372
|
-
...currentResource,
|
|
29373
|
-
...this.extractTemplateAttributes(template, logicalId)
|
|
29374
|
-
};
|
|
29375
|
-
if (counts) counts.updated++;
|
|
29376
|
-
if (progress) progress.current++;
|
|
29377
|
-
const attrPrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
|
|
29378
|
-
renderer.removeTask(logicalId);
|
|
29379
|
-
this.logger.info(safeMsg`${attrPrefix}${formatResourceLine("updated", logicalId, resourceType, "updated (metadata)")}`);
|
|
29380
|
-
};
|
|
29381
|
-
if (!typeChanged && !suppliesFreshMaskOnlyValue && lostChild === void 0 && this.recreateDirectionFor(stackName, logicalId) === void 0 && keyOrderFreeJson(desiredForSkipCheckAsWritten) === keyOrderFreeJson(currentPropsAsWritten)) {
|
|
29382
|
-
if (change.attributeChanges && change.attributeChanges.length > 0) {
|
|
29383
|
-
applyAttributeOnlyUpdate(change.attributeChanges);
|
|
29384
|
-
return;
|
|
29385
|
-
}
|
|
29386
|
-
this.logger.debug(`Skipping ${logicalId}: no actual changes after intrinsic function resolution`);
|
|
29387
|
-
if (counts) counts.skipped++;
|
|
29388
|
-
return;
|
|
29389
|
-
}
|
|
29390
|
-
this.refuseRedactedAttributeReads(logicalId, resourceType, context);
|
|
29391
|
-
this.attemptedResolvedProps.set(logicalId, resolvedProps);
|
|
29392
|
-
const noEchoHeldPaths = /* @__PURE__ */ new Set();
|
|
29393
|
-
if (change.propertyChanges?.some((pc) => pc.requiresReplacement) === true) {
|
|
29394
|
-
let readback;
|
|
29395
|
-
const lowered = [];
|
|
29396
|
-
for (const pc of change.propertyChanges) {
|
|
29397
|
-
if (!pc.requiresReplacement) {
|
|
29398
|
-
lowered.push(pc);
|
|
29399
|
-
continue;
|
|
29400
|
-
}
|
|
29401
|
-
const freshLeaves = freshNoEchoLeafPositions(resolvedProps[pc.path], updateSecrets);
|
|
29402
|
-
if (!isReplacementCeiling(pc) && freshLeaves.length === 0) {
|
|
29403
|
-
lowered.push(pc);
|
|
29404
|
-
continue;
|
|
29405
|
-
}
|
|
29406
|
-
const moved = keyOrderFreeJson(desiredForSkipCheckAsWritten[pc.path]) !== keyOrderFreeJson(currentPropsAsWritten[pc.path]);
|
|
29407
|
-
const conditionalVerdict = moved && !typeChanged && isReplacementCeiling(pc) ? this.diffCalculator.conditionalReplacementVerdict?.(resourceType, pc.path, currentPropsAsWritten[pc.path], desiredForSkipCheckAsWritten[pc.path]) : void 0;
|
|
29408
|
-
if (moved && conditionalVerdict !== false) {
|
|
29409
|
-
lowered.push(pc);
|
|
29410
|
-
continue;
|
|
29411
|
-
}
|
|
29412
|
-
if (freshLeaves.length > 0) {
|
|
29413
|
-
if (typeChanged) {
|
|
29414
|
-
lowered.push(pc);
|
|
29415
|
-
continue;
|
|
29416
|
-
}
|
|
29417
|
-
readback ??= this.readReaderForFreshNoEchoCeiling(logicalId, currentResource, updateSecrets);
|
|
29418
|
-
const read = await readback;
|
|
29419
|
-
let verdict;
|
|
29420
|
-
if ("failure" in read) verdict = read.failure;
|
|
29421
|
-
else if (!Object.prototype.hasOwnProperty.call(read.live, pc.path)) verdict = "not-readable";
|
|
29422
|
-
else verdict = liveHoldsFreshLeaves(read.live[pc.path], freshLeaves) ? "held" : "differs";
|
|
29423
|
-
if (verdict !== "held") {
|
|
29424
|
-
this.logger.warn(safeMsg`${logicalId}.${pc.path} carries a NoEcho value that AWS could not confirm unchanged (${verdict}): replacement kept.`);
|
|
29425
|
-
lowered.push(pc);
|
|
29426
|
-
continue;
|
|
29427
|
-
}
|
|
29428
|
-
this.logger.debug(safeMsg`${logicalId}.${pc.path} carries a NoEcho value AWS already holds: not replaced.`);
|
|
29429
|
-
noEchoHeldPaths.add(pc.path);
|
|
29430
|
-
}
|
|
29431
|
-
lowered.push({
|
|
29432
|
-
...pc,
|
|
29433
|
-
requiresReplacement: false
|
|
29434
|
-
});
|
|
29435
|
-
}
|
|
29436
|
-
change.propertyChanges = lowered;
|
|
29437
|
-
}
|
|
29438
|
-
if (noEchoHeldPaths.size > 0 && !typeChanged && lostChild === void 0 && this.recreateDirectionFor(stackName, logicalId) === void 0 && keyOrderFreeJson(desiredForSkipCheckAsWritten) === keyOrderFreeJson(currentPropsAsWritten) && Object.entries(resolvedProps).every(([key, value]) => noEchoHeldPaths.has(key) || freshNoEchoLeafPositions(value, updateSecrets).length === 0)) {
|
|
29439
|
-
this.logger.debug(safeMsg`Skipping ${logicalId}: AWS already holds every NoEcho value it carries, and nothing else changed`);
|
|
29440
|
-
this.attemptedResolvedProps.delete(logicalId);
|
|
29441
|
-
if (change.attributeChanges && change.attributeChanges.length > 0) {
|
|
29442
|
-
applyAttributeOnlyUpdate(change.attributeChanges);
|
|
29443
|
-
return;
|
|
29444
|
-
}
|
|
29445
|
-
if (counts) counts.skipped++;
|
|
29446
|
-
return;
|
|
29447
|
-
}
|
|
29448
|
-
const propertyDrivenReplacement = typeChanged || change.propertyChanges?.some((pc) => pc.requiresReplacement);
|
|
29449
|
-
const recreateTargets = this.options.recreateTargets?.stackName === stackName ? this.options.recreateTargets : void 0;
|
|
29450
|
-
const recreateViaCcApi = recreateTargets?.viaCcApi.has(logicalId) ?? false;
|
|
29451
|
-
const recreateViaSdkProvider = recreateTargets?.viaSdkProvider.has(logicalId) ?? false;
|
|
29452
|
-
const recreateFlagged = recreateViaCcApi || recreateViaSdkProvider;
|
|
29453
|
-
const needsReplacement = propertyDrivenReplacement || recreateFlagged || lostWithParent !== void 0;
|
|
29454
|
-
const liveLabel = this.liveTaskLabels.get(logicalId);
|
|
29455
|
-
if (needsReplacement && liveLabel !== void 0 && !liveLabel.replacing) {
|
|
29456
|
-
const label = `Replacing ${logicalId} (${resourceType})${this.peekRoutingForLabel(change, currentResource, stackName, logicalId, true, this.recreateDirectionFor(stackName, logicalId)) === "cc-api" ? " [CC API]" : ""}`;
|
|
29457
|
-
this.liveTaskLabels.set(logicalId, {
|
|
29458
|
-
label,
|
|
29459
|
-
replacing: true,
|
|
29460
|
-
...liveLabel.warnSuffix !== void 0 && { warnSuffix: liveLabel.warnSuffix }
|
|
29461
|
-
});
|
|
29462
|
-
renderer.updateTaskLabel(logicalId, `${label}${liveLabel.warnSuffix ?? ""}`);
|
|
29463
|
-
} else if (!needsReplacement && liveLabel !== void 0 && liveLabel.replacing) {
|
|
29464
|
-
const label = `Updating ${logicalId} (${resourceType})${this.peekRoutingForLabel(change, currentResource, stackName, logicalId, false, this.recreateDirectionFor(stackName, logicalId)) === "cc-api" ? " [CC API]" : ""}`;
|
|
29465
|
-
this.liveTaskLabels.set(logicalId, {
|
|
29466
|
-
label,
|
|
29467
|
-
replacing: false,
|
|
29468
|
-
...liveLabel.warnSuffix !== void 0 && { warnSuffix: liveLabel.warnSuffix }
|
|
29469
|
-
});
|
|
29470
|
-
renderer.updateTaskLabel(logicalId, `${label}${liveLabel.warnSuffix ?? ""}`);
|
|
29471
|
-
}
|
|
29472
|
-
const dependencies = this.extractAllDependencies(template, logicalId);
|
|
29473
|
-
const updateReplacePolicy = template?.Resources?.[logicalId]?.UpdateReplacePolicy;
|
|
29474
|
-
const markRestored = (outcome) => {
|
|
29475
|
-
if (lostChild === void 0) return outcome;
|
|
29476
|
-
if (this.updatesThatSentNothing.has(logicalId)) {
|
|
29477
|
-
this.logger.warn(survivesParent(resourceType) ? safeMsg` ⚠ ${logicalId} lost its attachment to ${lostChild.parent}, which was re-created, but its update sent no change (it is recorded on Cloud Control, which patches record against template); it may no longer be attached to ${lostChild.parent}. Attach it there by hand.` : safeMsg` ⚠ ${logicalId} went with ${lostChild.parent}, which was re-created, but its update sent no change (it is recorded on Cloud Control, which patches record against template); its policy may be missing from ${lostChild.parent}. Re-run with --recreate-via-sdk-provider ${logicalId} to write it again.`);
|
|
29478
|
-
return outcome;
|
|
29479
|
-
}
|
|
29480
|
-
this.restoredLostChildren.add(logicalId);
|
|
29481
|
-
return outcome;
|
|
29482
|
-
};
|
|
29483
|
-
if (needsReplacement) return markRestored(await this.updateByReplacement({
|
|
29484
|
-
change,
|
|
29485
|
-
counts,
|
|
29486
|
-
currentProps,
|
|
29487
|
-
currentResource,
|
|
29488
|
-
dependencies,
|
|
29489
|
-
logicalId,
|
|
29490
|
-
oldResourceType,
|
|
29491
|
-
progress,
|
|
29492
|
-
propertyDrivenReplacement,
|
|
29493
|
-
recreateFlagged,
|
|
29494
|
-
recreateViaCcApi,
|
|
29495
|
-
recreateViaSdkProvider,
|
|
29496
|
-
renderer,
|
|
29497
|
-
resolvedProps,
|
|
29498
|
-
resourceType,
|
|
29499
|
-
stackName,
|
|
29500
|
-
stateResources,
|
|
29501
|
-
template,
|
|
29502
|
-
typeChanged,
|
|
29503
|
-
updateReplacePolicy,
|
|
29504
|
-
updateSecrets,
|
|
29505
|
-
lostWithParent
|
|
29506
|
-
}));
|
|
29507
|
-
return markRestored(await this.updateInPlace({
|
|
29508
|
-
conditions,
|
|
29509
|
-
counts,
|
|
29510
|
-
currentProps,
|
|
29511
|
-
currentPropsAsWritten,
|
|
29512
|
-
currentResource,
|
|
29513
|
-
dependencies,
|
|
29514
|
-
desiredForSkipCheckAsWritten,
|
|
29515
|
-
logicalId,
|
|
29516
|
-
noEchoHeldPaths,
|
|
29517
|
-
parameterValues,
|
|
29518
|
-
progress,
|
|
29519
|
-
renderer,
|
|
29520
|
-
resolvedProps,
|
|
29521
|
-
resourceType,
|
|
29522
|
-
stackName,
|
|
29523
|
-
stateResources,
|
|
29524
|
-
template,
|
|
29525
|
-
updateReplacePolicy,
|
|
29526
|
-
updateSecrets,
|
|
29527
|
-
reattach: lostChild?.mode === "reattach"
|
|
29528
|
-
}));
|
|
29529
|
-
}
|
|
29530
|
-
|
|
29531
29837
|
//#endregion
|
|
29532
29838
|
//#region src/provisioning/data-delete-intent.ts
|
|
29533
29839
|
const S3_AUTO_DELETE_OBJECTS_TAG = "aws-cdk:auto-delete-objects";
|
|
@@ -32235,6 +32541,286 @@ function recordedProtectionNote(evidence, flags) {
|
|
|
32235
32541
|
return `${evidence}. AWS refuses to delete the resource while that protection is on, and cdkd deploy has no --remove-protection flag to clear it (only cdkd destroy and cdkd state destroy act on one), so ${flags} alone does not remove the old resource: depending on the replacement order, the deploy either fails at that delete or completes leaving the old resource, and its data, in AWS and no longer tracked by cdkd. Read ${DELETION_PROTECTION_DOC_POINTER} before you disable anything, then turn the protection off out of band (the console, or the service's own API) and re-run with ${flags}.`;
|
|
32236
32542
|
}
|
|
32237
32543
|
|
|
32544
|
+
//#endregion
|
|
32545
|
+
//#region src/deployment/deploy-value-equality.ts
|
|
32546
|
+
function isReplacementCeiling(pc) {
|
|
32547
|
+
return pc.requiresReplacement && (pc.inPlacePropagated === true || pc.replacementPropagated === true);
|
|
32548
|
+
}
|
|
32549
|
+
function keyOrderFreeJson(value) {
|
|
32550
|
+
return JSON.stringify(value, (_key, node) => {
|
|
32551
|
+
if (node === null || typeof node !== "object" || Array.isArray(node)) return node;
|
|
32552
|
+
const sorted = Object.create(null);
|
|
32553
|
+
for (const key of Object.keys(node).sort()) sorted[key] = node[key];
|
|
32554
|
+
return sorted;
|
|
32555
|
+
});
|
|
32556
|
+
}
|
|
32557
|
+
function liveHoldsFreshLeaves(live, leaves) {
|
|
32558
|
+
for (const leaf of leaves) {
|
|
32559
|
+
let node = live;
|
|
32560
|
+
for (const segment of leaf.path) {
|
|
32561
|
+
if (typeof segment === "number") {
|
|
32562
|
+
if (!Array.isArray(node) || segment >= node.length) return false;
|
|
32563
|
+
node = node[segment];
|
|
32564
|
+
continue;
|
|
32565
|
+
}
|
|
32566
|
+
if (node === null || typeof node !== "object" || Array.isArray(node)) return false;
|
|
32567
|
+
if (!Object.prototype.hasOwnProperty.call(node, segment)) return false;
|
|
32568
|
+
node = node[segment];
|
|
32569
|
+
}
|
|
32570
|
+
if (typeof node !== "string" || node !== leaf.plaintext) return false;
|
|
32571
|
+
}
|
|
32572
|
+
return true;
|
|
32573
|
+
}
|
|
32574
|
+
function outputMapsEqual(a, b) {
|
|
32575
|
+
return deepEqualValue(a, b);
|
|
32576
|
+
}
|
|
32577
|
+
function deepEqualValue(a, b) {
|
|
32578
|
+
if (a === b) return true;
|
|
32579
|
+
if (a == null || b == null) return a === b;
|
|
32580
|
+
if (typeof a !== typeof b) return false;
|
|
32581
|
+
if (typeof a !== "object") return false;
|
|
32582
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
32583
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
|
|
32584
|
+
return a.every((v, i) => deepEqualValue(v, b[i]));
|
|
32585
|
+
}
|
|
32586
|
+
const ao = a;
|
|
32587
|
+
const bo = b;
|
|
32588
|
+
const ak = Object.keys(ao);
|
|
32589
|
+
if (ak.length !== Object.keys(bo).length) return false;
|
|
32590
|
+
for (const k of ak) {
|
|
32591
|
+
if (!Object.prototype.hasOwnProperty.call(bo, k)) return false;
|
|
32592
|
+
if (!deepEqualValue(ao[k], bo[k])) return false;
|
|
32593
|
+
}
|
|
32594
|
+
return true;
|
|
32595
|
+
}
|
|
32596
|
+
|
|
32597
|
+
//#endregion
|
|
32598
|
+
//#region src/deployment/deploy-engine/update.ts
|
|
32599
|
+
async function provisionUpdate(logicalId, change, stateResources, stackName, template, parameterValues, conditions, counts, progress) {
|
|
32600
|
+
const resourceType = change.resourceType;
|
|
32601
|
+
const existingState = stateResources[logicalId];
|
|
32602
|
+
const renderer = getLiveRenderer();
|
|
32603
|
+
const currentResource = existingState;
|
|
32604
|
+
if (!currentResource) throw new Error(`Cannot update ${logicalId}: resource not found in state`);
|
|
32605
|
+
const oldResourceType = currentResource.resourceType;
|
|
32606
|
+
const typeChanged = oldResourceType !== resourceType;
|
|
32607
|
+
const desiredProps = change.desiredProperties || {};
|
|
32608
|
+
const currentProps = change.currentProperties || {};
|
|
32609
|
+
const context = this.buildResolverContext({
|
|
32610
|
+
template,
|
|
32611
|
+
resources: stateResources,
|
|
32612
|
+
...parameterValues && { parameters: parameterValues },
|
|
32613
|
+
...conditions && { conditions },
|
|
32614
|
+
redactedAttributeReads: []
|
|
32615
|
+
}, stackName);
|
|
32616
|
+
const updateSecrets = context.recordedSecretValues ?? /* @__PURE__ */ new Map();
|
|
32617
|
+
this.perResourceSecrets.set(logicalId, updateSecrets);
|
|
32618
|
+
const resolvedProps = await this.resolver.resolve(desiredProps, context);
|
|
32619
|
+
this.perResourceTemplateProps.set(logicalId, desiredProps);
|
|
32620
|
+
this.perResourceResolvedType.set(logicalId, resourceType);
|
|
32621
|
+
recordNestedStackParameterExpressions(updateSecrets, resourceType, resolvedProps, desiredProps);
|
|
32622
|
+
this.auditResolvedAssetReferences(logicalId, resourceType, resolvedProps);
|
|
32623
|
+
const lostCandidate = childLostWithRecreatedParent({
|
|
32624
|
+
resourceType,
|
|
32625
|
+
templateProperties: desiredProps,
|
|
32626
|
+
recreatedUnderSameId: this.recreatedUnderSameId,
|
|
32627
|
+
recordedTypeOf: (id) => Object.hasOwn(stateResources, id) ? stateResources[id]?.resourceType : void 0,
|
|
32628
|
+
conditions
|
|
32629
|
+
});
|
|
32630
|
+
const lostChild = lostCandidate?.mode === "reattach" || lostCandidate !== void 0 && Object.hasOwn(resolvedProps, lostCandidate.property) && Object.hasOwn(currentProps, lostCandidate.property) && keyOrderFreeJson(resolvedProps[lostCandidate.property]) === keyOrderFreeJson(currentProps[lostCandidate.property]) ? lostCandidate : void 0;
|
|
32631
|
+
const lostWithParent = lostChild?.mode === "recreate" ? lostChild.parent : void 0;
|
|
32632
|
+
const suppliesFreshMaskOnlyValue = carriesFreshNoEchoValue(resolvedProps, updateSecrets);
|
|
32633
|
+
const movedMasked = new Set(movedMaskedProperties(currentResource, desiredProps));
|
|
32634
|
+
const desiredForSkipCheck = redactSecretsForState(markSameGenerationBag({ ...resolvedProps }), updateSecrets, desiredProps);
|
|
32635
|
+
const allowedSilentDrops = this.providerRegistry.getAllowedUnsupportedProperties?.();
|
|
32636
|
+
const desiredForSkipCheckAsWritten = currentResource.provisionedBy !== "cc-api" && allowedSilentDrops ? withoutAcceptedSilentDropProperties(resourceType, desiredForSkipCheck, allowedSilentDrops, currentResource.properties) : desiredForSkipCheck;
|
|
32637
|
+
const allowedForRecord = allowedSilentDrops ?? /* @__PURE__ */ new Set();
|
|
32638
|
+
const createOnlyEvidence = acceptedCreateOnlyDropsOf(currentResource);
|
|
32639
|
+
const currentPropsAsWritten = currentResource.provisionedBy === "cc-api" ? currentProps : withoutUnwrittenSilentDropProperties(resourceType, currentProps, desiredForSkipCheck, allowedForRecord, createOnlyEvidence);
|
|
32640
|
+
const recordMatchesDesired = movedMasked.size === 0 && keyOrderFreeJson(desiredForSkipCheckAsWritten) === keyOrderFreeJson(currentPropsAsWritten);
|
|
32641
|
+
const applyAttributeOnlyUpdate = (attributeChanges) => {
|
|
32642
|
+
const attrSummary = attributeChanges.map((a) => `${a.attribute}: ${a.oldValue ?? "(unset)"} → ${a.newValue ?? "(unset)"}`).join(", ");
|
|
32643
|
+
this.logger.info(safeMsg` ↻ ${logicalId} (${resourceType}) attribute update: ${attrSummary}`);
|
|
32644
|
+
stateResources[logicalId] = {
|
|
32645
|
+
...currentResource,
|
|
32646
|
+
...this.extractTemplateAttributes(template, logicalId)
|
|
32647
|
+
};
|
|
32648
|
+
if (counts) counts.updated++;
|
|
32649
|
+
if (progress) progress.current++;
|
|
32650
|
+
const attrPrefix = progress ? `[${progress.current}/${progress.total}] ` : " ";
|
|
32651
|
+
renderer.removeTask(logicalId);
|
|
32652
|
+
this.logger.info(safeMsg`${attrPrefix}${formatResourceLine("updated", logicalId, resourceType, "updated (metadata)")}`);
|
|
32653
|
+
};
|
|
32654
|
+
if (!typeChanged && !suppliesFreshMaskOnlyValue && lostChild === void 0 && this.recreateDirectionFor(stackName, logicalId) === void 0 && recordMatchesDesired) {
|
|
32655
|
+
if (change.attributeChanges && change.attributeChanges.length > 0) {
|
|
32656
|
+
applyAttributeOnlyUpdate(change.attributeChanges);
|
|
32657
|
+
return;
|
|
32658
|
+
}
|
|
32659
|
+
this.logger.debug(`Skipping ${logicalId}: no actual changes after intrinsic function resolution`);
|
|
32660
|
+
if (counts) counts.skipped++;
|
|
32661
|
+
return;
|
|
32662
|
+
}
|
|
32663
|
+
this.refuseRedactedAttributeReads(logicalId, resourceType, context);
|
|
32664
|
+
this.attemptedResolvedProps.set(logicalId, resolvedProps);
|
|
32665
|
+
const noEchoHeldPaths = /* @__PURE__ */ new Set();
|
|
32666
|
+
if (change.propertyChanges?.some((pc) => pc.requiresReplacement) === true) {
|
|
32667
|
+
let readback;
|
|
32668
|
+
const lowered = [];
|
|
32669
|
+
for (const pc of change.propertyChanges) {
|
|
32670
|
+
if (!pc.requiresReplacement) {
|
|
32671
|
+
lowered.push(pc);
|
|
32672
|
+
continue;
|
|
32673
|
+
}
|
|
32674
|
+
const freshLeaves = freshNoEchoLeafPositions(resolvedProps[pc.path], updateSecrets);
|
|
32675
|
+
if (!isReplacementCeiling(pc) && freshLeaves.length === 0) {
|
|
32676
|
+
lowered.push(pc);
|
|
32677
|
+
continue;
|
|
32678
|
+
}
|
|
32679
|
+
const moved = movedMasked.has(pc.path) || keyOrderFreeJson(desiredForSkipCheckAsWritten[pc.path]) !== keyOrderFreeJson(currentPropsAsWritten[pc.path]);
|
|
32680
|
+
const conditionalVerdict = moved && !typeChanged && isReplacementCeiling(pc) ? this.diffCalculator.conditionalReplacementVerdict?.(resourceType, pc.path, currentPropsAsWritten[pc.path], desiredForSkipCheckAsWritten[pc.path]) : void 0;
|
|
32681
|
+
if (moved && conditionalVerdict !== false) {
|
|
32682
|
+
lowered.push(pc);
|
|
32683
|
+
continue;
|
|
32684
|
+
}
|
|
32685
|
+
if (freshLeaves.length > 0) {
|
|
32686
|
+
if (typeChanged) {
|
|
32687
|
+
lowered.push(pc);
|
|
32688
|
+
continue;
|
|
32689
|
+
}
|
|
32690
|
+
readback ??= this.readReaderForFreshNoEchoCeiling(logicalId, currentResource, updateSecrets);
|
|
32691
|
+
const read = await readback;
|
|
32692
|
+
let verdict;
|
|
32693
|
+
if ("failure" in read) verdict = read.failure;
|
|
32694
|
+
else if (!Object.prototype.hasOwnProperty.call(read.live, pc.path)) verdict = "not-readable";
|
|
32695
|
+
else verdict = liveHoldsFreshLeaves(read.live[pc.path], freshLeaves) ? "held" : "differs";
|
|
32696
|
+
if (verdict !== "held") {
|
|
32697
|
+
this.logger.warn(safeMsg`${logicalId}.${pc.path} carries a NoEcho value that AWS could not confirm unchanged (${verdict}): replacement kept.`);
|
|
32698
|
+
lowered.push(pc);
|
|
32699
|
+
continue;
|
|
32700
|
+
}
|
|
32701
|
+
this.logger.debug(safeMsg`${logicalId}.${pc.path} carries a NoEcho value AWS already holds: not replaced.`);
|
|
32702
|
+
noEchoHeldPaths.add(pc.path);
|
|
32703
|
+
}
|
|
32704
|
+
lowered.push({
|
|
32705
|
+
...pc,
|
|
32706
|
+
requiresReplacement: false
|
|
32707
|
+
});
|
|
32708
|
+
}
|
|
32709
|
+
change.propertyChanges = lowered;
|
|
32710
|
+
}
|
|
32711
|
+
if (noEchoHeldPaths.size > 0 && !typeChanged && lostChild === void 0 && this.recreateDirectionFor(stackName, logicalId) === void 0 && recordMatchesDesired && Object.entries(resolvedProps).every(([key, value]) => noEchoHeldPaths.has(key) || freshNoEchoLeafPositions(value, updateSecrets).length === 0)) {
|
|
32712
|
+
this.logger.debug(safeMsg`Skipping ${logicalId}: AWS already holds every NoEcho value it carries, and nothing else changed`);
|
|
32713
|
+
this.attemptedResolvedProps.delete(logicalId);
|
|
32714
|
+
if (change.attributeChanges && change.attributeChanges.length > 0) {
|
|
32715
|
+
applyAttributeOnlyUpdate(change.attributeChanges);
|
|
32716
|
+
return;
|
|
32717
|
+
}
|
|
32718
|
+
if (counts) counts.skipped++;
|
|
32719
|
+
return;
|
|
32720
|
+
}
|
|
32721
|
+
const propertyDrivenReplacement = typeChanged || change.propertyChanges?.some((pc) => pc.requiresReplacement);
|
|
32722
|
+
const recreateTargets = this.options.recreateTargets?.stackName === stackName ? this.options.recreateTargets : void 0;
|
|
32723
|
+
const recreateViaCcApi = recreateTargets?.viaCcApi.has(logicalId) ?? false;
|
|
32724
|
+
const recreateViaSdkProvider = recreateTargets?.viaSdkProvider.has(logicalId) ?? false;
|
|
32725
|
+
const recreateFlagged = recreateViaCcApi || recreateViaSdkProvider;
|
|
32726
|
+
const needsReplacement = propertyDrivenReplacement || recreateFlagged || lostWithParent !== void 0;
|
|
32727
|
+
if (propertyDrivenReplacement && !typeChanged && !recreateFlagged && lostWithParent === void 0 && this.options.replace !== true && currentResource.provisionedBy !== "cc-api") {
|
|
32728
|
+
const unwritten = unwrittenCreateOnlyReplacement(resourceType, currentProps, desiredForSkipCheck, allowedForRecord, createOnlyEvidence, (change.propertyChanges ?? []).filter((pc) => pc.requiresReplacement).map((pc) => pc.path));
|
|
32729
|
+
if (unwritten.length > 0) throw markRefusedBeforeApplying(markNonRetryable(new CdkdError(unwrittenCreateOnlyRefusal({
|
|
32730
|
+
logicalId,
|
|
32731
|
+
resourceType,
|
|
32732
|
+
unwritten,
|
|
32733
|
+
routeDriving: findActionableSilentDrops(resourceType, desiredForSkipCheck, allowedForRecord, currentProps).map(({ property }) => property),
|
|
32734
|
+
nested: this.options.parentStackInfo !== void 0,
|
|
32735
|
+
protectionEvidence: recordedProtectionEvidence(resourceType, currentProps, currentResource.observedProperties, this.stackRegion)
|
|
32736
|
+
}), "CREATE_ONLY_DROP_NEEDS_REPLACEMENT")));
|
|
32737
|
+
}
|
|
32738
|
+
const liveLabel = this.liveTaskLabels.get(logicalId);
|
|
32739
|
+
if (needsReplacement && liveLabel !== void 0 && !liveLabel.replacing) {
|
|
32740
|
+
const label = `Replacing ${logicalId} (${resourceType})${this.peekRoutingForLabel(change, currentResource, stackName, logicalId, true, this.recreateDirectionFor(stackName, logicalId)) === "cc-api" ? " [CC API]" : ""}`;
|
|
32741
|
+
this.liveTaskLabels.set(logicalId, {
|
|
32742
|
+
label,
|
|
32743
|
+
replacing: true,
|
|
32744
|
+
...liveLabel.warnSuffix !== void 0 && { warnSuffix: liveLabel.warnSuffix }
|
|
32745
|
+
});
|
|
32746
|
+
renderer.updateTaskLabel(logicalId, `${label}${liveLabel.warnSuffix ?? ""}`);
|
|
32747
|
+
} else if (!needsReplacement && liveLabel !== void 0 && liveLabel.replacing) {
|
|
32748
|
+
const label = `Updating ${logicalId} (${resourceType})${this.peekRoutingForLabel(change, currentResource, stackName, logicalId, false, this.recreateDirectionFor(stackName, logicalId)) === "cc-api" ? " [CC API]" : ""}`;
|
|
32749
|
+
this.liveTaskLabels.set(logicalId, {
|
|
32750
|
+
label,
|
|
32751
|
+
replacing: false,
|
|
32752
|
+
...liveLabel.warnSuffix !== void 0 && { warnSuffix: liveLabel.warnSuffix }
|
|
32753
|
+
});
|
|
32754
|
+
renderer.updateTaskLabel(logicalId, `${label}${liveLabel.warnSuffix ?? ""}`);
|
|
32755
|
+
}
|
|
32756
|
+
const dependencies = this.extractAllDependencies(template, logicalId);
|
|
32757
|
+
const updateReplacePolicy = template?.Resources?.[logicalId]?.UpdateReplacePolicy;
|
|
32758
|
+
const markRestored = (outcome) => {
|
|
32759
|
+
if (lostChild === void 0) return outcome;
|
|
32760
|
+
if (this.updatesThatSentNothing.has(logicalId)) {
|
|
32761
|
+
this.logger.warn(survivesParent(resourceType) ? safeMsg` ⚠ ${logicalId} lost its attachment to ${lostChild.parent}, which was re-created, but its update sent no change (it is recorded on Cloud Control, which patches record against template); it may no longer be attached to ${lostChild.parent}. Attach it there by hand.` : safeMsg` ⚠ ${logicalId} went with ${lostChild.parent}, which was re-created, but its update sent no change (it is recorded on Cloud Control, which patches record against template); its policy may be missing from ${lostChild.parent}. Re-run with --recreate-via-sdk-provider ${logicalId} to write it again.`);
|
|
32762
|
+
return outcome;
|
|
32763
|
+
}
|
|
32764
|
+
this.restoredLostChildren.add(logicalId);
|
|
32765
|
+
return outcome;
|
|
32766
|
+
};
|
|
32767
|
+
if (needsReplacement) return markRestored(await this.updateByReplacement({
|
|
32768
|
+
change,
|
|
32769
|
+
counts,
|
|
32770
|
+
currentProps,
|
|
32771
|
+
currentResource,
|
|
32772
|
+
dependencies,
|
|
32773
|
+
logicalId,
|
|
32774
|
+
oldResourceType,
|
|
32775
|
+
progress,
|
|
32776
|
+
propertyDrivenReplacement,
|
|
32777
|
+
recreateFlagged,
|
|
32778
|
+
recreateViaCcApi,
|
|
32779
|
+
recreateViaSdkProvider,
|
|
32780
|
+
renderer,
|
|
32781
|
+
resolvedProps,
|
|
32782
|
+
resourceType,
|
|
32783
|
+
stackName,
|
|
32784
|
+
stateResources,
|
|
32785
|
+
template,
|
|
32786
|
+
typeChanged,
|
|
32787
|
+
updateReplacePolicy,
|
|
32788
|
+
updateSecrets,
|
|
32789
|
+
lostWithParent
|
|
32790
|
+
}));
|
|
32791
|
+
return markRestored(await this.updateInPlace({
|
|
32792
|
+
conditions,
|
|
32793
|
+
counts,
|
|
32794
|
+
currentProps,
|
|
32795
|
+
currentPropsAsWritten,
|
|
32796
|
+
currentResource,
|
|
32797
|
+
dependencies,
|
|
32798
|
+
desiredForSkipCheckAsWritten,
|
|
32799
|
+
logicalId,
|
|
32800
|
+
noEchoHeldPaths,
|
|
32801
|
+
parameterValues,
|
|
32802
|
+
progress,
|
|
32803
|
+
renderer,
|
|
32804
|
+
resolvedProps,
|
|
32805
|
+
resourceType,
|
|
32806
|
+
stackName,
|
|
32807
|
+
stateResources,
|
|
32808
|
+
template,
|
|
32809
|
+
updateReplacePolicy,
|
|
32810
|
+
updateSecrets,
|
|
32811
|
+
reattach: lostChild?.mode === "reattach"
|
|
32812
|
+
}));
|
|
32813
|
+
}
|
|
32814
|
+
function unwrittenCreateOnlyRefusal(input) {
|
|
32815
|
+
const { logicalId, resourceType, unwritten, routeDriving, nested, protectionEvidence } = input;
|
|
32816
|
+
const one = unwritten.length === 1;
|
|
32817
|
+
const list = unwritten.join(", ");
|
|
32818
|
+
const keep = [.../* @__PURE__ */ new Set([...unwritten, ...routeDriving])].sort((a, b) => a.localeCompare(b)).map((property) => `${resourceType}:${property}`).join(",");
|
|
32819
|
+
const replaceFlags = nested ? "--replace (which creates the new resource before deleting the old one, so a resource holding a unique value other than its name, such as a CIDR block, collides)" : `--recreate-via-cc-api ${logicalId}, which deletes the old resource first (--replace also works, but creates the new one first, so a resource holding a unique value other than its name, such as a CIDR block, collides)`;
|
|
32820
|
+
const routed = [...routeDriving].sort((a, b) => a.localeCompare(b)).join(", ");
|
|
32821
|
+
return `${logicalId} (${resourceType}): ${list} ${one ? "is" : "are"} create-only, and the state record holds ${one ? "it" : "them"} although this type's SDK provider never writes ${one ? "it" : "them"}, so AWS does not. This deploy routes the resource through Cloud Control (--prefer-sdk-route does not cover ${routed}), and applying a create-only property means replacing the resource, which cdkd does not do on its own. This resource was not changed. To replace it and apply ${list}, re-run with ${replaceFlags}; a stateful resource also needs --force-stateful-recreation.` + (protectionEvidence !== void 0 ? ` ${recordedProtectionNote(protectionEvidence, nested ? "--replace" : `--recreate-via-cc-api ${logicalId} or --replace`)}` : "") + ` To keep dropping ${one ? "it" : "them"}, re-run with --prefer-sdk-route ${keep}.`;
|
|
32822
|
+
}
|
|
32823
|
+
|
|
32238
32824
|
//#endregion
|
|
32239
32825
|
//#region src/deployment/deploy-engine/update-replace.ts
|
|
32240
32826
|
async function updateByReplacement({ change, counts, currentProps, currentResource, dependencies, logicalId, oldResourceType, progress, propertyDrivenReplacement, recreateFlagged, recreateViaCcApi, recreateViaSdkProvider, renderer, resolvedProps, resourceType, stackName, stateResources, template, typeChanged, updateReplacePolicy, updateSecrets, lostWithParent }) {
|
|
@@ -32423,10 +33009,12 @@ async function updateByReplacement({ change, counts, currentProps, currentResour
|
|
|
32423
33009
|
}
|
|
32424
33010
|
this.registerNoEchoAttributes(logicalId, createResult, updateSecrets, resolvedProps);
|
|
32425
33011
|
if (equalIdIsSameResource && createResult.physicalId === currentResource.physicalId) this.recreatedUnderSameId.add(logicalId);
|
|
33012
|
+
const recordedReplacementProperties = this.propertiesToRecord(resolvedProps, createResult, resourceType, replaceDecision.provisionedBy);
|
|
32426
33013
|
stateResources[logicalId] = {
|
|
32427
33014
|
physicalId: createResult.physicalId,
|
|
32428
33015
|
resourceType,
|
|
32429
|
-
properties:
|
|
33016
|
+
properties: recordedReplacementProperties,
|
|
33017
|
+
...acceptedCreateOnlyDropsField(recordedReplacementProperties, resourceType, replaceDecision.provisionedBy, "new-resource"),
|
|
32430
33018
|
...createResult.attributes && { attributes: createResult.attributes },
|
|
32431
33019
|
...dependencies && dependencies.length > 0 && { dependencies },
|
|
32432
33020
|
...this.extractTemplateAttributes(template, logicalId),
|
|
@@ -32633,17 +33221,20 @@ async function updateInPlace({ conditions, counts, currentProps, currentPropsAsW
|
|
|
32633
33221
|
} else throw updateError;
|
|
32634
33222
|
}
|
|
32635
33223
|
if (result.wasReplaced) this.logger.info(`Resource ${logicalId} was replaced: ${currentResource.physicalId} -> ${result.physicalId}`);
|
|
32636
|
-
const
|
|
33224
|
+
const dischargedByReplacement = result.wasReplaced === true && result.physicalId !== currentResource.physicalId;
|
|
33225
|
+
const keepsParameterRefusal = !dischargedByReplacement && hasUnverifiableParameterRefusal(currentResource);
|
|
32637
33226
|
const carriedAttributes = result.attributes ?? (result.wasReplaced ? void 0 : currentResource.attributes);
|
|
32638
33227
|
this.registerNoEchoAttributes(logicalId, {
|
|
32639
33228
|
...carriedAttributes && { attributes: carriedAttributes },
|
|
32640
33229
|
...result.noEchoAttributes === true && { noEchoAttributes: true },
|
|
32641
33230
|
...result.noEchoAttributeNames && { noEchoAttributeNames: result.noEchoAttributeNames }
|
|
32642
33231
|
}, updateSecrets, resolvedProps);
|
|
33232
|
+
const recordedProperties = this.propertiesToRecord(resolvedProps, result, resourceType, resultProvisionedBy);
|
|
32643
33233
|
stateResources[logicalId] = {
|
|
32644
33234
|
physicalId: result.physicalId,
|
|
32645
33235
|
resourceType,
|
|
32646
|
-
properties:
|
|
33236
|
+
properties: recordedProperties,
|
|
33237
|
+
...acceptedCreateOnlyDropsField(recordedProperties, resourceType, resultProvisionedBy, dischargedByReplacement ? "new-resource" : "in-place", currentResource),
|
|
32647
33238
|
...carriedAttributes && { attributes: carriedAttributes },
|
|
32648
33239
|
...dependencies && dependencies.length > 0 && { dependencies },
|
|
32649
33240
|
...this.extractTemplateAttributes(template, logicalId),
|
|
@@ -32798,75 +33389,6 @@ async function withResourceDeadline(operation, opts) {
|
|
|
32798
33389
|
});
|
|
32799
33390
|
}
|
|
32800
33391
|
|
|
32801
|
-
//#endregion
|
|
32802
|
-
//#region src/deployment/prior-attempt-scope.ts
|
|
32803
|
-
const priorAttemptStore = new AsyncLocalStorage();
|
|
32804
|
-
function withPriorAttempts(lookup, fn) {
|
|
32805
|
-
return priorAttemptStore.run(lookup, fn);
|
|
32806
|
-
}
|
|
32807
|
-
function priorAttemptLookup(logicalId, attempts) {
|
|
32808
|
-
let landed = false;
|
|
32809
|
-
return {
|
|
32810
|
-
logicalId,
|
|
32811
|
-
attempts,
|
|
32812
|
-
notePossiblyLanded: () => {
|
|
32813
|
-
landed = true;
|
|
32814
|
-
},
|
|
32815
|
-
possiblyLanded: () => landed
|
|
32816
|
-
};
|
|
32817
|
-
}
|
|
32818
|
-
function getPriorAttempts(logicalId) {
|
|
32819
|
-
const bound = priorAttemptStore.getStore();
|
|
32820
|
-
return bound?.logicalId === logicalId ? bound : void 0;
|
|
32821
|
-
}
|
|
32822
|
-
function priorAttemptsInJournal(journal, logicalId, resourceType) {
|
|
32823
|
-
let bags = [];
|
|
32824
|
-
for (const segment of journal?.segments ?? []) {
|
|
32825
|
-
if (segment.operations.some((op) => op.logicalId === logicalId)) bags = [];
|
|
32826
|
-
for (const op of segment.failedOperations ?? []) {
|
|
32827
|
-
if (op.logicalId !== logicalId || op.resourceType !== resourceType) continue;
|
|
32828
|
-
if (op.changeType === "DELETE" || !isBag(op.attemptedProperties)) continue;
|
|
32829
|
-
if (recordedAsNewResource(op)) continue;
|
|
32830
|
-
bags.push(op.attemptedProperties);
|
|
32831
|
-
}
|
|
32832
|
-
if (segment.supersededLogicalIds?.includes(logicalId)) bags = [];
|
|
32833
|
-
}
|
|
32834
|
-
return bags;
|
|
32835
|
-
}
|
|
32836
|
-
function recordedAsNewResource(op) {
|
|
32837
|
-
if (typeof op.physicalId !== "string" || op.physicalId === "") return false;
|
|
32838
|
-
if (op.changeType === "CREATE") return true;
|
|
32839
|
-
const previous = op.previousState?.physicalId;
|
|
32840
|
-
return typeof previous === "string" && previous !== op.physicalId;
|
|
32841
|
-
}
|
|
32842
|
-
function isBag(value) {
|
|
32843
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
32844
|
-
}
|
|
32845
|
-
const REFUSED_BEFORE_APPLYING = Symbol.for("cdkd.refusedBeforeApplying");
|
|
32846
|
-
const MAX_CAUSE_DEPTH = 10;
|
|
32847
|
-
function markRefusedBeforeApplying(error) {
|
|
32848
|
-
if (!Object.isExtensible(error)) return error;
|
|
32849
|
-
Object.defineProperty(error, REFUSED_BEFORE_APPLYING, {
|
|
32850
|
-
value: true,
|
|
32851
|
-
enumerable: false,
|
|
32852
|
-
configurable: true,
|
|
32853
|
-
writable: false
|
|
32854
|
-
});
|
|
32855
|
-
return error;
|
|
32856
|
-
}
|
|
32857
|
-
function isRefusedBeforeApplying(error, logicalId) {
|
|
32858
|
-
let current = error;
|
|
32859
|
-
for (let depth = 0; depth < MAX_CAUSE_DEPTH && current != null; depth++) {
|
|
32860
|
-
if (typeof current === "object" || typeof current === "function") {
|
|
32861
|
-
const linkId = current.logicalId;
|
|
32862
|
-
if (typeof linkId === "string" && linkId !== logicalId) return false;
|
|
32863
|
-
if (current[REFUSED_BEFORE_APPLYING] === true) return true;
|
|
32864
|
-
}
|
|
32865
|
-
current = current.cause;
|
|
32866
|
-
}
|
|
32867
|
-
return false;
|
|
32868
|
-
}
|
|
32869
|
-
|
|
32870
33392
|
//#endregion
|
|
32871
33393
|
//#region src/deployment/deploy-engine/provision.ts
|
|
32872
33394
|
async function provisionResource(logicalId, change, stateResources, stackName, template, parameterValues, conditions, counts, progress) {
|
|
@@ -33245,14 +33767,17 @@ async function executeDeployment(template, currentState, changes, dag, execution
|
|
|
33245
33767
|
} catch (provisionError) {
|
|
33246
33768
|
this.interrupted = true;
|
|
33247
33769
|
this.interruptCause ??= "sibling-failure";
|
|
33770
|
+
const refused = isRefusedBeforeApplying(provisionError, logicalId);
|
|
33771
|
+
const physicalId = newResources[logicalId]?.physicalId ?? previousState?.physicalId;
|
|
33772
|
+
if (refused && change.changeType === "CREATE" && physicalId === void 0) throw provisionError;
|
|
33248
33773
|
failedOperations.push({
|
|
33249
33774
|
logicalId,
|
|
33250
33775
|
changeType: change.changeType,
|
|
33251
33776
|
resourceType: change.resourceType,
|
|
33252
33777
|
provisionedBy: newResources[logicalId]?.provisionedBy ?? previousState?.provisionedBy,
|
|
33253
33778
|
...previousState && { previousState },
|
|
33254
|
-
physicalId
|
|
33255
|
-
...!
|
|
33779
|
+
physicalId,
|
|
33780
|
+
...!refused && { attemptedProperties: this.attemptedResolvedProps.get(logicalId) }
|
|
33256
33781
|
});
|
|
33257
33782
|
throw provisionError;
|
|
33258
33783
|
}
|
|
@@ -33402,6 +33927,7 @@ async function executeDeployment(template, currentState, changes, dag, execution
|
|
|
33402
33927
|
this.logger.warn(`Failed to save partial state before rollback: ${saveError instanceof Error ? saveError.message : String(saveError)}`);
|
|
33403
33928
|
}
|
|
33404
33929
|
let autoRollbackClean = false;
|
|
33930
|
+
let autoRollbackJournaled = false;
|
|
33405
33931
|
let rollbackOrphans = [];
|
|
33406
33932
|
let rollbackSettledNested = /* @__PURE__ */ new Map();
|
|
33407
33933
|
if (error instanceof InterruptedError || isInterruptedWaitError(error) || this.interruptCause === "user") {
|
|
@@ -33414,9 +33940,16 @@ async function executeDeployment(template, currentState, changes, dag, execution
|
|
|
33414
33940
|
this.logger.warn("Deployment failed. --no-rollback is set, skipping rollback.");
|
|
33415
33941
|
this.logger.warn(safeMsg`Partial state has been saved. ${this.recoveryHint("Run 'cdkd deploy' to resume, 'cdkd rollback' to revert, or destroy to clean up.")}`);
|
|
33416
33942
|
} else {
|
|
33417
|
-
await this.writeRollbackJournalSegment(stackName, completedOperations, failedOperations, "auto-rollback-started", initialDeploy);
|
|
33943
|
+
autoRollbackJournaled = await this.writeRollbackJournalSegment(stackName, completedOperations, failedOperations, "auto-rollback-started", initialDeploy);
|
|
33418
33944
|
const rollbackResult = await this.performRollback(completedOperations, newResources, stackName, currentState);
|
|
33419
|
-
autoRollbackClean = rollbackResult.failures === 0;
|
|
33945
|
+
autoRollbackClean = autoRollbackJournaled && rollbackResult.failures === 0 && rollbackResult.skipped === 0;
|
|
33946
|
+
if (rollbackResult.failures === 0 && rollbackResult.skipped > 0) {
|
|
33947
|
+
this.logger.warn(safeMsg`The automatic rollback could not revert ${rollbackResult.skipped} operation(s) ` + `(see the warnings above; each is recorded as a ROLLBACK_RESOURCE_SKIPPED event).` + (autoRollbackJournaled ? ` The rollback journal keeps them.` : ""));
|
|
33948
|
+
if (autoRollbackJournaled && this.options.parentStackInfo === void 0 && failedOperations.some((op) => op.changeType !== "DELETE")) this.logger.warn(safeMsg`The record of the operation that failed is kept too. Revert it with: ${pasteableCommand("cdkd rollback", [{
|
|
33949
|
+
value: stackName,
|
|
33950
|
+
hole: "stack"
|
|
33951
|
+
}, { literal: "--revert-failed" }]).command}`);
|
|
33952
|
+
}
|
|
33420
33953
|
rollbackOrphans = rollbackResult.orphaned;
|
|
33421
33954
|
rollbackSettledNested = rollbackResult.settledNested;
|
|
33422
33955
|
}
|
|
@@ -33941,6 +34474,9 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
|
|
|
33941
34474
|
});
|
|
33942
34475
|
const parameterValues = await this.resolver.resolveParameters(template, this.options.parameters, { ...this.options.inheritedSecrets && hasMaskableValues(this.options.inheritedSecrets) && { inheritedSecrets: this.options.inheritedSecrets } });
|
|
33943
34476
|
this.logger.debug(`Resolved ${Object.keys(parameterValues).length} parameters: ${Object.keys(parameterValues).join(", ")}`);
|
|
34477
|
+
const backfillNoEchoValues = noEchoParameterValueSeed(template.Parameters, parameterValues, this.options.inheritedSecrets, this.options.parameters);
|
|
34478
|
+
this.fingerprintNoEchoValues = backfillNoEchoValues;
|
|
34479
|
+
let maskedFingerprintsBackfilled = backfillMaskedPropertyFingerprints(currentState.resources, template, backfillNoEchoValues) > 0;
|
|
33944
34480
|
const context = this.buildResolverContext({
|
|
33945
34481
|
template,
|
|
33946
34482
|
resources: currentState.resources,
|
|
@@ -33962,7 +34498,10 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
|
|
|
33962
34498
|
const effectiveTemplate = this.templateParser.filterResourcesByCondition(template, conditions);
|
|
33963
34499
|
const orphanCountBeforeAdoption = (currentState.orphans ?? []).length;
|
|
33964
34500
|
const orphanPlan = await this.adoptRollbackOrphans(currentState, effectiveTemplate);
|
|
33965
|
-
if (Object.keys(orphanPlan.adopted).length > 0)
|
|
34501
|
+
if (Object.keys(orphanPlan.adopted).length > 0) {
|
|
34502
|
+
this.stampReasonlessParameterRefusals(currentState.resources, template);
|
|
34503
|
+
if (backfillMaskedPropertyFingerprints(currentState.resources, template, backfillNoEchoValues) > 0) maskedFingerprintsBackfilled = true;
|
|
34504
|
+
}
|
|
33966
34505
|
const orphansChanged = Object.keys(orphanPlan.adopted).length > 0 || (currentState.orphans ?? []).length !== orphanCountBeforeAdoption;
|
|
33967
34506
|
const resourceTypes = new Set(Object.values(effectiveTemplate.Resources || {}).map((r) => r.Type).filter((type) => type !== "AWS::CDK::Metadata"));
|
|
33968
34507
|
this.providerRegistry.validateResourceTypes(resourceTypes);
|
|
@@ -34039,7 +34578,7 @@ async function doDeployWithPrefetch(stackName, template, createOnlyPrefetch) {
|
|
|
34039
34578
|
const skippedOutputsChanged = !skippedOutputsEqual(currentState.skippedOutputs, this.skippedOutputs);
|
|
34040
34579
|
const healedAttributesPending = this.hasUnpersistedHeals(currentState.resources);
|
|
34041
34580
|
const conditionVerdictsChanged = !conditionVerdictRecordsEqual(readRecordedConditionVerdicts(currentState), conditionVerdicts);
|
|
34042
|
-
if (observedRefresh || conditionVerdictsChanged || outputsChanged || exportSetChanged || skippedOutputsChanged || orphansChanged || healedAttributesPending) try {
|
|
34581
|
+
if (observedRefresh || maskedFingerprintsBackfilled || conditionVerdictsChanged || outputsChanged || exportSetChanged || skippedOutputsChanged || orphansChanged || healedAttributesPending) try {
|
|
34043
34582
|
const refreshedState = {
|
|
34044
34583
|
version: 10,
|
|
34045
34584
|
region: this.stackRegion,
|
|
@@ -34218,37 +34757,6 @@ function preparePropertiesForCcApi(resourceType, resolvedProps, logicalId) {
|
|
|
34218
34757
|
return applyDefaultNameForFallback(logicalId, resourceType, resolvedProps);
|
|
34219
34758
|
}
|
|
34220
34759
|
|
|
34221
|
-
//#endregion
|
|
34222
|
-
//#region src/deployment/deploy-engine/record-shape.ts
|
|
34223
|
-
function auditResolvedAssetReferences(logicalId, resourceType, resolvedProps) {
|
|
34224
|
-
const redirect = this.options.assetRedirect;
|
|
34225
|
-
if (!redirect) return;
|
|
34226
|
-
const findings = findUnrewrittenAssetReferences(resolvedProps, redirect);
|
|
34227
|
-
if (findings.length === 0) return;
|
|
34228
|
-
const detail = findings.map((f) => ` - ${f.path}: still references '${f.source}'`).join("\n");
|
|
34229
|
-
throw new ProvisioningError(`Unrewritten asset reference on '${logicalId}' (${resourceType}): this region uses cdkd-owned asset storage, but the following resolved properties still point at the CDK bootstrap storage that 'cdk gc' may garbage-collect:\n${detail}\nThis is a template shape cdkd's asset-reference rewrite did not cover — deploying it would split-brain the stack (assets in cdkd storage, properties reading the CDK bucket). Please report this at https://github.com/go-to-k/cdkd/issues with the property shape. Workaround: deploy with --use-cdk-bootstrap-assets to pin the legacy destinations for this app.`, resourceType, logicalId);
|
|
34230
|
-
}
|
|
34231
|
-
function extractAllDependencies(template, logicalId) {
|
|
34232
|
-
const resource = template?.Resources?.[logicalId];
|
|
34233
|
-
if (!resource) return void 0;
|
|
34234
|
-
const parser = new TemplateParser();
|
|
34235
|
-
const parameterNames = new Set(Object.keys(template?.Parameters ?? {}));
|
|
34236
|
-
const deps = [...parser.extractDependencies(resource)].filter((dep) => !parameterNames.has(dep));
|
|
34237
|
-
return deps.length > 0 ? deps : void 0;
|
|
34238
|
-
}
|
|
34239
|
-
function propertiesToRecord(desiredProperties, result, resourceType, provisionedBy) {
|
|
34240
|
-
if (result.effectiveProperties) return provisionedBy === "sdk" ? withoutSilentDropProperties(resourceType, result.effectiveProperties) : result.effectiveProperties;
|
|
34241
|
-
const written = provisionedBy === "sdk" ? withoutSilentDropProperties(resourceType, desiredProperties) : desiredProperties;
|
|
34242
|
-
return markSameGenerationBag(written);
|
|
34243
|
-
}
|
|
34244
|
-
function extractTemplateAttributes(template, logicalId) {
|
|
34245
|
-
const resource = template?.Resources?.[logicalId];
|
|
34246
|
-
return {
|
|
34247
|
-
deletionPolicy: resource?.DeletionPolicy,
|
|
34248
|
-
updateReplacePolicy: resource?.UpdateReplacePolicy
|
|
34249
|
-
};
|
|
34250
|
-
}
|
|
34251
|
-
|
|
34252
34760
|
//#endregion
|
|
34253
34761
|
//#region src/analyzer/implicit-delete-deps.ts
|
|
34254
34762
|
const IMPLICIT_DELETE_DEPENDENCIES = {
|
|
@@ -34427,6 +34935,7 @@ var DeployEngine = class {
|
|
|
34427
34935
|
noEchoAttributeResources = /* @__PURE__ */ new Map();
|
|
34428
34936
|
perResourceTemplateProps = /* @__PURE__ */ new Map();
|
|
34429
34937
|
perResourceResolvedType = /* @__PURE__ */ new Map();
|
|
34938
|
+
fingerprintNoEchoValues = void 0;
|
|
34430
34939
|
outputSecrets = /* @__PURE__ */ new Map();
|
|
34431
34940
|
outputsPassSecretMaps = [];
|
|
34432
34941
|
outputsTemplateSource = Object.create(null);
|
|
@@ -34474,6 +34983,7 @@ var DeployEngine = class {
|
|
|
34474
34983
|
this.noEchoAttributeResources = /* @__PURE__ */ new Map();
|
|
34475
34984
|
this.perResourceTemplateProps = /* @__PURE__ */ new Map();
|
|
34476
34985
|
this.perResourceResolvedType = /* @__PURE__ */ new Map();
|
|
34986
|
+
this.fingerprintNoEchoValues = void 0;
|
|
34477
34987
|
this.attemptedResolvedProps = /* @__PURE__ */ new Map();
|
|
34478
34988
|
this.liveTaskLabels = /* @__PURE__ */ new Map();
|
|
34479
34989
|
this.outputSecrets = /* @__PURE__ */ new Map();
|
|
@@ -34500,13 +35010,15 @@ var DeployEngine = class {
|
|
|
34500
35010
|
for (const [logicalId, record] of Object.entries(state.resources)) {
|
|
34501
35011
|
const secrets = this.perResourceSecrets.get(logicalId);
|
|
34502
35012
|
const templateProps = this.perResourceTemplateProps.get(logicalId);
|
|
34503
|
-
|
|
35013
|
+
const scrubbed = scrubResourceRecord(this.withHealedAttributes(logicalId, record), secrets ?? /* @__PURE__ */ new Map(), templateProps);
|
|
35014
|
+
resources[logicalId] = withMaskedPropertyFingerprints(scrubbed, record.properties, templateProps, secrets, this.fingerprintNoEchoValues);
|
|
34504
35015
|
}
|
|
34505
35016
|
const orphans = state.orphans?.map((entry) => {
|
|
34506
35017
|
const sameResource = this.perResourceResolvedType.get(entry.logicalId) === entry.state.resourceType;
|
|
35018
|
+
const orphanTemplateProps = sameResource ? this.perResourceTemplateProps.get(entry.logicalId) : void 0;
|
|
34507
35019
|
return {
|
|
34508
35020
|
...entry,
|
|
34509
|
-
state: scrubResourceRecord(entry.state, (sameResource ? this.perResourceSecrets.get(entry.logicalId) : void 0) ?? /* @__PURE__ */ new Map(), sameResource ? this.
|
|
35021
|
+
state: withMaskedPropertyFingerprints(scrubResourceRecord(entry.state, (sameResource ? this.perResourceSecrets.get(entry.logicalId) : void 0) ?? /* @__PURE__ */ new Map(), orphanTemplateProps), entry.state.properties, orphanTemplateProps, sameResource ? this.perResourceSecrets.get(entry.logicalId) : void 0, sameResource ? this.fingerprintNoEchoValues : void 0)
|
|
34510
35022
|
};
|
|
34511
35023
|
});
|
|
34512
35024
|
const outputs = this.redactOutputs(state.outputs);
|
|
@@ -34679,5 +35191,5 @@ DeployEngine.prototype.replacementNameOrigin = replacementNameOrigin;
|
|
|
34679
35191
|
DeployEngine.prototype.orphanedNameCollisionAdvice = orphanedNameCollisionAdvice;
|
|
34680
35192
|
|
|
34681
35193
|
//#endregion
|
|
34682
|
-
export { nestedChildStackName as $, shouldRetainResource as $a, markAuxiliaryFailure as $i, isUnboundTemplateParameter as $n, ssmResolvedValueType as $o, refuseMalformedOutputsForDestroy as $r, DBPROXY_TAGS_WHAT as $t, deleteTableWithIndexBusyRetry as A, carriesSecretMask as Aa, MULTI_REGION_RECREATE_BLOCKED_TYPES as Ai, DiffCalculator as An, partitionSensitiveEnv as Ao, malformedLocalResourcesWarning as Ar, extractDeploymentEventError as At, gray as B, isLockInfoExpired as Ba, coerceCfnInteger as Bi, isIamRoleArn as Bn, Synthesizer as Bo, malformedResourcePropertiesWarning as Br, collectDeclaredOutputNames as Bt, beginDeleteWait as C, scrubResourceRecord as Ca, findActionableSilentDrops as Ci, markWaitAbandoned as Cn, describeDockerFailure as Co, isReadableOrphanRecord as Cr, resolveAssemblyPath as Cs, isFinalSnapshotError as Ct, resolveDynamoDbDeleteBudgetMs as D, isUncertifiedBaselineMaskPosition as Da, hasNoCloudControlHandlers as Di, observeThenDisableInstanceApiTermination as Dn, getDockerCmd as Do, malformedExportSourceWarning as Dr, updatePartialReason as Dt, resolveDynamoDbDeleteBudgetClock as E, dynamicReferenceTokens as Ea, NO_CC_HANDLERS_REASON as Ei, isTerminationProtectionPropagationError as En, formatDockerLoginError as Eo, malformedExportNamesWarning as Er, updatePartialMessage as Et, isTruthyCfnBoolean as F, STATE_SOURCED_READBACK_RULES as Fa, coerceWarmThroughput as Fi, regionLessSecretName as Fn, warnFinchArgvExposure as Fo, malformedOrphansWarning as Fr, secretBearingStateKeyWarning as Ft, namesAnyDeclaredParameter as G, hasReadableExportSet as Ga, replayWarn as Gi, envCredentialsAreAssumedRole as Gn, resolveAutoAssetStorage as Go, refuseDivergentRecordRegionForDestroy as Gr, DEFAULT_RESOURCE_WARN_AFTER_MS as Gt, red as H, rebuildClientForBucketRegion as Ha, configIntegerRefusal as Hi, AWS_CREDENTIAL_ENV_KEYS as Hn, getDefaultStateBucketName as Ho, malformedScrubResourceEntriesRefusalMessage as Hr, isExportAliasCollision as Ht, formatResourceLine as I, TEMPLATE_SOURCED_RULES as Ia, isWarmThroughputDecrease as Ii, applyRoleArnIfSet as In, escapeRegExp$2 as Io, malformedOutputsRefusalMessage as Ir, secretSafeKeyDisplay as It, makeSiblingClaimReader as J, importableOutputKeys as Ja, requireConfigString as Ji, s3BucketDomainName as Jn, resolveStateBucketWithDefault as Jo, refuseMalformedOrphanRecordsForDestroy as Jr, earliestDefined as Jt, reachableDivergentParameters as K, hasReasonlessBaselineRefusal as Ka, requireConfigArray as Ki, isSettledInstanceState as Kn, resolveCaptureObservedState as Ko, refuseMalformedNestedChildOutputs as Kr, maskingRetryLogger as Kt, createReadOnlyAttributeHealerFactory as L, WHOLE_DYNAMIC_REFERENCE_PATTERN as La, toCfnInteger as Li, assertFlagRoleArn as Ln, stripControlChars as Lo, malformedOutputsWarning as Lr, exportNameSecretExposure as Lt, ECR_AUTO_DELETE_IMAGES_TAG as M, recordMaskOnlyValuesIn as Ma, isStatefulRecreateTargetSync as Mi, IntrinsicFunctionResolver as Mn, redactedDockerCause as Mo, malformedOrphanRecordsWarning as Mr, displayTextOrWithheld as Mt, S3_AUTO_DELETE_OBJECTS_TAG as N, STATE_SOURCED_BASELINE_RULES as Na, renderStatefulReason as Ni, classifyReplaySecretRegion as Nn, runDockerForeground as No, malformedOrphanRowsKeptWarning as Nr, exportAliasCollisionScrubWarning as Nt, DELETE_INDEX_WAIT_PROCEED_NOTE as O, pathCrossesDottedKey as Oa, findReplacedReadersOfRecreateTargets as Oi, reDisableInstanceApiTermination as On, isDockerClientEnvKey as Oo, malformedLocalOutputsWarning as Or, DeploymentEventsReader as Ot, hasCdkAutoDeleteTag as P, STATE_SOURCED_CROSS_GENERATION_RULES as Pa, WARM_THROUGHPUT_MEMBERS as Pi, producerRegionsFromState as Pn, runDockerStreaming as Po, malformedOrphansRefusalMessage as Pr, secretBearing as Pt, getNestedRevertRun as Q, orphansCarriedFrom as Qa, withRetry as Qi, coerceParameterTypedValue as Qn, warnDeprecatedNoPrefixCliFlag as Qo, refuseMalformedOutputs as Qr, acquireIdempotencyToken as Qt, bold as R, isSingleDynamicReferenceToken as Ra, toFiniteNumber as Ri, assertSendableRoleArn as Rn, AssetManifestLoader as Ro, malformedRenderedContainersWarning as Rr, isWholeDynamicReferenceValue as Rt, DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS as S, redactSecretsForState as Sa, unreadableResourceEntries as Si, isWaitAbandonedError as Sn, describeDockerExecFailure as So, hasReadableResources as Sr, renderAssemblyPathEscape as Ss, effectiveDeletionPolicy as St, deleteBudgetKey as T, DYNAMIC_REFERENCE_TOKEN_SCAN as Ta, withoutSilentDropProperties as Ti, ec2InstanceProtectionSite as Tn, finchSecretArgvRefusal as To, malformedDriftResourcePropertiesWarning as Tr, resolveBucketRegion as Ts, unsupportedFinalSnapshotError as Tt, yellow as U, describeRegionValueKind as Ua, configStringRefusal as Ui, applyCallerIdentityCredentials as Un, getLegacyStateBucketName as Uo, malformedStateRefusalMessage as Ur, bindingSkippedOutputs as Ut, green as V, S3StateBackend as Va, configBooleanRefusal as Vi, sendableAssumeRoleCommand as Vn, synthesisStatusMessage as Vo, malformedResourcesWarning as Vr, collectPublishedOutputNames as Vt, computeParameterTaint as W, exportNamesCarriedFrom as Wa, readConfigString as Wi, callerEnvCredentials as Wn, resolveApp as Wo, previewableOrphanRecords as Wr, DEFAULT_RESOURCE_TIMEOUT_MS as Wt, NESTED_PENDING_PARENT_REASON as X, isReadableBag as Xa, templateResourceTypes as Xi, s3BucketRegionalDomainName as Xn, resolveUseCdkBootstrapAssets as Xo, refuseMalformedOrphansForDestroy as Xr, reserveStackCreateToken as Xt, planOrphanAdoption as Y, importableOutputs as Ya, prefetchCreateOnlyPropertyPaths as Yi, s3BucketDualStackDomainName as Yn, resolveStateBucketWithDefaultAndSource as Yo, refuseMalformedOrphans as Yr, ledgerForStack as Yt, dropSettledNestedJournals as Z, orphansAfterRollback as Za, describeTypeWithThrottleRetry as Zi, s3BucketWebsiteUrl as Zn, stateBucketExistenceConfirmed as Zo, refuseMalformedOrphansForOrphan as Zr, withCreateTokenLedger as Zt, PROTECTION_PROPERTY_BY_TYPE as _, createSecretMasker as _a, repairMalformedResourceEntriesForReadOnly as _i, unchangedBehindSecretReference as _n, validateContainerRepoName as _o, UNREADABLE_RESOURCES_MAP_ROW as _r, nullPrototypeRecord as _s, ATOMIC_FINAL_SNAPSHOT_TYPES as _t, conditionInputsFrom as a, liveMatchesUnresolvedTokenFrame as aa, refuseMalformedResourceEntriesForImportSave as ai, tagPlanWarning as an, createAssetRedirectResolver as ao, isNamelessDynamicReferenceError as ar, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as as, replayFailedOperations as at, readProtection as b, maskSecretsInText as ba, unpreviewableOrphanRecords as bi, deleteSkipReason as bn, resolveDockerContextDirectory as bo, hasReadableOrphans as br, describeFileReadFailure as bs, ccRoutedFinalSnapshotError as bt, isSecretBearingReferenceString as c, carryLogOnlyValues as ca, refuseMalformedResourcePropertiesForExport as ci, CC_BROKEN_REASON as cn, AssetModeResolver as co, cfnRefValueFromPhysicalId as cr, PARTITION_TABLE as cs, destroyStackRecordsView as ct, makeCanonicalizePropertiesFn as d, literalSplitDelimitersOf as da, refuseMalformedResourcesForDestroy as di, wouldReturnToSdkProvider as dn, ensureAssetStorage as do, parseWebACLArn as dr, AssemblyReader as ds, planFailedOps as dt, canonicalLambdaFunctionName as ea, refuseMalformedResourceAttributesForOrphan as ei, DBPROXY_TAG_OPTIONS as en, AssetPublisher as eo, parameterTypeMayLoseSecretIdentity as er, CFN_TEMPLATE_BODY_LIMIT as es, revertNestedChildFromJournal as et, getPriorAttempts as f, logOnlyValueCount as fa, refuseMalformedResourcesForExport as fi, isCustomResourceType as fn, getBootstrapMarkerKey as fo, withSharedDrainBudget as fr, StageLoadError as fs, planRollback as ft, recordedProtectionNote as g, shareLogOnlyValues as ga, repairMalformedOutputsForReadOnly as gi, ccProtectionRegistryTypes as gn, validateAssetBucketName as go, UNREADABLE_ORPHANS_CONTAINER_ROW as gr, hasPlainPrototype$1 as gs, inheritProducerRegions as gt, recordedProtectionEvidence as h, recordLogOnlyValue as ha, repairMalformedOrphansForReadOnly as hi, slowCcOperationTimeoutMs as hn, readBootstrapMarkerBody as ho, STATE_RESOURCES_MALFORMED as hr, hasOwnKey as hs, getCurrentProducerRegions as ht, conditionFingerprint as i, TemplateParser as ia, refuseMalformedResourceEntriesForImport as ii, tagMapAsList as in, buildAssetRedirectMap as io, carriesFetchableDynamicReference as ir, uploadCfnTemplate as is, withNestedStackContext as it, waitForIndexesSettled as j, recordMaskOnlyValue as ja, isStatefulRecreateTargetForReplace as ji, INTRINSIC_KEYS as jn, redactDockerArgvValues as jo, malformedOrphanRecordsRefusalMessage as jr, WITHHELD_NAME_DISPLAY as jt, INDEX_SETTLE_POLL_INTERVAL_MS as k, identityKeyFor as ka, shownType as ki, reEnableInstanceApiTermination as kn, isMalformedEnvKey as ko, malformedLocalResourceEntriesWarning as kr, DeploymentEventsStore as kt, keptWholeReasonText as l, createUnionSecretMasker as la, refuseMalformedResourcePropertiesForOrphan as li, ProviderRegistry as ln, BOOTSTRAP_MARKER_PREFIX as lo, isStalePlaceholderArnAttribute as lr, canonicalizeRegion as ls, getStackRecords as lt, withResourceDeadline as m, recordLogOnlyParameterValue as ma, repairMalformedOrphanRecordsForReadOnly as mi, CloudControlProvider as mn, parseBootstrapMarker as mo, STATE_REGION_DIVERGED as mr, defineOwnKey as ms, findNestedStackTypeChanges as mt, IMPLICIT_DELETE_DEPENDENCIES as n, sameLambdaFunctionAddress as na, refuseMalformedResourceEntriesForDeploy as ni, readTagList as nn, stringifyValue as no, isImpossibleEmptyStoredAttribute as nr, MIGRATE_TMP_PREFIX as ns, withNestedRevertRun as nt, parentSuppliedValues as o, inheritNestedStackParameterAssociations as oa, refuseMalformedResourceEntriesForOrphan as oi, definedAttributes as on, loadPublishableAssetManifest as oo, keepsSecretReferenceToken as or, decodeListingKey as os, replayRollback as ot, markRefusedBeforeApplying as p, printingCorpusOf as pa, refuseMalformedState as pi, getCurrentResourceSecrets as pn, isCrossRegionRedirect as po, JSON_LISTING_HOLE_VALUE as pr, processStackMessages as ps, resolveReplacementOldType as pt, resourcesNamingDeclaredParameter as q, hasUnverifiableParameterRefusal as qa, requireConfigObject as qi, s3BucketArn as qn, resolveSkipPrefix as qo, refuseMalformedOrphanRecords as qr, RollbackInlinePolicyWriters as qt, computeImplicitDeleteEdges as r, DagBuilder as ra, refuseMalformedResourceEntriesForDestroy as ri, refuseMalformedDesiredTags as rn, WorkGraph as ro, carriesDynamicReference as rr, findLargeInlineResources as rs, getCurrentNestedStackContext as rt, readRecordedConditionVerdicts as s, recordNestedStackParameterExpressions as sa, refuseMalformedResourcePropertiesForDrift as si, stringifyIfAssigned as sn, rewriteTemplateAssetReferences as so, AWS_NO_VALUE as sr, expectedOwnerParam as ss, awaitInFlightSibling as st, DeployEngine as t, lambdaFunctionNameForMask as ta, refuseMalformedResourceEntries as ti, planTagDiff as tn, isSensitiveAttributeName as to, getAccountInfo as tr, CFN_TEMPLATE_URL_LIMIT as ts, revertedNestedRowIds as tt, mergeNoChangeOutputs as u, hasMaskableValues as ua, refuseMalformedResourcesForDeploy as ui, ccBrokenReason as un, assertAssetBucketRegion as uo, refStateLookupFromResource as ur, derivePartitionAndUrlSuffix as us, withStackRecords as ut, isProtectionValueActive as v, errorCauseChain as va, repairMalformedResourcePropertiesForReadOnly as vi, UNSPECIFIED_SKIP_REASON as vn, buildDenyExternalAccessPolicy as vo, deployRefusesOrphanRowsReason as vr, ownValue as vs, PRE_DELETE_SNAPSHOT_TYPES as vt, compensateRemovedDeletionProtection as w, recoverMaskedOutput as wa, findSilentDropProperties as wi, disableInstanceApiTermination as wn, dockerSpawnEnvWithSensitive as wo, isReadableResourceEntry as wr, clearBucketRegionCache as ws, refusesFinalSnapshot as wt, DynamoDBGlobalTableProvider as x, markSameGenerationBag as xa, unreadableOrphanRecords as xi, withIndeterminateGuard as xn, describeDockerCapturedOutput as xo, hasReadableOutputs as xr, displayAssemblyPath as xs, createPreDeleteFinalSnapshot as xt, perType as y, maskSecretsInError as ya, repairMalformedResourcesForReadOnly as yi, deleteIndeterminateGuards as yn, buildDockerImage as yo, displayLogicalId as yr, absoluteAssemblyPathEscape as ys, buildFinalSnapshotIdentifier as yt, cyan as z, LockManager as za, coerceCfnBoolean as zi, explicitRoleArnOrThrow as zn, getDockerImageBySourceHash as zo, malformedResourceEntriesWarning as zr, noEchoParameterValueSeed as zt };
|
|
34683
|
-
//# sourceMappingURL=deploy-engine-
|
|
35194
|
+
export { nestedChildStackName as $, hasReadableExportSet as $a, requireConfigObject as $i, s3BucketDomainName as $n, resolveAutoAssetStorage as $o, refuseMalformedOrphanRecordsForDestroy as $r, earliestDefined as $t, ECR_AUTO_DELETE_IMAGES_TAG as A, recoverMaskedOutput as Aa, unwrittenCreateOnlyReplacement as Ai, ec2InstanceProtectionSite as An, describeDockerExecFailure as Ao, malformedDriftResourcePropertiesWarning as Ar, renderAssemblyPathEscape as As, redactedDeleteAddressSkip as At, gray as B, STATE_SOURCED_BASELINE_RULES as Ba, WARM_THROUGHPUT_MEMBERS as Bi, regionLessSecretName as Bn, redactedDockerCause as Bo, malformedOrphansWarning as Br, secretBearingStateKeyWarning as Bt, deleteBudgetKey as C, createSecretMasker as Ca, repairMalformedResourcePropertiesForReadOnly as Ci, UNSPECIFIED_SKIP_REASON as Cn, readBootstrapMarkerBody as Co, deployRefusesOrphanRowsReason as Cr, hasOwnKey as Cs, effectiveDeletionPolicy as Ct, INDEX_SETTLE_POLL_INTERVAL_MS as D, markSameGenerationBag as Da, unreadableResourceEntries as Di, isWaitAbandonedError as Dn, buildDockerImage as Do, hasReadableResources as Dr, absoluteAssemblyPathEscape as Ds, REDACTED_DELETE_ADDRESS_SKIP_REASON as Dt, DELETE_INDEX_WAIT_PROCEED_NOTE as E, maskSecretsInText as Ea, unreadableOrphanRecords as Ei, withIndeterminateGuard as En, buildDenyExternalAccessPolicy as Eo, hasReadableOutputs as Er, ownValue as Es, unsupportedFinalSnapshotError as Et, markRefusedBeforeApplying as F, identityKeyFor as Fa, shownType as Fi, DiffCalculator as Fn, getDockerCmd as Fo, malformedLocalResourcesWarning as Fr, extractDeploymentEventError as Ft, namesAnyDeclaredParameter as G, isSingleDynamicReferenceToken as Ga, coerceCfnBoolean as Gi, isIamRoleArn as Gn, stripControlChars as Go, malformedResourcePropertiesWarning as Gr, collectDeclaredOutputNames as Gt, red as H, STATE_SOURCED_READBACK_RULES as Ha, isWarmThroughputDecrease as Hi, assertFlagRoleArn as Hn, runDockerStreaming as Ho, malformedOutputsWarning as Hr, exportNameSecretExposure as Ht, formatResourceLine as I, carriesSecretMask as Ia, MULTI_REGION_RECREATE_BLOCKED_TYPES as Ii, INTRINSIC_KEYS as In, isDockerClientEnvKey as Io, malformedOrphanRecordsRefusalMessage as Ir, WITHHELD_NAME_DISPLAY as It, makeSiblingClaimReader as J, S3StateBackend as Ja, configIntegerRefusal as Ji, applyCallerIdentityCredentials as Jn, Synthesizer as Jo, malformedStateRefusalMessage as Jr, bindingSkippedOutputs as Jt, reachableDivergentParameters as K, LockManager as Ka, coerceCfnInteger as Ki, sendableAssumeRoleCommand as Kn, AssetManifestLoader as Ko, malformedResourcesWarning as Kr, collectPublishedOutputNames as Kt, createReadOnlyAttributeHealerFactory as L, recordMaskOnlyValue as La, isStatefulRecreateTargetForReplace as Li, IntrinsicFunctionResolver as Ln, isMalformedEnvKey as Lo, malformedOrphanRecordsWarning as Lr, displayTextOrWithheld as Lt, hasCdkAutoDeleteTag as M, dynamicReferenceTokens as Ma, NO_CC_HANDLERS_REASON as Mi, observeThenDisableInstanceApiTermination as Mn, dockerSpawnEnvWithSensitive as Mo, malformedExportSourceWarning as Mr, clearBucketRegionCache as Ms, updatePartialReason as Mt, isTruthyCfnBoolean as N, isUncertifiedBaselineMaskPosition as Na, hasNoCloudControlHandlers as Ni, reDisableInstanceApiTermination as Nn, finchSecretArgvRefusal as No, malformedLocalOutputsWarning as Nr, resolveBucketRegion as Ns, DeploymentEventsReader as Nt, deleteTableWithIndexBusyRetry as O, redactSecretsForState as Oa, findActionableSilentDrops as Oi, markWaitAbandoned as On, resolveDockerContextDirectory as Oo, isReadableOrphanRecord as Or, describeFileReadFailure as Os, isRedactedRecordedValue as Ot, getPriorAttempts as P, pathCrossesDottedKey as Pa, findReplacedReadersOfRecreateTargets as Pi, reEnableInstanceApiTermination as Pn, formatDockerLoginError as Po, malformedLocalResourceEntriesWarning as Pr, DeploymentEventsStore as Pt, getNestedRevertRun as Q, exportNamesCarriedFrom as Qa, requireConfigArray as Qi, s3BucketArn as Qn, resolveApp as Qo, refuseMalformedOrphanRecords as Qr, RollbackInlinePolicyWriters as Qt, bold as R, recordMaskOnlyValuesIn as Ra, isStatefulRecreateTargetSync as Ri, classifyReplaySecretRegion as Rn, partitionSensitiveEnv as Ro, malformedOrphanRowsKeptWarning as Rr, exportAliasCollisionScrubWarning as Rt, compensateRemovedDeletionProtection as S, shareLogOnlyValues as Sa, repairMalformedResourceEntriesForReadOnly as Si, unchangedBehindSecretReference as Sn, parseBootstrapMarker as So, UNREADABLE_RESOURCES_MAP_ROW as Sr, defineOwnKey as Ss, createPreDeleteFinalSnapshot as St, resolveDynamoDbDeleteBudgetMs as T, maskSecretsInError as Ta, unpreviewableOrphanRecords as Ti, deleteSkipReason as Tn, validateContainerRepoName as To, hasReadableOrphans as Tr, nullPrototypeRecord as Ts, refusesFinalSnapshot as Tt, yellow as U, TEMPLATE_SOURCED_RULES as Ua, toCfnInteger as Ui, assertSendableRoleArn as Un, warnFinchArgvExposure as Uo, malformedRenderedContainersWarning as Ur, isWholeDynamicReferenceValue as Ut, green as V, STATE_SOURCED_CROSS_GENERATION_RULES as Va, coerceWarmThroughput as Vi, applyRoleArnIfSet as Vn, runDockerForeground as Vo, malformedOutputsRefusalMessage as Vr, secretSafeKeyDisplay as Vt, computeParameterTaint as W, WHOLE_DYNAMIC_REFERENCE_PATTERN as Wa, toFiniteNumber as Wi, explicitRoleArnOrThrow as Wn, escapeRegExp$2 as Wo, malformedResourceEntriesWarning as Wr, noEchoParameterValueSeed as Wt, NESTED_PENDING_PARENT_REASON as X, acceptedCreateOnlyDropsOf as Xa, readConfigString as Xi, envCredentialsAreAssumedRole as Xn, getDefaultStateBucketName as Xo, refuseDivergentRecordRegionForDestroy as Xr, DEFAULT_RESOURCE_WARN_AFTER_MS as Xt, planOrphanAdoption as Y, rebuildClientForBucketRegion as Ya, configStringRefusal as Yi, callerEnvCredentials as Yn, synthesisStatusMessage as Yo, previewableOrphanRecords as Yr, DEFAULT_RESOURCE_TIMEOUT_MS as Yt, dropSettledNestedJournals as Z, describeRegionValueKind as Za, replayWarn as Zi, isSettledInstanceState as Zn, getLegacyStateBucketName as Zo, refuseMalformedNestedChildOutputs as Zr, maskingRetryLogger as Zt, perType as _, literalSplitDelimitersOf as _a, refuseMalformedResourcesForExport as _i, isCustomResourceType as _n, BOOTSTRAP_MARKER_PREFIX as _o, withSharedDrainBudget as _r, canonicalizeRegion as _s, inheritProducerRegions as _t, conditionInputsFrom as a, markAuxiliaryFailure as aa, refuseMalformedResourceAttributesForOrphan as ai, DBPROXY_TAG_OPTIONS as an, orphansAfterRollback as ao, parameterTypeMayLoseSecretIdentity as ar, stateBucketExistenceConfirmed as as, replayFailedOperations as at, DYNAMODB_DELETE_MIN_RESOURCE_TIMEOUT_MS as b, recordLogOnlyParameterValue as ba, repairMalformedOrphansForReadOnly as bi, slowCcOperationTimeoutMs as bn, getBootstrapMarkerKey as bo, STATE_RESOURCES_MALFORMED as br, StageLoadError as bs, buildFinalSnapshotIdentifier as bt, isSecretBearingReferenceString as c, sameLambdaFunctionAddress as ca, refuseMalformedResourceEntriesForDestroy as ci, refuseMalformedDesiredTags as cn, AssetPublisher as co, carriesDynamicReference as cr, CFN_TEMPLATE_BODY_LIMIT as cs, destroyStackRecordsView as ct, makeCanonicalizePropertiesFn as d, liveMatchesUnresolvedTokenFrame as da, refuseMalformedResourceEntriesForOrphan as di, definedAttributes as dn, WorkGraph as do, keepsSecretReferenceToken as dr, findLargeInlineResources as ds, recordAfterRollbackUpdate as dt, requireConfigString as ea, refuseMalformedOrphans as ei, ledgerForStack as en, hasReasonlessBaselineRefusal as eo, s3BucketDualStackDomainName as er, resolveCaptureObservedState as es, revertNestedChildFromJournal as et, withResourceDeadline as f, inheritNestedStackParameterAssociations as fa, refuseMalformedResourcePropertiesForDrift as fi, stringifyIfAssigned as fn, buildAssetRedirectMap as fo, AWS_NO_VALUE as fr, uploadCfnTemplate as fs, planFailedOps as ft, isProtectionValueActive as g, hasMaskableValues as ga, refuseMalformedResourcesForDestroy as gi, wouldReturnToSdkProvider as gn, AssetModeResolver as go, parseWebACLArn as gr, PARTITION_TABLE as gs, getCurrentProducerRegions as gt, PROTECTION_PROPERTY_BY_TYPE as h, createUnionSecretMasker as ha, refuseMalformedResourcesForDeploy as hi, ccBrokenReason as hn, rewriteTemplateAssetReferences as ho, refStateLookupFromResource as hr, expectedOwnerParam as hs, findNestedStackTypeChanges as ht, conditionFingerprint as i, withRetry as ia, refuseMalformedOutputsForDestroy as ii, DBPROXY_TAGS_WHAT as in, isReadableBag as io, isUnboundTemplateParameter as ir, resolveUseCdkBootstrapAssets as is, withNestedStackContext as it, S3_AUTO_DELETE_OBJECTS_TAG as j, DYNAMIC_REFERENCE_TOKEN_SCAN as ja, withoutSilentDropProperties as ji, isTerminationProtectionPropagationError as jn, describeDockerFailure as jo, malformedExportNamesWarning as jr, resolveAssemblyPath as js, updatePartialMessage as jt, waitForIndexesSettled as k, scrubResourceRecord as ka, findSilentDropProperties as ki, disableInstanceApiTermination as kn, describeDockerCapturedOutput as ko, isReadableResourceEntry as kr, displayAssemblyPath as ks, redactedDeleteAddressFields as kt, keptWholeReasonText as l, DagBuilder as la, refuseMalformedResourceEntriesForImport as li, tagMapAsList as ln, isSensitiveAttributeName as lo, carriesFetchableDynamicReference as lr, CFN_TEMPLATE_URL_LIMIT as ls, getStackRecords as lt, recordedProtectionNote as m, carryLogOnlyValues as ma, refuseMalformedResourcePropertiesForOrphan as mi, ProviderRegistry as mn, loadPublishableAssetManifest as mo, isStalePlaceholderArnAttribute as mr, decodeListingKey as ms, resolveReplacementOldType as mt, IMPLICIT_DELETE_DEPENDENCIES as n, templateResourceTypes as na, refuseMalformedOrphansForOrphan as ni, withCreateTokenLedger as nn, importableOutputKeys as no, s3BucketWebsiteUrl as nr, resolveStateBucketWithDefault as ns, withNestedRevertRun as nt, parentSuppliedValues as o, canonicalLambdaFunctionName as oa, refuseMalformedResourceEntries as oi, planTagDiff as on, orphansCarriedFrom as oo, getAccountInfo as or, warnDeprecatedNoPrefixCliFlag as os, replayRollback as ot, recordedProtectionEvidence as p, recordNestedStackParameterExpressions as pa, refuseMalformedResourcePropertiesForExport as pi, CC_BROKEN_REASON as pn, createAssetRedirectResolver as po, cfnRefValueFromPhysicalId as pr, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as ps, planRollback as pt, resourcesNamingDeclaredParameter as q, isLockInfoExpired as qa, configBooleanRefusal as qi, AWS_CREDENTIAL_ENV_KEYS as qn, getDockerImageBySourceHash as qo, malformedScrubResourceEntriesRefusalMessage as qr, isExportAliasCollision as qt, computeImplicitDeleteEdges as r, describeTypeWithThrottleRetry as ra, refuseMalformedOutputs as ri, acquireIdempotencyToken as rn, importableOutputs as ro, coerceParameterTypedValue as rr, resolveStateBucketWithDefaultAndSource as rs, getCurrentNestedStackContext as rt, readRecordedConditionVerdicts as s, lambdaFunctionNameForMask as sa, refuseMalformedResourceEntriesForDeploy as si, readTagList as sn, shouldRetainResource as so, isImpossibleEmptyStoredAttribute as sr, ssmResolvedValueType as ss, awaitInFlightSibling as st, DeployEngine as t, prefetchCreateOnlyPropertyPaths as ta, refuseMalformedOrphansForDestroy as ti, reserveStackCreateToken as tn, hasUnverifiableParameterRefusal as to, s3BucketRegionalDomainName as tr, resolveSkipPrefix as ts, revertedNestedRowIds as tt, mergeNoChangeOutputs as u, TemplateParser as ua, refuseMalformedResourceEntriesForImportSave as ui, tagPlanWarning as un, stringifyValue as uo, isNamelessDynamicReferenceError as ur, MIGRATE_TMP_PREFIX as us, withStackRecords as ut, readProtection as v, logOnlyValueCount as va, refuseMalformedState as vi, getCurrentResourceSecrets as vn, assertAssetBucketRegion as vo, JSON_LISTING_HOLE_VALUE as vr, derivePartitionAndUrlSuffix as vs, ATOMIC_FINAL_SNAPSHOT_TYPES as vt, resolveDynamoDbDeleteBudgetClock as w, errorCauseChain as wa, repairMalformedResourcesForReadOnly as wi, deleteIndeterminateGuards as wn, validateAssetBucketName as wo, displayLogicalId as wr, hasPlainPrototype$1 as ws, isFinalSnapshotError as wt, beginDeleteWait as x, recordLogOnlyValue as xa, repairMalformedOutputsForReadOnly as xi, ccProtectionRegistryTypes as xn, isCrossRegionRedirect as xo, UNREADABLE_ORPHANS_CONTAINER_ROW as xr, processStackMessages as xs, ccRoutedFinalSnapshotError as xt, DynamoDBGlobalTableProvider as y, printingCorpusOf as ya, repairMalformedOrphanRecordsForReadOnly as yi, CloudControlProvider as yn, ensureAssetStorage as yo, STATE_REGION_DIVERGED as yr, AssemblyReader as ys, PRE_DELETE_SNAPSHOT_TYPES as yt, cyan as z, recordNoEchoAttributeValues as za, renderStatefulReason as zi, producerRegionsFromState as zn, redactDockerArgvValues as zo, malformedOrphansRefusalMessage as zr, secretBearing as zt };
|
|
35195
|
+
//# sourceMappingURL=deploy-engine-RCOtmm4V.js.map
|