@go-to-k/cdkd 0.281.17 → 0.281.19
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-MpqWnvQI.js → asg-provider-CE-lwlgZ.js} +2 -2
- package/dist/{asg-provider-MpqWnvQI.js.map → asg-provider-CE-lwlgZ.js.map} +1 -1
- package/dist/cli.js +266 -29
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-B6DcyQpS.js → deploy-engine-DPnxkjWi.js} +207 -99
- package/dist/deploy-engine-DPnxkjWi.js.map +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-B6DcyQpS.js.map +0 -1
|
@@ -4176,11 +4176,70 @@ function cacheOptionToFlag(option) {
|
|
|
4176
4176
|
return flag;
|
|
4177
4177
|
}
|
|
4178
4178
|
|
|
4179
|
+
//#endregion
|
|
4180
|
+
//#region src/utils/aws-partition.ts
|
|
4181
|
+
/**
|
|
4182
|
+
* AWS partition / URL-suffix derivation, shared across layers.
|
|
4183
|
+
*
|
|
4184
|
+
* Lives in `src/utils/` because it has consumers in two different layers: the
|
|
4185
|
+
* `cdkd local *` command family (which passes a region in once to keep the STS
|
|
4186
|
+
* hop minimal) and the provisioning layer's `AppSyncProvider`, which rebuilds a
|
|
4187
|
+
* child resource's ARN when AWS did not report one.
|
|
4188
|
+
*
|
|
4189
|
+
* It was originally defined in `src/local/ecs-task-resolver.ts`, which
|
|
4190
|
+
* re-exports it so every existing call site is unchanged; a provisioning
|
|
4191
|
+
* provider importing from `src/local/**` would invert the layering.
|
|
4192
|
+
*
|
|
4193
|
+
* NOTE `getAccountInfo().partition`
|
|
4194
|
+
* (`src/deployment/intrinsic-function-resolver.ts`) was hardcoded to `'aws'`
|
|
4195
|
+
* until issue #1730, which made it derive through THIS helper — so the two now
|
|
4196
|
+
* agree and either spelling is correct. Prefer this one where a region is
|
|
4197
|
+
* already in hand, since it needs no STS round trip.
|
|
4198
|
+
*/
|
|
4199
|
+
/**
|
|
4200
|
+
* Derive the AWS partition / URL suffix for an AWS region. Same mapping
|
|
4201
|
+
* CloudFormation applies to `${AWS::Partition}` / `${AWS::URLSuffix}`.
|
|
4202
|
+
*/
|
|
4203
|
+
function derivePartitionAndUrlSuffix(region) {
|
|
4204
|
+
if (region.startsWith("cn-")) return {
|
|
4205
|
+
partition: "aws-cn",
|
|
4206
|
+
urlSuffix: "amazonaws.com.cn"
|
|
4207
|
+
};
|
|
4208
|
+
if (region.startsWith("us-gov-")) return {
|
|
4209
|
+
partition: "aws-us-gov",
|
|
4210
|
+
urlSuffix: "amazonaws.com"
|
|
4211
|
+
};
|
|
4212
|
+
if (region.startsWith("us-iso-")) return {
|
|
4213
|
+
partition: "aws-iso",
|
|
4214
|
+
urlSuffix: "c2s.ic.gov"
|
|
4215
|
+
};
|
|
4216
|
+
if (region.startsWith("us-isob-")) return {
|
|
4217
|
+
partition: "aws-iso-b",
|
|
4218
|
+
urlSuffix: "sc2s.sgov.gov"
|
|
4219
|
+
};
|
|
4220
|
+
return {
|
|
4221
|
+
partition: "aws",
|
|
4222
|
+
urlSuffix: "amazonaws.com"
|
|
4223
|
+
};
|
|
4224
|
+
}
|
|
4225
|
+
|
|
4179
4226
|
//#endregion
|
|
4180
4227
|
//#region src/assets/docker-asset-publisher.ts
|
|
4181
4228
|
/**
|
|
4229
|
+
* The ECR registry host suffix for a region (issue #1745).
|
|
4230
|
+
*
|
|
4231
|
+
* Every registry URI here was hardcoded to `amazonaws.com`, so outside the
|
|
4232
|
+
* commercial partition cdkd built a hostname that does not resolve —
|
|
4233
|
+
* `aws-cn` registries live under `amazonaws.com.cn`, and `us-iso*` under
|
|
4234
|
+
* `c2s.ic.gov` / `sc2s.sgov.gov`. Commercial output is byte-identical, which is
|
|
4235
|
+
* what makes the change safe to ship without a non-commercial account.
|
|
4236
|
+
*/
|
|
4237
|
+
function ecrUrlSuffix(region) {
|
|
4238
|
+
return derivePartitionAndUrlSuffix(region).urlSuffix;
|
|
4239
|
+
}
|
|
4240
|
+
/**
|
|
4182
4241
|
* Registries this process has already logged in to, keyed by registry host
|
|
4183
|
-
* (`<accountId>.dkr.ecr.<region
|
|
4242
|
+
* (`<accountId>.dkr.ecr.<region>.<urlSuffix>`, which uniquely encodes the
|
|
4184
4243
|
* account + region credential context). ECR authorization tokens are valid
|
|
4185
4244
|
* for ~12h and a deploy process is short-lived, so a successful login is
|
|
4186
4245
|
* reused for the process lifetime — mirroring `cdk-assets`, which skips
|
|
@@ -4221,7 +4280,7 @@ var DockerAssetPublisher = class {
|
|
|
4221
4280
|
const repositoryName = this.resolvePlaceholders(dest.repositoryName, accountId, region);
|
|
4222
4281
|
const imageTag = this.resolvePlaceholders(dest.imageTag, accountId, region);
|
|
4223
4282
|
const destRegion = dest.region ? this.resolvePlaceholders(dest.region, accountId, region) : region;
|
|
4224
|
-
const ecrUri = `${accountId}.dkr.ecr.${destRegion}
|
|
4283
|
+
const ecrUri = `${accountId}.dkr.ecr.${destRegion}.${ecrUrlSuffix(destRegion)}/${repositoryName}:${imageTag}`;
|
|
4225
4284
|
this.logger.debug(`Publishing Docker image ${asset.displayName || assetHash} → ${ecrUri}`);
|
|
4226
4285
|
const client = new ECRClient({ region: destRegion });
|
|
4227
4286
|
try {
|
|
@@ -4231,8 +4290,7 @@ var DockerAssetPublisher = class {
|
|
|
4231
4290
|
}
|
|
4232
4291
|
const localTag = `cdkd-asset-${assetHash}`;
|
|
4233
4292
|
await this.buildImage(asset, cdkOutputDir, localTag);
|
|
4234
|
-
|
|
4235
|
-
await this.tagAndPushWithLazyLogin(client, localTag, fullUri, accountId, destRegion);
|
|
4293
|
+
await this.tagAndPushWithLazyLogin(client, localTag, ecrUri, accountId, destRegion);
|
|
4236
4294
|
this.logger.debug(`✅ Published: ${ecrUri}`);
|
|
4237
4295
|
} finally {
|
|
4238
4296
|
client.destroy();
|
|
@@ -4259,15 +4317,14 @@ var DockerAssetPublisher = class {
|
|
|
4259
4317
|
const repositoryName = this.resolvePlaceholders(dest.repositoryName, accountId, region);
|
|
4260
4318
|
const imageTag = this.resolvePlaceholders(dest.imageTag, accountId, region);
|
|
4261
4319
|
const destRegion = dest.region ? this.resolvePlaceholders(dest.region, accountId, region) : region;
|
|
4262
|
-
const ecrUri = `${accountId}.dkr.ecr.${destRegion}
|
|
4320
|
+
const ecrUri = `${accountId}.dkr.ecr.${destRegion}.${ecrUrlSuffix(destRegion)}/${repositoryName}:${imageTag}`;
|
|
4263
4321
|
const client = new ECRClient({ region: destRegion });
|
|
4264
4322
|
try {
|
|
4265
4323
|
if (await this.imageExists(client, repositoryName, imageTag)) {
|
|
4266
4324
|
this.logger.debug(`Image already exists, skipping: ${ecrUri}`);
|
|
4267
4325
|
continue;
|
|
4268
4326
|
}
|
|
4269
|
-
|
|
4270
|
-
await this.tagAndPushWithLazyLogin(client, localTag, fullUri, accountId, destRegion);
|
|
4327
|
+
await this.tagAndPushWithLazyLogin(client, localTag, ecrUri, accountId, destRegion);
|
|
4271
4328
|
this.logger.debug(`✅ Published: ${ecrUri}`);
|
|
4272
4329
|
} finally {
|
|
4273
4330
|
client.destroy();
|
|
@@ -4354,7 +4411,7 @@ var DockerAssetPublisher = class {
|
|
|
4354
4411
|
/**
|
|
4355
4412
|
* Authenticate with ECR via `docker login --password-stdin`.
|
|
4356
4413
|
*
|
|
4357
|
-
* The login is cached per registry (`<accountId>.dkr.ecr.<region>`) for the
|
|
4414
|
+
* The login is cached per registry (`<accountId>.dkr.ecr.<region>.<urlSuffix>`) for the
|
|
4358
4415
|
* process lifetime: a repeat publish to the same registry returns early
|
|
4359
4416
|
* without the `GetAuthorizationToken` call or the `docker login` subprocess
|
|
4360
4417
|
* (mirrors `cdk-assets`). ECR tokens are valid ~12h and a deploy process is
|
|
@@ -4366,7 +4423,7 @@ var DockerAssetPublisher = class {
|
|
|
4366
4423
|
* token that AWS has already expired.
|
|
4367
4424
|
*/
|
|
4368
4425
|
async ecrLogin(client, accountId, region, options = {}) {
|
|
4369
|
-
const registryKey = `${accountId}.dkr.ecr.${region}
|
|
4426
|
+
const registryKey = `${accountId}.dkr.ecr.${region}.${ecrUrlSuffix(region)}`;
|
|
4370
4427
|
if (!options.force && loggedInRegistries.has(registryKey)) {
|
|
4371
4428
|
this.logger.debug(`Reusing cached ECR login for ${registryKey}`);
|
|
4372
4429
|
return;
|
|
@@ -4375,7 +4432,7 @@ var DockerAssetPublisher = class {
|
|
|
4375
4432
|
if (!authData?.authorizationToken) throw new AssetError("Failed to get ECR authorization token");
|
|
4376
4433
|
const [username, password] = Buffer.from(authData.authorizationToken, "base64").toString().split(":");
|
|
4377
4434
|
if (!username || password === void 0) throw new AssetError("ECR authorization token has unexpected shape (missing username/password)");
|
|
4378
|
-
const endpoint = authData.proxyEndpoint || `https://${accountId}.dkr.ecr.${region}
|
|
4435
|
+
const endpoint = authData.proxyEndpoint || `https://${accountId}.dkr.ecr.${region}.${ecrUrlSuffix(region)}`;
|
|
4379
4436
|
try {
|
|
4380
4437
|
await runDockerStreaming([
|
|
4381
4438
|
"login",
|
|
@@ -4995,8 +5052,8 @@ function evaluatePseudoParam(name, map) {
|
|
|
4995
5052
|
switch (name) {
|
|
4996
5053
|
case "AWS::AccountId": return map.accountId;
|
|
4997
5054
|
case "AWS::Region": return map.region;
|
|
4998
|
-
case "AWS::Partition": return map.partition;
|
|
4999
|
-
case "AWS::URLSuffix": return
|
|
5055
|
+
case "AWS::Partition": return derivePartitionAndUrlSuffix(map.region).partition;
|
|
5056
|
+
case "AWS::URLSuffix": return derivePartitionAndUrlSuffix(map.region).urlSuffix;
|
|
5000
5057
|
default: throw new Error(`Not a foldable pseudo parameter: ${name}`);
|
|
5001
5058
|
}
|
|
5002
5059
|
}
|
|
@@ -9232,53 +9289,6 @@ async function applyRoleArnIfSet(opts) {
|
|
|
9232
9289
|
}
|
|
9233
9290
|
}
|
|
9234
9291
|
|
|
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
|
-
|
|
9282
9292
|
//#endregion
|
|
9283
9293
|
//#region src/provisioning/config-shape.ts
|
|
9284
9294
|
/**
|
|
@@ -9561,6 +9571,63 @@ function configStringRefusal(container, key, fallback, containerPath, options) {
|
|
|
9561
9571
|
if (!isPlainObject$1(container)) return `${containerPath} must be an object ${malformedShapeDetail(container)}`;
|
|
9562
9572
|
return configValueRefusal(container[key], fallback, `${containerPath}.${key}`, options);
|
|
9563
9573
|
}
|
|
9574
|
+
/**
|
|
9575
|
+
* Coerce a CFn boolean, which may arrive as the string `"true"` / `"false"`.
|
|
9576
|
+
*
|
|
9577
|
+
* CloudFormation is stringly typed and cdkd is not, so a hand-written or
|
|
9578
|
+
* imported template legitimately spells a boolean as a string. Case-insensitive
|
|
9579
|
+
* on purpose: CDK renders lowercase, but this also feeds
|
|
9580
|
+
* `AWS::S3::Bucket NotificationConfiguration.EventBridgeConfiguration` (issue
|
|
9581
|
+
* #1430), where "not false" means "enable" — so a `'False'` that fell through to
|
|
9582
|
+
* `undefined` would silently ENABLE EventBridge delivery, the exact inversion
|
|
9583
|
+
* #1430 fixed.
|
|
9584
|
+
*
|
|
9585
|
+
* Lives here rather than in the one provider that used to own it because
|
|
9586
|
+
* {@link configBooleanRefusal} must run the SAME primitive the wire read runs;
|
|
9587
|
+
* a second hand-written test would disagree with it on exactly the interesting
|
|
9588
|
+
* values, which is the guard-mismatch shape this module exists to stop.
|
|
9589
|
+
*
|
|
9590
|
+
* @returns the boolean, or `undefined` when the value is not one.
|
|
9591
|
+
*/
|
|
9592
|
+
function coerceCfnBoolean(value) {
|
|
9593
|
+
if (typeof value === "boolean") return value;
|
|
9594
|
+
if (typeof value === "string") {
|
|
9595
|
+
const lowered = value.toLowerCase();
|
|
9596
|
+
if (lowered === "true") return true;
|
|
9597
|
+
if (lowered === "false") return false;
|
|
9598
|
+
}
|
|
9599
|
+
}
|
|
9600
|
+
/**
|
|
9601
|
+
* The BOOLEAN twin of {@link configStringRefusal} — the refusal SENTENCE for a
|
|
9602
|
+
* config member that is read as a boolean, with no action clause attached.
|
|
9603
|
+
*
|
|
9604
|
+
* Same two halves in the same order (CONTAINER, then FIELD) and the same
|
|
9605
|
+
* shared detail clause, so a boolean guard and a string guard on sibling
|
|
9606
|
+
* members of one block cannot word the same fault differently. The FIELD half
|
|
9607
|
+
* is `coerceCfnBoolean`, i.e. literally the function the wire read calls, per
|
|
9608
|
+
* this module's "share the predicate, never restate it" rule.
|
|
9609
|
+
*
|
|
9610
|
+
* It exists because a boolean member read as `x ?? <default>` is the one shape
|
|
9611
|
+
* neither string guard can see: `??` treats a DECLARED `null` as absent and
|
|
9612
|
+
* substitutes the default, which for `AWS::S3::Bucket
|
|
9613
|
+
* InventoryConfigurations[].Enabled` meant a declared `Enabled: null` went on
|
|
9614
|
+
* the wire as `true` — a report the template may have been disabling, ENABLED
|
|
9615
|
+
* with no warning anywhere (issue #1751). Its string siblings on the same item
|
|
9616
|
+
* are SKIP-guarded (#1595) or warn-and-substitute (#1670); this was the one
|
|
9617
|
+
* member that silently coerced.
|
|
9618
|
+
*
|
|
9619
|
+
* @returns The refusal sentence (`<path> must be …`), or `undefined` when the
|
|
9620
|
+
* value is usable — including the ABSENT container / ABSENT key cases, which
|
|
9621
|
+
* legitimately take the caller's default.
|
|
9622
|
+
*/
|
|
9623
|
+
function configBooleanRefusal(container, key, containerPath) {
|
|
9624
|
+
if (container === void 0 || container === null) return void 0;
|
|
9625
|
+
if (!isPlainObject$1(container)) return `${containerPath} must be an object ${malformedShapeDetail(container)}`;
|
|
9626
|
+
const value = container[key];
|
|
9627
|
+
if (value === void 0) return void 0;
|
|
9628
|
+
if (coerceCfnBoolean(value) !== void 0) return void 0;
|
|
9629
|
+
return `${containerPath}.${key} must be a boolean ${malformedShapeDetail(value)}`;
|
|
9630
|
+
}
|
|
9564
9631
|
/** The FIELD half of {@link configStringRefusal}, shared with {@link requireConfigString}. */
|
|
9565
9632
|
function configValueRefusal(value, fallback, path, options) {
|
|
9566
9633
|
if (value === void 0) return void 0;
|
|
@@ -10767,7 +10834,7 @@ function buildUnknownIntrinsicError(key) {
|
|
|
10767
10834
|
const issueUrl = `https://github.com/go-to-k/cdkd/issues/new?title=${encodeURIComponent(title)}&labels=intrinsic-support`;
|
|
10768
10835
|
return /* @__PURE__ */ new Error(`Unsupported CloudFormation intrinsic function "${key}": cdkd does not support resolving it yet. Deploying this template would produce a broken value. Please request support by opening an issue: ${issueUrl}`);
|
|
10769
10836
|
}
|
|
10770
|
-
let
|
|
10837
|
+
let cachedAccountIdentity = null;
|
|
10771
10838
|
/**
|
|
10772
10839
|
* Cache for availability zones per region
|
|
10773
10840
|
*/
|
|
@@ -10786,18 +10853,30 @@ const cachedDynamicReferences = {};
|
|
|
10786
10853
|
*/
|
|
10787
10854
|
const cachedEc2InstanceAttributes = {};
|
|
10788
10855
|
/**
|
|
10789
|
-
*
|
|
10856
|
+
* The region this call answers for: the caller's override, else the ambient one.
|
|
10857
|
+
*
|
|
10858
|
+
* Kept as one helper so the cached and the freshly-resolved paths cannot pick
|
|
10859
|
+
* different defaults (issue #1746).
|
|
10860
|
+
*/
|
|
10861
|
+
function effectiveAccountInfoRegion(overrideRegion) {
|
|
10862
|
+
return overrideRegion || process.env["AWS_REGION"] || "us-east-1";
|
|
10863
|
+
}
|
|
10864
|
+
/**
|
|
10865
|
+
* Build the caller's full answer from the cached account identity (issue #1746).
|
|
10790
10866
|
*
|
|
10791
|
-
* `partition` is a FUNCTION of `region`, so
|
|
10792
|
-
*
|
|
10793
|
-
* `arn:aws:...:cn-north-1
|
|
10794
|
-
*
|
|
10867
|
+
* `partition` is a FUNCTION of `region`, so it is derived HERE — per call —
|
|
10868
|
+
* rather than carried alongside the account. This is what makes the cache safe
|
|
10869
|
+
* to share between callers with different regions: an `arn:aws:...:cn-north-1`
|
|
10870
|
+
* (or the inverse `arn:aws-cn:...:us-east-1`) is structurally valid, so nothing
|
|
10871
|
+
* downstream could catch it.
|
|
10795
10872
|
*/
|
|
10796
|
-
function
|
|
10873
|
+
function accountInfoFor(identity, overrideRegion) {
|
|
10874
|
+
const region = effectiveAccountInfoRegion(overrideRegion);
|
|
10797
10875
|
return {
|
|
10798
|
-
|
|
10876
|
+
accountId: identity.accountId,
|
|
10799
10877
|
region,
|
|
10800
|
-
partition: derivePartitionAndUrlSuffix(region).partition
|
|
10878
|
+
partition: derivePartitionAndUrlSuffix(region).partition,
|
|
10879
|
+
...identity.fabricated ? { fabricated: true } : {}
|
|
10801
10880
|
};
|
|
10802
10881
|
}
|
|
10803
10882
|
/**
|
|
@@ -10814,7 +10893,7 @@ function withOverrideRegion(info, region) {
|
|
|
10814
10893
|
const FABRICATED_ACCOUNT_INFO_TTL_MS = 1e4;
|
|
10815
10894
|
/** Test seam for {@link FABRICATED_ACCOUNT_INFO_TTL_MS} expiry. */
|
|
10816
10895
|
const accountInfoClock = { now: () => Date.now() };
|
|
10817
|
-
let
|
|
10896
|
+
let fabricatedAccountIdentity = null;
|
|
10818
10897
|
/**
|
|
10819
10898
|
* The single in-flight lookup, so N concurrent callers share ONE round trip.
|
|
10820
10899
|
*
|
|
@@ -10826,66 +10905,95 @@ let fabricatedAccountInfo = null;
|
|
|
10826
10905
|
*/
|
|
10827
10906
|
let accountInfoInFlight = null;
|
|
10828
10907
|
/**
|
|
10908
|
+
* Bumped by {@link resetAccountInfoCache}, so a lookup that was already in
|
|
10909
|
+
* flight cannot write the cache it was asked to forget.
|
|
10910
|
+
*
|
|
10911
|
+
* Without it the reset only cleared the SETTLED caches: an in-flight resolve
|
|
10912
|
+
* would land afterwards and re-populate `cachedAccountIdentity`, so the next
|
|
10913
|
+
* caller read the pre-reset account. That is the `*Once`-leak shape one layer
|
|
10914
|
+
* down — a later test silently inheriting an earlier one's answer — and the
|
|
10915
|
+
* reset's own comment already claimed to forget it.
|
|
10916
|
+
*/
|
|
10917
|
+
let accountInfoGeneration = 0;
|
|
10918
|
+
/**
|
|
10829
10919
|
* Get AWS account information from STS
|
|
10830
10920
|
*/
|
|
10831
10921
|
async function getAccountInfo(overrideRegion) {
|
|
10832
|
-
|
|
10833
|
-
if (
|
|
10834
|
-
if (
|
|
10835
|
-
|
|
10836
|
-
accountInfoInFlight =
|
|
10922
|
+
if (cachedAccountIdentity) return accountInfoFor(cachedAccountIdentity, overrideRegion);
|
|
10923
|
+
if (fabricatedAccountIdentity && accountInfoClock.now() < fabricatedAccountIdentity.expiresAt) return accountInfoFor(fabricatedAccountIdentity.identity, overrideRegion);
|
|
10924
|
+
if (accountInfoInFlight) return accountInfoFor(await accountInfoInFlight, overrideRegion);
|
|
10925
|
+
const inFlight = resolveAccountIdentity();
|
|
10926
|
+
accountInfoInFlight = inFlight;
|
|
10837
10927
|
try {
|
|
10838
|
-
return
|
|
10928
|
+
return accountInfoFor(await inFlight, overrideRegion);
|
|
10839
10929
|
} finally {
|
|
10840
|
-
accountInfoInFlight = null;
|
|
10930
|
+
if (accountInfoInFlight === inFlight) accountInfoInFlight = null;
|
|
10841
10931
|
}
|
|
10842
10932
|
}
|
|
10843
|
-
async function
|
|
10933
|
+
async function resolveAccountIdentity() {
|
|
10934
|
+
const generation = accountInfoGeneration;
|
|
10935
|
+
const stillCurrent = () => generation === accountInfoGeneration;
|
|
10844
10936
|
const logger = getLogger().child("IntrinsicFunctionResolver");
|
|
10845
10937
|
const stsClient = getAwsClients().sts;
|
|
10846
10938
|
try {
|
|
10847
10939
|
const response = await stsClient.send(new GetCallerIdentityCommand({}));
|
|
10848
10940
|
const accountId = response.Account || "123456789012";
|
|
10849
|
-
const region = overrideRegion || process.env["AWS_REGION"] || "us-east-1";
|
|
10850
|
-
const partition = derivePartitionAndUrlSuffix(region).partition;
|
|
10851
10941
|
const resolved = {
|
|
10852
10942
|
accountId,
|
|
10853
|
-
region,
|
|
10854
|
-
partition,
|
|
10855
10943
|
...response.Account ? {} : { fabricated: true }
|
|
10856
10944
|
};
|
|
10857
|
-
if (resolved.fabricated)
|
|
10858
|
-
|
|
10945
|
+
if (!stillCurrent()) {} else if (resolved.fabricated) fabricatedAccountIdentity = {
|
|
10946
|
+
identity: resolved,
|
|
10859
10947
|
expiresAt: accountInfoClock.now() + FABRICATED_ACCOUNT_INFO_TTL_MS
|
|
10860
10948
|
};
|
|
10861
10949
|
else {
|
|
10862
|
-
|
|
10863
|
-
|
|
10950
|
+
cachedAccountIdentity = resolved;
|
|
10951
|
+
fabricatedAccountIdentity = null;
|
|
10864
10952
|
}
|
|
10865
|
-
logger.debug(`Retrieved AWS account info: ${accountId}
|
|
10953
|
+
logger.debug(`Retrieved AWS account info: ${accountId}`);
|
|
10866
10954
|
return resolved;
|
|
10867
10955
|
} catch (error) {
|
|
10868
10956
|
logger.warn(`Failed to get AWS account info from STS: ${error instanceof Error ? error.message : String(error)}, using defaults`);
|
|
10869
|
-
const region = overrideRegion || process.env["AWS_REGION"] || "us-east-1";
|
|
10870
10957
|
const fallback = {
|
|
10871
10958
|
accountId: process.env["AWS_ACCOUNT_ID"] || "123456789012",
|
|
10872
|
-
region,
|
|
10873
|
-
partition: derivePartitionAndUrlSuffix(region).partition,
|
|
10874
10959
|
...process.env["AWS_ACCOUNT_ID"] ? {} : { fabricated: true }
|
|
10875
10960
|
};
|
|
10961
|
+
if (!stillCurrent()) return fallback;
|
|
10876
10962
|
if (fallback.fabricated) {
|
|
10877
|
-
if (!
|
|
10878
|
-
|
|
10963
|
+
if (!cachedAccountIdentity) fabricatedAccountIdentity = {
|
|
10964
|
+
identity: fallback,
|
|
10879
10965
|
expiresAt: accountInfoClock.now() + FABRICATED_ACCOUNT_INFO_TTL_MS
|
|
10880
10966
|
};
|
|
10881
10967
|
} else {
|
|
10882
|
-
|
|
10883
|
-
|
|
10968
|
+
cachedAccountIdentity = fallback;
|
|
10969
|
+
fabricatedAccountIdentity = null;
|
|
10884
10970
|
}
|
|
10885
10971
|
return fallback;
|
|
10886
10972
|
}
|
|
10887
10973
|
}
|
|
10888
10974
|
/**
|
|
10975
|
+
* Does a constructed `Fn::GetAtt` answer embed the placeholder account id?
|
|
10976
|
+
*
|
|
10977
|
+
* The guard in `constructGuardedAttribute` used to test
|
|
10978
|
+
* `typeof value === 'string'` directly (issue #1746). Every account-bearing
|
|
10979
|
+
* branch of `constructAttribute` returns a string today — the only non-string
|
|
10980
|
+
* returns are the EC2 IPv6 CIDR LISTS, which carry no account — so that was
|
|
10981
|
+
* complete as written, but a future list-valued attribute embedding an account
|
|
10982
|
+
* would have slipped past silently with no test failing. Walking string arrays
|
|
10983
|
+
* (one level, which is the shape `constructAttribute` actually produces) closes
|
|
10984
|
+
* it now rather than at the moment someone adds one. A non-string, non-array
|
|
10985
|
+
* value is not account-bearing by construction and is left alone.
|
|
10986
|
+
*
|
|
10987
|
+
* EXPORTED for its own test: no `constructAttribute` branch returns an
|
|
10988
|
+
* account-bearing array today, so the array arm is unreachable through the
|
|
10989
|
+
* public resolver API and would ship unexercised otherwise.
|
|
10990
|
+
*/
|
|
10991
|
+
function embedsAccountId(value, accountId) {
|
|
10992
|
+
if (typeof value === "string") return value.includes(accountId);
|
|
10993
|
+
if (Array.isArray(value)) return value.some((entry) => typeof entry === "string" && entry.includes(accountId));
|
|
10994
|
+
return false;
|
|
10995
|
+
}
|
|
10996
|
+
/**
|
|
10889
10997
|
* Collect every name referenced (Ref / Fn::Sub placeholder / other intrinsic
|
|
10890
10998
|
* argument) by the sections cdkd actually evaluates: Resources, Outputs, and
|
|
10891
10999
|
* Conditions. Deliberately excludes `Rules` (assertion-only, never evaluated
|
|
@@ -11349,7 +11457,7 @@ var IntrinsicFunctionResolver = class {
|
|
|
11349
11457
|
async constructGuardedAttribute(resource, attributeName, context, logicalId) {
|
|
11350
11458
|
const accountInfo = await getAccountInfo(this.resolverRegion);
|
|
11351
11459
|
const value = await this.constructAttribute(resource, attributeName, context, logicalId, accountInfo);
|
|
11352
|
-
if (accountInfo.fabricated &&
|
|
11460
|
+
if (accountInfo.fabricated && embedsAccountId(value, 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
11461
|
return value;
|
|
11354
11462
|
}
|
|
11355
11463
|
/**
|
|
@@ -13404,7 +13512,7 @@ var CloudControlProvider = class {
|
|
|
13404
13512
|
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);
|
|
13405
13513
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
13406
13514
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
13407
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
13515
|
+
const { ASGProvider } = await import("./asg-provider-CE-lwlgZ.js").then((n) => n.n);
|
|
13408
13516
|
await new ASGProvider().delete(logicalId, physicalId, resourceType, _properties, context);
|
|
13409
13517
|
return;
|
|
13410
13518
|
}
|
|
@@ -13554,7 +13662,7 @@ var CloudControlProvider = class {
|
|
|
13554
13662
|
* else's account, and the record heals on the resource's next update.
|
|
13555
13663
|
*/
|
|
13556
13664
|
async accountInfoForSynthesizedArn(resourceType, attributeName, physicalId) {
|
|
13557
|
-
const accountInfo = await getAccountInfo();
|
|
13665
|
+
const accountInfo = await getAccountInfo(await this.cloudControlClient.config.region());
|
|
13558
13666
|
if (accountInfo.fabricated) {
|
|
13559
13667
|
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
13668
|
return;
|
|
@@ -20312,7 +20420,7 @@ const FLUSH_INTERVAL_MS = 2e3;
|
|
|
20312
20420
|
const FLUSH_EVENT_THRESHOLD = 50;
|
|
20313
20421
|
/** Build-time cdkd version, with a dev fallback for non-built contexts. */
|
|
20314
20422
|
function getCdkdVersion() {
|
|
20315
|
-
return "0.281.
|
|
20423
|
+
return "0.281.19";
|
|
20316
20424
|
}
|
|
20317
20425
|
/**
|
|
20318
20426
|
* Generate a time-sortable unique run id, e.g.
|
|
@@ -22480,5 +22588,5 @@ var DeployEngine = class {
|
|
|
22480
22588
|
};
|
|
22481
22589
|
|
|
22482
22590
|
//#endregion
|
|
22483
|
-
export {
|
|
22484
|
-
//# sourceMappingURL=deploy-engine-
|
|
22591
|
+
export { configStringRefusal as $, CFN_TEMPLATE_URL_LIMIT as $t, green as A, SynthesisError as An, validateContainerRepoName as At, slowCcOperationTimeoutMs as B, synthesisStatusMessage as Bt, MULTI_REGION_RECREATE_BLOCKED_TYPES as C, PartialFailureError as Cn, rewriteTemplateAssetReferences as Ct, bold as D, StackHasActiveImportsError as Dn, getBootstrapMarkerKey as Dt, formatResourceLine as E, ResourceUpdateNotSupportedError as En, ensureAssetStorage as Et, clearOnUpdateRemoval as F, __exportAll as Fn, runDockerForeground as Ft, getAccountInfo as G, resolveCaptureObservedState as Gt, isTerminationProtectionPropagationError as H, getLegacyStateBucketName as Ht, ProviderRegistry as I, runDockerStreaming as It, normalizeAwsTagsToCfn as J, resolveStateBucketWithDefaultAndSource as Jt, refStateLookupFromResource as K, resolveSkipPrefix as Kt, findActionableSilentDrops as L, AssetManifestLoader as Lt, yellow as M, isCdkdError as Mn, buildDockerImage as Mt, IAMRoleProvider as N, normalizeAwsError as Nn, formatDockerLoginError as Nt, cyan as O, StackTerminationProtectionError as On, parseBootstrapMarker as Ot, collectInlinePolicyNamesManagedBySiblings as P, withErrorHandling as Pn, getDockerCmd as Pt, configBooleanRefusal as Q, CFN_TEMPLATE_BODY_LIMIT as Qt, findSilentDropProperties as R, getDockerImageBySourceHash as Rt, extractDeploymentEventError as S, NestedStackChildDirectDestroyError as Sn, loadPublishableAssetManifest as St, renderStatefulReason as T, ResourceTimeoutError as Tn, BOOTSTRAP_MARKER_PREFIX as Tt, IntrinsicFunctionResolver as U, resolveApp as Ut, disableInstanceApiTermination as V, getDefaultStateBucketName as Vt, cfnRefValueFromPhysicalId as W, resolveAutoAssetStorage as Wt, assertRegionMatch as X, stateBucketExistenceConfirmed as Xt, resolveExplicitPhysicalId as Y, resolveUseCdkBootstrapAssets as Yt, coerceCfnBoolean as Z, warnDeprecatedNoPrefixCliFlag as Zt, createPreDeleteFinalSnapshot as _, LocalInvokeBuildError as _n, AssetPublisher as _t, DeploymentEventsStore as a, processStackMessages as an, applyRoleArnIfSet as at, unsupportedFinalSnapshotError as b, LockError as bn, buildAssetRedirectMap as bt, replayFailedOperations as c, AwsClients as cn, withRetry as ct, IMPLICIT_DELETE_DEPENDENCIES as d, setAwsClients as dn, DagBuilder as dt, MIGRATE_TMP_PREFIX as en, readConfigString as et, computeImplicitDeleteEdges as f, AssetError as fn, TemplateParser as ft, ccRoutedFinalSnapshotError as g, DeployCancelledError as gn, shouldRetainResource as gt, buildFinalSnapshotIdentifier as h, DependencyError as hn, rebuildClientForBucketRegion as ht, DeploymentEventsReader as i, AssemblyReader as in, requireConfigString as it, red as j, formatError as jn, derivePartitionAndUrlSuffix as jt, gray as k, StateError as kn, validateAssetBucketName as kt, replayRollback as l, getAwsClients as ln, isRetryableTransientError as lt, PRE_DELETE_SNAPSHOT_TYPES as m, ConfigError as mn, S3StateBackend as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, uploadCfnTemplate as nn, requireConfigArray as nt, planFailedOps as o, clearBucketRegionCache as on, DiffCalculator as ot, ATOMIC_FINAL_SNAPSHOT_TYPES as p, CdkdError as pn, LockManager as pt, WAFv2WebACLProvider as q, resolveStateBucketWithDefault as qt, DeployEngine as r, expectedOwnerParam as rn, requireConfigObject as rt, planRollback as s, resolveBucketRegion as sn, describeTypeWithThrottleRetry as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, findLargeInlineResources as tn, replayWarn as tt, withResourceDeadline as u, resetAwsClients as un, isThrottlingError as ut, isFinalSnapshotError as v, LocalMigrateError as vn, stringifyValue as vt, isStatefulRecreateTargetSync as w, ProvisioningError as wn, AssetModeResolver as wt, makeCanonicalizePropertiesFn as x, MissingCdkCliError as xn, createAssetRedirectResolver as xt, refusesFinalSnapshot as y, LocalStartServiceError as yn, WorkGraph as yt, CloudControlProvider as z, Synthesizer as zt };
|
|
22592
|
+
//# sourceMappingURL=deploy-engine-DPnxkjWi.js.map
|