@go-to-k/cdkd 0.284.11 → 0.284.13
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-C9OlqHee.js → asg-provider-H-Dl8qng.js} +2 -2
- package/dist/{asg-provider-C9OlqHee.js.map → asg-provider-H-Dl8qng.js.map} +1 -1
- package/dist/cli.js +231 -45
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-K5kHzsB-.js → deploy-engine-4Eh5Qqlm.js} +328 -35
- package/dist/deploy-engine-4Eh5Qqlm.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-K5kHzsB-.js.map +0 -1
|
@@ -11095,6 +11095,159 @@ function maskSecretsInText(text, secrets) {
|
|
|
11095
11095
|
return text.replace(regex, "***");
|
|
11096
11096
|
}
|
|
11097
11097
|
/**
|
|
11098
|
+
* How deep {@link maskSecretsInError} follows a `cause` chain. A CYCLE is
|
|
11099
|
+
* already handled by the visited-set, so this bounds only a pathologically long
|
|
11100
|
+
* chain; the same bounded-walk shape `extractDeploymentEventError` (depth 10)
|
|
11101
|
+
* and the retry classifiers (depth 5) use. A link BEYOND the cap keeps its
|
|
11102
|
+
* original, UNMASKED message, and the last cloned link points at it.
|
|
11103
|
+
*/
|
|
11104
|
+
const ERROR_CAUSE_MASK_MAX_DEPTH = 20;
|
|
11105
|
+
/**
|
|
11106
|
+
* The `cause` chain of `root`, root first, stopping at the first non-`Error`
|
|
11107
|
+
* link, at a link already visited (so a cycle terminates instead of hanging),
|
|
11108
|
+
* or at {@link ERROR_CAUSE_MASK_MAX_DEPTH}.
|
|
11109
|
+
*/
|
|
11110
|
+
function errorCauseChain(root) {
|
|
11111
|
+
const chain = [];
|
|
11112
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11113
|
+
let current = root;
|
|
11114
|
+
while (current instanceof Error && !seen.has(current) && chain.length < ERROR_CAUSE_MASK_MAX_DEPTH) {
|
|
11115
|
+
seen.add(current);
|
|
11116
|
+
chain.push(current);
|
|
11117
|
+
current = current.cause;
|
|
11118
|
+
}
|
|
11119
|
+
return chain;
|
|
11120
|
+
}
|
|
11121
|
+
/**
|
|
11122
|
+
* Return `error` with {@link maskSecretsInText} applied to the `message` AND the
|
|
11123
|
+
* `stack` of every link in its `cause` chain, with everything else about each
|
|
11124
|
+
* link preserved (issue [#2038](https://github.com/go-to-k/cdkd/issues/2038)
|
|
11125
|
+
* review).
|
|
11126
|
+
*
|
|
11127
|
+
* Two bounds on that "every link", both stated here because the PUBLIC contract
|
|
11128
|
+
* is what a caller reads and neither is visible from the call site:
|
|
11129
|
+
* - The walk stops at {@link ERROR_CAUSE_MASK_MAX_DEPTH}. A chain longer than
|
|
11130
|
+
* that keeps its remaining links' ORIGINAL, UNMASKED messages, and the last
|
|
11131
|
+
* cloned link points straight at them.
|
|
11132
|
+
* - A `cause` that is not an `Error` (a string, a plain object) is carried
|
|
11133
|
+
* through VERBATIM and is never masked — the walk has nothing to clone. No
|
|
11134
|
+
* cdkd or AWS SDK site constructs one today, so this is a documented residual
|
|
11135
|
+
* rather than a reachable leak, but a caller attaching arbitrary data as a
|
|
11136
|
+
* `cause` must mask it itself. `src/cli/index.ts`'s `console.error` renders
|
|
11137
|
+
* such a cause via `util.inspect`, so it WOULD reach the terminal.
|
|
11138
|
+
*
|
|
11139
|
+
* **Why an error and not just its text.** `formatError` (`src/utils/error-handler.ts`)
|
|
11140
|
+
* renders a `CdkdError`'s CAUSE as `Caused by: <cause.message>`, and `handleError`
|
|
11141
|
+
* logs that at `error` level for any failure that escapes a command — so a raw
|
|
11142
|
+
* provider error attached as a `ProvisioningError`'s cause reaches the terminal
|
|
11143
|
+
* verbatim, at DEFAULT verbosity, even when every log site that INTERPOLATED
|
|
11144
|
+
* the message masked it. Masking the string at each log site cannot close that:
|
|
11145
|
+
* the sink reads the error OBJECT.
|
|
11146
|
+
*
|
|
11147
|
+
* `formatError` is not the only such sink, and the second one is what makes the
|
|
11148
|
+
* CHAIN argument below concrete rather than hypothetical: `src/cli/index.ts`'s
|
|
11149
|
+
* top-level `main().catch(...)` does `console.error('Fatal error:', error)`,
|
|
11150
|
+
* which renders the whole object through `util.inspect` — every `[cause]` link
|
|
11151
|
+
* AND every link's `stack`. Measured: an outer `Error('top')` wrapping
|
|
11152
|
+
* `Error("Value 'hunter2' failed")` prints as
|
|
11153
|
+
* `Error: top ... { [cause]: Error: Value 'hunter2' failed ... }`. So a
|
|
11154
|
+
* multi-level sink exists TODAY, and it is why `stack` is masked below rather
|
|
11155
|
+
* than merely preserved.
|
|
11156
|
+
*
|
|
11157
|
+
* **Why the whole CHAIN and not just the top link.** Masking only `error.message`
|
|
11158
|
+
* looks sufficient because `formatError` renders one level — and it is not, in
|
|
11159
|
+
* two ways that a top-level-only fix gets exactly backwards. A provider that
|
|
11160
|
+
* wraps an AWS failure in a generic sentence (`new Error('the call failed',
|
|
11161
|
+
* { cause: awsError })`) leaves the plaintext ONE link down, where the
|
|
11162
|
+
* identity-return below then reports "nothing to mask" and hands back an object
|
|
11163
|
+
* still carrying it — the function's own contract says the returned error is
|
|
11164
|
+
* safe to render, and every later reader believes it. And `formatError`
|
|
11165
|
+
* rendering a single level is an implementation detail: one edit there (walking
|
|
11166
|
+
* the chain is the obvious improvement) re-opens the hole with nothing failing.
|
|
11167
|
+
* So the invariant is about the OBJECT, not about today's renderer.
|
|
11168
|
+
*
|
|
11169
|
+
* **Why a clone rather than assigning to `error.message`.** The argument is an
|
|
11170
|
+
* error cdkd did not create — usually the AWS SDK's — and mutating a caller's
|
|
11171
|
+
* object is visible to every other holder of it, including a retry loop that
|
|
11172
|
+
* may still classify it. Each link's clone copies the prototype and EVERY own
|
|
11173
|
+
* property descriptor, symbols included, so the three things that read a cause
|
|
11174
|
+
* chain keep working: `isMarkedNonRetryable` (a non-enumerable `Symbol.for`
|
|
11175
|
+
* marker), `extractDeploymentEventError` / `isThrottlingError` /
|
|
11176
|
+
* `isTransientServerError` (`$metadata`, `Code`, `name`), and the chain itself.
|
|
11177
|
+
* `Object.assign` would have dropped the marker, which is why the descriptors
|
|
11178
|
+
* form is used.
|
|
11179
|
+
*
|
|
11180
|
+
* `message`, `cause` and `stack` are the three descriptors deliberately NOT
|
|
11181
|
+
* copied through: each is re-defined per link, and copying a NON-CONFIGURABLE
|
|
11182
|
+
* original would make that re-definition throw. `cause` is rewired to the CLONE
|
|
11183
|
+
* of whatever it pointed at, in a second pass over the already-built clone map —
|
|
11184
|
+
* which is what makes a cyclic chain terminate rather than recurse. A `cause`
|
|
11185
|
+
* that is not an `Error` (a string, a plain object) keeps its original
|
|
11186
|
+
* descriptor verbatim.
|
|
11187
|
+
*
|
|
11188
|
+
* **Why `stack` is re-defined as DATA rather than copied.** V8 installs `stack`
|
|
11189
|
+
* as an own ACCESSOR whose getter reads a slot the engine attaches to an error
|
|
11190
|
+
* IT created, so copying that descriptor onto an `Object.create` clone yields a
|
|
11191
|
+
* getter with nothing behind it and `clone.stack` reads `undefined` (measured).
|
|
11192
|
+
* That is not a leak today — the clone is only ever reached as a `cause`, and
|
|
11193
|
+
* `handleError` prints the TOP-level error's stack — but this function is
|
|
11194
|
+
* exported and generic, so a future top-level caller would get back an error
|
|
11195
|
+
* with no trace at all. The clone therefore carries a masked COPY of the
|
|
11196
|
+
* original's stack text, which both preserves the trace and closes the sink the
|
|
11197
|
+
* copy would otherwise open: a stack's first line embeds the message, so an
|
|
11198
|
+
* unmasked stack re-exposes exactly the plaintext the `message` mask removed —
|
|
11199
|
+
* and `util.inspect` prints it. An original with no readable string `stack`
|
|
11200
|
+
* (not an engine-created error) simply gets no own `stack`, as before.
|
|
11201
|
+
*
|
|
11202
|
+
* Returns the ORIGINAL object by identity when NOTHING ANYWHERE IN THE CHAIN
|
|
11203
|
+
* changed, so a non-secret failure keeps referential equality and the common
|
|
11204
|
+
* path allocates nothing.
|
|
11205
|
+
*/
|
|
11206
|
+
function maskSecretsInError(error, secrets) {
|
|
11207
|
+
if (secrets.size === 0 || !(error instanceof Error)) return error;
|
|
11208
|
+
const chain = errorCauseChain(error);
|
|
11209
|
+
const maskedMessages = chain.map((link) => maskSecretsInText(link.message, secrets));
|
|
11210
|
+
if (maskedMessages.every((masked, i) => masked === chain[i].message)) return error;
|
|
11211
|
+
const clones = /* @__PURE__ */ new Map();
|
|
11212
|
+
for (const [i, original] of chain.entries()) {
|
|
11213
|
+
const descriptors = {};
|
|
11214
|
+
for (const key of Reflect.ownKeys(original)) {
|
|
11215
|
+
if (key === "message" || key === "cause" || key === "stack") continue;
|
|
11216
|
+
const descriptor = Object.getOwnPropertyDescriptor(original, key);
|
|
11217
|
+
if (descriptor) descriptors[key] = descriptor;
|
|
11218
|
+
}
|
|
11219
|
+
const clone = Object.create(Object.getPrototypeOf(original), descriptors);
|
|
11220
|
+
Object.defineProperty(clone, "message", {
|
|
11221
|
+
value: maskedMessages[i],
|
|
11222
|
+
writable: true,
|
|
11223
|
+
enumerable: false,
|
|
11224
|
+
configurable: true
|
|
11225
|
+
});
|
|
11226
|
+
const originalStack = original.stack;
|
|
11227
|
+
if (typeof originalStack === "string") Object.defineProperty(clone, "stack", {
|
|
11228
|
+
value: maskSecretsInText(originalStack, secrets),
|
|
11229
|
+
writable: true,
|
|
11230
|
+
enumerable: false,
|
|
11231
|
+
configurable: true
|
|
11232
|
+
});
|
|
11233
|
+
clones.set(original, clone);
|
|
11234
|
+
}
|
|
11235
|
+
for (const original of chain) {
|
|
11236
|
+
const causeDescriptor = Object.getOwnPropertyDescriptor(original, "cause");
|
|
11237
|
+
if (!causeDescriptor) continue;
|
|
11238
|
+
const clone = clones.get(original);
|
|
11239
|
+
const causeValue = original.cause;
|
|
11240
|
+
const replacement = causeValue instanceof Error ? clones.get(causeValue) : void 0;
|
|
11241
|
+
Object.defineProperty(clone, "cause", replacement ? {
|
|
11242
|
+
value: replacement,
|
|
11243
|
+
writable: true,
|
|
11244
|
+
enumerable: causeDescriptor.enumerable === true,
|
|
11245
|
+
configurable: true
|
|
11246
|
+
} : causeDescriptor);
|
|
11247
|
+
}
|
|
11248
|
+
return clones.get(error);
|
|
11249
|
+
}
|
|
11250
|
+
/**
|
|
11098
11251
|
* Bind a {@link RecordedSecretValues} bag into a {@link SecretMasker} for a
|
|
11099
11252
|
* caller to hand to a provider.
|
|
11100
11253
|
*
|
|
@@ -16053,7 +16206,7 @@ var CloudControlProvider = class {
|
|
|
16053
16206
|
if (context?.finalSnapshotIdentifier !== void 0) throw new ProvisioningError(`${logicalId} (${resourceType}) requires a final snapshot (DeletionPolicy: Snapshot), but the Cloud Control API delete route has no final-snapshot parameter. Re-run with --skip-final-snapshot after snapshotting manually, or retain the resource.`, resourceType, logicalId, physicalId);
|
|
16054
16207
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
16055
16208
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
16056
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
16209
|
+
const { ASGProvider } = await import("./asg-provider-H-Dl8qng.js").then((n) => n.n);
|
|
16057
16210
|
return await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);
|
|
16058
16211
|
}
|
|
16059
16212
|
const isProtectedEc2Instance = context?.removeProtection === true && resourceType === "AWS::EC2::Instance";
|
|
@@ -22877,6 +23030,22 @@ function computeImplicitDeleteEdges(resources) {
|
|
|
22877
23030
|
return edges;
|
|
22878
23031
|
}
|
|
22879
23032
|
|
|
23033
|
+
//#endregion
|
|
23034
|
+
//#region src/deployment/masking-retry-logger.ts
|
|
23035
|
+
/**
|
|
23036
|
+
* Bind `logger` to `secrets`, masking every line the retry loop emits.
|
|
23037
|
+
*
|
|
23038
|
+
* No-op when the caller resolved no secret ({@link maskSecretsInText} returns
|
|
23039
|
+
* the text unchanged for an empty bag), so a non-secret resource's output is
|
|
23040
|
+
* byte-identical to threading the raw logger.
|
|
23041
|
+
*/
|
|
23042
|
+
function maskingRetryLogger(logger, secrets) {
|
|
23043
|
+
return {
|
|
23044
|
+
debug: (msg) => logger.debug(maskSecretsInText(msg, secrets)),
|
|
23045
|
+
warn: (msg) => logger.warn(maskSecretsInText(msg, secrets))
|
|
23046
|
+
};
|
|
23047
|
+
}
|
|
23048
|
+
|
|
22880
23049
|
//#endregion
|
|
22881
23050
|
//#region src/deployment/resource-deadline.ts
|
|
22882
23051
|
/**
|
|
@@ -23156,6 +23325,38 @@ function replayingStateCreateContext(secrets) {
|
|
|
23156
23325
|
};
|
|
23157
23326
|
}
|
|
23158
23327
|
/**
|
|
23328
|
+
* The {@link DeploymentEventError} a failed replay records, with this op's
|
|
23329
|
+
* re-resolved secrets masked out of its message (issue
|
|
23330
|
+
* [#2031](https://github.com/go-to-k/cdkd/issues/2031) acceptance item 2).
|
|
23331
|
+
*
|
|
23332
|
+
* `extractDeploymentEventError` copies `err.message` VERBATIM, and the events
|
|
23333
|
+
* store is a DURABLE sink — `deployments/{runId}.jsonl` in S3 outlives the
|
|
23334
|
+
* terminal the `logger.warn` beside it scrolls past, and `cdkd events` replays
|
|
23335
|
+
* it later. The standalone `cdkd rollback` command wires
|
|
23336
|
+
* `recordEvent: (e) => eventRecorder.record(e)` (`src/cli/commands/rollback.ts`)
|
|
23337
|
+
* with NO masking of its own, so without this the plaintext the terminal line
|
|
23338
|
+
* masks is persisted one statement later.
|
|
23339
|
+
*
|
|
23340
|
+
* The in-process caller (`DeployEngine.rollbackExecutorContext`) routes through
|
|
23341
|
+
* `maskSecretsInEvent`, but that masks with the DEPLOY's `perResourceSecrets`
|
|
23342
|
+
* for the resource — a different bag from the one this replay re-resolved from
|
|
23343
|
+
* the JOURNAL, which can name a different secret version or a reference the
|
|
23344
|
+
* deploy never resolved. Masking here is what makes both callers equal, and
|
|
23345
|
+
* double-masking is a no-op (the mask is not a key of either bag).
|
|
23346
|
+
*
|
|
23347
|
+
* `name` / `awsErrorCode` / `requestId` are deliberately left alone: they are
|
|
23348
|
+
* AWS-authored identifiers, not message text, and #2038 traced all three as
|
|
23349
|
+
* non-sensitive.
|
|
23350
|
+
*/
|
|
23351
|
+
function maskedRollbackEventError(error, secrets) {
|
|
23352
|
+
const extracted = extractDeploymentEventError(error);
|
|
23353
|
+
if (secrets.size === 0) return extracted;
|
|
23354
|
+
return {
|
|
23355
|
+
...extracted,
|
|
23356
|
+
message: maskSecretsInText(extracted.message, secrets)
|
|
23357
|
+
};
|
|
23358
|
+
}
|
|
23359
|
+
/**
|
|
23159
23360
|
* Which provisioning layer a delete must be judged against: the CURRENT
|
|
23160
23361
|
* state record wins (it is what state says AWS holds right now), with the
|
|
23161
23362
|
* journaled op's routing as the legacy-state fallback. Shared by both
|
|
@@ -23514,10 +23715,10 @@ function redactRollbackRecord(record, secrets, journaledProps) {
|
|
|
23514
23715
|
properties: redactSecretsForState(record.properties, secrets, journaledProps, STATE_DERIVED_RULES)
|
|
23515
23716
|
}, secrets);
|
|
23516
23717
|
}
|
|
23517
|
-
async function updateWithRollbackRetry(provider, args, logicalId, logger, isInterrupted) {
|
|
23718
|
+
async function updateWithRollbackRetry(provider, args, logicalId, logger, isInterrupted, secrets) {
|
|
23518
23719
|
if (provider.disableOuterRetry) return await provider.update(...args);
|
|
23519
23720
|
return await withRetry(() => provider.update(...args), logicalId, {
|
|
23520
|
-
logger,
|
|
23721
|
+
logger: maskingRetryLogger(logger, secrets),
|
|
23521
23722
|
...isInterrupted && {
|
|
23522
23723
|
isInterrupted,
|
|
23523
23724
|
onInterrupted: () => /* @__PURE__ */ new Error("Rollback interrupted while retrying a resource update")
|
|
@@ -23572,6 +23773,21 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
23572
23773
|
const action = classifyRollbackOp(op, stateResources, orphanLogicalIds);
|
|
23573
23774
|
const { logger } = ctx;
|
|
23574
23775
|
/**
|
|
23776
|
+
* This op's `plaintext -> {{resolve:...}}expression` bag, filled by
|
|
23777
|
+
* {@link resolveReplayProps} on whichever arm runs (issues
|
|
23778
|
+
* [#2038](https://github.com/go-to-k/cdkd/issues/2038) /
|
|
23779
|
+
* [#2031](https://github.com/go-to-k/cdkd/issues/2031)).
|
|
23780
|
+
*
|
|
23781
|
+
* HOISTED above the `try` rather than declared per arm, which is what the
|
|
23782
|
+
* two arms used to do: the shared catch below logs the thrown AWS message and
|
|
23783
|
+
* persists it to the events store, and it cannot see a binding scoped to the
|
|
23784
|
+
* arm that threw. Exactly one arm runs per call, so a single per-op bag is
|
|
23785
|
+
* equivalent to the per-arm ones for every existing reader (`secrets.size`
|
|
23786
|
+
* stays 0 on the arms that resolve nothing, so `redactRollbackRecord` and the
|
|
23787
|
+
* maskers keep their identity behavior).
|
|
23788
|
+
*/
|
|
23789
|
+
const secrets = /* @__PURE__ */ new Map();
|
|
23790
|
+
/**
|
|
23575
23791
|
* The route a CREATE-rollback arm resolved for this op (issue #1366) —
|
|
23576
23792
|
* hoisted so the shared catch's ROLLBACK_RESOURCE_FAILED reports the route
|
|
23577
23793
|
* the delete was going to take, which is the one a refusal is about. Stays
|
|
@@ -23695,7 +23911,6 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
23695
23911
|
case "reverse-replacement": {
|
|
23696
23912
|
const current = stateResources[op.logicalId];
|
|
23697
23913
|
const prev = op.previousState;
|
|
23698
|
-
const secrets = /* @__PURE__ */ new Map();
|
|
23699
23914
|
const resolvedPrevProps = await resolveReplayProps(prev.properties, resolver, secrets) ?? {};
|
|
23700
23915
|
logger.info(` Rollback: Reversing replacement of ${op.logicalId} (${op.resourceType}) — re-creating the old resource and deleting the new one`);
|
|
23701
23916
|
if (STATEFUL_TYPES.has(op.resourceType)) logger.warn(` ⚠ ${op.logicalId} (${op.resourceType}) is a stateful type — the old physical resource's data was destroyed by the replacement and CANNOT be recovered; the re-created resource starts empty.`);
|
|
@@ -23712,7 +23927,7 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
23712
23927
|
try {
|
|
23713
23928
|
createResult = await withRetry(() => createProvider.create(op.logicalId, op.resourceType, { ...resolvedPrevProps }, replayingStateCreateContext(secrets)), op.logicalId, {
|
|
23714
23929
|
...RECREATE_RETRY_SCHEDULE,
|
|
23715
|
-
logger,
|
|
23930
|
+
logger: maskingRetryLogger(logger, secrets),
|
|
23716
23931
|
...isInterrupted && {
|
|
23717
23932
|
isInterrupted,
|
|
23718
23933
|
onInterrupted: () => /* @__PURE__ */ new Error("Rollback interrupted while waiting out the name cooldown")
|
|
@@ -23735,7 +23950,7 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
23735
23950
|
try {
|
|
23736
23951
|
createResult = await withRetry(() => createProvider.create(op.logicalId, op.resourceType, { ...resolvedPrevProps }, replayingStateCreateContext(secrets)), op.logicalId, {
|
|
23737
23952
|
...RECREATE_RETRY_SCHEDULE,
|
|
23738
|
-
logger,
|
|
23953
|
+
logger: maskingRetryLogger(logger, secrets),
|
|
23739
23954
|
...isInterrupted && {
|
|
23740
23955
|
isInterrupted,
|
|
23741
23956
|
onInterrupted: () => /* @__PURE__ */ new Error("Rollback interrupted while waiting for the old name to release")
|
|
@@ -23743,7 +23958,7 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
23743
23958
|
isRetryable: isRecreateRetryableError
|
|
23744
23959
|
});
|
|
23745
23960
|
} catch (recreateError) {
|
|
23746
|
-
throw new Error(`Failed to re-create the old ${op.logicalId} after the new resource (${current.physicalId}) was already deleted: ${recreateError instanceof Error ? recreateError.message : String(recreateError)}. The resource is now absent — fix forward with 'cdkd deploy'
|
|
23961
|
+
throw new Error(maskSecretsInText(`Failed to re-create the old ${op.logicalId} after the new resource (${current.physicalId}) was already deleted: ${recreateError instanceof Error ? recreateError.message : String(recreateError)}. The resource is now absent — fix forward with 'cdkd deploy'.`, secrets));
|
|
23747
23962
|
}
|
|
23748
23963
|
}
|
|
23749
23964
|
const adoptedLiveNewResource = !deletedNewFirst && createResult.physicalId === current.physicalId;
|
|
@@ -23766,7 +23981,7 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
23766
23981
|
...finalSnapshotIdentifier !== void 0 && { finalSnapshotIdentifier }
|
|
23767
23982
|
}), op.logicalId, current.physicalId, "while deleting the new resource after re-creating the old one");
|
|
23768
23983
|
} catch (deleteError) {
|
|
23769
|
-
logger.warn(` Rollback: old ${op.logicalId} re-created, but deleting the new resource (${current.physicalId}) failed: ${deleteError instanceof Error ? deleteError.message : String(deleteError)}. Delete it manually — it is no longer tracked in state
|
|
23984
|
+
logger.warn(maskSecretsInText(` Rollback: old ${op.logicalId} re-created, but deleting the new resource (${current.physicalId}) failed: ${deleteError instanceof Error ? deleteError.message : String(deleteError)}. Delete it manually — it is no longer tracked in state.`, secrets));
|
|
23770
23985
|
result.warnings++;
|
|
23771
23986
|
}
|
|
23772
23987
|
logger.info(adoptedLiveNewResource ? ` Rollback: ${op.logicalId} adopted the live resource (${createResult.physicalId}) — replacement NOT fully reversed (name-idempotent Create API)` : ` Rollback: ${op.logicalId} replacement reversed (old resource re-created as ${createResult.physicalId})`);
|
|
@@ -23798,7 +24013,6 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
23798
24013
|
resourceType: op.resourceType,
|
|
23799
24014
|
provisionedBy: op.provisionedBy
|
|
23800
24015
|
});
|
|
23801
|
-
const secrets = /* @__PURE__ */ new Map();
|
|
23802
24016
|
const desiredProps = await resolveReplayProps(previousState.properties, resolver, secrets);
|
|
23803
24017
|
const currentProps = await resolveReplayProps(current.properties, resolver, secrets);
|
|
23804
24018
|
const revertResult = await updateWithRollbackRetry(provider, [
|
|
@@ -23808,10 +24022,10 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
23808
24022
|
desiredProps ?? {},
|
|
23809
24023
|
currentProps ?? {},
|
|
23810
24024
|
{ maskSecrets: createSecretMasker(secrets) }
|
|
23811
|
-
], op.logicalId, logger, isInterrupted);
|
|
24025
|
+
], op.logicalId, logger, isInterrupted, secrets);
|
|
23812
24026
|
stateResources[op.logicalId] = redactRollbackRecord(recordAfterRollbackUpdate(previousState, revertResult), secrets, previousState.properties);
|
|
23813
24027
|
const rollbackPartial = updatePartialReason(revertResult);
|
|
23814
|
-
if (rollbackPartial !== void 0) logger.warn(` Rollback: ${op.logicalId} restored, ${updatePartialMessage(rollbackPartial)}
|
|
24028
|
+
if (rollbackPartial !== void 0) logger.warn(maskSecretsInText(` Rollback: ${op.logicalId} restored, ${updatePartialMessage(rollbackPartial)}`, secrets));
|
|
23815
24029
|
else logger.info(` Rollback: ${op.logicalId} restored successfully`);
|
|
23816
24030
|
await afterOp?.(op.logicalId);
|
|
23817
24031
|
ctx.recordEvent?.({
|
|
@@ -23821,13 +24035,13 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
23821
24035
|
logicalId: op.logicalId,
|
|
23822
24036
|
resourceType: op.resourceType,
|
|
23823
24037
|
...op.provisionedBy && { provisionedBy: op.provisionedBy },
|
|
23824
|
-
...rollbackPartial !== void 0 && { reason: rollbackPartial }
|
|
24038
|
+
...rollbackPartial !== void 0 && { reason: maskSecretsInText(rollbackPartial, secrets) }
|
|
23825
24039
|
});
|
|
23826
24040
|
return;
|
|
23827
24041
|
}
|
|
23828
24042
|
}
|
|
23829
24043
|
} catch (rollbackError) {
|
|
23830
|
-
logger.warn(` Rollback failed for ${op.logicalId} (${op.changeType}): ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}
|
|
24044
|
+
logger.warn(maskSecretsInText(` Rollback failed for ${op.logicalId} (${op.changeType}): ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`, secrets));
|
|
23831
24045
|
logger.warn(" Continuing with remaining rollback operations...");
|
|
23832
24046
|
result.failures++;
|
|
23833
24047
|
const failedRoute = createRollbackRoute ?? op.provisionedBy;
|
|
@@ -23838,7 +24052,7 @@ async function replaySingle(op, stateResources, stackName, ctx, resolver, orphan
|
|
|
23838
24052
|
logicalId: op.logicalId,
|
|
23839
24053
|
resourceType: op.resourceType,
|
|
23840
24054
|
...failedRoute && { provisionedBy: failedRoute },
|
|
23841
|
-
error:
|
|
24055
|
+
error: maskedRollbackEventError(rollbackError, secrets)
|
|
23842
24056
|
});
|
|
23843
24057
|
}
|
|
23844
24058
|
}
|
|
@@ -23875,6 +24089,15 @@ async function replayFailedOperations(failedOps, stateResources, stackName, ctx,
|
|
|
23875
24089
|
}
|
|
23876
24090
|
const op = failedOps[i];
|
|
23877
24091
|
const action = classifyFailedOp(op, stateResources);
|
|
24092
|
+
/**
|
|
24093
|
+
* This op's re-resolved secret bag — the twin of `replaySingle`'s, and
|
|
24094
|
+
* hoisted above this iteration's `try` for the same reason (issues #2038 /
|
|
24095
|
+
* #2031): the shared catch below logs the thrown AWS message and persists it
|
|
24096
|
+
* to the events store, and could not see a binding scoped to the arm that
|
|
24097
|
+
* threw. Re-created per ITERATION, so one op's secrets can never mask
|
|
24098
|
+
* another's text.
|
|
24099
|
+
*/
|
|
24100
|
+
const secrets = /* @__PURE__ */ new Map();
|
|
23878
24101
|
let createRollbackRoute;
|
|
23879
24102
|
try {
|
|
23880
24103
|
switch (action) {
|
|
@@ -23942,7 +24165,6 @@ async function replayFailedOperations(failedOps, stateResources, stackName, ctx,
|
|
|
23942
24165
|
resourceType: op.resourceType,
|
|
23943
24166
|
provisionedBy: op.provisionedBy ?? current.provisionedBy
|
|
23944
24167
|
});
|
|
23945
|
-
const secrets = /* @__PURE__ */ new Map();
|
|
23946
24168
|
const desiredProps = await resolveReplayProps(prev.properties, resolver, secrets);
|
|
23947
24169
|
const attemptedProps = await resolveReplayProps(op.attemptedProperties ?? current.properties, resolver, secrets);
|
|
23948
24170
|
const revertFailedResult = await updateWithRollbackRetry(provider, [
|
|
@@ -23952,10 +24174,10 @@ async function replayFailedOperations(failedOps, stateResources, stackName, ctx,
|
|
|
23952
24174
|
desiredProps ?? {},
|
|
23953
24175
|
attemptedProps ?? {},
|
|
23954
24176
|
{ maskSecrets: createSecretMasker(secrets) }
|
|
23955
|
-
], op.logicalId, logger, options.isInterrupted);
|
|
24177
|
+
], op.logicalId, logger, options.isInterrupted, secrets);
|
|
23956
24178
|
stateResources[op.logicalId] = redactRollbackRecord(recordAfterRollbackUpdate(prev, revertFailedResult), secrets, prev.properties);
|
|
23957
24179
|
const revertFailedPartial = updatePartialReason(revertFailedResult);
|
|
23958
|
-
if (revertFailedPartial !== void 0) logger.warn(` Rollback: ${op.logicalId} reverted, ${updatePartialMessage(revertFailedPartial)}
|
|
24180
|
+
if (revertFailedPartial !== void 0) logger.warn(maskSecretsInText(` Rollback: ${op.logicalId} reverted, ${updatePartialMessage(revertFailedPartial)}`, secrets));
|
|
23959
24181
|
else logger.info(` Rollback: ${op.logicalId} reverted successfully`);
|
|
23960
24182
|
await options.afterOp?.(op.logicalId);
|
|
23961
24183
|
ctx.recordEvent?.({
|
|
@@ -23965,13 +24187,13 @@ async function replayFailedOperations(failedOps, stateResources, stackName, ctx,
|
|
|
23965
24187
|
logicalId: op.logicalId,
|
|
23966
24188
|
resourceType: op.resourceType,
|
|
23967
24189
|
...op.provisionedBy && { provisionedBy: op.provisionedBy },
|
|
23968
|
-
...revertFailedPartial !== void 0 && { reason: revertFailedPartial }
|
|
24190
|
+
...revertFailedPartial !== void 0 && { reason: maskSecretsInText(revertFailedPartial, secrets) }
|
|
23969
24191
|
});
|
|
23970
24192
|
break;
|
|
23971
24193
|
}
|
|
23972
24194
|
}
|
|
23973
24195
|
} catch (revertError) {
|
|
23974
|
-
logger.warn(` Rollback failed for failed-op ${op.logicalId} (${op.changeType}): ${revertError instanceof Error ? revertError.message : String(revertError)}
|
|
24196
|
+
logger.warn(maskSecretsInText(` Rollback failed for failed-op ${op.logicalId} (${op.changeType}): ${revertError instanceof Error ? revertError.message : String(revertError)}`, secrets));
|
|
23975
24197
|
result.failures++;
|
|
23976
24198
|
pending.add(op);
|
|
23977
24199
|
const failedRoute = createRollbackRoute ?? op.provisionedBy;
|
|
@@ -23982,7 +24204,7 @@ async function replayFailedOperations(failedOps, stateResources, stackName, ctx,
|
|
|
23982
24204
|
logicalId: op.logicalId,
|
|
23983
24205
|
resourceType: op.resourceType,
|
|
23984
24206
|
...failedRoute && { provisionedBy: failedRoute },
|
|
23985
|
-
error:
|
|
24207
|
+
error: maskedRollbackEventError(revertError, secrets)
|
|
23986
24208
|
});
|
|
23987
24209
|
}
|
|
23988
24210
|
}
|
|
@@ -24082,7 +24304,7 @@ const FLUSH_INTERVAL_MS = 2e3;
|
|
|
24082
24304
|
const FLUSH_EVENT_THRESHOLD = 50;
|
|
24083
24305
|
/** Build-time cdkd version, with a dev fallback for non-built contexts. */
|
|
24084
24306
|
function getCdkdVersion() {
|
|
24085
|
-
return "0.284.
|
|
24307
|
+
return "0.284.13";
|
|
24086
24308
|
}
|
|
24087
24309
|
/**
|
|
24088
24310
|
* Generate a time-sortable unique run id, e.g.
|
|
@@ -24547,6 +24769,14 @@ var DeploymentEventsReader = class {
|
|
|
24547
24769
|
//#endregion
|
|
24548
24770
|
//#region src/deployment/deploy-engine.ts
|
|
24549
24771
|
/**
|
|
24772
|
+
* The bag a resource with no recorded secret masks against (issue #2038).
|
|
24773
|
+
* Shared so the "no entry" path allocates nothing and — more usefully — so
|
|
24774
|
+
* every masking site takes the SAME branch: `maskSecretsInText` /
|
|
24775
|
+
* `maskSecretsInError` both return their input unchanged for an empty bag, so
|
|
24776
|
+
* an absent entry and an empty one cannot behave differently. Never written to.
|
|
24777
|
+
*/
|
|
24778
|
+
const EMPTY_SECRETS = /* @__PURE__ */ new Map();
|
|
24779
|
+
/**
|
|
24550
24780
|
* Default per-resource warn threshold: warn the user when a single
|
|
24551
24781
|
* resource has been in flight for 5 minutes. Most CC API resources
|
|
24552
24782
|
* complete in under a minute; 5m is the agreed elbow.
|
|
@@ -25750,7 +25980,7 @@ var DeployEngine = class {
|
|
|
25750
25980
|
} catch (error) {
|
|
25751
25981
|
renderer.removeTask(logicalId);
|
|
25752
25982
|
const message = error instanceof Error ? error.message : String(error);
|
|
25753
|
-
this.logger.error(`Failed to ${change.changeType.toLowerCase()} ${logicalId}: ${message}`);
|
|
25983
|
+
this.logger.error(this.maskForResource(logicalId, `Failed to ${change.changeType.toLowerCase()} ${logicalId}: ${message}`));
|
|
25754
25984
|
this.recordEvent({
|
|
25755
25985
|
eventType: "RESOURCE_FAILED",
|
|
25756
25986
|
stackName,
|
|
@@ -25761,7 +25991,7 @@ var DeployEngine = class {
|
|
|
25761
25991
|
durationMs: Date.now() - resourceStartedAt,
|
|
25762
25992
|
error: extractDeploymentEventError(error)
|
|
25763
25993
|
});
|
|
25764
|
-
throw new ProvisioningError(`Failed to ${change.changeType.toLowerCase()} resource ${logicalId}`, resourceType, logicalId, stateResources[logicalId]?.physicalId, error instanceof Error ? error : void 0);
|
|
25994
|
+
throw new ProvisioningError(`Failed to ${change.changeType.toLowerCase()} resource ${logicalId}`, resourceType, logicalId, stateResources[logicalId]?.physicalId, error instanceof Error ? maskSecretsInError(error, this.perResourceSecrets.get(logicalId) ?? EMPTY_SECRETS) : void 0);
|
|
25765
25995
|
} finally {
|
|
25766
25996
|
renderer.removeTask(logicalId);
|
|
25767
25997
|
}
|
|
@@ -25850,7 +26080,8 @@ var DeployEngine = class {
|
|
|
25850
26080
|
* #960 follow-up) and the name-idempotent same-id guard (issue #1238) so
|
|
25851
26081
|
* the two --replace escape hatches cannot drift apart.
|
|
25852
26082
|
*/
|
|
25853
|
-
async replaceDeleteFirstAndRecreate(logicalId, resourceType, currentResource, oldDeleteProvider, replaceProvider, replaceProps,
|
|
26083
|
+
async replaceDeleteFirstAndRecreate(logicalId, resourceType, currentResource, oldDeleteProvider, replaceProvider, replaceProps, secrets, updateReplacePolicy) {
|
|
26084
|
+
const createContext = { maskSecrets: createSecretMasker(secrets) };
|
|
25854
26085
|
const finalSnapshotIdentifier = await this.prepareFinalSnapshotForDelete(logicalId, resourceType, currentResource, updateReplacePolicy);
|
|
25855
26086
|
let deleteResult;
|
|
25856
26087
|
try {
|
|
@@ -25860,7 +26091,7 @@ var DeployEngine = class {
|
|
|
25860
26091
|
...finalSnapshotIdentifier !== void 0 && { finalSnapshotIdentifier }
|
|
25861
26092
|
});
|
|
25862
26093
|
} catch (deleteError) {
|
|
25863
|
-
throw new Error(`Failed to delete old resource ${logicalId} (${currentResource.physicalId}) during the --replace delete-first fallback: ${deleteError instanceof Error ? deleteError.message : String(deleteError)}
|
|
26094
|
+
throw new Error(maskSecretsInText(`Failed to delete old resource ${logicalId} (${currentResource.physicalId}) during the --replace delete-first fallback: ${deleteError instanceof Error ? deleteError.message : String(deleteError)}`, secrets));
|
|
25864
26095
|
}
|
|
25865
26096
|
const replaceSkipReason = deleteSkipReason(deleteResult);
|
|
25866
26097
|
if (replaceSkipReason !== void 0) throw new Error(deleteSkippedMessage(logicalId, currentResource.physicalId, replaceSkipReason, "during the --replace delete-first fallback"));
|
|
@@ -25871,13 +26102,13 @@ var DeployEngine = class {
|
|
|
25871
26102
|
maxRetries: 8,
|
|
25872
26103
|
initialDelayMs: 2e3,
|
|
25873
26104
|
maxDelayMs: 1e4,
|
|
25874
|
-
logger: this.
|
|
26105
|
+
logger: this.maskingRetryLoggerFor(secrets),
|
|
25875
26106
|
isInterrupted: () => this.interrupted,
|
|
25876
26107
|
onInterrupted: () => new InterruptedError(this.interruptCause ?? "user"),
|
|
25877
26108
|
isRetryable: isRecreateRetryableError
|
|
25878
26109
|
});
|
|
25879
26110
|
} catch (recreateError) {
|
|
25880
|
-
throw new Error(`Failed to re-create ${logicalId} after the --replace delete-first fallback already deleted the old resource (${currentResource.physicalId}): ${recreateError instanceof Error ? recreateError.message : String(recreateError)}. Re-run the deploy to create it fresh
|
|
26111
|
+
throw new Error(maskSecretsInText(`Failed to re-create ${logicalId} after the --replace delete-first fallback already deleted the old resource (${currentResource.physicalId}): ${recreateError instanceof Error ? recreateError.message : String(recreateError)}. Re-run the deploy to create it fresh.`, secrets));
|
|
25881
26112
|
}
|
|
25882
26113
|
}
|
|
25883
26114
|
/**
|
|
@@ -25899,10 +26130,10 @@ var DeployEngine = class {
|
|
|
25899
26130
|
...parameterValues && { parameters: parameterValues },
|
|
25900
26131
|
...conditions && { conditions }
|
|
25901
26132
|
}, stackName);
|
|
26133
|
+
if (context.recordedSecretValues) this.perResourceSecrets.set(logicalId, context.recordedSecretValues);
|
|
25902
26134
|
const resolvedProps = await this.resolver.resolve(desiredProps, context);
|
|
25903
26135
|
this.perResourceTemplateProps.set(logicalId, desiredProps);
|
|
25904
26136
|
const createSecrets = context.recordedSecretValues ?? /* @__PURE__ */ new Map();
|
|
25905
|
-
if (context.recordedSecretValues) this.perResourceSecrets.set(logicalId, context.recordedSecretValues);
|
|
25906
26137
|
this.auditResolvedAssetReferences(logicalId, resourceType, resolvedProps);
|
|
25907
26138
|
this.attemptedResolvedProps.set(logicalId, resolvedProps);
|
|
25908
26139
|
const createDecision = this.providerRegistry.getProviderFor({
|
|
@@ -25943,9 +26174,9 @@ var DeployEngine = class {
|
|
|
25943
26174
|
...parameterValues && { parameters: parameterValues },
|
|
25944
26175
|
...conditions && { conditions }
|
|
25945
26176
|
}, stackName);
|
|
25946
|
-
const resolvedProps = await this.resolver.resolve(desiredProps, context);
|
|
25947
26177
|
const updateSecrets = context.recordedSecretValues ?? /* @__PURE__ */ new Map();
|
|
25948
26178
|
this.perResourceSecrets.set(logicalId, updateSecrets);
|
|
26179
|
+
const resolvedProps = await this.resolver.resolve(desiredProps, context);
|
|
25949
26180
|
this.perResourceTemplateProps.set(logicalId, desiredProps);
|
|
25950
26181
|
this.auditResolvedAssetReferences(logicalId, resourceType, resolvedProps);
|
|
25951
26182
|
this.attemptedResolvedProps.set(logicalId, resolvedProps);
|
|
@@ -26026,7 +26257,7 @@ var DeployEngine = class {
|
|
|
26026
26257
|
maxRetries: 8,
|
|
26027
26258
|
initialDelayMs: 2e3,
|
|
26028
26259
|
maxDelayMs: 1e4,
|
|
26029
|
-
logger: this.
|
|
26260
|
+
logger: this.maskingRetryLoggerFor(updateSecrets),
|
|
26030
26261
|
isInterrupted: () => this.interrupted,
|
|
26031
26262
|
onInterrupted: () => new InterruptedError(this.interruptCause ?? "user"),
|
|
26032
26263
|
isRetryable: isRecreateRetryableError
|
|
@@ -26045,7 +26276,7 @@ var DeployEngine = class {
|
|
|
26045
26276
|
if (this.options.replace !== true) throw new CdkdError(`${logicalId} (${resourceType}) requires replacement, but the create-first attempt collided with the existing resource: ${createMsg}. ${nameOrigin.descriptor}, so the CloudFormation-style safe replacement order (create the new resource before deleting the old) cannot reuse the occupied name — CloudFormation refuses this shape with "cannot update a stack when a custom-named resource requires replacing". ${nameOrigin.remedy}, or re-run with \`cdkd deploy --replace\` to delete the old resource FIRST and recreate it under the same name (the resource is briefly unavailable while it is recreated).`, "NAMED_REPLACEMENT_COLLISION");
|
|
26046
26277
|
this.logger.info(` Create-first collided with the existing resource's name and --replace is set — deleting old ${logicalId} (${currentResource.physicalId}) first...`);
|
|
26047
26278
|
deletedOldFirst = true;
|
|
26048
|
-
createResult = await this.replaceDeleteFirstAndRecreate(logicalId, resourceType, currentResource, oldDeleteProvider, replaceProvider, replaceProps,
|
|
26279
|
+
createResult = await this.replaceDeleteFirstAndRecreate(logicalId, resourceType, currentResource, oldDeleteProvider, replaceProvider, replaceProps, updateSecrets, updateReplacePolicy);
|
|
26049
26280
|
}
|
|
26050
26281
|
if (!deletedOldFirst && createResult.physicalId === currentResource.physicalId) {
|
|
26051
26282
|
const idempotentNameOrigin = this.replacementNameOrigin(logicalId, currentResource.physicalId);
|
|
@@ -26053,7 +26284,7 @@ var DeployEngine = class {
|
|
|
26053
26284
|
if (this.options.replace !== true) throw new CdkdError(`${logicalId} (${resourceType}) requires replacement, but its Create API is name-idempotent: the create-first attempt returned the EXISTING resource (${currentResource.physicalId}) instead of creating a new one, so deleting the "old" resource would silently destroy the resource the deploy just reported as created. ${idempotentNameOrigin.descriptor}; ${idempotentNameOrigin.remedy}, or re-run with \`cdkd deploy --replace\` to delete the old resource FIRST and recreate it under the same name (the resource is briefly unavailable while it is recreated). Note: this branch is also reached when the old resource was deleted out-of-band and the physical id is name-derived — there the create was a genuine fresh create; \`--replace\` converges that case too.`, "NAMED_REPLACEMENT_IDEMPOTENT_CREATE");
|
|
26054
26285
|
this.logger.info(` Create-first returned the existing resource (name-idempotent Create API) and --replace is set — deleting old ${logicalId} (${currentResource.physicalId}) first...`);
|
|
26055
26286
|
deletedOldFirst = true;
|
|
26056
|
-
createResult = await this.replaceDeleteFirstAndRecreate(logicalId, resourceType, currentResource, oldDeleteProvider, replaceProvider, replaceProps,
|
|
26287
|
+
createResult = await this.replaceDeleteFirstAndRecreate(logicalId, resourceType, currentResource, oldDeleteProvider, replaceProvider, replaceProps, updateSecrets, updateReplacePolicy);
|
|
26057
26288
|
}
|
|
26058
26289
|
if (deletedOldFirst) {} else if (updateReplacePolicy === "Retain") this.logger.info(` Retaining old ${logicalId} (${currentResource.physicalId}) - UpdateReplacePolicy: Retain`);
|
|
26059
26290
|
else {
|
|
@@ -26444,12 +26675,74 @@ var DeployEngine = class {
|
|
|
26444
26675
|
return withRetry(operation, logicalId, {
|
|
26445
26676
|
...maxRetries !== void 0 && { maxRetries },
|
|
26446
26677
|
...initialDelayMs !== void 0 && { initialDelayMs },
|
|
26447
|
-
logger: this.
|
|
26678
|
+
logger: this.maskingRetryLogger(logicalId),
|
|
26448
26679
|
isInterrupted: () => this.interrupted,
|
|
26449
26680
|
onInterrupted: () => new InterruptedError(this.interruptCause ?? "user")
|
|
26450
26681
|
});
|
|
26451
26682
|
}
|
|
26452
26683
|
/**
|
|
26684
|
+
* Mask one line of engine-authored text with a resource's OWN recorded
|
|
26685
|
+
* secrets (issue [#2038](https://github.com/go-to-k/cdkd/issues/2038)).
|
|
26686
|
+
*
|
|
26687
|
+
* Per-resource, never session-wide — see the `perResourceSecrets` field doc
|
|
26688
|
+
* for why one resource's secret must not rewrite another's literal. A
|
|
26689
|
+
* `logicalId` with no entry (or an empty bag) forwards verbatim, so every
|
|
26690
|
+
* non-secret resource is byte-identical to before.
|
|
26691
|
+
*/
|
|
26692
|
+
maskForResource(logicalId, text) {
|
|
26693
|
+
return maskSecretsInText(text, this.perResourceSecrets.get(logicalId) ?? EMPTY_SECRETS);
|
|
26694
|
+
}
|
|
26695
|
+
/**
|
|
26696
|
+
* The LAZY `RetryLogger` the engine's generic `withRetry` wrapper threads
|
|
26697
|
+
* (issue [#2038](https://github.com/go-to-k/cdkd/issues/2038) acceptance
|
|
26698
|
+
* item 1) — the same masking shape `drift.ts` and `rollback-executor.ts`
|
|
26699
|
+
* install, bound to the resource's OWN recorded secrets.
|
|
26700
|
+
*
|
|
26701
|
+
* `retry.ts` interpolates the AWS message verbatim into both the per-attempt
|
|
26702
|
+
* `debug` line and the give-up `warn` summary, and the bag this engine hands
|
|
26703
|
+
* a provider is RESOLVED — `perResourceSecrets` is populated immediately after
|
|
26704
|
+
* `resolver.resolve` and BEFORE the create / update call, so a secret is in
|
|
26705
|
+
* scope at every retried provider call. An AWS validation error routinely
|
|
26706
|
+
* quotes the offending value back, so the give-up summary could print it at
|
|
26707
|
+
* DEFAULT verbosity: the same hole #2038 found on the rollback path, one
|
|
26708
|
+
* caller over.
|
|
26709
|
+
*
|
|
26710
|
+
* The bag is resolved PER LINE rather than captured, because `withRetry`
|
|
26711
|
+
* (the private wrapper below) is reached from call sites that hold no bag —
|
|
26712
|
+
* DELETE, the observed-capture drain, the Outputs pass — and a
|
|
26713
|
+
* `logicalId`-keyed read is the only thing available there. The two
|
|
26714
|
+
* `--replace` sites do hold their caller's bag and use
|
|
26715
|
+
* {@link maskingRetryLoggerFor} instead; see the note on
|
|
26716
|
+
* {@link replaceDeleteFirstAndRecreate}'s `secrets` parameter for why binding
|
|
26717
|
+
* the bag beats looking it up whenever the bag is in scope.
|
|
26718
|
+
*
|
|
26719
|
+
* Do NOT restate that the engine "already masked its own error text" — an
|
|
26720
|
+
* earlier revision of this comment did, and it was FALSE: `provisionResource`
|
|
26721
|
+
* logged the raw AWS message at `error` level (a HIGHER level than this
|
|
26722
|
+
* summary) until #2038's review round. Only the EVENT store was masked.
|
|
26723
|
+
*/
|
|
26724
|
+
maskingRetryLogger(logicalId) {
|
|
26725
|
+
return {
|
|
26726
|
+
debug: (msg) => this.logger.debug(this.maskForResource(logicalId, msg)),
|
|
26727
|
+
warn: (msg) => this.logger.warn(this.maskForResource(logicalId, msg))
|
|
26728
|
+
};
|
|
26729
|
+
}
|
|
26730
|
+
/**
|
|
26731
|
+
* The EAGER `RetryLogger`, bound to the bag the caller actually resolved
|
|
26732
|
+
* with (issue [#2038](https://github.com/go-to-k/cdkd/issues/2038)).
|
|
26733
|
+
*
|
|
26734
|
+
* Preferred over {@link maskingRetryLogger} wherever the resolution pass's
|
|
26735
|
+
* own `RecordedSecretValues` is in scope, for the reason
|
|
26736
|
+
* {@link replaceDeleteFirstAndRecreate}'s parameter list already states about
|
|
26737
|
+
* the masker it threads: a map looked up by logical id is a DIFFERENT thing
|
|
26738
|
+
* from the bag this call resolved with, and one file must not argue both
|
|
26739
|
+
* sides. It delegates to the shared `masking-retry-logger.ts` so the deploy
|
|
26740
|
+
* engine, `rollback-executor.ts` and `drift.ts` cannot drift apart.
|
|
26741
|
+
*/
|
|
26742
|
+
maskingRetryLoggerFor(secrets) {
|
|
26743
|
+
return maskingRetryLogger(this.logger, secrets);
|
|
26744
|
+
}
|
|
26745
|
+
/**
|
|
26453
26746
|
* What a failed Output resolution does, shared by both passes of
|
|
26454
26747
|
* {@link resolveOutputs} so they cannot drift — the alias pass reports the
|
|
26455
26748
|
* SAME failure for a name it could not resolve as the value pass does for a
|
|
@@ -26550,5 +26843,5 @@ var DeployEngine = class {
|
|
|
26550
26843
|
};
|
|
26551
26844
|
|
|
26552
26845
|
//#endregion
|
|
26553
|
-
export {
|
|
26554
|
-
//# sourceMappingURL=deploy-engine-
|
|
26846
|
+
export { isTerminationProtectionPropagationError as $, ResourceUpdateNotSupportedError as $n, validateAssetBucketName as $t, renderStatefulReason as A, derivePartitionAndUrlSuffix as An, INTRINSIC_KEYS as At, exportAliasCollisionScrubWarning as B, CdkdError as Bn, stringifyValue as Bt, isFinalSnapshotError as C, CFN_TEMPLATE_URL_LIMIT as Cn, s3BucketArn as Ct, extractDeploymentEventError as D, expectedOwnerParam as Dn, s3BucketWebsiteUrl as Dt, makeCanonicalizePropertiesFn as E, uploadCfnTemplate as En, s3BucketRegionalDomainName as Et, green as F, AwsClients as Fn, LockManager as Ft, collectInlinePolicyNamesManagedBySiblings as G, LocalMigrateError as Gn, rewriteTemplateAssetReferences as Gt, secretBearingStateKeyWarning as H, DependencyError as Hn, buildAssetRedirectMap as Ht, red as I, getAwsClients as In, S3StateBackend as It, findActionableSilentDrops as J, MissingCdkCliError as Jn, AssetModeResolver as Jt, clearOnUpdateRemoval as K, LocalStartServiceError as Kn, escapeRegExp$1 as Kt, yellow as L, resetAwsClients as Ln, rebuildClientForBucketRegion as Lt, bold as M, processStackMessages as Mn, withRetry as Mt, cyan as N, clearBucketRegionCache as Nn, DagBuilder as Nt, MULTI_REGION_RECREATE_BLOCKED_TYPES as O, PARTITION_TABLE as On, applyRoleArnIfSet as Ot, gray as P, resolveBucketRegion as Pn, TemplateParser as Pt, disableInstanceApiTermination as Q, ResourceTimeoutError as Qn, parseBootstrapMarker as Qt, collectDeclaredOutputNames as R, setAwsClients as Rn, shouldRetainResource as Rt, createPreDeleteFinalSnapshot as S, CFN_TEMPLATE_BODY_LIMIT as Sn, scrubResourceRecord as St, unsupportedFinalSnapshotError as T, findLargeInlineResources as Tn, s3BucketDualStackDomainName as Tt, stateKeySecretExposure as U, DeployCancelledError as Un, createAssetRedirectResolver as Ut, isExportAliasCollision as V, ConfigError as Vn, WorkGraph as Vt, IAMRoleProvider as W, LocalInvokeBuildError as Wn, loadPublishableAssetManifest as Wt, CloudControlProvider as X, PartialFailureError as Xn, ensureAssetStorage as Xt, findSilentDropProperties as Y, NestedStackChildDirectDestroyError as Yn, BOOTSTRAP_MARKER_PREFIX as Yt, slowCcOperationTimeoutMs as Z, ProvisioningError as Zn, getBootstrapMarkerKey as Zt, computeImplicitDeleteEdges as _, resolveStateBucketWithDefault as _n, STATE_SOURCED_READBACK_RULES as _t, DeploymentEventsStore as a, runDockerForeground as an, isCdkdError as ar, normalizeAwsTagsToCfn as at, buildFinalSnapshotIdentifier as b, stateBucketExistenceConfirmed as bn, maskSecretsInText as bt, replayFailedOperations as c, getDockerImageBySourceHash as cn, isMarkedNonRetryable as cr, coerceCfnBoolean as ct, updatePartialReason as d, getDefaultStateBucketName as dn, markNonRetryable as dr, readConfigString as dt, validateContainerRepoName as en, StackHasActiveImportsError as er, IntrinsicFunctionResolver as et, UNSPECIFIED_SKIP_REASON as f, getLegacyStateBucketName as fn, __exportAll as fr, replayWarn as ft, IMPLICIT_DELETE_DEPENDENCIES as g, resolveSkipPrefix as gn, STATE_SOURCED_CROSS_GENERATION_RULES as gt, maskingRetryLogger as h, resolveCaptureObservedState as hn, requireConfigString as ht, DeploymentEventsReader as i, getDockerCmd as in, formatError as ir, WAFv2WebACLProvider as it, formatResourceLine as j, AssemblyReader as jn, describeTypeWithThrottleRetry as jt, isStatefulRecreateTargetSync as k, canonicalizeRegion as kn, DiffCalculator as kt, replayRollback as l, Synthesizer as ln, isRetryableTransientError as lr, configBooleanRefusal as lt, withResourceDeadline as m, resolveAutoAssetStorage as mn, requireConfigObject as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, buildDockerImage as nn, StateError as nr, getAccountInfo as nt, planFailedOps as o, runDockerStreaming as on, normalizeAwsError as or, resolveExplicitPhysicalId as ot, deleteSkipReason as p, resolveApp as pn, requireConfigArray as pt, ProviderRegistry as q, LockError as qn, stripControlChars as qt, DeployEngine as r, formatDockerLoginError as rn, SynthesisError as rr, refStateLookupFromResource as rt, planRollback as s, AssetManifestLoader as sn, withErrorHandling as sr, assertRegionMatch as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, buildDenyExternalAccessPolicy as tn, StackTerminationProtectionError as tr, cfnRefValueFromPhysicalId as tt, updatePartialMessage as u, synthesisStatusMessage as un, isThrottlingError as ur, configStringRefusal as ut, ATOMIC_FINAL_SNAPSHOT_TYPES as v, resolveStateBucketWithDefaultAndSource as vn, TEMPLATE_SOURCED_RULES as vt, refusesFinalSnapshot as w, MIGRATE_TMP_PREFIX as wn, s3BucketDomainName as wt, ccRoutedFinalSnapshotError as x, warnDeprecatedNoPrefixCliFlag as xn, redactSecretsForState as xt, PRE_DELETE_SNAPSHOT_TYPES as y, resolveUseCdkBootstrapAssets as yn, createSecretMasker as yt, collectPublishedOutputNames as z, AssetError as zn, AssetPublisher as zt };
|
|
26847
|
+
//# sourceMappingURL=deploy-engine-4Eh5Qqlm.js.map
|