@go-to-k/cdkd 0.283.13 → 0.283.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-CrVgG0Pj.js → asg-provider-Dl2MK3if.js} +2 -2
- package/dist/{asg-provider-CrVgG0Pj.js.map → asg-provider-Dl2MK3if.js.map} +1 -1
- package/dist/cli.js +352 -74
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-BdKa1DA4.js → deploy-engine-D-4ZvtGc.js} +58 -8
- package/dist/{deploy-engine-BdKa1DA4.js.map → deploy-engine-D-4ZvtGc.js.map} +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as coerceCfnBoolean, $t as resolveStateBucketWithDefault, A as cyan, An as MissingCdkCliError, At as AssetModeResolver, B as findSilentDropProperties, Bt as getDockerCmd, C as makeCanonicalizePropertiesFn, Ct as stringifyValue, D as renderStatefulReason, Dn as LocalMigrateError, Dt as loadPublishableAssetManifest, E as isStatefulRecreateTargetSync, En as LocalInvokeBuildError$1, Et as createAssetRedirectResolver, F as IAMRoleProvider, Fn as ResourceUpdateNotSupportedError, Ft as validateAssetBucketName, G as IntrinsicFunctionResolver, Gn as isRetryableTransientError, Gt as Synthesizer, H as slowCcOperationTimeoutMs, Hn as normalizeAwsError, Ht as runDockerStreaming, I as collectInlinePolicyNamesManagedBySiblings, In as StackHasActiveImportsError, It as validateContainerRepoName, J as refStateLookupFromResource, Jt as getLegacyStateBucketName, K as cfnRefValueFromPhysicalId, Kn as isThrottlingError, Kt as synthesisStatusMessage, L as clearOnUpdateRemoval, Ln as StackTerminationProtectionError, Lt as buildDenyExternalAccessPolicy, M as green, Mn as PartialFailureError, Mt as ensureAssetStorage, N as red, Nn as ProvisioningError, Nt as getBootstrapMarkerKey, O as formatResourceLine, On as LocalStartServiceError, Ot as rewriteTemplateAssetReferences, P as yellow, Pn as ResourceTimeoutError, Pt as parseBootstrapMarker, Q as assertRegionMatch, Qt as resolveSkipPrefix, R as ProviderRegistry, Rt as buildDockerImage, S as unsupportedFinalSnapshotError, Sn as CdkdError, St as AssetPublisher, T as MULTI_REGION_RECREATE_BLOCKED_TYPES, Tn as DeployCancelledError, Tt as buildAssetRedirectMap, U as disableInstanceApiTermination, Un as withErrorHandling, Ut as AssetManifestLoader, V as CloudControlProvider, Vt as runDockerForeground, W as isTerminationProtectionPropagationError, Wn as isMarkedNonRetryable, Wt as getDockerImageBySourceHash, X as normalizeAwsTagsToCfn, Xt as resolveAutoAssetStorage, Y as WAFv2WebACLProvider, Yt as resolveApp, Z as resolveExplicitPhysicalId, Zt as resolveCaptureObservedState, _ as buildFinalSnapshotIdentifier, _n as AwsClients, _t as TemplateParser, a as DeploymentEventsStore, an as CFN_TEMPLATE_URL_LIMIT, at as requireConfigObject, b as isFinalSnapshotError, bn as setAwsClients, bt as rebuildClientForBucketRegion, c as replayFailedOperations, cn as uploadCfnTemplate, ct as s3BucketDomainName, d as deleteSkipReason, dn as canonicalizeRegion, dt as s3BucketWebsiteUrl, en as resolveStateBucketWithDefaultAndSource, et as configBooleanRefusal, f as withResourceDeadline, fn as derivePartitionAndUrlSuffix, ft as applyRoleArnIfSet, g as PRE_DELETE_SNAPSHOT_TYPES, gn as resolveBucketRegion, gt as DagBuilder, h as ATOMIC_FINAL_SNAPSHOT_TYPES, ht as withRetry, i as DeploymentEventsReader, in as CFN_TEMPLATE_BODY_LIMIT, it as requireConfigArray, j as gray, jn as NestedStackChildDirectDestroyError, jt as BOOTSTRAP_MARKER_PREFIX, k as bold, kt as escapeRegExp, l as replayRollback, ln as expectedOwnerParam, lt as s3BucketDualStackDomainName, m as computeImplicitDeleteEdges, mn as processStackMessages, mt as describeTypeWithThrottleRetry, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as stateBucketExistenceConfirmed, nt as readConfigString, o as planFailedOps, on as MIGRATE_TMP_PREFIX, ot as requireConfigString, p as IMPLICIT_DELETE_DEPENDENCIES, pn as AssemblyReader, pt as DiffCalculator, q as getAccountInfo, qn as markNonRetryable, qt as getDefaultStateBucketName, r as DeployEngine, rn as warnDeprecatedNoPrefixCliFlag, rt as replayWarn, s as planRollback, sn as findLargeInlineResources, st as s3BucketArn, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveUseCdkBootstrapAssets, tt as configStringRefusal, u as UNSPECIFIED_SKIP_REASON, un as PARTITION_TABLE, ut as s3BucketRegionalDomainName, v as ccRoutedFinalSnapshotError, vn as getAwsClients, vt as LockManager, w as extractDeploymentEventError, wt as WorkGraph, x as refusesFinalSnapshot, xt as shouldRetainResource, y as createPreDeleteFinalSnapshot, yn as resetAwsClients, yt as S3StateBackend, z as findActionableSilentDrops, zt as formatDockerLoginError } from "./deploy-engine-
|
|
2
|
+
import { $ as coerceCfnBoolean, $t as resolveStateBucketWithDefault, A as cyan, An as MissingCdkCliError, At as AssetModeResolver, B as findSilentDropProperties, Bt as getDockerCmd, C as makeCanonicalizePropertiesFn, Ct as stringifyValue, D as renderStatefulReason, Dn as LocalMigrateError, Dt as loadPublishableAssetManifest, E as isStatefulRecreateTargetSync, En as LocalInvokeBuildError$1, Et as createAssetRedirectResolver, F as IAMRoleProvider, Fn as ResourceUpdateNotSupportedError, Ft as validateAssetBucketName, G as IntrinsicFunctionResolver, Gn as isRetryableTransientError, Gt as Synthesizer, H as slowCcOperationTimeoutMs, Hn as normalizeAwsError, Ht as runDockerStreaming, I as collectInlinePolicyNamesManagedBySiblings, In as StackHasActiveImportsError, It as validateContainerRepoName, J as refStateLookupFromResource, Jt as getLegacyStateBucketName, K as cfnRefValueFromPhysicalId, Kn as isThrottlingError, Kt as synthesisStatusMessage, L as clearOnUpdateRemoval, Ln as StackTerminationProtectionError, Lt as buildDenyExternalAccessPolicy, M as green, Mn as PartialFailureError, Mt as ensureAssetStorage, N as red, Nn as ProvisioningError, Nt as getBootstrapMarkerKey, O as formatResourceLine, On as LocalStartServiceError, Ot as rewriteTemplateAssetReferences, P as yellow, Pn as ResourceTimeoutError, Pt as parseBootstrapMarker, Q as assertRegionMatch, Qt as resolveSkipPrefix, R as ProviderRegistry, Rt as buildDockerImage, S as unsupportedFinalSnapshotError, Sn as CdkdError, St as AssetPublisher, T as MULTI_REGION_RECREATE_BLOCKED_TYPES, Tn as DeployCancelledError, Tt as buildAssetRedirectMap, U as disableInstanceApiTermination, Un as withErrorHandling, Ut as AssetManifestLoader, V as CloudControlProvider, Vt as runDockerForeground, W as isTerminationProtectionPropagationError, Wn as isMarkedNonRetryable, Wt as getDockerImageBySourceHash, X as normalizeAwsTagsToCfn, Xt as resolveAutoAssetStorage, Y as WAFv2WebACLProvider, Yt as resolveApp, Z as resolveExplicitPhysicalId, Zt as resolveCaptureObservedState, _ as buildFinalSnapshotIdentifier, _n as AwsClients, _t as TemplateParser, a as DeploymentEventsStore, an as CFN_TEMPLATE_URL_LIMIT, at as requireConfigObject, b as isFinalSnapshotError, bn as setAwsClients, bt as rebuildClientForBucketRegion, c as replayFailedOperations, cn as uploadCfnTemplate, ct as s3BucketDomainName, d as deleteSkipReason, dn as canonicalizeRegion, dt as s3BucketWebsiteUrl, en as resolveStateBucketWithDefaultAndSource, et as configBooleanRefusal, f as withResourceDeadline, fn as derivePartitionAndUrlSuffix, ft as applyRoleArnIfSet, g as PRE_DELETE_SNAPSHOT_TYPES, gn as resolveBucketRegion, gt as DagBuilder, h as ATOMIC_FINAL_SNAPSHOT_TYPES, ht as withRetry, i as DeploymentEventsReader, in as CFN_TEMPLATE_BODY_LIMIT, it as requireConfigArray, j as gray, jn as NestedStackChildDirectDestroyError, jt as BOOTSTRAP_MARKER_PREFIX, k as bold, kt as escapeRegExp, l as replayRollback, ln as expectedOwnerParam, lt as s3BucketDualStackDomainName, m as computeImplicitDeleteEdges, mn as processStackMessages, mt as describeTypeWithThrottleRetry, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as stateBucketExistenceConfirmed, nt as readConfigString, o as planFailedOps, on as MIGRATE_TMP_PREFIX, ot as requireConfigString, p as IMPLICIT_DELETE_DEPENDENCIES, pn as AssemblyReader, pt as DiffCalculator, q as getAccountInfo, qn as markNonRetryable, qt as getDefaultStateBucketName, r as DeployEngine, rn as warnDeprecatedNoPrefixCliFlag, rt as replayWarn, s as planRollback, sn as findLargeInlineResources, st as s3BucketArn, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveUseCdkBootstrapAssets, tt as configStringRefusal, u as UNSPECIFIED_SKIP_REASON, un as PARTITION_TABLE, ut as s3BucketRegionalDomainName, v as ccRoutedFinalSnapshotError, vn as getAwsClients, vt as LockManager, w as extractDeploymentEventError, wt as WorkGraph, x as refusesFinalSnapshot, xt as shouldRetainResource, y as createPreDeleteFinalSnapshot, yn as resetAwsClients, yt as S3StateBackend, z as findActionableSilentDrops, zt as formatDockerLoginError } from "./deploy-engine-D-4ZvtGc.js";
|
|
3
3
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-Dl2MK3if.js";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
7
7
|
import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -2991,6 +2991,28 @@ var IAMPolicyProvider = class {
|
|
|
2991
2991
|
//#endregion
|
|
2992
2992
|
//#region src/provisioning/providers/iam-managed-policy-provider.ts
|
|
2993
2993
|
/**
|
|
2994
|
+
* Matches an AWS-managed IAM policy ARN in ANY AWS partition (issue #1815).
|
|
2995
|
+
*
|
|
2996
|
+
* AWS-managed policies are the ones whose ACCOUNT segment is the literal
|
|
2997
|
+
* `aws` — `arn:<partition>:iam::aws:policy/<path><name>` — as opposed to a
|
|
2998
|
+
* customer-managed policy, which carries a 12-digit account id. They exist
|
|
2999
|
+
* under every partition (`arn:aws-us-gov:iam::aws:policy/AdministratorAccess`
|
|
3000
|
+
* is a real, attachable ARN in GovCloud), so pinning the PARTITION segment to
|
|
3001
|
+
* the commercial `aws` recognised only a third of them.
|
|
3002
|
+
*
|
|
3003
|
+
* The partition is read OFF THE ARN rather than derived from a region through
|
|
3004
|
+
* `derivePartitionAndUrlSuffix` (`src/utils/aws-partition.ts`): that helper
|
|
3005
|
+
* answers "given a region, which partition am I in", which is the right
|
|
3006
|
+
* question when BUILDING an ARN (the seven sites PR #1834 fixed) and the wrong
|
|
3007
|
+
* one when CLASSIFYING an ARN the caller already holds. The loose
|
|
3008
|
+
* `aws[a-z0-9-]*` partition segment matches `IAM_ROLE_ARN_RE` in
|
|
3009
|
+
* `src/utils/role-arn.ts`; a closed partition list is precisely what goes
|
|
3010
|
+
* stale when AWS adds a partition, and here staleness fails DANGEROUS (see the
|
|
3011
|
+
* refusal's own comment — an unrecognised AWS-managed policy gets adopted, and
|
|
3012
|
+
* destroy then detaches it from every principal in the account).
|
|
3013
|
+
*/
|
|
3014
|
+
const AWS_MANAGED_POLICY_ARN_RE = /^arn:aws[a-z0-9-]*:iam::aws:/;
|
|
3015
|
+
/**
|
|
2994
3016
|
* AWS IAM Managed Policy Provider
|
|
2995
3017
|
*
|
|
2996
3018
|
* Implements resource provisioning for AWS::IAM::ManagedPolicy using the IAM SDK.
|
|
@@ -3002,7 +3024,7 @@ var IAMPolicyProvider = class {
|
|
|
3002
3024
|
* - Detach-before-delete cleanup (a ManagedPolicy with attached principals
|
|
3003
3025
|
* or with non-default versions cannot be deleted directly)
|
|
3004
3026
|
*
|
|
3005
|
-
* Physical id is the policy ARN (`arn
|
|
3027
|
+
* Physical id is the policy ARN (`arn:<partition>:iam::<account>:policy/<path><name>`)
|
|
3006
3028
|
* since path is part of the identity — two policies with the same name in
|
|
3007
3029
|
* different paths are distinct.
|
|
3008
3030
|
*/
|
|
@@ -3239,7 +3261,7 @@ var IAMManagedPolicyProvider = class {
|
|
|
3239
3261
|
const explicit = resolveExplicitPhysicalId(input, "ManagedPolicyName");
|
|
3240
3262
|
if (explicit) {
|
|
3241
3263
|
if (explicit.startsWith("arn:")) {
|
|
3242
|
-
if (
|
|
3264
|
+
if (AWS_MANAGED_POLICY_ARN_RE.test(explicit)) throw new Error(`Refusing to import AWS-managed policy ${explicit}: cdkd only adopts customer-managed policies. If you need to attach an AWS-managed policy to a role / user / group, reference it via ManagedPolicyArns on the principal instead.`);
|
|
3243
3265
|
try {
|
|
3244
3266
|
await this.iamClient.send(new GetPolicyCommand({ PolicyArn: explicit }));
|
|
3245
3267
|
return {
|
|
@@ -3468,7 +3490,7 @@ var IAMManagedPolicyProvider = class {
|
|
|
3468
3490
|
}
|
|
3469
3491
|
};
|
|
3470
3492
|
/**
|
|
3471
|
-
* Recover the policy name from `arn
|
|
3493
|
+
* Recover the policy name from `arn:<partition>:iam::<account>:policy/<path><name>`.
|
|
3472
3494
|
* Used to decide whether `ManagedPolicyName` was mutated relative to the
|
|
3473
3495
|
* physical id we recorded — name + path are immutable on AWS, so any
|
|
3474
3496
|
* difference is a replacement signal.
|
|
@@ -12354,6 +12376,47 @@ var LambdaUrlProvider = class {
|
|
|
12354
12376
|
|
|
12355
12377
|
//#endregion
|
|
12356
12378
|
//#region src/provisioning/providers/lambda-eventsource-provider.ts
|
|
12379
|
+
/**
|
|
12380
|
+
* Captures the SERVICE segment of an ARN in ANY AWS partition (issue #1815).
|
|
12381
|
+
*
|
|
12382
|
+
* `arn:<partition>:<service>:<region>:<account>:<rest>` — group 1 is the
|
|
12383
|
+
* service. The partition segment is matched loosely as `aws[a-z0-9-]*`, the
|
|
12384
|
+
* same shape `IAM_ROLE_ARN_RE` (`src/utils/role-arn.ts`) uses, rather than
|
|
12385
|
+
* enumerated: this classifier previously hand-listed `aws` and `aws-cn` per
|
|
12386
|
+
* service and so returned `'unknown'` for every OTHER partition — GovCloud,
|
|
12387
|
+
* all four iso partitions and `aws-eusc` — which silently disabled the
|
|
12388
|
+
* type-discriminator gating below (see the `KINDS_WITH_*` sets) and let
|
|
12389
|
+
* `cdkd drift --revert` push `FunctionResponseTypes` / `SourceAccessConfigurations`
|
|
12390
|
+
* placeholders AWS rejects. A closed partition list is exactly what goes stale
|
|
12391
|
+
* when AWS adds a partition; reading the partition off the ARN cannot.
|
|
12392
|
+
*
|
|
12393
|
+
* Note the partition is not derived from a region via
|
|
12394
|
+
* `derivePartitionAndUrlSuffix` (`src/utils/aws-partition.ts`) because this
|
|
12395
|
+
* CLASSIFIES an ARN already in hand — the partition is in the input, and no
|
|
12396
|
+
* region reaches this function.
|
|
12397
|
+
*/
|
|
12398
|
+
const ARN_SERVICE_RE = /^arn:aws[a-z0-9-]*:([^:]+):/;
|
|
12399
|
+
/**
|
|
12400
|
+
* ARN service segment -> {@link EventSourceKind}. Replaces the per-partition
|
|
12401
|
+
* `startsWith` chain; every entry is partition-independent by construction.
|
|
12402
|
+
*/
|
|
12403
|
+
const ARN_SERVICE_TO_EVENT_SOURCE_KIND = {
|
|
12404
|
+
sqs: "sqs",
|
|
12405
|
+
kinesis: "kinesis",
|
|
12406
|
+
dynamodb: "dynamodb",
|
|
12407
|
+
kafka: "kafka",
|
|
12408
|
+
mq: "mq",
|
|
12409
|
+
rds: "documentdb"
|
|
12410
|
+
};
|
|
12411
|
+
/**
|
|
12412
|
+
* @internal EXPORTED for tests only (issue #1815). Every production consumer is a
|
|
12413
|
+
* `Set.has(kind)`, so the `Object.hasOwn` guard below is UNOBSERVABLE through
|
|
12414
|
+
* behavior — a prototype member and `'unknown'` both miss every set. That was
|
|
12415
|
+
* measured, not assumed: a behavior-level test for it passed with the guard
|
|
12416
|
+
* reverted. Rather than ship an unverifiable guard or a test that only looks
|
|
12417
|
+
* like one, the classifier's RETURN VALUE is asserted directly. Do not read the
|
|
12418
|
+
* export as an invitation to call this from outside the provider.
|
|
12419
|
+
*/
|
|
12357
12420
|
function classifyEventSource(resp) {
|
|
12358
12421
|
if (resp.SelfManagedEventSource !== void 0) return "kafka";
|
|
12359
12422
|
if (resp.SelfManagedKafkaEventSourceConfig !== void 0) return "kafka";
|
|
@@ -12361,13 +12424,9 @@ function classifyEventSource(resp) {
|
|
|
12361
12424
|
if (resp.DocumentDBEventSourceConfig !== void 0) return "documentdb";
|
|
12362
12425
|
const arn = resp.EventSourceArn;
|
|
12363
12426
|
if (!arn) return "unknown";
|
|
12364
|
-
|
|
12365
|
-
if (
|
|
12366
|
-
|
|
12367
|
-
if (arn.startsWith("arn:aws:kafka:") || arn.startsWith("arn:aws-cn:kafka:")) return "kafka";
|
|
12368
|
-
if (arn.startsWith("arn:aws:mq:") || arn.startsWith("arn:aws-cn:mq:")) return "mq";
|
|
12369
|
-
if (arn.startsWith("arn:aws:rds:") || arn.startsWith("arn:aws-cn:rds:")) return "documentdb";
|
|
12370
|
-
return "unknown";
|
|
12427
|
+
const service = ARN_SERVICE_RE.exec(arn)?.[1];
|
|
12428
|
+
if (!service) return "unknown";
|
|
12429
|
+
return Object.hasOwn(ARN_SERVICE_TO_EVENT_SOURCE_KIND, service) ? ARN_SERVICE_TO_EVENT_SOURCE_KIND[service] : "unknown";
|
|
12371
12430
|
}
|
|
12372
12431
|
/**
|
|
12373
12432
|
* Classify an event source mapping from its CFn property bag (as opposed to
|
|
@@ -55372,7 +55431,10 @@ var NestedStackProvider = class {
|
|
|
55372
55431
|
...ctx.destroyOptions?.resourceWarnAfterByType && { resourceWarnAfterByType: ctx.destroyOptions.resourceWarnAfterByType },
|
|
55373
55432
|
...ctx.destroyOptions?.resourceTimeoutByType && { resourceTimeoutByType: ctx.destroyOptions.resourceTimeoutByType }
|
|
55374
55433
|
}));
|
|
55375
|
-
if (childResult.errorCount > 0)
|
|
55434
|
+
if (childResult.errorCount > 0) {
|
|
55435
|
+
const failure = new Error(nestedStackChildFailureMessage(childStackName, childResult.errorCount, childResult.skippedCount, childResult.interrupted));
|
|
55436
|
+
throw childResult.interrupted ? markNonRetryable(failure) : failure;
|
|
55437
|
+
}
|
|
55376
55438
|
if (childResult.skippedCount > 0 || childResult.interrupted) {
|
|
55377
55439
|
const causes = [];
|
|
55378
55440
|
if (childResult.skippedCount > 0) causes.push(`skipped ${childResult.skippedCount} resource(s)`);
|
|
@@ -55410,7 +55472,7 @@ var NestedStackProvider = class {
|
|
|
55410
55472
|
}
|
|
55411
55473
|
requireContext() {
|
|
55412
55474
|
const ctx = getCurrentNestedStackContext();
|
|
55413
|
-
if (!ctx) throw new Error("NestedStackProvider invoked outside withNestedStackContext() scope. The deploy / destroy CLI entry point must wrap its DeployEngine.deploy / runDestroyForStack call in withNestedStackContext(ctx, () => ...).");
|
|
55475
|
+
if (!ctx) throw markNonRetryable(/* @__PURE__ */ new Error("NestedStackProvider invoked outside withNestedStackContext() scope. The deploy / destroy CLI entry point must wrap its DeployEngine.deploy / runDestroyForStack call in withNestedStackContext(ctx, () => ...)."));
|
|
55414
55476
|
return ctx;
|
|
55415
55477
|
}
|
|
55416
55478
|
requireDeployContext(ctx, op) {
|
|
@@ -68858,12 +68920,21 @@ function indexGrandchildTemplatePaths(childTemplate, childTemplatePath) {
|
|
|
68858
68920
|
* `--stack-region`, bucket resolution failure) logs at warn and returns
|
|
68859
68921
|
* `undefined`. Auth failures and other genuine errors propagate.
|
|
68860
68922
|
*
|
|
68861
|
-
*
|
|
68923
|
+
* No lock acquisition and no `state.json` write path here. That is NOT the
|
|
68924
|
+
* same as "read-only against the bucket", and the difference was mis-stated
|
|
68925
|
+
* until issue #1836: {@link buildCrossStackResolver}'s exports-index lookup
|
|
68926
|
+
* goes through {@link ExportIndexStore}, whose `load` REBUILDS the index from
|
|
68927
|
+
* the per-stack state files on a miss and PUTs the rebuilt object
|
|
68928
|
+
* (`cdkd/_index/{region}/exports.json`). So an `Fn::ImportValue` lookup from a
|
|
68929
|
+
* `cdkd local` command can write that one derived key. Keeping the index KEY
|
|
68930
|
+
* the same spelling the state records carry is what keeps that write CORRECT
|
|
68931
|
+
* rather than an empty index — see the note on `consumerRegion` below.
|
|
68862
68932
|
*/
|
|
68863
68933
|
async function loadStateForStack(stackName, synthRegion, opts) {
|
|
68864
68934
|
const logger = getLogger();
|
|
68865
68935
|
const prefix = opts.logPrefix ?? "--from-state";
|
|
68866
|
-
const region = opts.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? synthRegion ?? "us-east-1";
|
|
68936
|
+
const region = canonicalizeRegion(opts.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? synthRegion ?? "us-east-1");
|
|
68937
|
+
const clientRegion = canonicalizeRegion(opts.region) || void 0;
|
|
68867
68938
|
let stateBucket;
|
|
68868
68939
|
try {
|
|
68869
68940
|
stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, region);
|
|
@@ -68872,7 +68943,7 @@ async function loadStateForStack(stackName, synthRegion, opts) {
|
|
|
68872
68943
|
return;
|
|
68873
68944
|
}
|
|
68874
68945
|
const awsClients = new AwsClients({
|
|
68875
|
-
...
|
|
68946
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
68876
68947
|
...opts.profile !== void 0 && { profile: opts.profile }
|
|
68877
68948
|
});
|
|
68878
68949
|
setAwsClients(awsClients);
|
|
@@ -68882,7 +68953,7 @@ async function loadStateForStack(stackName, synthRegion, opts) {
|
|
|
68882
68953
|
prefix: opts.statePrefix
|
|
68883
68954
|
};
|
|
68884
68955
|
const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
|
|
68885
|
-
...
|
|
68956
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
68886
68957
|
...opts.profile !== void 0 && { profile: opts.profile }
|
|
68887
68958
|
});
|
|
68888
68959
|
await stateBackend.verifyBucketExists();
|
|
@@ -68891,15 +68962,58 @@ async function loadStateForStack(stackName, synthRegion, opts) {
|
|
|
68891
68962
|
logger.warn(`${prefix}: no cdkd state found for stack '${stackName}' in bucket '${stateBucket}'. Was it deployed via 'cdkd deploy'? Falling back.`);
|
|
68892
68963
|
return;
|
|
68893
68964
|
}
|
|
68965
|
+
/**
|
|
68966
|
+
* Issue #1836: EXACT match first, case-insensitive second.
|
|
68967
|
+
*
|
|
68968
|
+
* `S3StateBackend.listStacks` dedupes on the EXACT `{stack}\0{region}` pair,
|
|
68969
|
+
* so `cdkd/MyStack/US-EAST-1/state.json` and
|
|
68970
|
+
* `cdkd/MyStack/us-east-1/state.json` are two DISTINCT refs — and
|
|
68971
|
+
* ListObjectsV2 returns them in ASCII order, i.e. the upper-cased one FIRST.
|
|
68972
|
+
* A fold-only `find` therefore hands `--stack-region us-east-1` the OTHER
|
|
68973
|
+
* record, silently reading state the user did not name; the pre-fold `===`
|
|
68974
|
+
* got that case right. So the fold is the RECOVERY for a case mismatch, never
|
|
68975
|
+
* an override of a spelling that exists verbatim.
|
|
68976
|
+
*
|
|
68977
|
+
* `candidate` is therefore the spelling the USER supplied, not the folded
|
|
68978
|
+
* one (round 3 of the #1836 review): the fold at each command's handler
|
|
68979
|
+
* entry and at the `--from-state` factory is what an SDK client needs, but
|
|
68980
|
+
* feeding it to this compare made the exact-match rule unreachable from any
|
|
68981
|
+
* real CLI invocation — every candidate was already canonical, so `exact`
|
|
68982
|
+
* could only ever match an already-canonical record and `--stack-region
|
|
68983
|
+
* US-EAST-1` read the `us-east-1` one while REPORTING it as the exact
|
|
68984
|
+
* spelling. `opts.rawStackRegion` carries the unfolded value here; the
|
|
68985
|
+
* recovery arm folds both sides itself, so nothing upstream has to.
|
|
68986
|
+
*
|
|
68987
|
+
* When more than one ref is canonical-equal the choice is announced, because
|
|
68988
|
+
* either answer reads some record the user did not spell out in full and the
|
|
68989
|
+
* silence is what made the original miss hard to diagnose. The wording
|
|
68990
|
+
* states which of the two rules decided it — and does so from the same
|
|
68991
|
+
* `exact` binding the choice was made from, so it cannot claim an exact
|
|
68992
|
+
* match the compare did not make.
|
|
68993
|
+
*/
|
|
68994
|
+
const findByRegion = (candidate, source) => {
|
|
68995
|
+
const exact = refs.find((r) => r.region === candidate);
|
|
68996
|
+
const folded = refs.filter((r) => canonicalizeRegion(r.region) === canonicalizeRegion(candidate));
|
|
68997
|
+
const chosen = exact ?? folded[0];
|
|
68998
|
+
if (folded.length > 1 && chosen) {
|
|
68999
|
+
const how = exact ? `matches ${source} '${candidate}' exactly` : `no record spells ${source} '${candidate}' exactly, so this is a case-insensitive recovery`;
|
|
69000
|
+
logger.warn(`${prefix}: stack '${stackName}' has state under ${folded.length} case-variant spellings of ${source} '${candidate}' (${folded.map((r) => r.region ?? "(legacy)").join(", ")}). Reading '${chosen.region ?? "(legacy)"}' — ${how}.`);
|
|
69001
|
+
}
|
|
69002
|
+
return chosen;
|
|
69003
|
+
};
|
|
69004
|
+
let synthMatch;
|
|
69005
|
+
if (!opts.stackRegion && synthRegion) synthMatch = findByRegion(synthRegion, "the synth-derived stack region");
|
|
68894
69006
|
let targetRegion;
|
|
68895
69007
|
if (opts.stackRegion) {
|
|
68896
|
-
|
|
69008
|
+
const requestedRegion = opts.rawStackRegion ?? opts.stackRegion;
|
|
69009
|
+
const found = findByRegion(requestedRegion, "--stack-region");
|
|
69010
|
+
if (!found) {
|
|
68897
69011
|
const seen = refs.map((r) => r.region ?? "(legacy)").join(", ");
|
|
68898
|
-
logger.warn(`${prefix}: stack '${stackName}' has no state in region '${
|
|
69012
|
+
logger.warn(`${prefix}: stack '${stackName}' has no state in region '${requestedRegion}' (available: ${seen}). Falling back.`);
|
|
68899
69013
|
return;
|
|
68900
69014
|
}
|
|
68901
|
-
targetRegion =
|
|
68902
|
-
} else if (synthRegion &&
|
|
69015
|
+
targetRegion = found.region ?? requestedRegion;
|
|
69016
|
+
} else if (synthRegion && synthMatch) targetRegion = synthMatch.region ?? synthRegion;
|
|
68903
69017
|
else if (refs.length === 1) targetRegion = refs[0].region ?? synthRegion ?? region;
|
|
68904
69018
|
else {
|
|
68905
69019
|
const seen = refs.map((r) => r.region ?? "(legacy)").join(", ");
|
|
@@ -68948,7 +69062,8 @@ async function loadStateForStack(stackName, synthRegion, opts) {
|
|
|
68948
69062
|
async function loadBootstrapContainerRepo(synthRegion, opts) {
|
|
68949
69063
|
const logger = getLogger();
|
|
68950
69064
|
const prefix = opts.logPrefix ?? "--from-state";
|
|
68951
|
-
const
|
|
69065
|
+
const rawRegion = opts.region ?? opts.rawStackRegion ?? opts.stackRegion ?? synthRegion ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? "us-east-1";
|
|
69066
|
+
const region = canonicalizeRegion(rawRegion);
|
|
68952
69067
|
let stateBucket;
|
|
68953
69068
|
try {
|
|
68954
69069
|
stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, region);
|
|
@@ -68956,8 +69071,9 @@ async function loadBootstrapContainerRepo(synthRegion, opts) {
|
|
|
68956
69071
|
logger.debug(`${prefix}: could not resolve state bucket for the bootstrap-marker read: ${err instanceof Error ? err.message : String(err)}. Falling back to conventional asset-repo names.`);
|
|
68957
69072
|
return;
|
|
68958
69073
|
}
|
|
69074
|
+
const clientRegion = canonicalizeRegion(opts.region) || void 0;
|
|
68959
69075
|
const awsClients = new AwsClients({
|
|
68960
|
-
...
|
|
69076
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
68961
69077
|
...opts.profile !== void 0 && { profile: opts.profile }
|
|
68962
69078
|
});
|
|
68963
69079
|
setAwsClients(awsClients);
|
|
@@ -68967,16 +69083,25 @@ async function loadBootstrapContainerRepo(synthRegion, opts) {
|
|
|
68967
69083
|
prefix: opts.statePrefix
|
|
68968
69084
|
};
|
|
68969
69085
|
const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
|
|
68970
|
-
...
|
|
69086
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
68971
69087
|
...opts.profile !== void 0 && { profile: opts.profile }
|
|
68972
69088
|
});
|
|
68973
69089
|
const markerKey = getBootstrapMarkerKey(region);
|
|
68974
|
-
|
|
69090
|
+
let resolvedKey = markerKey;
|
|
69091
|
+
let body = await stateBackend.getRawObject(markerKey);
|
|
69092
|
+
if (body === null && rawRegion !== region) {
|
|
69093
|
+
const rawKey = getBootstrapMarkerKey(rawRegion);
|
|
69094
|
+
body = await stateBackend.getRawObject(rawKey);
|
|
69095
|
+
if (body !== null) {
|
|
69096
|
+
resolvedKey = rawKey;
|
|
69097
|
+
logger.debug(`${prefix}: bootstrap marker found at the un-folded key '${rawKey}' (no '${markerKey}') — an upper-cased region was used at 'cdkd bootstrap' time.`);
|
|
69098
|
+
}
|
|
69099
|
+
}
|
|
68975
69100
|
if (body === null) {
|
|
68976
|
-
logger.debug(`${prefix}: no bootstrap marker at '${markerKey}' in bucket '${stateBucket}' — assuming conventional asset-repo names.`);
|
|
69101
|
+
logger.debug(`${prefix}: no bootstrap marker at '${markerKey}'${rawRegion !== region ? ` (nor '${getBootstrapMarkerKey(rawRegion)}')` : ""} in bucket '${stateBucket}' — assuming conventional asset-repo names.`);
|
|
68977
69102
|
return;
|
|
68978
69103
|
}
|
|
68979
|
-
const marker = parseBootstrapMarker(body,
|
|
69104
|
+
const marker = parseBootstrapMarker(body, resolvedKey);
|
|
68980
69105
|
logger.debug(`${prefix}: bootstrap marker for ${region} names container repo '${marker.containerRepo}'.`);
|
|
68981
69106
|
return marker.containerRepo;
|
|
68982
69107
|
} catch (err) {
|
|
@@ -69015,13 +69140,14 @@ async function buildCrossStackResolver(consumerRegion, opts) {
|
|
|
69015
69140
|
const prefix = opts.logPrefix ?? "--from-state";
|
|
69016
69141
|
let stateBucket;
|
|
69017
69142
|
try {
|
|
69018
|
-
stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, consumerRegion);
|
|
69143
|
+
stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, canonicalizeRegion(consumerRegion));
|
|
69019
69144
|
} catch (err) {
|
|
69020
69145
|
logger.warn(`${prefix}: cross-stack resolver could not resolve state bucket: ${err instanceof Error ? err.message : String(err)}. Fn::ImportValue / Fn::GetStackOutput env entries will warn-and-drop.`);
|
|
69021
69146
|
return;
|
|
69022
69147
|
}
|
|
69148
|
+
const clientRegion = canonicalizeRegion(opts.region) || void 0;
|
|
69023
69149
|
const awsClients = new AwsClients({
|
|
69024
|
-
...
|
|
69150
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
69025
69151
|
...opts.profile !== void 0 && { profile: opts.profile }
|
|
69026
69152
|
});
|
|
69027
69153
|
const stateConfig = {
|
|
@@ -69029,7 +69155,7 @@ async function buildCrossStackResolver(consumerRegion, opts) {
|
|
|
69029
69155
|
prefix: opts.statePrefix
|
|
69030
69156
|
};
|
|
69031
69157
|
const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
|
|
69032
|
-
...
|
|
69158
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
69033
69159
|
...opts.profile !== void 0 && { profile: opts.profile }
|
|
69034
69160
|
});
|
|
69035
69161
|
try {
|
|
@@ -69061,9 +69187,10 @@ async function buildCrossStackResolver(consumerRegion, opts) {
|
|
|
69061
69187
|
logger.debug(`${prefix}: failed to list stacks during Fn::ImportValue fallback for '${exportName}': ${err instanceof Error ? err.message : String(err)}`);
|
|
69062
69188
|
return;
|
|
69063
69189
|
}
|
|
69190
|
+
const canonicalConsumerRegion = canonicalizeRegion(consumerRegion);
|
|
69064
69191
|
for (const ref of refs) {
|
|
69065
69192
|
const region = ref.region ?? consumerRegion;
|
|
69066
|
-
if (region !==
|
|
69193
|
+
if (canonicalizeRegion(region) !== canonicalConsumerRegion) continue;
|
|
69067
69194
|
try {
|
|
69068
69195
|
const got = await stateBackend.getState(ref.stackName, region);
|
|
69069
69196
|
if (!got || !got.state.outputs) continue;
|
|
@@ -69080,14 +69207,31 @@ async function buildCrossStackResolver(consumerRegion, opts) {
|
|
|
69080
69207
|
}
|
|
69081
69208
|
},
|
|
69082
69209
|
async resolveGetStackOutput(producerStack, producerRegion, outputName) {
|
|
69083
|
-
|
|
69084
|
-
const got = await stateBackend.getState(producerStack, producerRegion);
|
|
69210
|
+
const readOutput = (got) => {
|
|
69085
69211
|
if (!got || !got.state.outputs) return void 0;
|
|
69086
69212
|
if (!(outputName in got.state.outputs)) return void 0;
|
|
69087
69213
|
const value = got.state.outputs[outputName];
|
|
69088
69214
|
if (typeof value === "string") return value;
|
|
69089
69215
|
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
69090
69216
|
return JSON.stringify(value);
|
|
69217
|
+
};
|
|
69218
|
+
try {
|
|
69219
|
+
const got = await stateBackend.getState(producerStack, producerRegion);
|
|
69220
|
+
if (got) return readOutput(got);
|
|
69221
|
+
const canonicalProducerRegion = canonicalizeRegion(producerRegion);
|
|
69222
|
+
const refs = await stateBackend.listStacks();
|
|
69223
|
+
for (const ref of refs) {
|
|
69224
|
+
if (ref.stackName !== producerStack) continue;
|
|
69225
|
+
const region = ref.region ?? producerRegion;
|
|
69226
|
+
if (region === producerRegion) continue;
|
|
69227
|
+
if (canonicalizeRegion(region) !== canonicalProducerRegion) continue;
|
|
69228
|
+
const recovered = readOutput(await stateBackend.getState(producerStack, region));
|
|
69229
|
+
if (recovered !== void 0) {
|
|
69230
|
+
logger.debug(`${prefix}: Fn::GetStackOutput '${producerStack}.${outputName}' resolved from the case-variant state record '${region}' (no record spells '${producerRegion}' exactly).`);
|
|
69231
|
+
return recovered;
|
|
69232
|
+
}
|
|
69233
|
+
}
|
|
69234
|
+
return;
|
|
69091
69235
|
} catch (err) {
|
|
69092
69236
|
logger.debug(`${prefix}: state read failed for Fn::GetStackOutput '${producerStack}.${outputName}' (${producerRegion}): ${err instanceof Error ? err.message : String(err)}`);
|
|
69093
69237
|
return;
|
|
@@ -69127,6 +69271,7 @@ var S3LocalStateProvider = class {
|
|
|
69127
69271
|
const loaded = await loadStateForStack(stackName, synthRegion, {
|
|
69128
69272
|
statePrefix: this.opts.statePrefix,
|
|
69129
69273
|
...this.opts.stackRegion !== void 0 && { stackRegion: this.opts.stackRegion },
|
|
69274
|
+
...this.opts.rawStackRegion !== void 0 && { rawStackRegion: this.opts.rawStackRegion },
|
|
69130
69275
|
...this.opts.stateBucket !== void 0 && { stateBucket: this.opts.stateBucket },
|
|
69131
69276
|
...this.opts.region !== void 0 && { region: this.opts.region },
|
|
69132
69277
|
...this.opts.profile !== void 0 && { profile: this.opts.profile }
|
|
@@ -69193,12 +69338,16 @@ var S3LocalStateProvider = class {
|
|
|
69193
69338
|
*/
|
|
69194
69339
|
const fromStateFactory = (options) => {
|
|
69195
69340
|
const opts = options;
|
|
69341
|
+
const clientRegion = canonicalizeRegion(opts.region) || void 0;
|
|
69342
|
+
const stackRegion = canonicalizeRegion(opts.stackRegion) || void 0;
|
|
69343
|
+
const rawStackRegion = opts.rawStackRegion || opts.stackRegion || void 0;
|
|
69196
69344
|
return new S3LocalStateProvider({
|
|
69197
69345
|
statePrefix: opts.statePrefix,
|
|
69198
69346
|
...opts.stateBucket !== void 0 && { stateBucket: opts.stateBucket },
|
|
69199
|
-
...
|
|
69347
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
69200
69348
|
...opts.profile !== void 0 && { profile: opts.profile },
|
|
69201
|
-
...
|
|
69349
|
+
...stackRegion !== void 0 && { stackRegion },
|
|
69350
|
+
...rawStackRegion !== void 0 && { rawStackRegion }
|
|
69202
69351
|
});
|
|
69203
69352
|
};
|
|
69204
69353
|
/**
|
|
@@ -72380,6 +72529,10 @@ async function localStartApiCommand(target, options) {
|
|
|
72380
72529
|
const logger = getLogger();
|
|
72381
72530
|
if (options.verbose) logger.setLevel("debug");
|
|
72382
72531
|
if (options.region !== void 0) options.region = canonicalizeRegion(options.region);
|
|
72532
|
+
if (options.stackRegion !== void 0) {
|
|
72533
|
+
options.rawStackRegion = options.stackRegion;
|
|
72534
|
+
options.stackRegion = canonicalizeRegion(options.stackRegion);
|
|
72535
|
+
}
|
|
72383
72536
|
let apiFilter = target;
|
|
72384
72537
|
if (options.api !== void 0) {
|
|
72385
72538
|
if (target !== void 0) throw new Error(`Cannot specify both positional target ('${target}') and --api flag ('${options.api}'). Use one or the other. The positional form is preferred — '--api' is a deprecated alias.`);
|
|
@@ -74722,6 +74875,10 @@ async function localRunTaskCommand(target, options) {
|
|
|
74722
74875
|
if (options.verbose) logger.setLevel("debug");
|
|
74723
74876
|
warnIfDeprecatedRegion(options);
|
|
74724
74877
|
if (options.region !== void 0) options.region = canonicalizeRegion(options.region);
|
|
74878
|
+
if (options.stackRegion !== void 0) {
|
|
74879
|
+
options.rawStackRegion = options.stackRegion;
|
|
74880
|
+
options.stackRegion = canonicalizeRegion(options.stackRegion);
|
|
74881
|
+
}
|
|
74725
74882
|
const state = createEcsRunState();
|
|
74726
74883
|
let sigintHandler;
|
|
74727
74884
|
let sigintCount = 0;
|
|
@@ -74766,12 +74923,12 @@ async function localRunTaskCommand(target, options) {
|
|
|
74766
74923
|
const { stacks } = await synthesizer.synthesize(synthOpts);
|
|
74767
74924
|
const candidate = pickCandidateStack(parseEcsTarget(target).stackPattern, stacks);
|
|
74768
74925
|
stateProvider = createLocalStateProvider$1(options, candidate?.stackName ?? "", candidate?.region);
|
|
74769
|
-
const imageContext = await buildEcsImageResolutionContext$1(candidate, stateProvider, options);
|
|
74926
|
+
const { context: imageContext, stateRecordRegion } = await buildEcsImageResolutionContext$1(candidate, stateProvider, options);
|
|
74770
74927
|
const task = resolveEcsTaskTarget(target, stacks, imageContext);
|
|
74771
74928
|
logger.info(`Target: ${task.stack.stackName}/${task.taskDefinitionLogicalId} (family=${task.family}, containers=${task.containers.length})`);
|
|
74772
74929
|
const taskNeeds = detectEcsImageResolutionNeeds(stacks.find((s) => s.stackName === task.stack.stackName) ?? task.stack);
|
|
74773
74930
|
if (stateProvider && taskNeeds.needsCrossStackResolver) {
|
|
74774
|
-
const consumerRegion = options
|
|
74931
|
+
const consumerRegion = resolveEcsConsumerRegion(stateRecordRegion, options, task.stack.region);
|
|
74775
74932
|
const resolver = await stateProvider.buildCrossStackResolver(consumerRegion);
|
|
74776
74933
|
if (resolver) await applyCrossStackResolverToTask(task, {
|
|
74777
74934
|
resources: imageContext?.stateResources ?? {},
|
|
@@ -74881,9 +75038,48 @@ async function assumeTaskRole(roleArn, region) {
|
|
|
74881
75038
|
}
|
|
74882
75039
|
}
|
|
74883
75040
|
/**
|
|
75041
|
+
* Region the cross-stack resolver is scoped to for `cdkd local run-task`
|
|
75042
|
+
* (issue #1836).
|
|
75043
|
+
*
|
|
75044
|
+
* This value does THREE things (round 3 said TWO and was wrong — corrected in
|
|
75045
|
+
* round 4), and the first is why the state record's own spelling outranks the env
|
|
75046
|
+
* chain:
|
|
75047
|
+
*
|
|
75048
|
+
* 1. it becomes the exports-index KEY `cdkd/_index/{region}/exports.json`;
|
|
75049
|
+
* 2. it is the same-region filter of the index-miss per-stack scan; and
|
|
75050
|
+
* 3. via `SubstitutionContext.consumerRegion`, it is cdk-local's DEFAULT producer
|
|
75051
|
+
* region for an `Fn::GetStackOutput` that carries no explicit `Region` — i.e.
|
|
75052
|
+
* it becomes the `cdkd/{producer}/{region}/state.json` key that intrinsic
|
|
75053
|
+
* reads.
|
|
75054
|
+
*
|
|
75055
|
+
* All three land in `buildCrossStackResolver` (`local-state-loader.ts`), whose
|
|
75056
|
+
* `resolveImport` scan (2) and — since round 4 — `resolveGetStackOutput` (3) fold
|
|
75057
|
+
* BOTH sides for the comparison / recovery, while (1) is deliberately raw.
|
|
75058
|
+
*
|
|
75059
|
+
* `local invoke` / `local invoke-agentcore` both pass `loaded.region` there; this
|
|
75060
|
+
* command passed a FOLDED env-chain value instead, so the two commands keyed one
|
|
75061
|
+
* index differently — and a key the state records do not spell makes
|
|
75062
|
+
* `ExportIndexStore`'s rebuild filter match zero refs and PUT an EMPTY index,
|
|
75063
|
+
* permanently degrading every `Fn::ImportValue` to the O(N) scan. Keyed by a
|
|
75064
|
+
* record's spelling the filter always matches.
|
|
75065
|
+
*
|
|
75066
|
+
* The env-chain arm remains for the case where no state record was loaded at all
|
|
75067
|
+
* (nothing in the task needed one), and it stays FOLDED for the reason the rest
|
|
75068
|
+
* of this file folds: both env-var links escape the handler-entry `--region`
|
|
75069
|
+
* fold, and a raw upper-cased spelling reaches an S3 key and an SDK client.
|
|
75070
|
+
*
|
|
75071
|
+
* @internal exported for unit tests.
|
|
75072
|
+
*/
|
|
75073
|
+
function resolveEcsConsumerRegion(stateRecordRegion, options, synthRegion) {
|
|
75074
|
+
if (stateRecordRegion) return stateRecordRegion;
|
|
75075
|
+
return canonicalizeRegion((options.region || process.env["AWS_REGION"]) ?? process.env["AWS_DEFAULT_REGION"] ?? synthRegion ?? "us-east-1");
|
|
75076
|
+
}
|
|
75077
|
+
/**
|
|
74884
75078
|
* Build the substitution context the ECS task resolver consumes (issue
|
|
74885
|
-
* #264)
|
|
74886
|
-
*
|
|
75079
|
+
* #264), plus the region of the state record it loaded (if any — issue #1836;
|
|
75080
|
+
* see {@link resolveEcsConsumerRegion} for what reads it). `context` is
|
|
75081
|
+
* `undefined` when no container's `Image` field needs substitution — the
|
|
75082
|
+
* resolver behaves as before in that case.
|
|
74887
75083
|
*
|
|
74888
75084
|
* Tier 1 (pseudo parameters) fires `sts:GetCallerIdentity` once for
|
|
74889
75085
|
* `${AWS::AccountId}`; region / partition / URL suffix come from the CLI
|
|
@@ -74902,10 +75098,11 @@ async function assumeTaskRole(roleArn, region) {
|
|
|
74902
75098
|
*/
|
|
74903
75099
|
async function buildEcsImageResolutionContext$1(candidate, stateProvider, options) {
|
|
74904
75100
|
const logger = getLogger();
|
|
74905
|
-
if (!candidate) return
|
|
75101
|
+
if (!candidate) return {};
|
|
74906
75102
|
const needs = detectEcsImageResolutionNeeds(candidate);
|
|
74907
|
-
if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution && !needs.needsAssetRepoMarker) return;
|
|
75103
|
+
if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution && !needs.needsAssetRepoMarker) return {};
|
|
74908
75104
|
const ctx = {};
|
|
75105
|
+
let stateRecordRegion;
|
|
74909
75106
|
const wantsPseudoForEnvOrSecret = !!stateProvider && needs.needsEnvOrSecretSubstitution;
|
|
74910
75107
|
if (needs.needsPseudoParameters || wantsPseudoForEnvOrSecret) {
|
|
74911
75108
|
const region = canonicalizeRegion(options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? candidate.region);
|
|
@@ -74929,13 +75126,17 @@ async function buildEcsImageResolutionContext$1(candidate, stateProvider, option
|
|
|
74929
75126
|
const wantsState = needs.needsStateResources || needs.needsEnvOrSecretSubstitution;
|
|
74930
75127
|
if (stateProvider && wantsState) {
|
|
74931
75128
|
const loaded = await stateProvider.load(candidate.stackName, candidate.region);
|
|
74932
|
-
if (loaded)
|
|
75129
|
+
if (loaded) {
|
|
75130
|
+
ctx.stateResources = loaded.resources;
|
|
75131
|
+
stateRecordRegion = loaded.region;
|
|
75132
|
+
}
|
|
74933
75133
|
} else if (!stateProvider && needs.needsStateResources) logger.warn("Container Image references a same-stack AWS::ECR::Repository. Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute the deployed repository URI. Otherwise the resolver will surface its existing error.");
|
|
74934
75134
|
else if (!stateProvider && needs.needsEnvOrSecretSubstitution) logger.warn("Container Environment / Secrets entries contain CloudFormation intrinsics (Ref / Fn::GetAtt / Fn::Sub / Fn::Join). Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute them against deployed state. Without a state source these entries are dropped (per-key warnings will follow).");
|
|
74935
75135
|
if (needs.needsAssetRepoMarker && options.fromState) {
|
|
74936
75136
|
const containerRepo = await loadBootstrapContainerRepo(candidate.region, {
|
|
74937
75137
|
statePrefix: options.statePrefix,
|
|
74938
75138
|
...options.stackRegion !== void 0 && { stackRegion: options.stackRegion },
|
|
75139
|
+
...options.rawStackRegion !== void 0 && { rawStackRegion: options.rawStackRegion },
|
|
74939
75140
|
...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
|
|
74940
75141
|
...options.region !== void 0 && { region: options.region },
|
|
74941
75142
|
...options.profile !== void 0 && { profile: options.profile },
|
|
@@ -74943,7 +75144,10 @@ async function buildEcsImageResolutionContext$1(candidate, stateProvider, option
|
|
|
74943
75144
|
});
|
|
74944
75145
|
if (containerRepo !== void 0) ctx.cdkAssetContainerRepo = containerRepo;
|
|
74945
75146
|
}
|
|
74946
|
-
return
|
|
75147
|
+
return {
|
|
75148
|
+
context: ctx,
|
|
75149
|
+
...stateRecordRegion !== void 0 && { stateRecordRegion }
|
|
75150
|
+
};
|
|
74947
75151
|
}
|
|
74948
75152
|
function pickCandidateStack(stackPattern, stacks) {
|
|
74949
75153
|
if (stackPattern === null) {
|
|
@@ -75376,6 +75580,10 @@ async function localInvokeAgentCoreCommand(target, options) {
|
|
|
75376
75580
|
if (options.verbose) logger.setLevel("debug");
|
|
75377
75581
|
warnIfDeprecatedRegion(options);
|
|
75378
75582
|
if (options.region !== void 0) options.region = canonicalizeRegion(options.region);
|
|
75583
|
+
if (options.stackRegion !== void 0) {
|
|
75584
|
+
options.rawStackRegion = options.stackRegion;
|
|
75585
|
+
options.stackRegion = canonicalizeRegion(options.stackRegion);
|
|
75586
|
+
}
|
|
75379
75587
|
let containerId;
|
|
75380
75588
|
let stopLogs;
|
|
75381
75589
|
let sigintHandler;
|
|
@@ -76266,16 +76474,37 @@ function emitA2aResult(result) {
|
|
|
76266
76474
|
function platformToArchitecture(platform) {
|
|
76267
76475
|
return platform === "linux/amd64" ? "x86_64" : "arm64";
|
|
76268
76476
|
}
|
|
76477
|
+
/**
|
|
76478
|
+
* Copy the developer's AWS credentials + region into the container env for the
|
|
76479
|
+
* DEFAULT (no `--assume-role`) path.
|
|
76480
|
+
*
|
|
76481
|
+
* Issue [#1836](https://github.com/go-to-k/cdkd/issues/1836): the two REGION
|
|
76482
|
+
* entries are folded through `canonicalizeRegion` on the way in; the credentials
|
|
76483
|
+
* are copied verbatim (an AKID / session token is case-SENSITIVE, so lower-casing
|
|
76484
|
+
* the whole pass-through list would corrupt every one of them). The value lands
|
|
76485
|
+
* in the container's own `AWS_REGION`, i.e. in every SDK client the agent builds,
|
|
76486
|
+
* and SDK endpoint resolution is case-SENSITIVE (`CN-NORTH-1` resolves the
|
|
76487
|
+
* COMMERCIAL partition).
|
|
76488
|
+
*
|
|
76489
|
+
* Folding only inside `applyAgentCoreCredentialEnv`'s assume-role arm — which is
|
|
76490
|
+
* where issue #1814 stopped, and what round 3 of the #1836 review found still
|
|
76491
|
+
* open here after the same gap was closed in `local-invoke.ts` — made ONE command
|
|
76492
|
+
* yield two different container regions for the same shell: `cn-north-1` with
|
|
76493
|
+
* `--assume-role`, `CN-NORTH-1` without.
|
|
76494
|
+
*/
|
|
76269
76495
|
function forwardAwsEnv$1(env) {
|
|
76270
|
-
|
|
76496
|
+
const passThrough = [
|
|
76271
76497
|
"AWS_ACCESS_KEY_ID",
|
|
76272
76498
|
"AWS_SECRET_ACCESS_KEY",
|
|
76273
76499
|
"AWS_SESSION_TOKEN",
|
|
76274
76500
|
"AWS_REGION",
|
|
76275
76501
|
"AWS_DEFAULT_REGION"
|
|
76276
|
-
]
|
|
76502
|
+
];
|
|
76503
|
+
const regionKeys = /* @__PURE__ */ new Set(["AWS_REGION", "AWS_DEFAULT_REGION"]);
|
|
76504
|
+
for (const key of passThrough) {
|
|
76277
76505
|
const value = process.env[key];
|
|
76278
|
-
if (value
|
|
76506
|
+
if (value === void 0) continue;
|
|
76507
|
+
env[key] = regionKeys.has(key) ? canonicalizeRegion(value) : value;
|
|
76279
76508
|
}
|
|
76280
76509
|
}
|
|
76281
76510
|
async function assumeAgentCoreExecutionRole(roleArn, region) {
|
|
@@ -76578,6 +76807,10 @@ async function localInvokeCommand(target, options) {
|
|
|
76578
76807
|
if (options.verbose) logger.setLevel("debug");
|
|
76579
76808
|
warnIfDeprecatedRegion(options);
|
|
76580
76809
|
if (options.region !== void 0) options.region = canonicalizeRegion(options.region);
|
|
76810
|
+
if (options.stackRegion !== void 0) {
|
|
76811
|
+
options.rawStackRegion = options.stackRegion;
|
|
76812
|
+
options.stackRegion = canonicalizeRegion(options.stackRegion);
|
|
76813
|
+
}
|
|
76581
76814
|
let imagePlan;
|
|
76582
76815
|
let containerId;
|
|
76583
76816
|
let stopLogs;
|
|
@@ -76731,29 +76964,12 @@ async function localInvokeCommand(target, options) {
|
|
|
76731
76964
|
AWS_LAMBDA_LOG_STREAM_NAME: "local",
|
|
76732
76965
|
...envResult.resolved
|
|
76733
76966
|
};
|
|
76734
|
-
|
|
76735
|
-
|
|
76736
|
-
|
|
76737
|
-
|
|
76738
|
-
|
|
76739
|
-
|
|
76740
|
-
dockerEnv["AWS_SECRET_ACCESS_KEY"] = creds.secretAccessKey;
|
|
76741
|
-
dockerEnv["AWS_SESSION_TOKEN"] = creds.sessionToken;
|
|
76742
|
-
if (stsRegion) dockerEnv["AWS_REGION"] = stsRegion;
|
|
76743
|
-
assumeSucceeded = true;
|
|
76744
|
-
} catch (err) {
|
|
76745
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
76746
|
-
logger.warn(`--assume-role: STS AssumeRole(${resolvedAssumeRoleArn}) failed: ${reason}. Falling back to the developer's shell credentials.`);
|
|
76747
|
-
}
|
|
76748
|
-
}
|
|
76749
|
-
if (!assumeSucceeded) {
|
|
76750
|
-
forwardAwsEnv(dockerEnv);
|
|
76751
|
-
applyProfileCredentialsOverlay(dockerEnv, profileCredentials, false);
|
|
76752
|
-
if (profileCredsFile) {
|
|
76753
|
-
dockerEnv["AWS_SHARED_CREDENTIALS_FILE"] = profileCredsFile.containerPath;
|
|
76754
|
-
dockerEnv["AWS_PROFILE"] = profileCredsFile.profileName;
|
|
76755
|
-
}
|
|
76756
|
-
}
|
|
76967
|
+
await applyLambdaCredentialEnv(dockerEnv, {
|
|
76968
|
+
...resolvedAssumeRoleArn && { assumeRoleArn: resolvedAssumeRoleArn },
|
|
76969
|
+
...options.region !== void 0 && { region: options.region },
|
|
76970
|
+
...profileCredentials && { profileCredentials },
|
|
76971
|
+
...profileCredsFile && { profileCredsFile }
|
|
76972
|
+
});
|
|
76757
76973
|
let debugPort;
|
|
76758
76974
|
if (options.debugPort) {
|
|
76759
76975
|
debugPort = Number(options.debugPort);
|
|
@@ -77153,6 +77369,55 @@ async function readStdin() {
|
|
|
77153
77369
|
return Buffer.concat(chunks).toString("utf-8");
|
|
77154
77370
|
}
|
|
77155
77371
|
/**
|
|
77372
|
+
* Inject AWS credentials into the container env. Precedence:
|
|
77373
|
+
* 1. `--assume-role` → STS-issued temp creds for the resolved ARN (on
|
|
77374
|
+
* STS failure, warn + fall through to dev creds). Swapping the
|
|
77375
|
+
* developer's credentials for the function's deployed execution role
|
|
77376
|
+
* degrades rather than hard-errors: this is a developer-loop tool, not
|
|
77377
|
+
* a security boundary, and the most common cause is the role's
|
|
77378
|
+
* `AssumeRolePolicy` not trusting the developer's IAM principal (a
|
|
77379
|
+
* config gap, not a cdkd bug).
|
|
77380
|
+
* 2. dev shell creds (`forwardAwsEnv` — which folds the region case for the
|
|
77381
|
+
* same reason arm 1 does) + `--profile` overlay
|
|
77382
|
+
* ({@link applyProfileCredentialsOverlay}) + the bind-mounted
|
|
77383
|
+
* credentials-file env so handler `fromIni({ profile })` resolves.
|
|
77384
|
+
* Applied in that order: the overlay intentionally wins over whatever
|
|
77385
|
+
* `forwardAwsEnv` copied, and the credentials-file env is additive.
|
|
77386
|
+
*
|
|
77387
|
+
* Extracted from `localInvokeCommand`'s body by issue
|
|
77388
|
+
* [#1836](https://github.com/go-to-k/cdkd/issues/1836) and shaped to mirror
|
|
77389
|
+
* `applyAgentCoreCredentialEnv` in `local-invoke-agentcore.ts` (the sibling
|
|
77390
|
+
* issue [#1814](https://github.com/go-to-k/cdkd/issues/1814) fixed), so a unit
|
|
77391
|
+
* test can lock the STS-client region binding with a mocked STS instead of
|
|
77392
|
+
* driving the full synth + docker pipeline.
|
|
77393
|
+
*/
|
|
77394
|
+
async function applyLambdaCredentialEnv(dockerEnv, args) {
|
|
77395
|
+
const logger = getLogger();
|
|
77396
|
+
let assumeSucceeded = false;
|
|
77397
|
+
if (args.assumeRoleArn) {
|
|
77398
|
+
const stsRegion = canonicalizeRegion(args.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"]);
|
|
77399
|
+
try {
|
|
77400
|
+
const creds = await assumeLambdaExecutionRole(args.assumeRoleArn, stsRegion);
|
|
77401
|
+
dockerEnv["AWS_ACCESS_KEY_ID"] = creds.accessKeyId;
|
|
77402
|
+
dockerEnv["AWS_SECRET_ACCESS_KEY"] = creds.secretAccessKey;
|
|
77403
|
+
dockerEnv["AWS_SESSION_TOKEN"] = creds.sessionToken;
|
|
77404
|
+
if (stsRegion) dockerEnv["AWS_REGION"] = stsRegion;
|
|
77405
|
+
assumeSucceeded = true;
|
|
77406
|
+
} catch (err) {
|
|
77407
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
77408
|
+
logger.warn(`--assume-role: STS AssumeRole(${args.assumeRoleArn}) failed: ${reason}. Falling back to the developer's shell credentials.`);
|
|
77409
|
+
}
|
|
77410
|
+
}
|
|
77411
|
+
if (!assumeSucceeded) {
|
|
77412
|
+
forwardAwsEnv(dockerEnv);
|
|
77413
|
+
applyProfileCredentialsOverlay(dockerEnv, args.profileCredentials, false);
|
|
77414
|
+
if (args.profileCredsFile) {
|
|
77415
|
+
dockerEnv["AWS_SHARED_CREDENTIALS_FILE"] = args.profileCredsFile.containerPath;
|
|
77416
|
+
dockerEnv["AWS_PROFILE"] = args.profileCredsFile.profileName;
|
|
77417
|
+
}
|
|
77418
|
+
}
|
|
77419
|
+
}
|
|
77420
|
+
/**
|
|
77156
77421
|
* Assume the Lambda execution role and return temporary credentials.
|
|
77157
77422
|
*
|
|
77158
77423
|
* Closes the "developer has admin creds, the deployed function has narrow
|
|
@@ -77192,17 +77457,30 @@ async function assumeLambdaExecutionRole(roleArn, region) {
|
|
|
77192
77457
|
*
|
|
77193
77458
|
* Region is inherited from `AWS_REGION` / `AWS_DEFAULT_REGION` so
|
|
77194
77459
|
* `aws.config.region` inside the handler works without extra setup.
|
|
77460
|
+
*
|
|
77461
|
+
* Issue [#1836](https://github.com/go-to-k/cdkd/issues/1836): the two REGION
|
|
77462
|
+
* entries are folded through `canonicalizeRegion` on the way in, the credentials
|
|
77463
|
+
* are copied verbatim. This is the DEFAULT path — no `--assume-role` — and it
|
|
77464
|
+
* carries the same exposure the assume-role branch had: the value lands in the
|
|
77465
|
+
* container's own `AWS_REGION`, i.e. in every SDK client the handler builds, and
|
|
77466
|
+
* SDK endpoint resolution is case-SENSITIVE (`CN-NORTH-1` resolves the
|
|
77467
|
+
* COMMERCIAL partition). Folding only inside `applyLambdaCredentialEnv`'s
|
|
77468
|
+
* assume-role arm made ONE command yield two different container regions for the
|
|
77469
|
+
* same shell — `cn-north-1` with `--assume-role`, `CN-NORTH-1` without.
|
|
77195
77470
|
*/
|
|
77196
77471
|
function forwardAwsEnv(env) {
|
|
77197
|
-
|
|
77472
|
+
const passThrough = [
|
|
77198
77473
|
"AWS_ACCESS_KEY_ID",
|
|
77199
77474
|
"AWS_SECRET_ACCESS_KEY",
|
|
77200
77475
|
"AWS_SESSION_TOKEN",
|
|
77201
77476
|
"AWS_REGION",
|
|
77202
77477
|
"AWS_DEFAULT_REGION"
|
|
77203
|
-
]
|
|
77478
|
+
];
|
|
77479
|
+
const regionKeys = /* @__PURE__ */ new Set(["AWS_REGION", "AWS_DEFAULT_REGION"]);
|
|
77480
|
+
for (const key of passThrough) {
|
|
77204
77481
|
const value = process.env[key];
|
|
77205
|
-
if (value
|
|
77482
|
+
if (value === void 0) continue;
|
|
77483
|
+
env[key] = regionKeys.has(key) ? canonicalizeRegion(value) : value;
|
|
77206
77484
|
}
|
|
77207
77485
|
}
|
|
77208
77486
|
/**
|
|
@@ -78448,7 +78726,7 @@ function createMigrateCommand() {
|
|
|
78448
78726
|
*/
|
|
78449
78727
|
function buildProgram() {
|
|
78450
78728
|
const program = new Command();
|
|
78451
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.
|
|
78729
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.15");
|
|
78452
78730
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
78453
78731
|
const { profile } = actionCommand.optsWithGlobals();
|
|
78454
78732
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|