@go-to-k/cdkd 0.283.13 → 0.283.14
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-CgnPBnOS.js} +2 -2
- package/dist/{asg-provider-CrVgG0Pj.js.map → asg-provider-CgnPBnOS.js.map} +1 -1
- package/dist/cli.js +278 -62
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-BdKa1DA4.js → deploy-engine-B8gQpv93.js} +3 -3
- package/dist/{deploy-engine-BdKa1DA4.js.map → deploy-engine-B8gQpv93.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-B8gQpv93.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-CgnPBnOS.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";
|
|
@@ -68858,12 +68858,21 @@ function indexGrandchildTemplatePaths(childTemplate, childTemplatePath) {
|
|
|
68858
68858
|
* `--stack-region`, bucket resolution failure) logs at warn and returns
|
|
68859
68859
|
* `undefined`. Auth failures and other genuine errors propagate.
|
|
68860
68860
|
*
|
|
68861
|
-
*
|
|
68861
|
+
* No lock acquisition and no `state.json` write path here. That is NOT the
|
|
68862
|
+
* same as "read-only against the bucket", and the difference was mis-stated
|
|
68863
|
+
* until issue #1836: {@link buildCrossStackResolver}'s exports-index lookup
|
|
68864
|
+
* goes through {@link ExportIndexStore}, whose `load` REBUILDS the index from
|
|
68865
|
+
* the per-stack state files on a miss and PUTs the rebuilt object
|
|
68866
|
+
* (`cdkd/_index/{region}/exports.json`). So an `Fn::ImportValue` lookup from a
|
|
68867
|
+
* `cdkd local` command can write that one derived key. Keeping the index KEY
|
|
68868
|
+
* the same spelling the state records carry is what keeps that write CORRECT
|
|
68869
|
+
* rather than an empty index — see the note on `consumerRegion` below.
|
|
68862
68870
|
*/
|
|
68863
68871
|
async function loadStateForStack(stackName, synthRegion, opts) {
|
|
68864
68872
|
const logger = getLogger();
|
|
68865
68873
|
const prefix = opts.logPrefix ?? "--from-state";
|
|
68866
|
-
const region = opts.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? synthRegion ?? "us-east-1";
|
|
68874
|
+
const region = canonicalizeRegion(opts.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? synthRegion ?? "us-east-1");
|
|
68875
|
+
const clientRegion = canonicalizeRegion(opts.region) || void 0;
|
|
68867
68876
|
let stateBucket;
|
|
68868
68877
|
try {
|
|
68869
68878
|
stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, region);
|
|
@@ -68872,7 +68881,7 @@ async function loadStateForStack(stackName, synthRegion, opts) {
|
|
|
68872
68881
|
return;
|
|
68873
68882
|
}
|
|
68874
68883
|
const awsClients = new AwsClients({
|
|
68875
|
-
...
|
|
68884
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
68876
68885
|
...opts.profile !== void 0 && { profile: opts.profile }
|
|
68877
68886
|
});
|
|
68878
68887
|
setAwsClients(awsClients);
|
|
@@ -68882,7 +68891,7 @@ async function loadStateForStack(stackName, synthRegion, opts) {
|
|
|
68882
68891
|
prefix: opts.statePrefix
|
|
68883
68892
|
};
|
|
68884
68893
|
const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
|
|
68885
|
-
...
|
|
68894
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
68886
68895
|
...opts.profile !== void 0 && { profile: opts.profile }
|
|
68887
68896
|
});
|
|
68888
68897
|
await stateBackend.verifyBucketExists();
|
|
@@ -68891,15 +68900,58 @@ async function loadStateForStack(stackName, synthRegion, opts) {
|
|
|
68891
68900
|
logger.warn(`${prefix}: no cdkd state found for stack '${stackName}' in bucket '${stateBucket}'. Was it deployed via 'cdkd deploy'? Falling back.`);
|
|
68892
68901
|
return;
|
|
68893
68902
|
}
|
|
68903
|
+
/**
|
|
68904
|
+
* Issue #1836: EXACT match first, case-insensitive second.
|
|
68905
|
+
*
|
|
68906
|
+
* `S3StateBackend.listStacks` dedupes on the EXACT `{stack}\0{region}` pair,
|
|
68907
|
+
* so `cdkd/MyStack/US-EAST-1/state.json` and
|
|
68908
|
+
* `cdkd/MyStack/us-east-1/state.json` are two DISTINCT refs — and
|
|
68909
|
+
* ListObjectsV2 returns them in ASCII order, i.e. the upper-cased one FIRST.
|
|
68910
|
+
* A fold-only `find` therefore hands `--stack-region us-east-1` the OTHER
|
|
68911
|
+
* record, silently reading state the user did not name; the pre-fold `===`
|
|
68912
|
+
* got that case right. So the fold is the RECOVERY for a case mismatch, never
|
|
68913
|
+
* an override of a spelling that exists verbatim.
|
|
68914
|
+
*
|
|
68915
|
+
* `candidate` is therefore the spelling the USER supplied, not the folded
|
|
68916
|
+
* one (round 3 of the #1836 review): the fold at each command's handler
|
|
68917
|
+
* entry and at the `--from-state` factory is what an SDK client needs, but
|
|
68918
|
+
* feeding it to this compare made the exact-match rule unreachable from any
|
|
68919
|
+
* real CLI invocation — every candidate was already canonical, so `exact`
|
|
68920
|
+
* could only ever match an already-canonical record and `--stack-region
|
|
68921
|
+
* US-EAST-1` read the `us-east-1` one while REPORTING it as the exact
|
|
68922
|
+
* spelling. `opts.rawStackRegion` carries the unfolded value here; the
|
|
68923
|
+
* recovery arm folds both sides itself, so nothing upstream has to.
|
|
68924
|
+
*
|
|
68925
|
+
* When more than one ref is canonical-equal the choice is announced, because
|
|
68926
|
+
* either answer reads some record the user did not spell out in full and the
|
|
68927
|
+
* silence is what made the original miss hard to diagnose. The wording
|
|
68928
|
+
* states which of the two rules decided it — and does so from the same
|
|
68929
|
+
* `exact` binding the choice was made from, so it cannot claim an exact
|
|
68930
|
+
* match the compare did not make.
|
|
68931
|
+
*/
|
|
68932
|
+
const findByRegion = (candidate, source) => {
|
|
68933
|
+
const exact = refs.find((r) => r.region === candidate);
|
|
68934
|
+
const folded = refs.filter((r) => canonicalizeRegion(r.region) === canonicalizeRegion(candidate));
|
|
68935
|
+
const chosen = exact ?? folded[0];
|
|
68936
|
+
if (folded.length > 1 && chosen) {
|
|
68937
|
+
const how = exact ? `matches ${source} '${candidate}' exactly` : `no record spells ${source} '${candidate}' exactly, so this is a case-insensitive recovery`;
|
|
68938
|
+
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}.`);
|
|
68939
|
+
}
|
|
68940
|
+
return chosen;
|
|
68941
|
+
};
|
|
68942
|
+
let synthMatch;
|
|
68943
|
+
if (!opts.stackRegion && synthRegion) synthMatch = findByRegion(synthRegion, "the synth-derived stack region");
|
|
68894
68944
|
let targetRegion;
|
|
68895
68945
|
if (opts.stackRegion) {
|
|
68896
|
-
|
|
68946
|
+
const requestedRegion = opts.rawStackRegion ?? opts.stackRegion;
|
|
68947
|
+
const found = findByRegion(requestedRegion, "--stack-region");
|
|
68948
|
+
if (!found) {
|
|
68897
68949
|
const seen = refs.map((r) => r.region ?? "(legacy)").join(", ");
|
|
68898
|
-
logger.warn(`${prefix}: stack '${stackName}' has no state in region '${
|
|
68950
|
+
logger.warn(`${prefix}: stack '${stackName}' has no state in region '${requestedRegion}' (available: ${seen}). Falling back.`);
|
|
68899
68951
|
return;
|
|
68900
68952
|
}
|
|
68901
|
-
targetRegion =
|
|
68902
|
-
} else if (synthRegion &&
|
|
68953
|
+
targetRegion = found.region ?? requestedRegion;
|
|
68954
|
+
} else if (synthRegion && synthMatch) targetRegion = synthMatch.region ?? synthRegion;
|
|
68903
68955
|
else if (refs.length === 1) targetRegion = refs[0].region ?? synthRegion ?? region;
|
|
68904
68956
|
else {
|
|
68905
68957
|
const seen = refs.map((r) => r.region ?? "(legacy)").join(", ");
|
|
@@ -68948,7 +69000,8 @@ async function loadStateForStack(stackName, synthRegion, opts) {
|
|
|
68948
69000
|
async function loadBootstrapContainerRepo(synthRegion, opts) {
|
|
68949
69001
|
const logger = getLogger();
|
|
68950
69002
|
const prefix = opts.logPrefix ?? "--from-state";
|
|
68951
|
-
const
|
|
69003
|
+
const rawRegion = opts.region ?? opts.rawStackRegion ?? opts.stackRegion ?? synthRegion ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? "us-east-1";
|
|
69004
|
+
const region = canonicalizeRegion(rawRegion);
|
|
68952
69005
|
let stateBucket;
|
|
68953
69006
|
try {
|
|
68954
69007
|
stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, region);
|
|
@@ -68956,8 +69009,9 @@ async function loadBootstrapContainerRepo(synthRegion, opts) {
|
|
|
68956
69009
|
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
69010
|
return;
|
|
68958
69011
|
}
|
|
69012
|
+
const clientRegion = canonicalizeRegion(opts.region) || void 0;
|
|
68959
69013
|
const awsClients = new AwsClients({
|
|
68960
|
-
...
|
|
69014
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
68961
69015
|
...opts.profile !== void 0 && { profile: opts.profile }
|
|
68962
69016
|
});
|
|
68963
69017
|
setAwsClients(awsClients);
|
|
@@ -68967,16 +69021,25 @@ async function loadBootstrapContainerRepo(synthRegion, opts) {
|
|
|
68967
69021
|
prefix: opts.statePrefix
|
|
68968
69022
|
};
|
|
68969
69023
|
const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
|
|
68970
|
-
...
|
|
69024
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
68971
69025
|
...opts.profile !== void 0 && { profile: opts.profile }
|
|
68972
69026
|
});
|
|
68973
69027
|
const markerKey = getBootstrapMarkerKey(region);
|
|
68974
|
-
|
|
69028
|
+
let resolvedKey = markerKey;
|
|
69029
|
+
let body = await stateBackend.getRawObject(markerKey);
|
|
69030
|
+
if (body === null && rawRegion !== region) {
|
|
69031
|
+
const rawKey = getBootstrapMarkerKey(rawRegion);
|
|
69032
|
+
body = await stateBackend.getRawObject(rawKey);
|
|
69033
|
+
if (body !== null) {
|
|
69034
|
+
resolvedKey = rawKey;
|
|
69035
|
+
logger.debug(`${prefix}: bootstrap marker found at the un-folded key '${rawKey}' (no '${markerKey}') — an upper-cased region was used at 'cdkd bootstrap' time.`);
|
|
69036
|
+
}
|
|
69037
|
+
}
|
|
68975
69038
|
if (body === null) {
|
|
68976
|
-
logger.debug(`${prefix}: no bootstrap marker at '${markerKey}' in bucket '${stateBucket}' — assuming conventional asset-repo names.`);
|
|
69039
|
+
logger.debug(`${prefix}: no bootstrap marker at '${markerKey}'${rawRegion !== region ? ` (nor '${getBootstrapMarkerKey(rawRegion)}')` : ""} in bucket '${stateBucket}' — assuming conventional asset-repo names.`);
|
|
68977
69040
|
return;
|
|
68978
69041
|
}
|
|
68979
|
-
const marker = parseBootstrapMarker(body,
|
|
69042
|
+
const marker = parseBootstrapMarker(body, resolvedKey);
|
|
68980
69043
|
logger.debug(`${prefix}: bootstrap marker for ${region} names container repo '${marker.containerRepo}'.`);
|
|
68981
69044
|
return marker.containerRepo;
|
|
68982
69045
|
} catch (err) {
|
|
@@ -69015,13 +69078,14 @@ async function buildCrossStackResolver(consumerRegion, opts) {
|
|
|
69015
69078
|
const prefix = opts.logPrefix ?? "--from-state";
|
|
69016
69079
|
let stateBucket;
|
|
69017
69080
|
try {
|
|
69018
|
-
stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, consumerRegion);
|
|
69081
|
+
stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, canonicalizeRegion(consumerRegion));
|
|
69019
69082
|
} catch (err) {
|
|
69020
69083
|
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
69084
|
return;
|
|
69022
69085
|
}
|
|
69086
|
+
const clientRegion = canonicalizeRegion(opts.region) || void 0;
|
|
69023
69087
|
const awsClients = new AwsClients({
|
|
69024
|
-
...
|
|
69088
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
69025
69089
|
...opts.profile !== void 0 && { profile: opts.profile }
|
|
69026
69090
|
});
|
|
69027
69091
|
const stateConfig = {
|
|
@@ -69029,7 +69093,7 @@ async function buildCrossStackResolver(consumerRegion, opts) {
|
|
|
69029
69093
|
prefix: opts.statePrefix
|
|
69030
69094
|
};
|
|
69031
69095
|
const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
|
|
69032
|
-
...
|
|
69096
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
69033
69097
|
...opts.profile !== void 0 && { profile: opts.profile }
|
|
69034
69098
|
});
|
|
69035
69099
|
try {
|
|
@@ -69061,9 +69125,10 @@ async function buildCrossStackResolver(consumerRegion, opts) {
|
|
|
69061
69125
|
logger.debug(`${prefix}: failed to list stacks during Fn::ImportValue fallback for '${exportName}': ${err instanceof Error ? err.message : String(err)}`);
|
|
69062
69126
|
return;
|
|
69063
69127
|
}
|
|
69128
|
+
const canonicalConsumerRegion = canonicalizeRegion(consumerRegion);
|
|
69064
69129
|
for (const ref of refs) {
|
|
69065
69130
|
const region = ref.region ?? consumerRegion;
|
|
69066
|
-
if (region !==
|
|
69131
|
+
if (canonicalizeRegion(region) !== canonicalConsumerRegion) continue;
|
|
69067
69132
|
try {
|
|
69068
69133
|
const got = await stateBackend.getState(ref.stackName, region);
|
|
69069
69134
|
if (!got || !got.state.outputs) continue;
|
|
@@ -69080,14 +69145,31 @@ async function buildCrossStackResolver(consumerRegion, opts) {
|
|
|
69080
69145
|
}
|
|
69081
69146
|
},
|
|
69082
69147
|
async resolveGetStackOutput(producerStack, producerRegion, outputName) {
|
|
69083
|
-
|
|
69084
|
-
const got = await stateBackend.getState(producerStack, producerRegion);
|
|
69148
|
+
const readOutput = (got) => {
|
|
69085
69149
|
if (!got || !got.state.outputs) return void 0;
|
|
69086
69150
|
if (!(outputName in got.state.outputs)) return void 0;
|
|
69087
69151
|
const value = got.state.outputs[outputName];
|
|
69088
69152
|
if (typeof value === "string") return value;
|
|
69089
69153
|
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
69090
69154
|
return JSON.stringify(value);
|
|
69155
|
+
};
|
|
69156
|
+
try {
|
|
69157
|
+
const got = await stateBackend.getState(producerStack, producerRegion);
|
|
69158
|
+
if (got) return readOutput(got);
|
|
69159
|
+
const canonicalProducerRegion = canonicalizeRegion(producerRegion);
|
|
69160
|
+
const refs = await stateBackend.listStacks();
|
|
69161
|
+
for (const ref of refs) {
|
|
69162
|
+
if (ref.stackName !== producerStack) continue;
|
|
69163
|
+
const region = ref.region ?? producerRegion;
|
|
69164
|
+
if (region === producerRegion) continue;
|
|
69165
|
+
if (canonicalizeRegion(region) !== canonicalProducerRegion) continue;
|
|
69166
|
+
const recovered = readOutput(await stateBackend.getState(producerStack, region));
|
|
69167
|
+
if (recovered !== void 0) {
|
|
69168
|
+
logger.debug(`${prefix}: Fn::GetStackOutput '${producerStack}.${outputName}' resolved from the case-variant state record '${region}' (no record spells '${producerRegion}' exactly).`);
|
|
69169
|
+
return recovered;
|
|
69170
|
+
}
|
|
69171
|
+
}
|
|
69172
|
+
return;
|
|
69091
69173
|
} catch (err) {
|
|
69092
69174
|
logger.debug(`${prefix}: state read failed for Fn::GetStackOutput '${producerStack}.${outputName}' (${producerRegion}): ${err instanceof Error ? err.message : String(err)}`);
|
|
69093
69175
|
return;
|
|
@@ -69127,6 +69209,7 @@ var S3LocalStateProvider = class {
|
|
|
69127
69209
|
const loaded = await loadStateForStack(stackName, synthRegion, {
|
|
69128
69210
|
statePrefix: this.opts.statePrefix,
|
|
69129
69211
|
...this.opts.stackRegion !== void 0 && { stackRegion: this.opts.stackRegion },
|
|
69212
|
+
...this.opts.rawStackRegion !== void 0 && { rawStackRegion: this.opts.rawStackRegion },
|
|
69130
69213
|
...this.opts.stateBucket !== void 0 && { stateBucket: this.opts.stateBucket },
|
|
69131
69214
|
...this.opts.region !== void 0 && { region: this.opts.region },
|
|
69132
69215
|
...this.opts.profile !== void 0 && { profile: this.opts.profile }
|
|
@@ -69193,12 +69276,16 @@ var S3LocalStateProvider = class {
|
|
|
69193
69276
|
*/
|
|
69194
69277
|
const fromStateFactory = (options) => {
|
|
69195
69278
|
const opts = options;
|
|
69279
|
+
const clientRegion = canonicalizeRegion(opts.region) || void 0;
|
|
69280
|
+
const stackRegion = canonicalizeRegion(opts.stackRegion) || void 0;
|
|
69281
|
+
const rawStackRegion = opts.rawStackRegion || opts.stackRegion || void 0;
|
|
69196
69282
|
return new S3LocalStateProvider({
|
|
69197
69283
|
statePrefix: opts.statePrefix,
|
|
69198
69284
|
...opts.stateBucket !== void 0 && { stateBucket: opts.stateBucket },
|
|
69199
|
-
...
|
|
69285
|
+
...clientRegion !== void 0 && { region: clientRegion },
|
|
69200
69286
|
...opts.profile !== void 0 && { profile: opts.profile },
|
|
69201
|
-
...
|
|
69287
|
+
...stackRegion !== void 0 && { stackRegion },
|
|
69288
|
+
...rawStackRegion !== void 0 && { rawStackRegion }
|
|
69202
69289
|
});
|
|
69203
69290
|
};
|
|
69204
69291
|
/**
|
|
@@ -72380,6 +72467,10 @@ async function localStartApiCommand(target, options) {
|
|
|
72380
72467
|
const logger = getLogger();
|
|
72381
72468
|
if (options.verbose) logger.setLevel("debug");
|
|
72382
72469
|
if (options.region !== void 0) options.region = canonicalizeRegion(options.region);
|
|
72470
|
+
if (options.stackRegion !== void 0) {
|
|
72471
|
+
options.rawStackRegion = options.stackRegion;
|
|
72472
|
+
options.stackRegion = canonicalizeRegion(options.stackRegion);
|
|
72473
|
+
}
|
|
72383
72474
|
let apiFilter = target;
|
|
72384
72475
|
if (options.api !== void 0) {
|
|
72385
72476
|
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 +74813,10 @@ async function localRunTaskCommand(target, options) {
|
|
|
74722
74813
|
if (options.verbose) logger.setLevel("debug");
|
|
74723
74814
|
warnIfDeprecatedRegion(options);
|
|
74724
74815
|
if (options.region !== void 0) options.region = canonicalizeRegion(options.region);
|
|
74816
|
+
if (options.stackRegion !== void 0) {
|
|
74817
|
+
options.rawStackRegion = options.stackRegion;
|
|
74818
|
+
options.stackRegion = canonicalizeRegion(options.stackRegion);
|
|
74819
|
+
}
|
|
74725
74820
|
const state = createEcsRunState();
|
|
74726
74821
|
let sigintHandler;
|
|
74727
74822
|
let sigintCount = 0;
|
|
@@ -74766,12 +74861,12 @@ async function localRunTaskCommand(target, options) {
|
|
|
74766
74861
|
const { stacks } = await synthesizer.synthesize(synthOpts);
|
|
74767
74862
|
const candidate = pickCandidateStack(parseEcsTarget(target).stackPattern, stacks);
|
|
74768
74863
|
stateProvider = createLocalStateProvider$1(options, candidate?.stackName ?? "", candidate?.region);
|
|
74769
|
-
const imageContext = await buildEcsImageResolutionContext$1(candidate, stateProvider, options);
|
|
74864
|
+
const { context: imageContext, stateRecordRegion } = await buildEcsImageResolutionContext$1(candidate, stateProvider, options);
|
|
74770
74865
|
const task = resolveEcsTaskTarget(target, stacks, imageContext);
|
|
74771
74866
|
logger.info(`Target: ${task.stack.stackName}/${task.taskDefinitionLogicalId} (family=${task.family}, containers=${task.containers.length})`);
|
|
74772
74867
|
const taskNeeds = detectEcsImageResolutionNeeds(stacks.find((s) => s.stackName === task.stack.stackName) ?? task.stack);
|
|
74773
74868
|
if (stateProvider && taskNeeds.needsCrossStackResolver) {
|
|
74774
|
-
const consumerRegion = options
|
|
74869
|
+
const consumerRegion = resolveEcsConsumerRegion(stateRecordRegion, options, task.stack.region);
|
|
74775
74870
|
const resolver = await stateProvider.buildCrossStackResolver(consumerRegion);
|
|
74776
74871
|
if (resolver) await applyCrossStackResolverToTask(task, {
|
|
74777
74872
|
resources: imageContext?.stateResources ?? {},
|
|
@@ -74881,9 +74976,48 @@ async function assumeTaskRole(roleArn, region) {
|
|
|
74881
74976
|
}
|
|
74882
74977
|
}
|
|
74883
74978
|
/**
|
|
74979
|
+
* Region the cross-stack resolver is scoped to for `cdkd local run-task`
|
|
74980
|
+
* (issue #1836).
|
|
74981
|
+
*
|
|
74982
|
+
* This value does THREE things (round 3 said TWO and was wrong — corrected in
|
|
74983
|
+
* round 4), and the first is why the state record's own spelling outranks the env
|
|
74984
|
+
* chain:
|
|
74985
|
+
*
|
|
74986
|
+
* 1. it becomes the exports-index KEY `cdkd/_index/{region}/exports.json`;
|
|
74987
|
+
* 2. it is the same-region filter of the index-miss per-stack scan; and
|
|
74988
|
+
* 3. via `SubstitutionContext.consumerRegion`, it is cdk-local's DEFAULT producer
|
|
74989
|
+
* region for an `Fn::GetStackOutput` that carries no explicit `Region` — i.e.
|
|
74990
|
+
* it becomes the `cdkd/{producer}/{region}/state.json` key that intrinsic
|
|
74991
|
+
* reads.
|
|
74992
|
+
*
|
|
74993
|
+
* All three land in `buildCrossStackResolver` (`local-state-loader.ts`), whose
|
|
74994
|
+
* `resolveImport` scan (2) and — since round 4 — `resolveGetStackOutput` (3) fold
|
|
74995
|
+
* BOTH sides for the comparison / recovery, while (1) is deliberately raw.
|
|
74996
|
+
*
|
|
74997
|
+
* `local invoke` / `local invoke-agentcore` both pass `loaded.region` there; this
|
|
74998
|
+
* command passed a FOLDED env-chain value instead, so the two commands keyed one
|
|
74999
|
+
* index differently — and a key the state records do not spell makes
|
|
75000
|
+
* `ExportIndexStore`'s rebuild filter match zero refs and PUT an EMPTY index,
|
|
75001
|
+
* permanently degrading every `Fn::ImportValue` to the O(N) scan. Keyed by a
|
|
75002
|
+
* record's spelling the filter always matches.
|
|
75003
|
+
*
|
|
75004
|
+
* The env-chain arm remains for the case where no state record was loaded at all
|
|
75005
|
+
* (nothing in the task needed one), and it stays FOLDED for the reason the rest
|
|
75006
|
+
* of this file folds: both env-var links escape the handler-entry `--region`
|
|
75007
|
+
* fold, and a raw upper-cased spelling reaches an S3 key and an SDK client.
|
|
75008
|
+
*
|
|
75009
|
+
* @internal exported for unit tests.
|
|
75010
|
+
*/
|
|
75011
|
+
function resolveEcsConsumerRegion(stateRecordRegion, options, synthRegion) {
|
|
75012
|
+
if (stateRecordRegion) return stateRecordRegion;
|
|
75013
|
+
return canonicalizeRegion((options.region || process.env["AWS_REGION"]) ?? process.env["AWS_DEFAULT_REGION"] ?? synthRegion ?? "us-east-1");
|
|
75014
|
+
}
|
|
75015
|
+
/**
|
|
74884
75016
|
* Build the substitution context the ECS task resolver consumes (issue
|
|
74885
|
-
* #264)
|
|
74886
|
-
*
|
|
75017
|
+
* #264), plus the region of the state record it loaded (if any — issue #1836;
|
|
75018
|
+
* see {@link resolveEcsConsumerRegion} for what reads it). `context` is
|
|
75019
|
+
* `undefined` when no container's `Image` field needs substitution — the
|
|
75020
|
+
* resolver behaves as before in that case.
|
|
74887
75021
|
*
|
|
74888
75022
|
* Tier 1 (pseudo parameters) fires `sts:GetCallerIdentity` once for
|
|
74889
75023
|
* `${AWS::AccountId}`; region / partition / URL suffix come from the CLI
|
|
@@ -74902,10 +75036,11 @@ async function assumeTaskRole(roleArn, region) {
|
|
|
74902
75036
|
*/
|
|
74903
75037
|
async function buildEcsImageResolutionContext$1(candidate, stateProvider, options) {
|
|
74904
75038
|
const logger = getLogger();
|
|
74905
|
-
if (!candidate) return
|
|
75039
|
+
if (!candidate) return {};
|
|
74906
75040
|
const needs = detectEcsImageResolutionNeeds(candidate);
|
|
74907
|
-
if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution && !needs.needsAssetRepoMarker) return;
|
|
75041
|
+
if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution && !needs.needsAssetRepoMarker) return {};
|
|
74908
75042
|
const ctx = {};
|
|
75043
|
+
let stateRecordRegion;
|
|
74909
75044
|
const wantsPseudoForEnvOrSecret = !!stateProvider && needs.needsEnvOrSecretSubstitution;
|
|
74910
75045
|
if (needs.needsPseudoParameters || wantsPseudoForEnvOrSecret) {
|
|
74911
75046
|
const region = canonicalizeRegion(options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? candidate.region);
|
|
@@ -74929,13 +75064,17 @@ async function buildEcsImageResolutionContext$1(candidate, stateProvider, option
|
|
|
74929
75064
|
const wantsState = needs.needsStateResources || needs.needsEnvOrSecretSubstitution;
|
|
74930
75065
|
if (stateProvider && wantsState) {
|
|
74931
75066
|
const loaded = await stateProvider.load(candidate.stackName, candidate.region);
|
|
74932
|
-
if (loaded)
|
|
75067
|
+
if (loaded) {
|
|
75068
|
+
ctx.stateResources = loaded.resources;
|
|
75069
|
+
stateRecordRegion = loaded.region;
|
|
75070
|
+
}
|
|
74933
75071
|
} 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
75072
|
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
75073
|
if (needs.needsAssetRepoMarker && options.fromState) {
|
|
74936
75074
|
const containerRepo = await loadBootstrapContainerRepo(candidate.region, {
|
|
74937
75075
|
statePrefix: options.statePrefix,
|
|
74938
75076
|
...options.stackRegion !== void 0 && { stackRegion: options.stackRegion },
|
|
75077
|
+
...options.rawStackRegion !== void 0 && { rawStackRegion: options.rawStackRegion },
|
|
74939
75078
|
...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
|
|
74940
75079
|
...options.region !== void 0 && { region: options.region },
|
|
74941
75080
|
...options.profile !== void 0 && { profile: options.profile },
|
|
@@ -74943,7 +75082,10 @@ async function buildEcsImageResolutionContext$1(candidate, stateProvider, option
|
|
|
74943
75082
|
});
|
|
74944
75083
|
if (containerRepo !== void 0) ctx.cdkAssetContainerRepo = containerRepo;
|
|
74945
75084
|
}
|
|
74946
|
-
return
|
|
75085
|
+
return {
|
|
75086
|
+
context: ctx,
|
|
75087
|
+
...stateRecordRegion !== void 0 && { stateRecordRegion }
|
|
75088
|
+
};
|
|
74947
75089
|
}
|
|
74948
75090
|
function pickCandidateStack(stackPattern, stacks) {
|
|
74949
75091
|
if (stackPattern === null) {
|
|
@@ -75376,6 +75518,10 @@ async function localInvokeAgentCoreCommand(target, options) {
|
|
|
75376
75518
|
if (options.verbose) logger.setLevel("debug");
|
|
75377
75519
|
warnIfDeprecatedRegion(options);
|
|
75378
75520
|
if (options.region !== void 0) options.region = canonicalizeRegion(options.region);
|
|
75521
|
+
if (options.stackRegion !== void 0) {
|
|
75522
|
+
options.rawStackRegion = options.stackRegion;
|
|
75523
|
+
options.stackRegion = canonicalizeRegion(options.stackRegion);
|
|
75524
|
+
}
|
|
75379
75525
|
let containerId;
|
|
75380
75526
|
let stopLogs;
|
|
75381
75527
|
let sigintHandler;
|
|
@@ -76266,16 +76412,37 @@ function emitA2aResult(result) {
|
|
|
76266
76412
|
function platformToArchitecture(platform) {
|
|
76267
76413
|
return platform === "linux/amd64" ? "x86_64" : "arm64";
|
|
76268
76414
|
}
|
|
76415
|
+
/**
|
|
76416
|
+
* Copy the developer's AWS credentials + region into the container env for the
|
|
76417
|
+
* DEFAULT (no `--assume-role`) path.
|
|
76418
|
+
*
|
|
76419
|
+
* Issue [#1836](https://github.com/go-to-k/cdkd/issues/1836): the two REGION
|
|
76420
|
+
* entries are folded through `canonicalizeRegion` on the way in; the credentials
|
|
76421
|
+
* are copied verbatim (an AKID / session token is case-SENSITIVE, so lower-casing
|
|
76422
|
+
* the whole pass-through list would corrupt every one of them). The value lands
|
|
76423
|
+
* in the container's own `AWS_REGION`, i.e. in every SDK client the agent builds,
|
|
76424
|
+
* and SDK endpoint resolution is case-SENSITIVE (`CN-NORTH-1` resolves the
|
|
76425
|
+
* COMMERCIAL partition).
|
|
76426
|
+
*
|
|
76427
|
+
* Folding only inside `applyAgentCoreCredentialEnv`'s assume-role arm — which is
|
|
76428
|
+
* where issue #1814 stopped, and what round 3 of the #1836 review found still
|
|
76429
|
+
* open here after the same gap was closed in `local-invoke.ts` — made ONE command
|
|
76430
|
+
* yield two different container regions for the same shell: `cn-north-1` with
|
|
76431
|
+
* `--assume-role`, `CN-NORTH-1` without.
|
|
76432
|
+
*/
|
|
76269
76433
|
function forwardAwsEnv$1(env) {
|
|
76270
|
-
|
|
76434
|
+
const passThrough = [
|
|
76271
76435
|
"AWS_ACCESS_KEY_ID",
|
|
76272
76436
|
"AWS_SECRET_ACCESS_KEY",
|
|
76273
76437
|
"AWS_SESSION_TOKEN",
|
|
76274
76438
|
"AWS_REGION",
|
|
76275
76439
|
"AWS_DEFAULT_REGION"
|
|
76276
|
-
]
|
|
76440
|
+
];
|
|
76441
|
+
const regionKeys = /* @__PURE__ */ new Set(["AWS_REGION", "AWS_DEFAULT_REGION"]);
|
|
76442
|
+
for (const key of passThrough) {
|
|
76277
76443
|
const value = process.env[key];
|
|
76278
|
-
if (value
|
|
76444
|
+
if (value === void 0) continue;
|
|
76445
|
+
env[key] = regionKeys.has(key) ? canonicalizeRegion(value) : value;
|
|
76279
76446
|
}
|
|
76280
76447
|
}
|
|
76281
76448
|
async function assumeAgentCoreExecutionRole(roleArn, region) {
|
|
@@ -76578,6 +76745,10 @@ async function localInvokeCommand(target, options) {
|
|
|
76578
76745
|
if (options.verbose) logger.setLevel("debug");
|
|
76579
76746
|
warnIfDeprecatedRegion(options);
|
|
76580
76747
|
if (options.region !== void 0) options.region = canonicalizeRegion(options.region);
|
|
76748
|
+
if (options.stackRegion !== void 0) {
|
|
76749
|
+
options.rawStackRegion = options.stackRegion;
|
|
76750
|
+
options.stackRegion = canonicalizeRegion(options.stackRegion);
|
|
76751
|
+
}
|
|
76581
76752
|
let imagePlan;
|
|
76582
76753
|
let containerId;
|
|
76583
76754
|
let stopLogs;
|
|
@@ -76731,29 +76902,12 @@ async function localInvokeCommand(target, options) {
|
|
|
76731
76902
|
AWS_LAMBDA_LOG_STREAM_NAME: "local",
|
|
76732
76903
|
...envResult.resolved
|
|
76733
76904
|
};
|
|
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
|
-
}
|
|
76905
|
+
await applyLambdaCredentialEnv(dockerEnv, {
|
|
76906
|
+
...resolvedAssumeRoleArn && { assumeRoleArn: resolvedAssumeRoleArn },
|
|
76907
|
+
...options.region !== void 0 && { region: options.region },
|
|
76908
|
+
...profileCredentials && { profileCredentials },
|
|
76909
|
+
...profileCredsFile && { profileCredsFile }
|
|
76910
|
+
});
|
|
76757
76911
|
let debugPort;
|
|
76758
76912
|
if (options.debugPort) {
|
|
76759
76913
|
debugPort = Number(options.debugPort);
|
|
@@ -77153,6 +77307,55 @@ async function readStdin() {
|
|
|
77153
77307
|
return Buffer.concat(chunks).toString("utf-8");
|
|
77154
77308
|
}
|
|
77155
77309
|
/**
|
|
77310
|
+
* Inject AWS credentials into the container env. Precedence:
|
|
77311
|
+
* 1. `--assume-role` → STS-issued temp creds for the resolved ARN (on
|
|
77312
|
+
* STS failure, warn + fall through to dev creds). Swapping the
|
|
77313
|
+
* developer's credentials for the function's deployed execution role
|
|
77314
|
+
* degrades rather than hard-errors: this is a developer-loop tool, not
|
|
77315
|
+
* a security boundary, and the most common cause is the role's
|
|
77316
|
+
* `AssumeRolePolicy` not trusting the developer's IAM principal (a
|
|
77317
|
+
* config gap, not a cdkd bug).
|
|
77318
|
+
* 2. dev shell creds (`forwardAwsEnv` — which folds the region case for the
|
|
77319
|
+
* same reason arm 1 does) + `--profile` overlay
|
|
77320
|
+
* ({@link applyProfileCredentialsOverlay}) + the bind-mounted
|
|
77321
|
+
* credentials-file env so handler `fromIni({ profile })` resolves.
|
|
77322
|
+
* Applied in that order: the overlay intentionally wins over whatever
|
|
77323
|
+
* `forwardAwsEnv` copied, and the credentials-file env is additive.
|
|
77324
|
+
*
|
|
77325
|
+
* Extracted from `localInvokeCommand`'s body by issue
|
|
77326
|
+
* [#1836](https://github.com/go-to-k/cdkd/issues/1836) and shaped to mirror
|
|
77327
|
+
* `applyAgentCoreCredentialEnv` in `local-invoke-agentcore.ts` (the sibling
|
|
77328
|
+
* issue [#1814](https://github.com/go-to-k/cdkd/issues/1814) fixed), so a unit
|
|
77329
|
+
* test can lock the STS-client region binding with a mocked STS instead of
|
|
77330
|
+
* driving the full synth + docker pipeline.
|
|
77331
|
+
*/
|
|
77332
|
+
async function applyLambdaCredentialEnv(dockerEnv, args) {
|
|
77333
|
+
const logger = getLogger();
|
|
77334
|
+
let assumeSucceeded = false;
|
|
77335
|
+
if (args.assumeRoleArn) {
|
|
77336
|
+
const stsRegion = canonicalizeRegion(args.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"]);
|
|
77337
|
+
try {
|
|
77338
|
+
const creds = await assumeLambdaExecutionRole(args.assumeRoleArn, stsRegion);
|
|
77339
|
+
dockerEnv["AWS_ACCESS_KEY_ID"] = creds.accessKeyId;
|
|
77340
|
+
dockerEnv["AWS_SECRET_ACCESS_KEY"] = creds.secretAccessKey;
|
|
77341
|
+
dockerEnv["AWS_SESSION_TOKEN"] = creds.sessionToken;
|
|
77342
|
+
if (stsRegion) dockerEnv["AWS_REGION"] = stsRegion;
|
|
77343
|
+
assumeSucceeded = true;
|
|
77344
|
+
} catch (err) {
|
|
77345
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
77346
|
+
logger.warn(`--assume-role: STS AssumeRole(${args.assumeRoleArn}) failed: ${reason}. Falling back to the developer's shell credentials.`);
|
|
77347
|
+
}
|
|
77348
|
+
}
|
|
77349
|
+
if (!assumeSucceeded) {
|
|
77350
|
+
forwardAwsEnv(dockerEnv);
|
|
77351
|
+
applyProfileCredentialsOverlay(dockerEnv, args.profileCredentials, false);
|
|
77352
|
+
if (args.profileCredsFile) {
|
|
77353
|
+
dockerEnv["AWS_SHARED_CREDENTIALS_FILE"] = args.profileCredsFile.containerPath;
|
|
77354
|
+
dockerEnv["AWS_PROFILE"] = args.profileCredsFile.profileName;
|
|
77355
|
+
}
|
|
77356
|
+
}
|
|
77357
|
+
}
|
|
77358
|
+
/**
|
|
77156
77359
|
* Assume the Lambda execution role and return temporary credentials.
|
|
77157
77360
|
*
|
|
77158
77361
|
* Closes the "developer has admin creds, the deployed function has narrow
|
|
@@ -77192,17 +77395,30 @@ async function assumeLambdaExecutionRole(roleArn, region) {
|
|
|
77192
77395
|
*
|
|
77193
77396
|
* Region is inherited from `AWS_REGION` / `AWS_DEFAULT_REGION` so
|
|
77194
77397
|
* `aws.config.region` inside the handler works without extra setup.
|
|
77398
|
+
*
|
|
77399
|
+
* Issue [#1836](https://github.com/go-to-k/cdkd/issues/1836): the two REGION
|
|
77400
|
+
* entries are folded through `canonicalizeRegion` on the way in, the credentials
|
|
77401
|
+
* are copied verbatim. This is the DEFAULT path — no `--assume-role` — and it
|
|
77402
|
+
* carries the same exposure the assume-role branch had: the value lands in the
|
|
77403
|
+
* container's own `AWS_REGION`, i.e. in every SDK client the handler builds, and
|
|
77404
|
+
* SDK endpoint resolution is case-SENSITIVE (`CN-NORTH-1` resolves the
|
|
77405
|
+
* COMMERCIAL partition). Folding only inside `applyLambdaCredentialEnv`'s
|
|
77406
|
+
* assume-role arm made ONE command yield two different container regions for the
|
|
77407
|
+
* same shell — `cn-north-1` with `--assume-role`, `CN-NORTH-1` without.
|
|
77195
77408
|
*/
|
|
77196
77409
|
function forwardAwsEnv(env) {
|
|
77197
|
-
|
|
77410
|
+
const passThrough = [
|
|
77198
77411
|
"AWS_ACCESS_KEY_ID",
|
|
77199
77412
|
"AWS_SECRET_ACCESS_KEY",
|
|
77200
77413
|
"AWS_SESSION_TOKEN",
|
|
77201
77414
|
"AWS_REGION",
|
|
77202
77415
|
"AWS_DEFAULT_REGION"
|
|
77203
|
-
]
|
|
77416
|
+
];
|
|
77417
|
+
const regionKeys = /* @__PURE__ */ new Set(["AWS_REGION", "AWS_DEFAULT_REGION"]);
|
|
77418
|
+
for (const key of passThrough) {
|
|
77204
77419
|
const value = process.env[key];
|
|
77205
|
-
if (value
|
|
77420
|
+
if (value === void 0) continue;
|
|
77421
|
+
env[key] = regionKeys.has(key) ? canonicalizeRegion(value) : value;
|
|
77206
77422
|
}
|
|
77207
77423
|
}
|
|
77208
77424
|
/**
|
|
@@ -78448,7 +78664,7 @@ function createMigrateCommand() {
|
|
|
78448
78664
|
*/
|
|
78449
78665
|
function buildProgram() {
|
|
78450
78666
|
const program = new Command();
|
|
78451
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.
|
|
78667
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.14");
|
|
78452
78668
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
78453
78669
|
const { profile } = actionCommand.optsWithGlobals();
|
|
78454
78670
|
if (profile !== void 0) process.env["AWS_PROFILE"] = profile;
|