@go-to-k/cdkd 0.281.13 → 0.281.15
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-CsY1ahKM.js → asg-provider-CgQFJdPo.js} +2 -2
- package/dist/{asg-provider-CsY1ahKM.js.map → asg-provider-CgQFJdPo.js.map} +1 -1
- package/dist/cli.js +428 -161
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-BWM9RxAW.js → deploy-engine-B2MDP-jK.js} +276 -46
- package/dist/deploy-engine-B2MDP-jK.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-BWM9RxAW.js.map +0 -1
|
@@ -216,6 +216,33 @@ function formatDuration(ms) {
|
|
|
216
216
|
return minutes === 0 ? `${hours}h` : `${hours}h${minutes}m`;
|
|
217
217
|
}
|
|
218
218
|
/**
|
|
219
|
+
* A DELIBERATE refusal to resolve an intrinsic function, as opposed to a
|
|
220
|
+
* "the referenced thing does not exist" miss (issue
|
|
221
|
+
* [#1740](https://github.com/go-to-k/cdkd/issues/1740)).
|
|
222
|
+
*
|
|
223
|
+
* The distinction exists for exactly one consumer: `Fn::Sub`'s variable
|
|
224
|
+
* resolution, which speculatively tries `Ref` and then `Fn::GetAtt` and keeps
|
|
225
|
+
* the raw `${...}` placeholder when neither resolves. That warn-and-keep is the
|
|
226
|
+
* long-standing, deliberate behavior for a genuinely unknown variable — but a
|
|
227
|
+
* bare `catch` around it also swallowed every REFUSAL the resolver raises on
|
|
228
|
+
* purpose (`guardedPhysicalIdFallback`'s ARN / URL shape hard-fail, the
|
|
229
|
+
* `--strict-getatt` rejection, `rejectPlaceholderArnAttribute`), so a template
|
|
230
|
+
* that hard-fails when the reference sits in a resource property silently
|
|
231
|
+
* degraded to shipping a literal `${Resource.Attribute}` to AWS when the
|
|
232
|
+
* IDENTICAL reference was written inside an `Fn::Sub`.
|
|
233
|
+
*
|
|
234
|
+
* Throwing this class rather than a bare `Error` is what lets that catch
|
|
235
|
+
* re-raise a refusal (carrying its own message and remedy) while leaving the
|
|
236
|
+
* not-found path on warn-and-keep. Nothing else branches on it.
|
|
237
|
+
*/
|
|
238
|
+
var IntrinsicResolutionRefusalError = class IntrinsicResolutionRefusalError extends CdkdError {
|
|
239
|
+
constructor(message, cause) {
|
|
240
|
+
super(message, "INTRINSIC_RESOLUTION_REFUSAL", cause);
|
|
241
|
+
this.name = "IntrinsicResolutionRefusalError";
|
|
242
|
+
Object.setPrototypeOf(this, IntrinsicResolutionRefusalError.prototype);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
/**
|
|
219
246
|
* Dependency resolution errors
|
|
220
247
|
*/
|
|
221
248
|
var DependencyError = class DependencyError extends CdkdError {
|
|
@@ -9205,6 +9232,53 @@ async function applyRoleArnIfSet(opts) {
|
|
|
9205
9232
|
}
|
|
9206
9233
|
}
|
|
9207
9234
|
|
|
9235
|
+
//#endregion
|
|
9236
|
+
//#region src/utils/aws-partition.ts
|
|
9237
|
+
/**
|
|
9238
|
+
* AWS partition / URL-suffix derivation, shared across layers.
|
|
9239
|
+
*
|
|
9240
|
+
* Lives in `src/utils/` because it has consumers in two different layers: the
|
|
9241
|
+
* `cdkd local *` command family (which passes a region in once to keep the STS
|
|
9242
|
+
* hop minimal) and the provisioning layer's `AppSyncProvider`, which rebuilds a
|
|
9243
|
+
* child resource's ARN when AWS did not report one.
|
|
9244
|
+
*
|
|
9245
|
+
* It was originally defined in `src/local/ecs-task-resolver.ts`, which
|
|
9246
|
+
* re-exports it so every existing call site is unchanged; a provisioning
|
|
9247
|
+
* provider importing from `src/local/**` would invert the layering.
|
|
9248
|
+
*
|
|
9249
|
+
* NOTE `getAccountInfo().partition`
|
|
9250
|
+
* (`src/deployment/intrinsic-function-resolver.ts`) was hardcoded to `'aws'`
|
|
9251
|
+
* until issue #1730, which made it derive through THIS helper — so the two now
|
|
9252
|
+
* agree and either spelling is correct. Prefer this one where a region is
|
|
9253
|
+
* already in hand, since it needs no STS round trip.
|
|
9254
|
+
*/
|
|
9255
|
+
/**
|
|
9256
|
+
* Derive the AWS partition / URL suffix for an AWS region. Same mapping
|
|
9257
|
+
* CloudFormation applies to `${AWS::Partition}` / `${AWS::URLSuffix}`.
|
|
9258
|
+
*/
|
|
9259
|
+
function derivePartitionAndUrlSuffix(region) {
|
|
9260
|
+
if (region.startsWith("cn-")) return {
|
|
9261
|
+
partition: "aws-cn",
|
|
9262
|
+
urlSuffix: "amazonaws.com.cn"
|
|
9263
|
+
};
|
|
9264
|
+
if (region.startsWith("us-gov-")) return {
|
|
9265
|
+
partition: "aws-us-gov",
|
|
9266
|
+
urlSuffix: "amazonaws.com"
|
|
9267
|
+
};
|
|
9268
|
+
if (region.startsWith("us-iso-")) return {
|
|
9269
|
+
partition: "aws-iso",
|
|
9270
|
+
urlSuffix: "c2s.ic.gov"
|
|
9271
|
+
};
|
|
9272
|
+
if (region.startsWith("us-isob-")) return {
|
|
9273
|
+
partition: "aws-iso-b",
|
|
9274
|
+
urlSuffix: "sc2s.sgov.gov"
|
|
9275
|
+
};
|
|
9276
|
+
return {
|
|
9277
|
+
partition: "aws",
|
|
9278
|
+
urlSuffix: "amazonaws.com"
|
|
9279
|
+
};
|
|
9280
|
+
}
|
|
9281
|
+
|
|
9208
9282
|
//#endregion
|
|
9209
9283
|
//#region src/provisioning/config-shape.ts
|
|
9210
9284
|
/**
|
|
@@ -10712,44 +10786,103 @@ const cachedDynamicReferences = {};
|
|
|
10712
10786
|
*/
|
|
10713
10787
|
const cachedEc2InstanceAttributes = {};
|
|
10714
10788
|
/**
|
|
10789
|
+
* Re-derive the partition for a region the caller overrode (issue #1730).
|
|
10790
|
+
*
|
|
10791
|
+
* `partition` is a FUNCTION of `region`, so handing back a cached entry with a
|
|
10792
|
+
* different region than the one its partition was derived from would produce
|
|
10793
|
+
* `arn:aws:...:cn-north-1:...` for a `cn-` override. Every return path that
|
|
10794
|
+
* swaps the region goes through here.
|
|
10795
|
+
*/
|
|
10796
|
+
function withOverrideRegion(info, region) {
|
|
10797
|
+
return {
|
|
10798
|
+
...info,
|
|
10799
|
+
region,
|
|
10800
|
+
partition: derivePartitionAndUrlSuffix(region).partition
|
|
10801
|
+
};
|
|
10802
|
+
}
|
|
10803
|
+
/**
|
|
10804
|
+
* How long a FABRICATED answer is reused before STS is retried (issue #1730,
|
|
10805
|
+
* PR review). Deliberately not the success path's forever-cache — the whole
|
|
10806
|
+
* point is that a transient blip must not poison the run — but not zero either:
|
|
10807
|
+
* `getAccountInfo` is on the path of EVERY `Fn::GetAtt` and every
|
|
10808
|
+
* `AWS::AccountId` / `AWS::Partition` / `AWS::StackId` pseudo-parameter, so an
|
|
10809
|
+
* uncached failure re-issues `GetCallerIdentity` (with the SDK's own 3-attempt
|
|
10810
|
+
* retry + backoff) dozens of times per stack and prints one warning each. This
|
|
10811
|
+
* window collapses a burst into one call while still letting a later phase of
|
|
10812
|
+
* the same deploy heal.
|
|
10813
|
+
*/
|
|
10814
|
+
const FABRICATED_ACCOUNT_INFO_TTL_MS = 1e4;
|
|
10815
|
+
/** Test seam for {@link FABRICATED_ACCOUNT_INFO_TTL_MS} expiry. */
|
|
10816
|
+
const accountInfoClock = { now: () => Date.now() };
|
|
10817
|
+
let fabricatedAccountInfo = null;
|
|
10818
|
+
/**
|
|
10819
|
+
* The single in-flight lookup, so N concurrent callers share ONE round trip.
|
|
10820
|
+
*
|
|
10821
|
+
* The TTL above collapses SEQUENTIAL callers; this collapses PARALLEL ones
|
|
10822
|
+
* (PR review). `cdkd deploy --concurrency 10` resolves ten resources' intrinsics
|
|
10823
|
+
* at once, so without it an STS outage costs ten `GetCallerIdentity` calls —
|
|
10824
|
+
* each with the SDK's own 3-attempt retry — and ten identical warnings per
|
|
10825
|
+
* window. Cleared in a `finally` so a failure cannot wedge it.
|
|
10826
|
+
*/
|
|
10827
|
+
let accountInfoInFlight = null;
|
|
10828
|
+
/**
|
|
10715
10829
|
* Get AWS account information from STS
|
|
10716
10830
|
*/
|
|
10717
10831
|
async function getAccountInfo(overrideRegion) {
|
|
10718
|
-
|
|
10719
|
-
|
|
10720
|
-
|
|
10721
|
-
|
|
10722
|
-
|
|
10723
|
-
|
|
10832
|
+
const forRegion = (info) => overrideRegion && overrideRegion !== info.region ? withOverrideRegion(info, overrideRegion) : info;
|
|
10833
|
+
if (cachedAccountInfo) return forRegion(cachedAccountInfo);
|
|
10834
|
+
if (fabricatedAccountInfo && accountInfoClock.now() < fabricatedAccountInfo.expiresAt) return forRegion(fabricatedAccountInfo.info);
|
|
10835
|
+
if (accountInfoInFlight) return forRegion(await accountInfoInFlight);
|
|
10836
|
+
accountInfoInFlight = resolveAccountInfo(overrideRegion);
|
|
10837
|
+
try {
|
|
10838
|
+
return forRegion(await accountInfoInFlight);
|
|
10839
|
+
} finally {
|
|
10840
|
+
accountInfoInFlight = null;
|
|
10724
10841
|
}
|
|
10842
|
+
}
|
|
10843
|
+
async function resolveAccountInfo(overrideRegion) {
|
|
10725
10844
|
const logger = getLogger().child("IntrinsicFunctionResolver");
|
|
10726
10845
|
const stsClient = getAwsClients().sts;
|
|
10727
10846
|
try {
|
|
10728
10847
|
const response = await stsClient.send(new GetCallerIdentityCommand({}));
|
|
10729
10848
|
const accountId = response.Account || "123456789012";
|
|
10730
10849
|
const region = overrideRegion || process.env["AWS_REGION"] || "us-east-1";
|
|
10731
|
-
const partition =
|
|
10732
|
-
|
|
10850
|
+
const partition = derivePartitionAndUrlSuffix(region).partition;
|
|
10851
|
+
const resolved = {
|
|
10733
10852
|
accountId,
|
|
10734
10853
|
region,
|
|
10735
10854
|
partition,
|
|
10736
10855
|
...response.Account ? {} : { fabricated: true }
|
|
10737
10856
|
};
|
|
10738
|
-
|
|
10739
|
-
|
|
10740
|
-
|
|
10741
|
-
region: overrideRegion
|
|
10857
|
+
if (resolved.fabricated) fabricatedAccountInfo = {
|
|
10858
|
+
info: resolved,
|
|
10859
|
+
expiresAt: accountInfoClock.now() + FABRICATED_ACCOUNT_INFO_TTL_MS
|
|
10742
10860
|
};
|
|
10743
|
-
|
|
10861
|
+
else {
|
|
10862
|
+
cachedAccountInfo = resolved;
|
|
10863
|
+
fabricatedAccountInfo = null;
|
|
10864
|
+
}
|
|
10865
|
+
logger.debug(`Retrieved AWS account info: ${accountId}, ${region}, ${partition}`);
|
|
10866
|
+
return resolved;
|
|
10744
10867
|
} catch (error) {
|
|
10745
10868
|
logger.warn(`Failed to get AWS account info from STS: ${error instanceof Error ? error.message : String(error)}, using defaults`);
|
|
10746
|
-
|
|
10869
|
+
const region = overrideRegion || process.env["AWS_REGION"] || "us-east-1";
|
|
10870
|
+
const fallback = {
|
|
10747
10871
|
accountId: process.env["AWS_ACCOUNT_ID"] || "123456789012",
|
|
10748
|
-
region
|
|
10749
|
-
partition:
|
|
10872
|
+
region,
|
|
10873
|
+
partition: derivePartitionAndUrlSuffix(region).partition,
|
|
10750
10874
|
...process.env["AWS_ACCOUNT_ID"] ? {} : { fabricated: true }
|
|
10751
10875
|
};
|
|
10752
|
-
|
|
10876
|
+
if (fallback.fabricated) {
|
|
10877
|
+
if (!cachedAccountInfo) fabricatedAccountInfo = {
|
|
10878
|
+
info: fallback,
|
|
10879
|
+
expiresAt: accountInfoClock.now() + FABRICATED_ACCOUNT_INFO_TTL_MS
|
|
10880
|
+
};
|
|
10881
|
+
} else {
|
|
10882
|
+
cachedAccountInfo = fallback;
|
|
10883
|
+
fabricatedAccountInfo = null;
|
|
10884
|
+
}
|
|
10885
|
+
return fallback;
|
|
10753
10886
|
}
|
|
10754
10887
|
}
|
|
10755
10888
|
/**
|
|
@@ -11135,7 +11268,7 @@ var IntrinsicFunctionResolver = class {
|
|
|
11135
11268
|
}
|
|
11136
11269
|
}
|
|
11137
11270
|
}
|
|
11138
|
-
const value = await this.
|
|
11271
|
+
const value = await this.constructGuardedAttribute(resource, attributeName, context, logicalId);
|
|
11139
11272
|
this.logger.debug(`Resolved Fn::GetAtt: ${logicalId}.${attributeName} -> ${stringifyAttributeForLog(attributeName, value)}`);
|
|
11140
11273
|
return value;
|
|
11141
11274
|
}
|
|
@@ -11171,17 +11304,67 @@ var IntrinsicFunctionResolver = class {
|
|
|
11171
11304
|
rejectPlaceholderArnAttribute(resource, attributeName, value, logicalId) {
|
|
11172
11305
|
if (!REF_RETURNS_ARN_FROM_STATE.get(resource.resourceType)?.includes(attributeName)) return;
|
|
11173
11306
|
if (typeof value !== "string" || !isPlaceholderArn(value)) return;
|
|
11174
|
-
throw new
|
|
11307
|
+
throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${attributeName}] for ${resource.resourceType}: the recorded value "${value}" is a placeholder written by a cdkd version older than issue #1681 — its region and account fields are literal wildcards, so it is not a usable ARN. Deploy the stack again so the resource's next update heals the record (cdkd now records the real ARN), or re-import the resource.`);
|
|
11175
11308
|
}
|
|
11176
11309
|
/**
|
|
11177
|
-
* Construct resource attribute value based on resource type
|
|
11310
|
+
* Construct resource attribute value based on resource type, refusing to
|
|
11311
|
+
* SERVE one built from a fabricated account id (issue #1730).
|
|
11312
|
+
*
|
|
11313
|
+
* Thin wrapper over {@link constructAttribute}. ~30 branches there
|
|
11314
|
+
* build `arn:<partition>:<svc>:<region>:<accountId>:...`, and when the account
|
|
11315
|
+
* id is the hardcoded `123456789012` fallback the result is an ARN naming
|
|
11316
|
+
* SOMEONE ELSE'S account with no wildcard in it — so `isPlaceholderArn` cannot
|
|
11317
|
+
* catch it and every consumer downstream, the state record included, receives
|
|
11318
|
+
* a confidently wrong value. Refusing matches how
|
|
11319
|
+
* {@link guardedPhysicalIdFallback} already treats a knowably-wrong `*Arn`
|
|
11320
|
+
* (the #1103 class): a resource in this state has no correct value to serve.
|
|
11321
|
+
*
|
|
11322
|
+
* The test is on the CONSTRUCTED VALUE, not on the attribute NAME, and that
|
|
11323
|
+
* precision is the whole point: `AWS::S3::Bucket`'s `Arn` is
|
|
11324
|
+
* `arn:aws:s3:::<bucket>` with no account field, so a name-based `*Arn` guard
|
|
11325
|
+
* would refuse a value the fabricated id cannot corrupt. Everything the
|
|
11326
|
+
* account id does not appear in — `DomainName`, `Endpoint`, `WebsiteURL` —
|
|
11327
|
+
* keeps resolving unchanged.
|
|
11328
|
+
*
|
|
11329
|
+
* The match is a BARE substring rather than the colon-delimited `:<id>:` an
|
|
11330
|
+
* ARN uses, because not every account embedding is an ARN field: PR review
|
|
11331
|
+
* caught `AWS::ECR::Repository`'s `RepositoryUri`
|
|
11332
|
+
* (`<accountId>.dkr.ecr.<region>.amazonaws.com/<repo>`), the one such site in
|
|
11333
|
+
* this method, where a colon-delimited test served the fabricated URI and
|
|
11334
|
+
* silently nullified the `CloudControlProvider` omission of the SAME
|
|
11335
|
+
* attribute. The direction is deliberately fail-SAFE: refusing is the honest
|
|
11336
|
+
* answer whenever cdkd cannot confirm the account, so a value that merely
|
|
11337
|
+
* CONTAINS the placeholder digits (a physicalId recorded against the AWS
|
|
11338
|
+
* documentation account) is refused rather than served — and only while STS
|
|
11339
|
+
* is failing, when the deploy has bigger problems.
|
|
11340
|
+
*
|
|
11341
|
+
* NOTE the naming: the per-type construction below KEEPS the name
|
|
11342
|
+
* `constructAttribute` and this guard takes a new one, rather than the other
|
|
11343
|
+
* way round. `scripts/gen-sdk-attr-coverage.ts` collects the set of resource
|
|
11344
|
+
* types `constructAttribute` references to decide which `*Arn` attributes the
|
|
11345
|
+
* resolver can already answer, so renaming that method emptied its walk and
|
|
11346
|
+
* the critic reported fresh `gap`s for CloudTrail Trail / RDS DBCluster /
|
|
11347
|
+
* DBInstance (measured — the first cut of this change did exactly that).
|
|
11348
|
+
*/
|
|
11349
|
+
async constructGuardedAttribute(resource, attributeName, context, logicalId) {
|
|
11350
|
+
const accountInfo = await getAccountInfo(this.resolverRegion);
|
|
11351
|
+
const value = await this.constructAttribute(resource, attributeName, context, logicalId, accountInfo);
|
|
11352
|
+
if (accountInfo.fabricated && typeof value === "string" && value.includes(accountInfo.accountId)) throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${attributeName}] for ${resource.resourceType}: STS did not report this deploy's account id, so cdkd would build the value from the placeholder account ${accountInfo.accountId} — structurally valid, naming a different account, and indistinguishable downstream from a real one. Fix the AWS credentials (or set AWS_ACCOUNT_ID to this deploy's account) and deploy again.`);
|
|
11353
|
+
return value;
|
|
11354
|
+
}
|
|
11355
|
+
/**
|
|
11356
|
+
* The per-resource-type attribute construction itself.
|
|
11178
11357
|
*
|
|
11179
11358
|
* Many CloudFormation attributes are not returned by Cloud Control API,
|
|
11180
11359
|
* so we need to construct them manually.
|
|
11360
|
+
*
|
|
11361
|
+
* Reached only through {@link constructGuardedAttribute}, which vets the
|
|
11362
|
+
* result. Keep this method's NAME — `scripts/gen-sdk-attr-coverage.ts` reads
|
|
11363
|
+
* the resource types it references.
|
|
11181
11364
|
*/
|
|
11182
|
-
async constructAttribute(resource, attributeName, _context, logicalId) {
|
|
11365
|
+
async constructAttribute(resource, attributeName, _context, logicalId, accountInfo) {
|
|
11183
11366
|
const { resourceType, physicalId } = resource;
|
|
11184
|
-
const { region, accountId, partition } =
|
|
11367
|
+
const { region, accountId, partition } = accountInfo;
|
|
11185
11368
|
if (resourceType === "AWS::DynamoDB::Table" || resourceType === "AWS::DynamoDB::GlobalTable") switch (attributeName) {
|
|
11186
11369
|
case "Arn": return `arn:${partition}:dynamodb:${region}:${accountId}:table/${physicalId}`;
|
|
11187
11370
|
case "StreamArn": return;
|
|
@@ -11366,7 +11549,7 @@ var IntrinsicFunctionResolver = class {
|
|
|
11366
11549
|
}
|
|
11367
11550
|
if (resourceType === "AWS::ECR::Repository") switch (attributeName) {
|
|
11368
11551
|
case "Arn": return `arn:${partition}:ecr:${region}:${accountId}:repository/${physicalId}`;
|
|
11369
|
-
case "RepositoryUri": return `${accountId}.dkr.ecr.${region}.
|
|
11552
|
+
case "RepositoryUri": return `${accountId}.dkr.ecr.${region}.${derivePartitionAndUrlSuffix(region).urlSuffix}/${physicalId}`;
|
|
11370
11553
|
default: return this.guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId);
|
|
11371
11554
|
}
|
|
11372
11555
|
if (resourceType === "AWS::ECS::Cluster") switch (attributeName) {
|
|
@@ -11494,8 +11677,8 @@ var IntrinsicFunctionResolver = class {
|
|
|
11494
11677
|
guardedPhysicalIdFallback(logicalId, attributeName, resourceType, physicalId) {
|
|
11495
11678
|
const expectsArnShape = attributeName.endsWith("Arn") && !physicalId.startsWith("arn:");
|
|
11496
11679
|
const expectsUrlShape = attributeName.endsWith("Url") && !/^https?:\/\//.test(physicalId);
|
|
11497
|
-
if (expectsArnShape || expectsUrlShape) throw new
|
|
11498
|
-
if (this.strictGetAtt) throw new
|
|
11680
|
+
if (expectsArnShape || expectsUrlShape) throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${attributeName}] for ${resourceType}: attributes are not enriched for this resource type, and the physical ID fallback "${physicalId}" is not ${expectsArnShape ? "an ARN (arn:...)" : "a URL (http(s)://...)"}. CloudFormation would return a different value here, so falling back to the physical ID would silently produce a wrong value (e.g. in stack Outputs). Avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${attributeName}.`);
|
|
11681
|
+
if (this.strictGetAtt) throw new IntrinsicResolutionRefusalError(`Cannot resolve Fn::GetAtt [${logicalId}, ${attributeName}] for ${resourceType}: attributes are not enriched for this resource type, and --strict-getatt rejects the physical ID fallback "${physicalId}" (which may not be the value CloudFormation would return). Drop --strict-getatt to fall back with a warning, avoid this Fn::GetAtt, or file an issue at https://github.com/go-to-k/cdkd/issues so cdkd can enrich ${resourceType}.${attributeName}.`);
|
|
11499
11682
|
this.physicalIdFallbackCount++;
|
|
11500
11683
|
this.logger.warn(`Unknown attribute ${attributeName} for resource type ${resourceType}, returning physical ID`);
|
|
11501
11684
|
return physicalId;
|
|
@@ -11519,6 +11702,17 @@ var IntrinsicFunctionResolver = class {
|
|
|
11519
11702
|
return result;
|
|
11520
11703
|
}
|
|
11521
11704
|
/**
|
|
11705
|
+
* The warning emitted when `Fn::Sub` keeps a `${...}` placeholder verbatim.
|
|
11706
|
+
*
|
|
11707
|
+
* It carries the underlying reason (issue #1740 item 2): the old text
|
|
11708
|
+
* asserted `not found` for EVERY failure, which was the wrong cause whenever
|
|
11709
|
+
* the variable WAS found and its resolution failed for some other reason.
|
|
11710
|
+
* Deliberate refusals no longer reach this path at all — they re-throw.
|
|
11711
|
+
*/
|
|
11712
|
+
subPlaceholderWarning(varName, error) {
|
|
11713
|
+
return `Fn::Sub variable ${varName} could not be resolved (${error instanceof Error ? error.message : String(error)}), keeping placeholder`;
|
|
11714
|
+
}
|
|
11715
|
+
/**
|
|
11522
11716
|
* Resolve Fn::Sub intrinsic function
|
|
11523
11717
|
*
|
|
11524
11718
|
* Fn::Sub supports two forms:
|
|
@@ -11563,16 +11757,18 @@ var IntrinsicFunctionResolver = class {
|
|
|
11563
11757
|
else try {
|
|
11564
11758
|
const value = await this.resolveRef(varNameStr, context);
|
|
11565
11759
|
replacement = String(value);
|
|
11566
|
-
} catch {
|
|
11760
|
+
} catch (refError) {
|
|
11567
11761
|
if (varNameStr.includes(".")) try {
|
|
11568
11762
|
const value = await this.resolveGetAtt(varNameStr, context);
|
|
11569
11763
|
replacement = String(value);
|
|
11570
|
-
} catch {
|
|
11571
|
-
|
|
11764
|
+
} catch (getAttError) {
|
|
11765
|
+
if (getAttError instanceof IntrinsicResolutionRefusalError) throw getAttError;
|
|
11766
|
+
this.logger.warn(this.subPlaceholderWarning(varNameStr, getAttError));
|
|
11572
11767
|
replacement = match[0];
|
|
11573
11768
|
}
|
|
11574
11769
|
else {
|
|
11575
|
-
|
|
11770
|
+
if (refError instanceof IntrinsicResolutionRefusalError) throw refError;
|
|
11771
|
+
this.logger.warn(this.subPlaceholderWarning(varNameStr, refError));
|
|
11576
11772
|
replacement = match[0];
|
|
11577
11773
|
}
|
|
11578
11774
|
}
|
|
@@ -12178,9 +12374,9 @@ var IntrinsicFunctionResolver = class {
|
|
|
12178
12374
|
case "AWS::StackName": return context?.stackName ?? "UnknownStack";
|
|
12179
12375
|
case "AWS::StackId": {
|
|
12180
12376
|
const info = await getAccountInfo(this.resolverRegion);
|
|
12181
|
-
return `arn:
|
|
12377
|
+
return `arn:${info.partition}:cloudformation:${info.region}:${info.accountId}:stack/${context?.stackName ?? "UnknownStack"}/cdkd`;
|
|
12182
12378
|
}
|
|
12183
|
-
case "AWS::URLSuffix": return
|
|
12379
|
+
case "AWS::URLSuffix": return derivePartitionAndUrlSuffix(this.resolverRegion).urlSuffix;
|
|
12184
12380
|
case "AWS::NotificationARNs": return "";
|
|
12185
12381
|
case "AWS::NoValue": return AWS_NO_VALUE;
|
|
12186
12382
|
default: return;
|
|
@@ -13208,7 +13404,7 @@ var CloudControlProvider = class {
|
|
|
13208
13404
|
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);
|
|
13209
13405
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
13210
13406
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
13211
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
13407
|
+
const { ASGProvider } = await import("./asg-provider-CgQFJdPo.js").then((n) => n.n);
|
|
13212
13408
|
await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);
|
|
13213
13409
|
return;
|
|
13214
13410
|
}
|
|
@@ -13341,6 +13537,31 @@ var CloudControlProvider = class {
|
|
|
13341
13537
|
}
|
|
13342
13538
|
}
|
|
13343
13539
|
/**
|
|
13540
|
+
* Account info for an ARN / URI this provider SYNTHESIZES and records, or
|
|
13541
|
+
* `undefined` when it must not be built (issue
|
|
13542
|
+
* [#1730](https://github.com/go-to-k/cdkd/issues/1730)).
|
|
13543
|
+
*
|
|
13544
|
+
* `getAccountInfo` falls back to a hardcoded `123456789012` when STS cannot
|
|
13545
|
+
* answer, and an ARN built from it is structurally valid with no wildcard in
|
|
13546
|
+
* any field — so `isPlaceholderArn` (issue #1681) cannot catch it and every
|
|
13547
|
+
* downstream consumer receives a confidently wrong value that is then
|
|
13548
|
+
* RECORDED into state as the resource's `Fn::GetAtt` answer.
|
|
13549
|
+
*
|
|
13550
|
+
* Omitting the attribute is the honest answer and mirrors
|
|
13551
|
+
* `AppSyncProvider.childImportAttributes`: the resolver's own
|
|
13552
|
+
* `guardedPhysicalIdFallback` then hard-fails an `*Arn` read with a message
|
|
13553
|
+
* naming the cause, instead of a green deploy shipping an ARN for someone
|
|
13554
|
+
* else's account, and the record heals on the resource's next update.
|
|
13555
|
+
*/
|
|
13556
|
+
async accountInfoForSynthesizedArn(resourceType, attributeName, physicalId) {
|
|
13557
|
+
const accountInfo = await getAccountInfo();
|
|
13558
|
+
if (accountInfo.fabricated) {
|
|
13559
|
+
this.logger.warn(`Not enriching ${resourceType} ${attributeName} for ${physicalId}: STS did not report this deploy's account id, so the value would be built from a placeholder account and would be indistinguishable from a real one. Fix the credentials (or set AWS_ACCOUNT_ID) and deploy again — the record heals on the next update.`);
|
|
13560
|
+
return;
|
|
13561
|
+
}
|
|
13562
|
+
return accountInfo;
|
|
13563
|
+
}
|
|
13564
|
+
/**
|
|
13344
13565
|
* Enrich resource attributes with computed values
|
|
13345
13566
|
*
|
|
13346
13567
|
* CC API GetResource returns property names that match CloudFormation
|
|
@@ -13419,9 +13640,11 @@ var CloudControlProvider = class {
|
|
|
13419
13640
|
break;
|
|
13420
13641
|
case "AWS::KMS::Key":
|
|
13421
13642
|
if (!enriched["Arn"]) try {
|
|
13422
|
-
const kmsAccountInfo = await
|
|
13423
|
-
|
|
13424
|
-
|
|
13643
|
+
const kmsAccountInfo = await this.accountInfoForSynthesizedArn(resourceType, "Arn", physicalId);
|
|
13644
|
+
if (kmsAccountInfo) {
|
|
13645
|
+
enriched["Arn"] = `arn:${kmsAccountInfo.partition}:kms:${kmsAccountInfo.region}:${kmsAccountInfo.accountId}:key/${physicalId}`;
|
|
13646
|
+
this.logger.debug(`Enriched KMS Key Arn for ${physicalId}: ${String(enriched["Arn"])}`);
|
|
13647
|
+
}
|
|
13425
13648
|
} catch (error) {
|
|
13426
13649
|
this.logger.debug(`Failed to construct KMS Key Arn for ${physicalId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
13427
13650
|
}
|
|
@@ -13435,15 +13658,20 @@ var CloudControlProvider = class {
|
|
|
13435
13658
|
break;
|
|
13436
13659
|
case "AWS::ECR::Repository":
|
|
13437
13660
|
if (!enriched["Arn"]) try {
|
|
13438
|
-
const ecrAccountInfo = await
|
|
13439
|
-
|
|
13440
|
-
|
|
13661
|
+
const ecrAccountInfo = await this.accountInfoForSynthesizedArn(resourceType, "Arn", physicalId);
|
|
13662
|
+
if (ecrAccountInfo) {
|
|
13663
|
+
enriched["Arn"] = `arn:${ecrAccountInfo.partition}:ecr:${ecrAccountInfo.region}:${ecrAccountInfo.accountId}:repository/${physicalId}`;
|
|
13664
|
+
this.logger.debug(`Enriched ECR Repository Arn for ${physicalId}: ${String(enriched["Arn"])}`);
|
|
13665
|
+
}
|
|
13441
13666
|
} catch (error) {
|
|
13442
13667
|
this.logger.debug(`Failed to construct ECR Repository Arn: ${error instanceof Error ? error.message : String(error)}`);
|
|
13443
13668
|
}
|
|
13444
13669
|
if (!enriched["RepositoryUri"]) try {
|
|
13445
|
-
const ecrAccountInfo = await
|
|
13446
|
-
|
|
13670
|
+
const ecrAccountInfo = await this.accountInfoForSynthesizedArn(resourceType, "RepositoryUri", physicalId);
|
|
13671
|
+
if (ecrAccountInfo) {
|
|
13672
|
+
const { urlSuffix } = derivePartitionAndUrlSuffix(ecrAccountInfo.region);
|
|
13673
|
+
enriched["RepositoryUri"] = `${ecrAccountInfo.accountId}.dkr.ecr.${ecrAccountInfo.region}.${urlSuffix}/${physicalId}`;
|
|
13674
|
+
}
|
|
13447
13675
|
} catch {}
|
|
13448
13676
|
break;
|
|
13449
13677
|
case "AWS::EC2::EIP":
|
|
@@ -13464,9 +13692,11 @@ var CloudControlProvider = class {
|
|
|
13464
13692
|
break;
|
|
13465
13693
|
case "AWS::Kinesis::Stream":
|
|
13466
13694
|
if (!enriched["Arn"]) try {
|
|
13467
|
-
const kinesisAccountInfo = await
|
|
13468
|
-
|
|
13469
|
-
|
|
13695
|
+
const kinesisAccountInfo = await this.accountInfoForSynthesizedArn(resourceType, "Arn", physicalId);
|
|
13696
|
+
if (kinesisAccountInfo) {
|
|
13697
|
+
enriched["Arn"] = `arn:${kinesisAccountInfo.partition}:kinesis:${kinesisAccountInfo.region}:${kinesisAccountInfo.accountId}:stream/${physicalId}`;
|
|
13698
|
+
this.logger.debug(`Enriched Kinesis Stream Arn for ${physicalId}: ${String(enriched["Arn"])}`);
|
|
13699
|
+
}
|
|
13470
13700
|
} catch (error) {
|
|
13471
13701
|
this.logger.debug(`Failed to construct Kinesis Stream Arn for ${physicalId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
13472
13702
|
}
|
|
@@ -20082,7 +20312,7 @@ const FLUSH_INTERVAL_MS = 2e3;
|
|
|
20082
20312
|
const FLUSH_EVENT_THRESHOLD = 50;
|
|
20083
20313
|
/** Build-time cdkd version, with a dev fallback for non-built contexts. */
|
|
20084
20314
|
function getCdkdVersion() {
|
|
20085
|
-
return "0.281.
|
|
20315
|
+
return "0.281.15";
|
|
20086
20316
|
}
|
|
20087
20317
|
/**
|
|
20088
20318
|
* Generate a time-sortable unique run id, e.g.
|
|
@@ -22250,5 +22480,5 @@ var DeployEngine = class {
|
|
|
22250
22480
|
};
|
|
22251
22481
|
|
|
22252
22482
|
//#endregion
|
|
22253
|
-
export { replayWarn as $,
|
|
22254
|
-
//# sourceMappingURL=deploy-engine-
|
|
22483
|
+
export { replayWarn as $, findLargeInlineResources as $t, green as A, isCdkdError as An, buildDockerImage as At, slowCcOperationTimeoutMs as B, getLegacyStateBucketName as Bt, MULTI_REGION_RECREATE_BLOCKED_TYPES as C, ResourceTimeoutError as Cn, AssetModeResolver as Ct, bold as D, StateError as Dn, parseBootstrapMarker as Dt, formatResourceLine as E, StackTerminationProtectionError as En, getBootstrapMarkerKey as Et, clearOnUpdateRemoval as F, AssetManifestLoader as Ft, getAccountInfo as G, resolveStateBucketWithDefault as Gt, isTerminationProtectionPropagationError as H, resolveAutoAssetStorage as Ht, ProviderRegistry as I, getDockerImageBySourceHash as It, normalizeAwsTagsToCfn as J, stateBucketExistenceConfirmed as Jt, refStateLookupFromResource as K, resolveStateBucketWithDefaultAndSource as Kt, findActionableSilentDrops as L, Synthesizer as Lt, yellow as M, withErrorHandling as Mn, getDockerCmd as Mt, IAMRoleProvider as N, __exportAll as Nn, runDockerForeground as Nt, cyan as O, SynthesisError as On, validateAssetBucketName as Ot, collectInlinePolicyNamesManagedBySiblings as P, runDockerStreaming as Pt, readConfigString as Q, MIGRATE_TMP_PREFIX as Qt, findSilentDropProperties as R, synthesisStatusMessage as Rt, extractDeploymentEventError as S, ProvisioningError as Sn, rewriteTemplateAssetReferences as St, renderStatefulReason as T, StackHasActiveImportsError as Tn, ensureAssetStorage as Tt, IntrinsicFunctionResolver as U, resolveCaptureObservedState as Ut, disableInstanceApiTermination as V, resolveApp as Vt, cfnRefValueFromPhysicalId as W, resolveSkipPrefix as Wt, assertRegionMatch as X, CFN_TEMPLATE_BODY_LIMIT as Xt, resolveExplicitPhysicalId as Y, warnDeprecatedNoPrefixCliFlag as Yt, configStringRefusal as Z, CFN_TEMPLATE_URL_LIMIT as Zt, createPreDeleteFinalSnapshot as _, LocalStartServiceError as _n, stringifyValue as _t, DeploymentEventsStore as a, resolveBucketRegion as an, DiffCalculator as at, unsupportedFinalSnapshotError as b, NestedStackChildDirectDestroyError as bn, createAssetRedirectResolver as bt, replayFailedOperations as c, resetAwsClients as cn, isRetryableTransientError as ct, IMPLICIT_DELETE_DEPENDENCIES as d, CdkdError as dn, TemplateParser as dt, uploadCfnTemplate as en, requireConfigArray as et, computeImplicitDeleteEdges as f, ConfigError as fn, LockManager as ft, ccRoutedFinalSnapshotError as g, LocalMigrateError as gn, AssetPublisher as gt, buildFinalSnapshotIdentifier as h, LocalInvokeBuildError as hn, shouldRetainResource as ht, DeploymentEventsReader as i, clearBucketRegionCache as in, applyRoleArnIfSet as it, red as j, normalizeAwsError as jn, formatDockerLoginError as jt, gray as k, formatError as kn, validateContainerRepoName as kt, replayRollback as l, setAwsClients as ln, isThrottlingError as lt, PRE_DELETE_SNAPSHOT_TYPES as m, DeployCancelledError as mn, rebuildClientForBucketRegion as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, AssemblyReader as nn, requireConfigString as nt, planFailedOps as o, AwsClients as on, describeTypeWithThrottleRetry as ot, ATOMIC_FINAL_SNAPSHOT_TYPES as p, DependencyError as pn, S3StateBackend as pt, WAFv2WebACLProvider as q, resolveUseCdkBootstrapAssets as qt, DeployEngine as r, processStackMessages as rn, derivePartitionAndUrlSuffix as rt, planRollback as s, getAwsClients as sn, withRetry as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, expectedOwnerParam as tn, requireConfigObject as tt, withResourceDeadline as u, AssetError as un, DagBuilder as ut, isFinalSnapshotError as v, LockError as vn, WorkGraph as vt, isStatefulRecreateTargetSync as w, ResourceUpdateNotSupportedError as wn, BOOTSTRAP_MARKER_PREFIX as wt, makeCanonicalizePropertiesFn as x, PartialFailureError as xn, loadPublishableAssetManifest as xt, refusesFinalSnapshot as y, MissingCdkCliError as yn, buildAssetRedirectMap as yt, CloudControlProvider as z, getDefaultStateBucketName as zt };
|
|
22484
|
+
//# sourceMappingURL=deploy-engine-B2MDP-jK.js.map
|