@go-to-k/cdkd 0.286.4 → 0.287.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{asg-provider-dBSdt3nB.js → asg-provider-D10GWdiA.js} +2 -2
- package/dist/{asg-provider-dBSdt3nB.js.map → asg-provider-D10GWdiA.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-DylsbAdu.js → deploy-engine-B377YKBI.js} +334 -109
- package/dist/deploy-engine-B377YKBI.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-dEYAo80R.js → program-D2Vykejc.js} +202 -8
- package/dist/{program-dEYAo80R.js.map → program-D2Vykejc.js.map} +1 -1
- package/dist/{version-CKOnsgg1.js → version-Rs7TV7Gz.js} +2 -2
- package/dist/{version-CKOnsgg1.js.map → version-Rs7TV7Gz.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-DylsbAdu.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _ as withStackName, d as applyDefaultNameForFallback, f as generateResourceName, h as looksLikeCdkdGeneratedName, m as getCurrentStackName, n as getLogger, p as generateResourceNameWithFallback, r as isStdoutReservedForPayload, s as getLiveRenderer } from "./logger-Dw-3y48G.js";
|
|
2
2
|
import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-Rs7TV7Gz.js";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetBucketReplicationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -11161,7 +11161,9 @@ function recordResolvedPair(secrets, expression, plaintext) {
|
|
|
11161
11161
|
* evidence — so a literal `Output` embedding one of two same-plaintext
|
|
11162
11162
|
* references would fall back to the value scan and persist the sibling's
|
|
11163
11163
|
* expression. The engine's other entry-by-entry copy — an `Export.Name`'s
|
|
11164
|
-
* secrets into the pass map — deliberately does NOT call this
|
|
11164
|
+
* secrets into the pass map — deliberately does NOT call this (and `cdkd
|
|
11165
|
+
* scrub`'s name loop resolves through a VIEW whose pairs never reach the pass
|
|
11166
|
+
* map at all, issue #2531): a name never
|
|
11165
11167
|
* positions a leaf, a value re-using the same token records its own pair at
|
|
11166
11168
|
* the seam, and the only thing the merge could add is a CONFLICT (a
|
|
11167
11169
|
* non-cacheable `{{resolve:ssm:X}}` whose value moved between the value pass
|
|
@@ -20212,6 +20214,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20212
20214
|
const conditions = {};
|
|
20213
20215
|
const templateConditions = context.template.Conditions;
|
|
20214
20216
|
if (!templateConditions || typeof templateConditions !== "object") return conditions;
|
|
20217
|
+
const maskingContext = context.recordedSecretValues ? context : {
|
|
20218
|
+
...context,
|
|
20219
|
+
recordedSecretValues: /* @__PURE__ */ new Map()
|
|
20220
|
+
};
|
|
20215
20221
|
const inProgress = /* @__PURE__ */ new Set();
|
|
20216
20222
|
const evaluateByName = async (name) => {
|
|
20217
20223
|
if (name in conditions) return conditions[name];
|
|
@@ -20225,7 +20231,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20225
20231
|
inProgress.add(name);
|
|
20226
20232
|
try {
|
|
20227
20233
|
const result = await this.resolveValue(definition, {
|
|
20228
|
-
...
|
|
20234
|
+
...maskingContext,
|
|
20229
20235
|
conditionResolver: evaluateByName
|
|
20230
20236
|
});
|
|
20231
20237
|
const value = Boolean(result);
|
|
@@ -20239,7 +20245,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
20239
20245
|
for (const name of Object.keys(templateConditions)) try {
|
|
20240
20246
|
await evaluateByName(name);
|
|
20241
20247
|
} catch (error) {
|
|
20242
|
-
this.logger.warn(`Failed to evaluate condition ${name}: ${error instanceof Error ? error.message : String(error)}, assuming false
|
|
20248
|
+
this.logger.warn(this.maskSecretsForLog(`Failed to evaluate condition ${name}: ${error instanceof Error ? error.message : String(error)}, assuming false`, maskingContext));
|
|
20243
20249
|
conditions[name] = false;
|
|
20244
20250
|
inProgress.delete(name);
|
|
20245
20251
|
}
|
|
@@ -22341,10 +22347,10 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
22341
22347
|
* which caches nothing (issue #1933).
|
|
22342
22348
|
*/
|
|
22343
22349
|
let cacheable = true;
|
|
22344
|
-
if (service === "secretsmanager") resolved = await this.resolveSecretsManagerReference(inner);
|
|
22350
|
+
if (service === "secretsmanager") resolved = await this.resolveSecretsManagerReference(inner, context);
|
|
22345
22351
|
else if (service === "ssm") {
|
|
22346
22352
|
const decrypt = context?.skipDynamicReferences !== true;
|
|
22347
|
-
const param = await this.resolveSSMReference(parts, decrypt);
|
|
22353
|
+
const param = await this.resolveSSMReference(parts, decrypt, "ssm", context);
|
|
22348
22354
|
if (param.type === "SecureString") this.pinSecretVerdict(fullMatch, true);
|
|
22349
22355
|
else if (!param.secure) this.pinSecretVerdict(fullMatch, false);
|
|
22350
22356
|
else cacheable = false;
|
|
@@ -22354,12 +22360,12 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
22354
22360
|
}
|
|
22355
22361
|
resolved = param.value;
|
|
22356
22362
|
} else if (service === "ssm-secure") {
|
|
22357
|
-
const param = await this.resolveSSMReference(parts, true, "ssm-secure");
|
|
22363
|
+
const param = await this.resolveSSMReference(parts, true, "ssm-secure", context);
|
|
22358
22364
|
if (!param.secure) throw markNonRetryable(new IntrinsicResolutionRefusalError(`Refusing to resolve ${fullMatch}: the parameter is a ${param.type} parameter, and the ssm-secure spelling is defined for SecureString parameters only. Reference it as {{resolve:ssm:...}} if it is public configuration.`));
|
|
22359
22365
|
isSecret = true;
|
|
22360
22366
|
resolved = param.value;
|
|
22361
22367
|
} else {
|
|
22362
|
-
this.logger.warn(`Unsupported dynamic reference service: ${service}
|
|
22368
|
+
this.logger.warn(this.maskSecretsForLog(`Unsupported dynamic reference service: ${service}`, context));
|
|
22363
22369
|
continue;
|
|
22364
22370
|
}
|
|
22365
22371
|
if (cacheable) this.cachedDynamicReferences.set(fullMatch, {
|
|
@@ -22390,7 +22396,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
22390
22396
|
* runs when no mid-string ":SecretString:" delimiter is present, so the json-key / version
|
|
22391
22397
|
* forms are unaffected.)
|
|
22392
22398
|
*/
|
|
22393
|
-
async resolveSecretsManagerReference(inner) {
|
|
22399
|
+
async resolveSecretsManagerReference(inner, context) {
|
|
22394
22400
|
const afterService = inner.substring(15);
|
|
22395
22401
|
let secretId;
|
|
22396
22402
|
let jsonKey = "";
|
|
@@ -22419,14 +22425,14 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
22419
22425
|
} else secretId = afterService;
|
|
22420
22426
|
if (!versionStage) versionStage = "AWSCURRENT";
|
|
22421
22427
|
if (!secretId) throw new Error("Dynamic reference: secretsmanager SECRET_ID is required");
|
|
22422
|
-
this.logger.debug(`Resolving dynamic reference: secretsmanager:${secretId}:SecretString:${jsonKey}:${versionStage}:${versionId}
|
|
22428
|
+
this.logger.debug(this.maskSecretsForLog(`Resolving dynamic reference: secretsmanager:${secretId}:SecretString:${jsonKey}:${versionStage}:${versionId}`, context));
|
|
22423
22429
|
const client = this.clientsForRegion(this.explicitRegion).secretsManager;
|
|
22424
22430
|
const command = new GetSecretValueCommand({
|
|
22425
22431
|
SecretId: secretId,
|
|
22426
22432
|
...versionStage && versionStage !== "" && { VersionStage: versionStage },
|
|
22427
22433
|
...versionId && versionId !== "" && { VersionId: versionId }
|
|
22428
22434
|
});
|
|
22429
|
-
const secretString = (await this.sendWithThrottleRetry(() => client.send(command), `secretsmanager:${secretId}
|
|
22435
|
+
const secretString = (await this.sendWithThrottleRetry(() => client.send(command), this.maskSecretsForLog(`secretsmanager:${secretId}`, context))).SecretString;
|
|
22430
22436
|
if (!secretString) throw new Error(`Dynamic reference: secret '${secretId}' does not contain a SecretString value`);
|
|
22431
22437
|
if (jsonKey) try {
|
|
22432
22438
|
const keyValue = JSON.parse(secretString)[jsonKey];
|
|
@@ -22579,16 +22585,16 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
22579
22585
|
* discard the value when `secure` is set — it is ciphertext, not the resolved
|
|
22580
22586
|
* reference.
|
|
22581
22587
|
*/
|
|
22582
|
-
async resolveSSMReference(parts, decrypt = true, service = "ssm") {
|
|
22588
|
+
async resolveSSMReference(parts, decrypt = true, service = "ssm", context) {
|
|
22583
22589
|
const parameterName = parts.slice(1).join(":");
|
|
22584
22590
|
if (!parameterName) throw new Error(`Dynamic reference: ${service} PARAMETER_NAME is required`);
|
|
22585
|
-
this.logger.debug(`Resolving dynamic reference: ${service}:${parameterName}
|
|
22591
|
+
this.logger.debug(this.maskSecretsForLog(`Resolving dynamic reference: ${service}:${parameterName}`, context));
|
|
22586
22592
|
const client = this.clientsForRegion(this.explicitRegion).ssm;
|
|
22587
22593
|
const command = new GetParameterCommand({
|
|
22588
22594
|
Name: parameterName,
|
|
22589
22595
|
WithDecryption: decrypt
|
|
22590
22596
|
});
|
|
22591
|
-
const response = await this.sendWithThrottleRetry(() => client.send(command), `${service}:${parameterName}
|
|
22597
|
+
const response = await this.sendWithThrottleRetry(() => client.send(command), this.maskSecretsForLog(`${service}:${parameterName}`, context));
|
|
22592
22598
|
const paramValue = response.Parameter?.Value;
|
|
22593
22599
|
if (paramValue === void 0 || paramValue === null) throw new Error(`Dynamic reference: SSM parameter '${parameterName}' not found or has no value`);
|
|
22594
22600
|
const paramType = response.Parameter?.Type;
|
|
@@ -22596,7 +22602,7 @@ var IntrinsicFunctionResolver = class IntrinsicFunctionResolver {
|
|
|
22596
22602
|
if (secure && paramType !== "SecureString" && !this.warnedUnrecognizedSsmTypes.has(`${parameterName}\u0000${String(paramType)}`)) {
|
|
22597
22603
|
this.warnedUnrecognizedSsmTypes.add(`${parameterName}\u0000${String(paramType)}`);
|
|
22598
22604
|
const reported = paramType === void 0 ? "(absent)" : `'${String(paramType)}'`;
|
|
22599
|
-
this.logger.warn(`SSM parameter '${parameterName}' reported an unrecognized Type ${reported} — treating its value as a secret, so cdkd will persist the {{resolve:${service}:...}} expression rather than the resolved value. Declare the parameter as String / StringList if it is public config
|
|
22605
|
+
this.logger.warn(this.maskSecretsForLog(`SSM parameter '${parameterName}' reported an unrecognized Type ${reported} — treating its value as a secret, so cdkd will persist the {{resolve:${service}:...}} expression rather than the resolved value. Declare the parameter as String / StringList if it is public config.`, context));
|
|
22600
22606
|
}
|
|
22601
22607
|
return {
|
|
22602
22608
|
value: paramValue,
|
|
@@ -23549,9 +23555,14 @@ function describeJsonKeys(document) {
|
|
|
23549
23555
|
* `disableCcApiFallback` is not read on that path. The poisoned pre-guard
|
|
23550
23556
|
* state record the issue is about is precisely a record that already says
|
|
23551
23557
|
* `cc-api`, so it would still arrive here. Only adding the type to
|
|
23552
|
-
* `STICKY_CC_MIGRATION_EXEMPT` would divert it
|
|
23553
|
-
*
|
|
23554
|
-
*
|
|
23558
|
+
* `STICKY_CC_MIGRATION_EXEMPT` would divert it. Since issue #2719 that
|
|
23559
|
+
* table admits two modes, and NEITHER helps here: `'cc-broken'` is for
|
|
23560
|
+
* types Cloud Control cannot manage, and `'sdk-coverage'` diverts a
|
|
23561
|
+
* resource only when its property bags carry no actionable silent drop --
|
|
23562
|
+
* which is the opposite of this case by construction. Were a type somehow
|
|
23563
|
+
* admitted anyway, the divert would send the silent-drop property back
|
|
23564
|
+
* down the dropping path on the next deploy; the property gate is what
|
|
23565
|
+
* prevents it.
|
|
23555
23566
|
*
|
|
23556
23567
|
* So the confirmation belongs where the delete is actually issued. The set is
|
|
23557
23568
|
* a set rather than an `if` because the hazard is not S3-specific in kind: any
|
|
@@ -23800,7 +23811,7 @@ var CloudControlProvider = class {
|
|
|
23800
23811
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
23801
23812
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
23802
23813
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
23803
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
23814
|
+
const { ASGProvider } = await import("./asg-provider-D10GWdiA.js").then((n) => n.n);
|
|
23804
23815
|
return withIndeterminateGuard(await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
23805
23816
|
}
|
|
23806
23817
|
const isProtectedEc2Instance = context?.removeProtection === true && resourceType === "AWS::EC2::Instance";
|
|
@@ -29056,6 +29067,65 @@ function findActionableSilentDrops(resourceType, templateProperties, allowedKeys
|
|
|
29056
29067
|
if (drops.length === 0) return drops;
|
|
29057
29068
|
return drops.filter(({ property }) => !allowedKeys.has(`${resourceType}:${property}`));
|
|
29058
29069
|
}
|
|
29070
|
+
/**
|
|
29071
|
+
* A 1-click pre-filled GitHub issue link requesting cdkd support for a
|
|
29072
|
+
* specific top-level property on a resource type. Surfaced in the pre-flight
|
|
29073
|
+
* error so a user hitting a silent drop lands directly in the "request
|
|
29074
|
+
* support" flow.
|
|
29075
|
+
*/
|
|
29076
|
+
function unsupportedPropertyIssueUrl(resourceType, property) {
|
|
29077
|
+
return `https://github.com/go-to-k/cdkd/issues/new?title=${encodeURIComponent(`Support property ${resourceType}.${property}`)}&labels=resource-support`;
|
|
29078
|
+
}
|
|
29079
|
+
/**
|
|
29080
|
+
* Identify top-level template properties this type's committed CFn schema
|
|
29081
|
+
* snapshot does not know about at all — present in neither
|
|
29082
|
+
* `coverage.handled` nor `coverage.silentDrop` (issue
|
|
29083
|
+
* [#2718](https://github.com/go-to-k/cdkd/issues/2718)).
|
|
29084
|
+
*
|
|
29085
|
+
* The complement of {@link findSilentDropProperties}, which deliberately
|
|
29086
|
+
* PASSES these through: a property absent from the schema is indistinguishable
|
|
29087
|
+
* at deploy time from a user typo or an `addPropertyOverride` escape hatch, so
|
|
29088
|
+
* it cannot drive a routing decision. That tolerance is correct for routing
|
|
29089
|
+
* and wrong for silence — the routing table is built offline from
|
|
29090
|
+
* `tests/fixtures/cfn-schemas/*.json`, so every property AWS publishes AFTER
|
|
29091
|
+
* that snapshot lands here, and on the SDK route it reaches neither AWS nor an
|
|
29092
|
+
* error while the deploy reports success. That is the issue
|
|
29093
|
+
* [#614](https://github.com/go-to-k/cdkd/issues/614) failure class arriving
|
|
29094
|
+
* through the one input the #614 machinery cannot observe.
|
|
29095
|
+
*
|
|
29096
|
+
* **Why a warn built on this has no false-positive mode.** An SDK provider
|
|
29097
|
+
* writes only what it declares in `handledProperties`, so a top-level property
|
|
29098
|
+
* in neither set does not reach AWS under ANY of the three readings — a
|
|
29099
|
+
* post-snapshot AWS addition, a typo, or a deliberate `addPropertyOverride`.
|
|
29100
|
+
* "This value will not reach AWS" is true in all three, so the caller does not
|
|
29101
|
+
* have to guess intent, and at deploy time it could not: cdkd holds only the
|
|
29102
|
+
* template and the baked-in table. Firing on a typo is a feature rather than
|
|
29103
|
+
* noise — CloudFormation would have REJECTED that typo, so today's silence is
|
|
29104
|
+
* strictly the worst of the three behaviors.
|
|
29105
|
+
*
|
|
29106
|
+
* Deliberately NOT gated on the snapshot's age: the statement is true whatever
|
|
29107
|
+
* `generatedAt` says, and an age gate would trade the typo visibility away for
|
|
29108
|
+
* a wall-clock dependence.
|
|
29109
|
+
*
|
|
29110
|
+
* Returns `[]` for Tier 2 / Custom / unknown types (no coverage record — Cloud
|
|
29111
|
+
* Control forwards the full property map, so nothing is dropped) and sorts
|
|
29112
|
+
* alphabetically, mirroring {@link findSilentDropProperties}. The CALLER is
|
|
29113
|
+
* responsible for firing only when the resource actually resolves to the SDK
|
|
29114
|
+
* route; see `ProviderRegistry.reportSilentDropDecisions`.
|
|
29115
|
+
*/
|
|
29116
|
+
function findUnrecognizedProperties(resourceType, templateProperties) {
|
|
29117
|
+
if (!templateProperties) return [];
|
|
29118
|
+
const coverage = getPropertyCoverage(resourceType);
|
|
29119
|
+
if (!coverage) return [];
|
|
29120
|
+
const unrecognized = [];
|
|
29121
|
+
for (const prop of Object.keys(templateProperties)) {
|
|
29122
|
+
if (prop === "Ref" || prop.startsWith("Fn::")) continue;
|
|
29123
|
+
if (coverage.handled.has(prop)) continue;
|
|
29124
|
+
if (coverage.silentDrop.has(prop)) continue;
|
|
29125
|
+
unrecognized.push(prop);
|
|
29126
|
+
}
|
|
29127
|
+
return unrecognized.sort((a, b) => a.localeCompare(b));
|
|
29128
|
+
}
|
|
29059
29129
|
|
|
29060
29130
|
//#endregion
|
|
29061
29131
|
//#region src/provisioning/mutually-exclusive-properties.ts
|
|
@@ -29165,56 +29235,39 @@ function buildMutuallyExclusiveMessage(logicalId, violation) {
|
|
|
29165
29235
|
//#endregion
|
|
29166
29236
|
//#region src/provisioning/provider-registry.ts
|
|
29167
29237
|
/**
|
|
29168
|
-
* Provider registry for managing resource providers.
|
|
29169
|
-
*
|
|
29170
|
-
* Selection strategy for a fresh resource (see {@link getProviderFor}):
|
|
29171
|
-
* 1. Custom Resource (`Custom::*` / `AWS::CloudFormation::CustomResource`)
|
|
29172
|
-
* → Custom Resource provider (recorded as `provisionedBy: 'sdk'`).
|
|
29173
|
-
* 2. Existing-state `provisionedBy: 'cc-api'` → Cloud Control (sticky).
|
|
29174
|
-
* 3. SDK Provider registered, no silent-drop properties (after the
|
|
29175
|
-
* `--allow-unsupported-properties` override filter) → SDK Provider.
|
|
29176
|
-
* 4. SDK Provider registered, silent-drop properties present, NOT all
|
|
29177
|
-
* in the allow set → Cloud Control (auto-route, info-logged). When the
|
|
29178
|
-
* CC route is NOT viable — the type is `NON_PROVISIONABLE` (no CC
|
|
29179
|
-
* handlers, e.g. AWS::FSx::FileSystem) or the provider sets
|
|
29180
|
-
* `disableCcApiFallback` (e.g. NestedStackProvider) — throw the clear
|
|
29181
|
-
* pre-flight error instead of failing opaquely at provisioning time.
|
|
29182
|
-
* 5. SDK Provider registered, silent-drop properties present, ALL in
|
|
29183
|
-
* the allow set → SDK Provider (the user explicitly accepted the
|
|
29184
|
-
* silent drop, warn-logged).
|
|
29185
|
-
* 6. No SDK Provider, Cloud Control supports the type → Cloud Control.
|
|
29186
|
-
* 7. `--allow-unsupported-types` escape hatch → Cloud Control optimistically.
|
|
29187
|
-
* 8. Otherwise → throw (no provider available).
|
|
29188
|
-
*
|
|
29189
|
-
* SDK-provider-less Tier 3 (`NON_PROVISIONABLE`) types are rejected earlier
|
|
29190
|
-
* by {@link validateResourceTypes}. A Tier 1 type that is ALSO
|
|
29191
|
-
* NON_PROVISIONABLE (SDK provider registered for a type Cloud Control cannot
|
|
29192
|
-
* manage — e.g. AWS::FSx::FileSystem, AWS::DLM::LifecyclePolicy) passes the
|
|
29193
|
-
* type check but has no viable CC auto-route; rule 4's viability guard turns
|
|
29194
|
-
* that case into a clear pre-flight error.
|
|
29195
|
-
*/
|
|
29196
|
-
/**
|
|
29197
29238
|
* Types exempt from the sticky `provisionedBy: 'cc-api'` routing rule.
|
|
29198
29239
|
*
|
|
29199
|
-
* The sticky rule exists to avoid physical-ID
|
|
29200
|
-
* backfilled for a type Cloud Control was
|
|
29201
|
-
*
|
|
29202
|
-
*
|
|
29203
|
-
*
|
|
29204
|
-
*
|
|
29205
|
-
*
|
|
29206
|
-
*
|
|
29207
|
-
*
|
|
29208
|
-
|
|
29209
|
-
|
|
29210
|
-
|
|
29211
|
-
|
|
29212
|
-
|
|
29213
|
-
|
|
29214
|
-
|
|
29215
|
-
|
|
29216
|
-
|
|
29217
|
-
|
|
29240
|
+
* The sticky rule (rule 2 in `getProviderFor`) exists to avoid physical-ID
|
|
29241
|
+
* churn when an SDK provider is backfilled for a type Cloud Control was
|
|
29242
|
+
* already managing fine. Both exemption modes are narrow escapes from it; see
|
|
29243
|
+
* `StickyExemptMode` for which applies when.
|
|
29244
|
+
*
|
|
29245
|
+
* Condition 2 -- physicalId parity -- is a hard requirement in BOTH modes and
|
|
29246
|
+
* is what makes a flip churn-free. It is also why this is a curated table
|
|
29247
|
+
* rather than a predicate: an automatic flip keyed on "the type has coverage"
|
|
29248
|
+
* would assert parity for types nobody measured.
|
|
29249
|
+
*/
|
|
29250
|
+
const STICKY_CC_MIGRATION_EXEMPT = /* @__PURE__ */ new Map([["AWS::Scheduler::Schedule", {
|
|
29251
|
+
mode: "cc-broken",
|
|
29252
|
+
physicalIdForm: "both layers store the bare schedule name; the state properties carry GroupName, so the SDK provider addresses existing records correctly",
|
|
29253
|
+
issue: "https://github.com/go-to-k/cdkd/issues/961",
|
|
29254
|
+
integFixture: "scheduler-custom-group"
|
|
29255
|
+
}], ["AWS::SNS::Topic", {
|
|
29256
|
+
mode: "sdk-coverage",
|
|
29257
|
+
physicalIdForm: "both layers store the topic ARN: the schema primaryIdentifier is TopicArn and SnsTopicProvider.create records the CreateTopic TopicArn",
|
|
29258
|
+
issue: "https://github.com/go-to-k/cdkd/issues/2719",
|
|
29259
|
+
integFixture: "cc-to-sdk-reroute"
|
|
29260
|
+
}]]);
|
|
29261
|
+
function wouldReturnToSdkProvider(input) {
|
|
29262
|
+
const { resourceType, desiredProperties, previousProperties, allowedUnsupportedProperties = /* @__PURE__ */ new Set(), forceCcApi = false, exemptions = STICKY_CC_MIGRATION_EXEMPT } = input;
|
|
29263
|
+
const exemption = exemptions.get(resourceType);
|
|
29264
|
+
if (exemption === void 0) return false;
|
|
29265
|
+
if (exemption.mode === "cc-broken") return true;
|
|
29266
|
+
if (forceCcApi) return false;
|
|
29267
|
+
if (desiredProperties === void 0) return false;
|
|
29268
|
+
if (previousProperties === void 0) return false;
|
|
29269
|
+
return [desiredProperties, previousProperties].every((bag) => findActionableSilentDrops(resourceType, bag, allowedUnsupportedProperties).length === 0);
|
|
29270
|
+
}
|
|
29218
29271
|
var ProviderRegistry = class {
|
|
29219
29272
|
logger = getLogger().child("ProviderRegistry");
|
|
29220
29273
|
providers = /* @__PURE__ */ new Map();
|
|
@@ -29309,18 +29362,36 @@ var ProviderRegistry = class {
|
|
|
29309
29362
|
provisionedBy: "sdk"
|
|
29310
29363
|
};
|
|
29311
29364
|
}
|
|
29312
|
-
|
|
29313
|
-
|
|
29314
|
-
|
|
29315
|
-
|
|
29316
|
-
|
|
29317
|
-
|
|
29365
|
+
let returningToSdk = false;
|
|
29366
|
+
if (provisionedBy === "cc-api") {
|
|
29367
|
+
if (!wouldReturnToSdkProvider({
|
|
29368
|
+
resourceType,
|
|
29369
|
+
desiredProperties: properties,
|
|
29370
|
+
previousProperties: input.previousProperties,
|
|
29371
|
+
allowedUnsupportedProperties: this.allowedUnsupportedProperties,
|
|
29372
|
+
forceCcApi: input.forceCcApi === true
|
|
29373
|
+
})) {
|
|
29374
|
+
this.logger.debug(`Routing ${resourceType} via Cloud Control (state-recorded provisionedBy=cc-api)`);
|
|
29375
|
+
return {
|
|
29376
|
+
provider: this.cloudControlProvider,
|
|
29377
|
+
provisionedBy: "cc-api"
|
|
29378
|
+
};
|
|
29379
|
+
}
|
|
29380
|
+
returningToSdk = true;
|
|
29318
29381
|
}
|
|
29319
29382
|
const specificProvider = this.providers.get(resourceType);
|
|
29320
29383
|
if (specificProvider) {
|
|
29321
29384
|
const actionableDrops = findActionableSilentDrops(resourceType, properties, this.allowedUnsupportedProperties);
|
|
29322
29385
|
if (actionableDrops.length === 0) {
|
|
29323
29386
|
this.logger.debug(`Using specific SDK provider for ${resourceType}`);
|
|
29387
|
+
if (returningToSdk) {
|
|
29388
|
+
this.logger.debug(`${resourceType} is returning to its SDK provider from a state-recorded cc-api route; physical id is preserved`);
|
|
29389
|
+
return {
|
|
29390
|
+
provider: specificProvider,
|
|
29391
|
+
provisionedBy: "sdk",
|
|
29392
|
+
sdkMigration: true
|
|
29393
|
+
};
|
|
29394
|
+
}
|
|
29324
29395
|
return {
|
|
29325
29396
|
provider: specificProvider,
|
|
29326
29397
|
provisionedBy: "sdk"
|
|
@@ -29520,7 +29591,6 @@ var ProviderRegistry = class {
|
|
|
29520
29591
|
reportSilentDropDecisions(resources) {
|
|
29521
29592
|
for (const { logicalId, resourceType, properties, provisionedBy } of resources) {
|
|
29522
29593
|
const drops = findSilentDropProperties(resourceType, properties);
|
|
29523
|
-
if (drops.length === 0) continue;
|
|
29524
29594
|
const overridden = [];
|
|
29525
29595
|
const autoRouted = [];
|
|
29526
29596
|
for (const { property } of drops) {
|
|
@@ -29539,9 +29609,71 @@ var ProviderRegistry = class {
|
|
|
29539
29609
|
const propList = overridden.join(", ");
|
|
29540
29610
|
this.logger.warn(`${logicalId} (${resourceType}): ${propList} will be silently dropped (--allow-unsupported-properties override accepted). Remove the override to route this resource via Cloud Control API instead.`);
|
|
29541
29611
|
}
|
|
29612
|
+
this.reportUnrecognizedProperties(logicalId, resourceType, properties, {
|
|
29613
|
+
provisionedBy,
|
|
29614
|
+
autoRouted: autoRouted.length > 0
|
|
29615
|
+
});
|
|
29542
29616
|
}
|
|
29543
29617
|
}
|
|
29544
29618
|
/**
|
|
29619
|
+
* Warn about top-level template properties this type's committed CFn schema
|
|
29620
|
+
* snapshot does not know about, on resources that resolve to the SDK route
|
|
29621
|
+
* (issue [#2718](https://github.com/go-to-k/cdkd/issues/2718)).
|
|
29622
|
+
*
|
|
29623
|
+
* The gap this closes: {@link getProviderFor} decides SDK-vs-Cloud-Control
|
|
29624
|
+
* from `property-coverage.generated.ts`, built offline from the schema
|
|
29625
|
+
* fixtures, and there is no runtime `DescribeType` on that path. So a
|
|
29626
|
+
* property AWS publishes AFTER the fixture snapshot produces no
|
|
29627
|
+
* `silentDrop` entry, does not auto-route to Cloud Control, and is dropped
|
|
29628
|
+
* with the deploy reporting success — the issue
|
|
29629
|
+
* [#614](https://github.com/go-to-k/cdkd/issues/614) failure class reached
|
|
29630
|
+
* through the one input the #614 machinery cannot observe. The scheduled
|
|
29631
|
+
* fixture-refresh job is the FIX (the property enters the fixture and the
|
|
29632
|
+
* existing auto-route handles it); this warn is what protects a user
|
|
29633
|
+
* deploying BETWEEN refresh cycles.
|
|
29634
|
+
*
|
|
29635
|
+
* Fires only on the SDK route, which is where the drop actually happens.
|
|
29636
|
+
* The two Cloud-Control routes both forward the full property map verbatim,
|
|
29637
|
+
* so the property does reach AWS there and a warn would be false:
|
|
29638
|
+
* - `provisionedBy: 'cc-api'` from existing state (sticky rule 2 of
|
|
29639
|
+
* {@link getProviderFor}), minus the `STICKY_CC_MIGRATION_EXEMPT` types
|
|
29640
|
+
* that deliberately re-route back to their SDK provider;
|
|
29641
|
+
* - an actionable silent drop auto-routing this deploy (`autoRouted`).
|
|
29642
|
+
*
|
|
29643
|
+
* The route test MIRRORS `getProviderFor` rather than re-deriving it — the
|
|
29644
|
+
* two answering differently is the only way this warn can be wrong about a
|
|
29645
|
+
* resource, and it is not decidable from the message.
|
|
29646
|
+
*
|
|
29647
|
+
* **Known divergence, in the SAFE direction.** This runs on the template's
|
|
29648
|
+
* RAW properties (`deploy-engine.ts` calls `validateResourceProperties`
|
|
29649
|
+
* pre-flight) while `getProviderFor` runs on RESOLVED ones. So a silent-drop
|
|
29650
|
+
* key present only behind an `Fn::If` that resolves to `AWS::NoValue` makes
|
|
29651
|
+
* `autoRouted` true here and suppresses the warn, while the real route ends
|
|
29652
|
+
* up on the SDK provider and does drop the unrecognized property. The result
|
|
29653
|
+
* is a MISSING warn, never a false one — which is the right direction for an
|
|
29654
|
+
* advisory line, and why this is documented rather than fixed by resolving
|
|
29655
|
+
* twice. `getProviderFor` remains the authority on routing; nothing here
|
|
29656
|
+
* changes a routing decision.
|
|
29657
|
+
*
|
|
29658
|
+
* Suppressed per `<Type>:<Prop>` by `--allow-unsupported-properties`, whose
|
|
29659
|
+
* meaning ("accept the silent drop, stay on the SDK path") is exactly this
|
|
29660
|
+
* case; deliberately no new flag. Warn rather than error because the drop
|
|
29661
|
+
* may be intended, and deliberately NOT an auto-route: flipping to
|
|
29662
|
+
* Cloud Control on an UNRECOGNIZED property would let a typo trigger the
|
|
29663
|
+
* currently one-way `cc-api` state flip (issue
|
|
29664
|
+
* [#2719](https://github.com/go-to-k/cdkd/issues/2719)), and CC would reject
|
|
29665
|
+
* the unknown key anyway.
|
|
29666
|
+
*/
|
|
29667
|
+
reportUnrecognizedProperties(logicalId, resourceType, properties, route) {
|
|
29668
|
+
if (route.provisionedBy === "cc-api" && !STICKY_CC_MIGRATION_EXEMPT.has(resourceType) || route.autoRouted) return;
|
|
29669
|
+
const unrecognized = findUnrecognizedProperties(resourceType, properties).filter((property) => !this.allowedUnsupportedProperties.has(`${resourceType}:${property}`));
|
|
29670
|
+
if (unrecognized.length === 0) return;
|
|
29671
|
+
const propList = unrecognized.join(", ");
|
|
29672
|
+
const overrideHint = unrecognized.map((p) => `${resourceType}:${p}`).join(",");
|
|
29673
|
+
const one = unrecognized.length === 1;
|
|
29674
|
+
this.logger.warn(`${logicalId} (${resourceType}): ${propList} ${one ? "is" : "are"} not in cdkd's CFn schema snapshot for this type, so ${one ? "it" : "they"} will NOT reach AWS — the deploy will still report success. Anything of these shapes looks the same here: a misspelled name (fix the spelling); a read-only attribute, which is not settable on any engine (remove it); or a property AWS published after cdkd's snapshot, which cdkd should be routing via Cloud Control — please report that one: ${unsupportedPropertyIssueUrl(resourceType, unrecognized[0])}${one ? "" : ` (link is for ${unrecognized[0]})`}. If the drop is intended — an addPropertyOverride escape hatch — silence this via --allow-unsupported-properties ${overrideHint}.`);
|
|
29675
|
+
}
|
|
29676
|
+
/**
|
|
29545
29677
|
* Pure-functional discovery of every resource whose template uses one or
|
|
29546
29678
|
* more silent-drop properties that are NOT in the
|
|
29547
29679
|
* `--allow-unsupported-properties` allow set — i.e. every resource that
|
|
@@ -34244,31 +34376,15 @@ var InterruptedError = class extends Error {
|
|
|
34244
34376
|
this.name = "InterruptedError";
|
|
34245
34377
|
}
|
|
34246
34378
|
};
|
|
34247
|
-
|
|
34248
|
-
* Best-effort routing inference for the live-progress task label
|
|
34249
|
-
* (#614 §9). Mirrors the routing decision tree but is purely cosmetic:
|
|
34250
|
-
* errors here never surface — when the inference fails we return
|
|
34251
|
-
* `undefined` and the label gets no `[CC API]` tag. The real
|
|
34252
|
-
* `getProviderFor` call inside the deploy/destroy critical path is the
|
|
34253
|
-
* load-bearing dispatch.
|
|
34254
|
-
*
|
|
34255
|
-
* Inputs:
|
|
34256
|
-
* - CREATE / UPDATE → template-side `desiredProperties` (top-level CFn
|
|
34257
|
-
* property names; intrinsic resolution does not change those, so we
|
|
34258
|
-
* can route ahead of the resolver run).
|
|
34259
|
-
* - DELETE → sticky `provisionedBy` from the existing-state record.
|
|
34260
|
-
*
|
|
34261
|
-
* Exported so {@link DeployEngine.peekRoutingForLabel} stays a 1-line
|
|
34262
|
-
* delegate and the routing-inference logic is directly unit-testable
|
|
34263
|
-
* without standing up a full DeployEngine harness.
|
|
34264
|
-
*/
|
|
34265
|
-
function deriveLabelRouting(change, existingState, registry) {
|
|
34379
|
+
function deriveLabelRouting(change, existingState, registry, forceCcApi = false) {
|
|
34266
34380
|
try {
|
|
34267
34381
|
if (change.changeType === "DELETE") return existingState?.provisionedBy;
|
|
34268
34382
|
return registry.getProviderFor({
|
|
34269
34383
|
resourceType: change.resourceType,
|
|
34270
|
-
properties: change.desiredProperties,
|
|
34271
|
-
provisionedBy: existingState?.provisionedBy
|
|
34384
|
+
properties: change.desiredProperties ?? {},
|
|
34385
|
+
provisionedBy: existingState?.provisionedBy,
|
|
34386
|
+
previousProperties: existingState?.properties,
|
|
34387
|
+
forceCcApi
|
|
34272
34388
|
}).provisionedBy;
|
|
34273
34389
|
} catch {
|
|
34274
34390
|
return;
|
|
@@ -35660,9 +35776,10 @@ var DeployEngine = class {
|
|
|
35660
35776
|
async provisionResource(logicalId, change, stateResources, stackName, template, parameterValues, conditions, counts, progress) {
|
|
35661
35777
|
const resourceType = change.resourceType;
|
|
35662
35778
|
const renderer = getLiveRenderer();
|
|
35663
|
-
const
|
|
35779
|
+
const labelRecreateDirection = this.recreateDirectionFor(stackName, logicalId);
|
|
35780
|
+
const needsReplacement = change.changeType === "UPDATE" && (change.propertyChanges?.some((pc) => pc.requiresReplacement) ?? false) || labelRecreateDirection !== void 0;
|
|
35664
35781
|
const verb = change.changeType === "CREATE" ? "Creating" : change.changeType === "DELETE" ? "Deleting" : needsReplacement ? "Replacing" : "Updating";
|
|
35665
|
-
const labelRouting = this.peekRoutingForLabel(change, stateResources[logicalId]);
|
|
35782
|
+
const labelRouting = this.peekRoutingForLabel(change, stateResources[logicalId], stackName, logicalId, needsReplacement, labelRecreateDirection);
|
|
35666
35783
|
const baseLabel = `${verb} ${logicalId} (${resourceType})${labelRouting === "cc-api" ? " [CC API]" : ""}`;
|
|
35667
35784
|
renderer.addTask(logicalId, baseLabel);
|
|
35668
35785
|
const operationKind = change.changeType === "CREATE" ? "CREATE" : change.changeType === "DELETE" ? "DELETE" : "UPDATE";
|
|
@@ -35757,8 +35874,51 @@ var DeployEngine = class {
|
|
|
35757
35874
|
renderer.removeTask(logicalId);
|
|
35758
35875
|
}
|
|
35759
35876
|
}
|
|
35760
|
-
|
|
35761
|
-
|
|
35877
|
+
/**
|
|
35878
|
+
* Is this resource pinned to Cloud Control for this deploy (`--pin-cc-api`)?
|
|
35879
|
+
*
|
|
35880
|
+
* ONE implementation, called by the update dispatch and by the progress
|
|
35881
|
+
* label. They carried separate copies of this expression for one revision,
|
|
35882
|
+
* and a mutation probe caught the predictable result: neutering the LABEL's
|
|
35883
|
+
* copy left every test green, because the only cases that existed exercised
|
|
35884
|
+
* the dispatch's. Same shape as the duplicated flip predicate this lane
|
|
35885
|
+
* already collapsed once.
|
|
35886
|
+
*
|
|
35887
|
+
* SCOPED BY STACK, like `recreateTargets`. `NestedStackProvider.runChildDeploy`
|
|
35888
|
+
* spreads the parent's options into every child engine, and a logical id is
|
|
35889
|
+
* unique only within one template, so an unscoped set would pin a same-named
|
|
35890
|
+
* resource in a stack the user never named — silently, since a pin produces
|
|
35891
|
+
* no output of its own.
|
|
35892
|
+
*/
|
|
35893
|
+
isPinnedToCcApi(stackName, logicalId) {
|
|
35894
|
+
return this.options.pinCcApi?.stackName === stackName && this.options.pinCcApi.logicalIds.has(logicalId);
|
|
35895
|
+
}
|
|
35896
|
+
/**
|
|
35897
|
+
* The `--recreate-via-*` direction for this resource, or `undefined`.
|
|
35898
|
+
*
|
|
35899
|
+
* Stack-scoped for the same reason as {@link isPinnedToCcApi}, and extracted
|
|
35900
|
+
* for a sharper one: the LABEL and the DISPATCH were computing "is this a
|
|
35901
|
+
* replacement" from DIFFERENT expressions. The dispatch asks
|
|
35902
|
+
* `propertyDrivenReplacement || recreateFlagged`; the label asked only the
|
|
35903
|
+
* property half. So a `--recreate-via-*` target whose property change does
|
|
35904
|
+
* not itself force a replacement took the label's non-replacement path and
|
|
35905
|
+
* was routed from the state record, while the dispatch routed it from the
|
|
35906
|
+
* flag -- mislabelling in BOTH directions, and rendering `Updating` over a
|
|
35907
|
+
* destroy + recreate.
|
|
35908
|
+
*
|
|
35909
|
+
* Three review rounds fixed three instances of that one class (the pin, then
|
|
35910
|
+
* the sticky inputs, then this) by subtracting one input at a time from the
|
|
35911
|
+
* label. The class closes by asking the same QUESTION at both sites instead.
|
|
35912
|
+
*/
|
|
35913
|
+
recreateDirectionFor(stackName, logicalId) {
|
|
35914
|
+
const targets = this.options.recreateTargets?.stackName === stackName ? this.options.recreateTargets : void 0;
|
|
35915
|
+
if (targets === void 0) return void 0;
|
|
35916
|
+
if (targets.viaCcApi.has(logicalId)) return "cc-api";
|
|
35917
|
+
if (targets.viaSdkProvider.has(logicalId)) return "sdk";
|
|
35918
|
+
}
|
|
35919
|
+
peekRoutingForLabel(change, existingState, stackName, logicalId, needsReplacement = false, recreateDirection) {
|
|
35920
|
+
if (needsReplacement) return deriveLabelRouting(change, recreateDirection === void 0 ? void 0 : { provisionedBy: recreateDirection }, this.providerRegistry, recreateDirection === "cc-api");
|
|
35921
|
+
return deriveLabelRouting(change, existingState, this.providerRegistry, this.isPinnedToCcApi(stackName, logicalId));
|
|
35762
35922
|
}
|
|
35763
35923
|
/**
|
|
35764
35924
|
* #808 — forward one structured deployment event to the optional
|
|
@@ -35990,7 +36150,8 @@ var DeployEngine = class {
|
|
|
35990
36150
|
const replaceDecision = this.providerRegistry.getProviderFor({
|
|
35991
36151
|
resourceType,
|
|
35992
36152
|
properties: resolvedProps,
|
|
35993
|
-
...recreateDirectionHint && { provisionedBy: recreateDirectionHint }
|
|
36153
|
+
...recreateDirectionHint && { provisionedBy: recreateDirectionHint },
|
|
36154
|
+
...recreateViaCcApi && { forceCcApi: true }
|
|
35994
36155
|
});
|
|
35995
36156
|
const replaceProvider = replaceDecision.provider;
|
|
35996
36157
|
const replaceProps = replaceDecision.provisionedBy === "cc-api" ? this.preparePropertiesForCcApi(resourceType, resolvedProps, logicalId) : resolvedProps;
|
|
@@ -36109,8 +36270,25 @@ var DeployEngine = class {
|
|
|
36109
36270
|
const updateDecision = this.providerRegistry.getProviderFor({
|
|
36110
36271
|
resourceType,
|
|
36111
36272
|
properties: resolvedProps,
|
|
36112
|
-
provisionedBy: currentResource.provisionedBy
|
|
36273
|
+
provisionedBy: currentResource.provisionedBy,
|
|
36274
|
+
previousProperties: currentResource.properties,
|
|
36275
|
+
...this.isPinnedToCcApi(stackName, logicalId) && { forceCcApi: true }
|
|
36113
36276
|
});
|
|
36277
|
+
if (updateDecision.sdkMigration === true) {
|
|
36278
|
+
const exemptMode = STICKY_CC_MIGRATION_EXEMPT.get(resourceType)?.mode;
|
|
36279
|
+
const preserved = "The physical id is preserved";
|
|
36280
|
+
let message;
|
|
36281
|
+
switch (exemptMode) {
|
|
36282
|
+
case "cc-broken":
|
|
36283
|
+
message = `${logicalId} (${resourceType}): moving to the SDK provider — Cloud Control cannot manage this type correctly. ${preserved}, and this routing is not optional.`;
|
|
36284
|
+
break;
|
|
36285
|
+
case "sdk-coverage":
|
|
36286
|
+
message = `${logicalId} (${resourceType}): returning to the SDK provider — cdkd now covers every property this resource uses. ${preserved}; pass --pin-cc-api ${logicalId} to decline this for a deploy.`;
|
|
36287
|
+
break;
|
|
36288
|
+
default: message = `${logicalId} (${resourceType}): moving to the SDK provider. ${preserved}.`;
|
|
36289
|
+
}
|
|
36290
|
+
this.logger.info(message);
|
|
36291
|
+
}
|
|
36114
36292
|
const updateProvider = updateDecision.provider;
|
|
36115
36293
|
const updateProps = updateDecision.provisionedBy === "cc-api" ? this.preparePropertiesForCcApi(resourceType, resolvedProps, logicalId) : resolvedProps;
|
|
36116
36294
|
let result;
|
|
@@ -36563,10 +36741,55 @@ var DeployEngine = class {
|
|
|
36563
36741
|
* the deploy instead of silently publishing nothing (which breaks downstream
|
|
36564
36742
|
* `Fn::ImportValue` consumers with "export not found" long after this deploy
|
|
36565
36743
|
* exits 0).
|
|
36566
|
-
|
|
36567
|
-
|
|
36568
|
-
|
|
36569
|
-
|
|
36744
|
+
*
|
|
36745
|
+
* The error is masked on both arms (issue
|
|
36746
|
+
* [#2728](https://github.com/go-to-k/cdkd/issues/2728)). The resolver's own
|
|
36747
|
+
* failures echo the offending reference token, a secret id / JSON key, or
|
|
36748
|
+
* an SSM parameter name, and `resolveSub` / `resolveJoin` re-enter
|
|
36749
|
+
* `resolveDynamicReferences` with the ASSEMBLED string — so a reference
|
|
36750
|
+
* built out of a value this same pass resolved from a secret puts that
|
|
36751
|
+
* plaintext into the echoed field (a JSON key assembled from the resolved
|
|
36752
|
+
* password says `key '<password>' not found`).
|
|
36753
|
+
*
|
|
36754
|
+
* TWO bags, the inherited one first, the same pair the resolver's
|
|
36755
|
+
* `maskSecretsForLog` masks against and for the same reason (issue #1903
|
|
36756
|
+
* round 2): on a nested-stack child the parent-decrypted parameter
|
|
36757
|
+
* plaintext is in `inheritedSecrets` and not in the pass map until a
|
|
36758
|
+
* `{Ref: <Param>}` resolution copies it across. No shape reaching this
|
|
36759
|
+
* handler before that copy has been constructed (every `${Param}` route
|
|
36760
|
+
* goes through `resolveRef`, which records), so the inherited bag is
|
|
36761
|
+
* defense in depth here — but two masking sites in one flow must not argue
|
|
36762
|
+
* opposite sides of the same question. `secrets` is the outputs pass's own
|
|
36763
|
+
* map: everything recorded before this handler runs, an `Export.Name`
|
|
36764
|
+
* resolution's entries included (its `finally` merges them back before the
|
|
36765
|
+
* `catch` reaches here). What a still-pending concurrent part would have
|
|
36766
|
+
* recorded is outside both — issue #2563's late write, the same bound every
|
|
36767
|
+
* other masking site in this engine has.
|
|
36768
|
+
*
|
|
36769
|
+
* The strict arm's `cause` is masked as an OBJECT, through
|
|
36770
|
+
* `maskSecretsInError` — a clone of each `Error` link `errorCauseChain`
|
|
36771
|
+
* reaches (`ERROR_CAUSE_MASK_MAX_DEPTH` links, a cycle stops it; a
|
|
36772
|
+
* non-`Error` cause is kept verbatim), symbols included, so
|
|
36773
|
+
* `isMarkedNonRetryable`'s non-enumerable marker survives (the same reason
|
|
36774
|
+
* `provisionResource` uses it). No sink on the deploy path renders past
|
|
36775
|
+
* one level today (`formatError` prints `Caused by:` for a `CdkdError`'s
|
|
36776
|
+
* direct cause only), but that is a property of reachability, not of the
|
|
36777
|
+
* sinks: `cdkd scrub`'s `describeFailure` renders a chain, and is safe
|
|
36778
|
+
* because its boundary masks with this same helper and it walks the same
|
|
36779
|
+
* bounded `errorCauseChain`. Masking here gives a renderer within that
|
|
36780
|
+
* bound nothing to leak. A thrown STRING is masked as text; any other
|
|
36781
|
+
* non-`Error` value is not threaded as a cause at all (it would travel
|
|
36782
|
+
* unmasked, and `markNonRetryable` cannot have marked it).
|
|
36783
|
+
*/
|
|
36784
|
+
handleOutputResolutionFailure(error, outputKey, outputs, secrets, inheritedSecrets) {
|
|
36785
|
+
let detail = error instanceof Error ? error.message || error.name : String(error);
|
|
36786
|
+
for (const bag of [inheritedSecrets, secrets]) detail = maskSecretsInText(detail, bag);
|
|
36787
|
+
if (this.options.strictGetAtt) {
|
|
36788
|
+
let cause = error;
|
|
36789
|
+
for (const bag of [inheritedSecrets, secrets]) cause = typeof cause === "string" ? maskSecretsInText(cause, bag) : maskSecretsInError(cause, bag);
|
|
36790
|
+
throw new Error(`Failed to resolve output ${outputKey}: ${detail} (--strict-getatt promotes output resolution failures to deploy errors; drop the flag to warn and skip the output instead)`, cause instanceof Error || typeof cause === "string" ? { cause } : {});
|
|
36791
|
+
}
|
|
36792
|
+
this.logger.warn(`Failed to resolve output ${outputKey}: ${detail}`);
|
|
36570
36793
|
outputs[outputKey] = void 0;
|
|
36571
36794
|
}
|
|
36572
36795
|
/**
|
|
@@ -36588,6 +36811,8 @@ var DeployEngine = class {
|
|
|
36588
36811
|
...parameterValues && { parameters: parameterValues },
|
|
36589
36812
|
...conditions && { conditions }
|
|
36590
36813
|
}, stackName);
|
|
36814
|
+
const outputsPassSecrets = context.recordedSecretValues ?? EMPTY_SECRETS;
|
|
36815
|
+
const outputsPassInherited = context.inheritedSecrets ?? EMPTY_SECRETS;
|
|
36591
36816
|
const publishedOutputNames = collectPublishedOutputNames(template.Outputs, conditions);
|
|
36592
36817
|
let outputsPassCompleted = false;
|
|
36593
36818
|
try {
|
|
@@ -36599,7 +36824,7 @@ var DeployEngine = class {
|
|
|
36599
36824
|
try {
|
|
36600
36825
|
outputs[outputKey] = await this.resolver.resolve(output.Value, context);
|
|
36601
36826
|
} catch (error) {
|
|
36602
|
-
this.handleOutputResolutionFailure(error, outputKey, outputs);
|
|
36827
|
+
this.handleOutputResolutionFailure(error, outputKey, outputs, outputsPassSecrets, outputsPassInherited);
|
|
36603
36828
|
}
|
|
36604
36829
|
}
|
|
36605
36830
|
for (const [outputKey, output] of Object.entries(template.Outputs)) {
|
|
@@ -36619,7 +36844,7 @@ var DeployEngine = class {
|
|
|
36619
36844
|
for (const [plaintext, expression] of nameSecrets) context.recordedSecretValues?.set(plaintext, expression);
|
|
36620
36845
|
}
|
|
36621
36846
|
} catch (error) {
|
|
36622
|
-
this.handleOutputResolutionFailure(error, outputKey, outputs);
|
|
36847
|
+
this.handleOutputResolutionFailure(error, outputKey, outputs, outputsPassSecrets, outputsPassInherited);
|
|
36623
36848
|
continue;
|
|
36624
36849
|
}
|
|
36625
36850
|
if (typeof exportName !== "string") continue;
|
|
@@ -36658,5 +36883,5 @@ var DeployEngine = class {
|
|
|
36658
36883
|
};
|
|
36659
36884
|
|
|
36660
36885
|
//#endregion
|
|
36661
|
-
export {
|
|
36662
|
-
//# sourceMappingURL=deploy-engine-
|
|
36886
|
+
export { findActionableSilentDrops as $, runDockerStreaming as $n, StackTerminationProtectionError as $r, carriesSecretMask as $t, WARM_THROUGHPUT_MEMBERS as A, rewriteTemplateAssetReferences as An, clearBucketRegionCache as Ar, configStringRefusal as At, yellow as B, validateContainerRepoName as Bn, DependencyError as Br, s3BucketDualStackDomainName as Bt, unsupportedFinalSnapshotError as C, shouldRetainResource as Cn, displaySafe as Cr, refStateLookupFromResource as Ct, isStatefulRecreateTargetForReplace as D, buildAssetRedirectMap as Dn, derivePartitionAndUrlSuffix as Dr, assertRegionMatch as Dt, MULTI_REGION_RECREATE_BLOCKED_TYPES as E, WorkGraph as En, canonicalizeRegion as Er, resolveExplicitPhysicalId as Et, bold as F, getBootstrapMarkerKey as Fn, setAwsClients as Fr, requireConfigString as Ft, secretBearingStateKeyWarning as G, describeDockerExecFailure as Gn, LocalStartServiceError as Gr, INTRINSIC_KEYS as Gt, collectPublishedOutputNames as H, describeAwsFailure as Hn, DynamicReferenceRegionAmbiguousError as Hr, s3BucketWebsiteUrl as Ht, cyan as I, isCrossRegionRedirect as In, AssetError as Ir, classifyReplaySecretRegion as It, IAMRoleProvider as J, formatDockerLoginError as Jn, PartialFailureError as Jr, DagBuilder as Jt, stateKeySecretExposure as K, describeDockerFailure as Kn, LockError as Kr, describeTypeWithThrottleRetry as Kt, gray as L, parseBootstrapMarker as Ln, CdkdError as Lr, producerRegionsFromState as Lt, isWarmThroughputDecrease as M, BOOTSTRAP_MARKER_PREFIX as Mn, AwsClients as Mr, replayWarn as Mt, toFiniteNumber as N, assertAssetBucketRegion as Nn, getAwsClients as Nr, requireConfigArray as Nt, isStatefulRecreateTargetSync as O, createAssetRedirectResolver as On, AssemblyReader as Or, coerceCfnBoolean as Ot, formatResourceLine as P, ensureAssetStorage as Pn, resetAwsClients as Pr, requireConfigObject as Pt, wouldReturnToSdkProvider as Q, runDockerForeground as Qn, StackHasActiveImportsError as Qr, TEMPLATE_SOURCED_RULES as Qt, green as R, readBootstrapMarkerBody as Rn, ConfigError as Rr, s3BucketArn as Rt, refusesFinalSnapshot as S, importableOutputs as Sn, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Sr, parameterTypeMayLoseSecretIdentity as St, extractDeploymentEventError as T, stringifyValue as Tn, PARTITION_TABLE as Tr, normalizeAwsTagsToCfn as Tt, exportAliasCollisionScrubWarning as U, buildDockerImage as Un, IntrinsicResolutionRefusalError as Ur, applyRoleArnIfSet as Ut, collectDeclaredOutputNames as V, buildDenyExternalAccessPolicy as Vn, DeployCancelledError as Vr, s3BucketRegionalDomainName as Vt, isExportAliasCollision as W, describeDockerCapturedOutput as Wn, LocalInvokeBuildError as Wr, DiffCalculator as Wt, clearOnUpdateRemoval as X, partitionSensitiveEnv as Xn, ResourceTimeoutError as Xr, STATE_SOURCED_CROSS_GENERATION_RULES as Xt, collectInlinePolicyNamesManagedBySiblings as Y, getDockerCmd as Yn, ProvisioningError as Yr, TemplateParser as Yt, ProviderRegistry as Z, redactDockerArgvValues as Zn, ResourceUpdateNotSupportedError as Zr, STATE_SOURCED_READBACK_RULES as Zt, PRE_DELETE_SNAPSHOT_TYPES as _, shellQuote as _n, CFN_TEMPLATE_BODY_LIMIT as _r, carriesDynamicReference as _t, DeploymentEventsStore as a, withErrorHandling as ai, maskSecretsInText as an, synthesisStatusMessage as ar, endCommandInterruptScope as at, createPreDeleteFinalSnapshot as b, exportNamesCarriedFrom as bn, findLargeInlineResources as br, getAccountInfo as bt, replayFailedOperations as c, isThrottlingError as ci, redactSecretsForState as cn, resolveApp as cr, startInterruptWatch as ct, updatePartialReason as d, markRedactedCause as di, S3StateBackend as dn, resolveSkipPrefix as dr, UNSPECIFIED_SKIP_REASON as dt, StateError as ei, createSecretMasker as en, escapeRegExp$1 as er, findSilentDropProperties as et, withResourceDeadline as f, retryClassificationText as fi, rebuildClientForBucketRegion as fn, resolveStateBucketWithDefault as fr, deleteIndeterminateGuards as ft, ATOMIC_FINAL_SNAPSHOT_TYPES as g, forceQuitRecoveryClause as gn, warnDeprecatedNoPrefixCliFlag as gr, IntrinsicFunctionResolver as gt, computeImplicitDeleteEdges as h, buildLockContentionMessage as hn, stateBucketExistenceConfirmed as hr, isTerminationProtectionPropagationError as ht, DeploymentEventsReader as i, normalizeAwsError as ii, maskSecretsInError as in, Synthesizer as ir, beginCommandInterruptScope as it, coerceWarmThroughput as j, AssetModeResolver as jn, resolveBucketRegion as jr, readConfigString as jt, renderStatefulReason as k, loadPublishableAssetManifest as kn, processStackMessages as kr, configBooleanRefusal as kt, replayRollback as l, isTransientServerError as li, scrubResourceRecord as ln, resolveAutoAssetStorage as lr, CloudControlProvider as lt, IMPLICIT_DELETE_DEPENDENCIES as m, buildForceUnlockCommand as mn, resolveUseCdkBootstrapAssets as mr, disableInstanceApiTermination as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, formatError as ni, errorCauseChain as nn, AssetManifestLoader as nr, maskDeep as nt, planFailedOps as o, isMarkedNonRetryable as oi, recordMaskOnlyValue as on, getDefaultStateBucketName as or, interruptWatchListenerCount as ot, maskingRetryLogger as p, __exportAll as pi, UNRENDERABLE as pn, resolveStateBucketWithDefaultAndSource as pr, deleteSkipReason as pt, getCurrentResourceSecrets as q, dockerSpawnEnvWithSensitive as qn, NestedStackChildDirectDestroyError as qr, withRetry as qt, DeployEngine as r, isCdkdError as ri, isSingleDynamicReferenceToken as rn, getDockerImageBySourceHash as rr, maskerOrIdentity as rt, planRollback as s, isRetryableTransientError as si, recoverMaskedOutput as sn, getLegacyStateBucketName as sr, isInterruptedWaitError as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, SynthesisError as ti, dynamicReferenceTokens as tn, stripControlChars as tr, createMaskedRetryLogger as tt, updatePartialMessage as u, markNonRetryable as ui, LockManager as un, resolveCaptureObservedState as ur, slowCcOperationTimeoutMs as ut, buildFinalSnapshotIdentifier as v, CUSTOM_RESOURCE_RESPONSE_PREFIX as vn, CFN_TEMPLATE_URL_LIMIT as vr, cfnRefValueFromPhysicalId as vt, makeCanonicalizePropertiesFn as w, AssetPublisher as wn, expectedOwnerParam as wr, WAFv2WebACLProvider as wt, isFinalSnapshotError as x, importableOutputKeys as xn, uploadCfnTemplate as xr, isUnboundTemplateParameter as xt, ccRoutedFinalSnapshotError as y, DEFAULT_STATE_PREFIX as yn, MIGRATE_TMP_PREFIX as yr, coerceParameterTypedValue as yt, red as z, validateAssetBucketName as zn, CrossAccountSecretRefusalError as zr, s3BucketDomainName as zt };
|
|
36887
|
+
//# sourceMappingURL=deploy-engine-B377YKBI.js.map
|