@go-to-k/cdkd 0.115.3 → 0.116.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -4
- package/dist/cli.js +124 -95
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-VFIYh_NY.js → deploy-engine-Chzg_hDE.js} +58 -4
- package/dist/deploy-engine-Chzg_hDE.js.map +1 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-VFIYh_NY.js.map +0 -1
package/README.md
CHANGED
|
@@ -4,15 +4,14 @@ Drop-in CDK CLI for existing CDK apps — faster deploys via AWS SDK instead of
|
|
|
4
4
|
|
|
5
5
|
- **Drop-in CDK compatible** — your existing CDK app code runs as-is.
|
|
6
6
|
- **Up to 15x faster deploys than the AWS CDK CLI (CloudFormation)**
|
|
7
|
-
- **
|
|
7
|
+
- **Local dev for CDK apps** — invoke Lambdas, serve API Gateway routes, and run ECS tasks directly from your CDK code, no `cdk synth → sam local` round-trip.
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
11
11
|
**cdkd complements the AWS CDK CLI rather than replacing it.** Use cdkd in dev/test for rapid iteration and SAM-style local execution; use the AWS CDK CLI in production for full CloudFormation tooling. Bidirectional migration is supported — [import an existing CloudFormation stack](#importing-existing-resources) into cdkd for iteration, or [export back to CloudFormation](#exporting-a-stack-back-to-cloudformation) when ready for production.
|
|
12
12
|
|
|
13
|
-
>
|
|
14
|
-
>
|
|
15
|
-
> An experimental project exploring direct SDK provisioning as an alternative to the AWS CDK CLI — **NOT a replacement** and **NOT suitable for production use**. Features are incomplete, APIs may change without notice, and bugs may affect your AWS infrastructure. Use at your own risk in development / testing environments only.
|
|
13
|
+
> [!IMPORTANT]
|
|
14
|
+
> cdkd is for dev/test workflows only — early in development, not yet production-ready.
|
|
16
15
|
|
|
17
16
|
## Features
|
|
18
17
|
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-CuHRHcyW.js";
|
|
3
|
-
import { $ as
|
|
3
|
+
import { $ as RouteDiscoveryError, A as runDockerStreaming, B as AssemblyReader, C as AssetPublisher, D as formatDockerLoginError, E as buildDockerImage, F as resolveCaptureObservedState, H as resolveBucketRegion, I as resolveSkipPrefix, L as resolveStateBucketWithDefault, M as getDefaultStateBucketName, N as getLegacyStateBucketName, O as getDockerCmd, P as resolveApp, Q as ResourceUpdateNotSupportedError, R as resolveStateBucketWithDefaultAndSource, S as shouldRetainResource, T as WorkGraph, W as CdkdError, X as ProvisioningError, Y as PartialFailureError, Z as ResourceTimeoutError, _ as DiffCalculator, _t as withSkipPrefix, a as withRetry, b as LockManager, c as collectInlinePolicyNamesManagedBySiblings, d as normalizeAwsTagsToCfn, dt as runStackBuffered, et as StackHasActiveImportsError, f as resolveExplicitPhysicalId, ft as getLiveRenderer, g as IntrinsicFunctionResolver, gt as generateResourceNameWithFallback, h as assertRegionMatch, ht as generateResourceName, i as withResourceDeadline, j as Synthesizer, k as runDockerForeground, l as CDK_PATH_TAG, lt as getLogger, m as CloudControlProvider, mt as PATTERN_B_RESOURCE_TYPES, n as DEFAULT_RESOURCE_WARN_AFTER_MS, o as IMPLICIT_DELETE_DEPENDENCIES, ot as normalizeAwsError, p as ProviderRegistry, pt as PATTERN_B_NAME_PROPERTIES, q as LocalInvokeBuildError, r as DeployEngine, s as IAMRoleProvider, st as withErrorHandling, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as StackTerminationProtectionError, u as matchesCdkPath, v as DagBuilder, vt as withStackName, w as stringifyValue, x as S3StateBackend, y as TemplateParser, z as warnDeprecatedNoPrefixCliFlag } from "./deploy-engine-Chzg_hDE.js";
|
|
4
4
|
import { createHash, createPublicKey, createVerify, randomBytes, randomUUID } from "node:crypto";
|
|
5
5
|
import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListBucketsCommand, ListDirectoryBucketsCommand, 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";
|
|
6
6
|
import { AddRoleToInstanceProfileCommand, AddUserToGroupCommand, AttachGroupPolicyCommand, AttachUserPolicyCommand, CreateGroupCommand, CreateInstanceProfileCommand, CreateLoginProfileCommand, CreateUserCommand, DeleteAccessKeyCommand, DeleteGroupCommand, DeleteGroupPolicyCommand, DeleteInstanceProfileCommand, DeleteLoginProfileCommand, DeleteRolePolicyCommand, DeleteUserCommand, DeleteUserPermissionsBoundaryCommand, DeleteUserPolicyCommand, DetachGroupPolicyCommand, DetachUserPolicyCommand, GetGroupCommand, GetGroupPolicyCommand, GetInstanceProfileCommand, GetRolePolicyCommand, GetUserCommand, GetUserPolicyCommand, IAMClient, ListAccessKeysCommand, ListAttachedGroupPoliciesCommand, ListAttachedUserPoliciesCommand, ListGroupPoliciesCommand, ListGroupsForUserCommand, ListInstanceProfilesCommand, ListUserPoliciesCommand, ListUserTagsCommand, ListUsersCommand, NoSuchEntityException, PutGroupPolicyCommand, PutRolePolicyCommand, PutUserPermissionsBoundaryCommand, PutUserPolicyCommand, RemoveRoleFromInstanceProfileCommand, RemoveUserFromGroupCommand, TagUserCommand, UntagUserCommand, UpdateLoginProfileCommand } from "@aws-sdk/client-iam";
|
|
@@ -37366,43 +37366,22 @@ async function pullImage(image, skipPull) {
|
|
|
37366
37366
|
}
|
|
37367
37367
|
if (getLogger().getLevel() === "debug") {
|
|
37368
37368
|
logger.info(`Pulling ${image}...`);
|
|
37369
|
-
|
|
37369
|
+
try {
|
|
37370
|
+
await runDockerForeground(["pull", image]);
|
|
37371
|
+
} catch (err) {
|
|
37372
|
+
throw new DockerRunnerError(`docker pull ${image} failed: ${err.message}`);
|
|
37373
|
+
}
|
|
37370
37374
|
return;
|
|
37371
37375
|
}
|
|
37372
37376
|
logger.debug(`Pulling ${image} (silent — pass --verbose to stream progress)`);
|
|
37373
|
-
|
|
37374
|
-
}
|
|
37375
|
-
|
|
37376
|
-
|
|
37377
|
-
|
|
37378
|
-
|
|
37379
|
-
|
|
37380
|
-
|
|
37381
|
-
*/
|
|
37382
|
-
function runCaptured(cmd, args, image) {
|
|
37383
|
-
return new Promise((resolveProc, rejectProc) => {
|
|
37384
|
-
const proc = spawn(cmd, args, { stdio: [
|
|
37385
|
-
"ignore",
|
|
37386
|
-
"pipe",
|
|
37387
|
-
"pipe"
|
|
37388
|
-
] });
|
|
37389
|
-
let stdout = "";
|
|
37390
|
-
let stderr = "";
|
|
37391
|
-
proc.stdout?.on("data", (chunk) => {
|
|
37392
|
-
stdout += chunk.toString("utf-8");
|
|
37393
|
-
});
|
|
37394
|
-
proc.stderr?.on("data", (chunk) => {
|
|
37395
|
-
stderr += chunk.toString("utf-8");
|
|
37396
|
-
});
|
|
37397
|
-
proc.on("error", (err) => rejectProc(new DockerRunnerError(`${cmd} pull ${image} failed: ${err.message}`)));
|
|
37398
|
-
proc.on("close", (code) => {
|
|
37399
|
-
if (code === 0) {
|
|
37400
|
-
resolveProc();
|
|
37401
|
-
return;
|
|
37402
|
-
}
|
|
37403
|
-
rejectProc(new DockerRunnerError(`docker pull ${image} exited with code ${code}: ${stderr.trim() || stdout.trim() || "(no output)"}`));
|
|
37404
|
-
});
|
|
37405
|
-
});
|
|
37377
|
+
try {
|
|
37378
|
+
await runDockerStreaming(["pull", image], { streamLive: false });
|
|
37379
|
+
} catch (err) {
|
|
37380
|
+
const e = err;
|
|
37381
|
+
if (e.exitCode === void 0 || e.exitCode === null) throw new DockerRunnerError(`docker pull ${image} failed: ${e.message ?? String(err)}`);
|
|
37382
|
+
const detail = e.stderr?.trim() || e.stdout?.trim() || "(no output)";
|
|
37383
|
+
throw new DockerRunnerError(`docker pull ${image} exited with code ${e.exitCode}: ${detail}`);
|
|
37384
|
+
}
|
|
37406
37385
|
}
|
|
37407
37386
|
/**
|
|
37408
37387
|
* Run the container detached. Returns the container ID.
|
|
@@ -37576,21 +37555,6 @@ function redactAwsCredentialsInArgs(args) {
|
|
|
37576
37555
|
}
|
|
37577
37556
|
return out;
|
|
37578
37557
|
}
|
|
37579
|
-
/**
|
|
37580
|
-
* Run a child process attached to the parent's stdio (so users see
|
|
37581
|
-
* progress lines as they happen). Resolves on exit code 0; rejects with
|
|
37582
|
-
* the captured stderr otherwise. Used for `docker pull`.
|
|
37583
|
-
*/
|
|
37584
|
-
function runForeground$1(cmd, args) {
|
|
37585
|
-
return new Promise((resolveProc, rejectProc) => {
|
|
37586
|
-
const proc = spawn(cmd, args, { stdio: "inherit" });
|
|
37587
|
-
proc.on("error", (err) => rejectProc(new DockerRunnerError(`${cmd} failed: ${err.message}`)));
|
|
37588
|
-
proc.on("close", (code) => {
|
|
37589
|
-
if (code === 0) resolveProc();
|
|
37590
|
-
else rejectProc(new DockerRunnerError(`${cmd} exited with code ${code}`));
|
|
37591
|
-
});
|
|
37592
|
-
});
|
|
37593
|
-
}
|
|
37594
37558
|
|
|
37595
37559
|
//#endregion
|
|
37596
37560
|
//#region src/local/ecr-puller.ts
|
|
@@ -37665,7 +37629,11 @@ async function pullEcrImage(imageUri, options) {
|
|
|
37665
37629
|
ecr.destroy();
|
|
37666
37630
|
}
|
|
37667
37631
|
logger.info(`Pulling ${imageUri}...`);
|
|
37668
|
-
|
|
37632
|
+
try {
|
|
37633
|
+
await runDockerForeground(["pull", imageUri]);
|
|
37634
|
+
} catch (err) {
|
|
37635
|
+
throw new LocalInvokeBuildError(`docker pull ${imageUri} failed: ${err.message}`);
|
|
37636
|
+
}
|
|
37669
37637
|
return imageUri;
|
|
37670
37638
|
}
|
|
37671
37639
|
/**
|
|
@@ -37729,22 +37697,6 @@ async function isImageInLocalCache(imageRef) {
|
|
|
37729
37697
|
return false;
|
|
37730
37698
|
}
|
|
37731
37699
|
}
|
|
37732
|
-
/**
|
|
37733
|
-
* `docker pull` plumbed to the parent's stdio so the user sees layer
|
|
37734
|
-
* pull progress. Mirrors the runtime image's `pullImage` plumbing in
|
|
37735
|
-
* `docker-runner.ts` but local to this module to avoid a circular
|
|
37736
|
-
* dependency.
|
|
37737
|
-
*/
|
|
37738
|
-
function runForeground(cmd, args) {
|
|
37739
|
-
return new Promise((resolve, reject) => {
|
|
37740
|
-
const proc = spawn(cmd, args, { stdio: "inherit" });
|
|
37741
|
-
proc.on("error", (err) => reject(new LocalInvokeBuildError(`${cmd} failed: ${err.message}`)));
|
|
37742
|
-
proc.on("close", (code) => {
|
|
37743
|
-
if (code === 0) resolve();
|
|
37744
|
-
else reject(new LocalInvokeBuildError(`${cmd} exited with code ${code}`));
|
|
37745
|
-
});
|
|
37746
|
-
});
|
|
37747
|
-
}
|
|
37748
37700
|
|
|
37749
37701
|
//#endregion
|
|
37750
37702
|
//#region src/local/docker-image-builder.ts
|
|
@@ -38731,6 +38683,13 @@ function resolveLambdaArnOutcome(value) {
|
|
|
38731
38683
|
return resolveLambdaArnIntrinsic(value);
|
|
38732
38684
|
}
|
|
38733
38685
|
/**
|
|
38686
|
+
* Marker prefix the HTTP API v2 Route `Target` field always starts with —
|
|
38687
|
+
* AWS documents this as `integrations/<IntegrationId>`. Load-bearing
|
|
38688
|
+
* signal that an `Fn::Sub` shape on this field is actually pointing at
|
|
38689
|
+
* a same-template Integration rather than something unrelated.
|
|
38690
|
+
*/
|
|
38691
|
+
const TARGET_INTEGRATIONS_PREFIX = "integrations/";
|
|
38692
|
+
/**
|
|
38734
38693
|
* Parse an HTTP API Route's `Target` into the integration's logical ID.
|
|
38735
38694
|
*
|
|
38736
38695
|
* CDK emits one of:
|
|
@@ -38739,9 +38698,14 @@ function resolveLambdaArnOutcome(value) {
|
|
|
38739
38698
|
* (the shape `aws-cdk-lib/aws-apigatewayv2`'s `HttpApi.addRoutes`
|
|
38740
38699
|
* actually emits — empty separator + `'integrations/'` literal
|
|
38741
38700
|
* prefix in front of the Ref).
|
|
38701
|
+
* - `Fn::Sub: 'integrations/${MyIntegration}'` (1-arg form — AWS-docs
|
|
38702
|
+
* canonical; emitted by hand-rolled `CfnRoute` constructs).
|
|
38703
|
+
* - `Fn::Sub: ['integrations/${IntId}', { IntId: <Ref|GetAtt> }]`
|
|
38704
|
+
* (2-arg form — what `cdk.Fn.sub(template, vars)` synthesizes
|
|
38705
|
+
* when users build `target` programmatically).
|
|
38742
38706
|
* - `'integrations/abc123'` (literal — rare).
|
|
38743
38707
|
*
|
|
38744
|
-
* All
|
|
38708
|
+
* All five forms are accepted; anything else throws.
|
|
38745
38709
|
*/
|
|
38746
38710
|
function parseHttpApiTargetIntegration(target, location) {
|
|
38747
38711
|
if (typeof target === "string") {
|
|
@@ -38750,7 +38714,8 @@ function parseHttpApiTargetIntegration(target, location) {
|
|
|
38750
38714
|
throw new Error(`${location}: literal Target '${target}' must start with 'integrations/'`);
|
|
38751
38715
|
}
|
|
38752
38716
|
if (target && typeof target === "object" && !Array.isArray(target)) {
|
|
38753
|
-
const
|
|
38717
|
+
const obj = target;
|
|
38718
|
+
const join = obj["Fn::Join"];
|
|
38754
38719
|
if (Array.isArray(join) && join.length === 2 && Array.isArray(join[1])) {
|
|
38755
38720
|
const sep = join[0];
|
|
38756
38721
|
const parts = join[1];
|
|
@@ -38763,8 +38728,30 @@ function parseHttpApiTargetIntegration(target, location) {
|
|
|
38763
38728
|
if (ref) return ref;
|
|
38764
38729
|
}
|
|
38765
38730
|
}
|
|
38731
|
+
if ("Fn::Sub" in obj) {
|
|
38732
|
+
const sub = obj["Fn::Sub"];
|
|
38733
|
+
if (typeof sub === "string") {
|
|
38734
|
+
const m = new RegExp(`^${TARGET_INTEGRATIONS_PREFIX}\\$\\{([^}]+)\\}$`).exec(sub);
|
|
38735
|
+
if (m) {
|
|
38736
|
+
const placeholder = m[1];
|
|
38737
|
+
if (!placeholder.includes(".")) return placeholder;
|
|
38738
|
+
}
|
|
38739
|
+
}
|
|
38740
|
+
if (Array.isArray(sub) && sub.length === 2 && typeof sub[0] === "string" && sub[1] !== null && typeof sub[1] === "object" && !Array.isArray(sub[1])) {
|
|
38741
|
+
const template = sub[0];
|
|
38742
|
+
const bindings = sub[1];
|
|
38743
|
+
const m = new RegExp(`^${TARGET_INTEGRATIONS_PREFIX}\\$\\{([^}]+)\\}$`).exec(template);
|
|
38744
|
+
if (m) {
|
|
38745
|
+
const bound = bindings[m[1]];
|
|
38746
|
+
if (bound !== void 0) {
|
|
38747
|
+
const ref = pickRefLogicalId$2(bound);
|
|
38748
|
+
if (ref) return ref;
|
|
38749
|
+
}
|
|
38750
|
+
}
|
|
38751
|
+
}
|
|
38752
|
+
}
|
|
38766
38753
|
}
|
|
38767
|
-
throw new Error(`${location}: Target must be 'integrations/<id>'
|
|
38754
|
+
throw new Error(`${location}: Target must be 'integrations/<id>', Fn::Join with one of the documented shapes, or Fn::Sub with an 'integrations/\${...}' template (got ${shortJson$1(target)}).`);
|
|
38768
38755
|
}
|
|
38769
38756
|
/**
|
|
38770
38757
|
* Parse an HTTP API RouteKey (`'<METHOD> <path>'` or `'$default'`) into
|
|
@@ -43875,7 +43862,17 @@ async function localInvokeCommand(target, options) {
|
|
|
43875
43862
|
if (stateAudit && stateAudit.unresolved.some((u) => u.key === key)) continue;
|
|
43876
43863
|
logger.warn(`Environment variable ${key} contains a CloudFormation intrinsic and was dropped. Override it with --env-vars (e.g. {"${lambda.logicalId}":{"${key}":"<literal>"}}) or pass --from-state to recover deployed values.`);
|
|
43877
43864
|
}
|
|
43878
|
-
|
|
43865
|
+
let resolvedAssumeRoleArn;
|
|
43866
|
+
if (typeof options.assumeRole === "string") resolvedAssumeRoleArn = options.assumeRole;
|
|
43867
|
+
else if (options.assumeRole === true) if (!stateForRoleHint) logger.warn("--assume-role passed without an ARN, but no cdkd state was loaded. Pair it with --from-state, or pass the ARN explicitly: --assume-role <arn>. Falling back to the developer's shell credentials.");
|
|
43868
|
+
else {
|
|
43869
|
+
const arn = resolveExecutionRoleArnFromState(stateForRoleHint, lambda.logicalId);
|
|
43870
|
+
if (arn) {
|
|
43871
|
+
resolvedAssumeRoleArn = arn;
|
|
43872
|
+
logger.info(`--assume-role: auto-resolved execution role from cdkd state: ${arn}`);
|
|
43873
|
+
} else logger.warn(`--assume-role: could not resolve the execution role ARN from cdkd state for '${lambda.logicalId}'. Pass the ARN explicitly: --assume-role <arn>. Falling back to the developer's shell credentials.`);
|
|
43874
|
+
}
|
|
43875
|
+
else if (options.assumeRole === void 0 && options.fromState && stateForRoleHint) suggestAssumeRoleFromState(stateForRoleHint, lambda.logicalId);
|
|
43879
43876
|
const event = await readEvent(options);
|
|
43880
43877
|
const dockerEnv = {
|
|
43881
43878
|
AWS_LAMBDA_FUNCTION_NAME: lambda.logicalId,
|
|
@@ -43886,14 +43883,22 @@ async function localInvokeCommand(target, options) {
|
|
|
43886
43883
|
AWS_LAMBDA_LOG_STREAM_NAME: "local",
|
|
43887
43884
|
...envResult.resolved
|
|
43888
43885
|
};
|
|
43889
|
-
|
|
43886
|
+
let assumeSucceeded = false;
|
|
43887
|
+
if (resolvedAssumeRoleArn) {
|
|
43890
43888
|
const stsRegion = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"];
|
|
43891
|
-
|
|
43892
|
-
|
|
43893
|
-
|
|
43894
|
-
|
|
43895
|
-
|
|
43896
|
-
|
|
43889
|
+
try {
|
|
43890
|
+
const creds = await assumeLambdaExecutionRole(resolvedAssumeRoleArn, stsRegion);
|
|
43891
|
+
dockerEnv["AWS_ACCESS_KEY_ID"] = creds.accessKeyId;
|
|
43892
|
+
dockerEnv["AWS_SECRET_ACCESS_KEY"] = creds.secretAccessKey;
|
|
43893
|
+
dockerEnv["AWS_SESSION_TOKEN"] = creds.sessionToken;
|
|
43894
|
+
if (stsRegion) dockerEnv["AWS_REGION"] = stsRegion;
|
|
43895
|
+
assumeSucceeded = true;
|
|
43896
|
+
} catch (err) {
|
|
43897
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
43898
|
+
logger.warn(`--assume-role: STS AssumeRole(${resolvedAssumeRoleArn}) failed: ${reason}. Falling back to the developer's shell credentials.`);
|
|
43899
|
+
}
|
|
43900
|
+
}
|
|
43901
|
+
if (!assumeSucceeded) forwardAwsEnv(dockerEnv);
|
|
43897
43902
|
let debugPort;
|
|
43898
43903
|
if (options.debugPort) {
|
|
43899
43904
|
debugPort = Number(options.debugPort);
|
|
@@ -44184,11 +44189,10 @@ async function readStdin() {
|
|
|
44184
44189
|
/**
|
|
44185
44190
|
* Assume the Lambda execution role and return temporary credentials.
|
|
44186
44191
|
*
|
|
44187
|
-
*
|
|
44188
|
-
*
|
|
44189
|
-
*
|
|
44190
|
-
*
|
|
44191
|
-
* the user supplies the ARN explicitly.
|
|
44192
|
+
* Closes the "developer has admin creds, the deployed function has narrow
|
|
44193
|
+
* ones" skew that SAM users routinely hit. Off by default; opt-in via
|
|
44194
|
+
* `--assume-role <arn>` (explicit) or `--assume-role` (bare, auto-resolved
|
|
44195
|
+
* from cdkd state via `resolveExecutionRoleArnFromState`).
|
|
44192
44196
|
*
|
|
44193
44197
|
* Mirrors the env-var-write pattern from `applyRoleArnIfSet` in
|
|
44194
44198
|
* `src/utils/role-arn.ts` but writes the temp creds onto the container's
|
|
@@ -44263,26 +44267,51 @@ function materializeInlineCode(handler, source, fileExtension) {
|
|
|
44263
44267
|
return dir;
|
|
44264
44268
|
}
|
|
44265
44269
|
/**
|
|
44266
|
-
* When `--from-state` is set but `--assume-role`
|
|
44267
|
-
* deployed execution role ARN once as a hint.
|
|
44268
|
-
*
|
|
44269
|
-
*
|
|
44270
|
+
* When `--from-state` is set but `--assume-role` was NOT passed (not even
|
|
44271
|
+
* bare), log the function's deployed execution role ARN once as a hint.
|
|
44272
|
+
* This is the pre-(#442) behavior — kept for backward compatibility with
|
|
44273
|
+
* users who don't want auto-assume. The bare-`--assume-role` path opts
|
|
44274
|
+
* in to actually assuming the resolved ARN; this hint path stays
|
|
44275
|
+
* informational only.
|
|
44270
44276
|
*/
|
|
44271
44277
|
function suggestAssumeRoleFromState(state, logicalId) {
|
|
44272
44278
|
const logger = getLogger();
|
|
44279
|
+
const roleArn = resolveExecutionRoleArnFromState(state, logicalId);
|
|
44280
|
+
if (roleArn) logger.info(`Hint: the deployed function uses execution role ${roleArn}. Re-run with --assume-role to invoke under the deployed function's narrow permissions.`);
|
|
44281
|
+
}
|
|
44282
|
+
/**
|
|
44283
|
+
* Resolve the execution-role ARN for a Lambda from cdkd state. Used by
|
|
44284
|
+
* both the `--assume-role` auto-resolve path and the legacy hint path.
|
|
44285
|
+
*
|
|
44286
|
+
* Resolution rules (mirrors the v1 scope spelled out in (#442)):
|
|
44287
|
+
*
|
|
44288
|
+
* - Literal-string `Role` starting with `arn:` → returned verbatim.
|
|
44289
|
+
* - `{ Fn::GetAtt: [<RoleId>, 'Arn'] }` or `{ Ref: <RoleId> }` → looked up
|
|
44290
|
+
* against the sibling IAM Role resource's `attributes.Arn` (recorded
|
|
44291
|
+
* at deploy time by `IAMRoleProvider.create` / drift refresh).
|
|
44292
|
+
* - Any other shape (`Fn::Sub` / `Fn::Join` / cross-stack imports) → not
|
|
44293
|
+
* resolved in v1; the caller surfaces a warn and falls back to dev
|
|
44294
|
+
* creds. Once real CDK apps emit those shapes for `Role` we can
|
|
44295
|
+
* extend the resolver per `feedback_verify_cdk_synth_shape_before_resolver.md`.
|
|
44296
|
+
*
|
|
44297
|
+
* Returns `undefined` when state has no entry for the Lambda, the `Role`
|
|
44298
|
+
* is missing entirely, the referenced sibling has no `Arn` attribute
|
|
44299
|
+
* captured, or the shape is one we don't try to resolve.
|
|
44300
|
+
*
|
|
44301
|
+
* Exported for unit testing.
|
|
44302
|
+
*/
|
|
44303
|
+
function resolveExecutionRoleArnFromState(state, logicalId) {
|
|
44273
44304
|
const lambda = state.resources[logicalId];
|
|
44274
|
-
if (!lambda) return;
|
|
44305
|
+
if (!lambda) return void 0;
|
|
44275
44306
|
const roleRef = lambda.properties?.["Role"] ?? lambda.observedProperties?.["Role"];
|
|
44276
|
-
|
|
44277
|
-
if (typeof roleRef === "
|
|
44278
|
-
else if (typeof roleRef === "object" && roleRef !== null) {
|
|
44307
|
+
if (typeof roleRef === "string" && roleRef.startsWith("arn:")) return roleRef;
|
|
44308
|
+
if (typeof roleRef === "object" && roleRef !== null) {
|
|
44279
44309
|
const refLogicalId = pickReferencedLogicalId(roleRef);
|
|
44280
44310
|
if (refLogicalId) {
|
|
44281
44311
|
const cached = state.resources[refLogicalId]?.attributes?.["Arn"];
|
|
44282
|
-
if (typeof cached === "string" && cached.startsWith("arn:"))
|
|
44312
|
+
if (typeof cached === "string" && cached.startsWith("arn:")) return cached;
|
|
44283
44313
|
}
|
|
44284
44314
|
}
|
|
44285
|
-
if (roleArn) logger.info(`Hint: the deployed function uses execution role ${roleArn}. Re-run with --assume-role <that-arn> to invoke under the deployed function's narrow permissions.`);
|
|
44286
44315
|
}
|
|
44287
44316
|
/**
|
|
44288
44317
|
* Walk a single-key intrinsic and return the referenced logical ID, or
|
|
@@ -44306,7 +44335,7 @@ function pickReferencedLogicalId(intrinsic) {
|
|
|
44306
44335
|
*/
|
|
44307
44336
|
function createLocalCommand() {
|
|
44308
44337
|
const local = new Command("local").description("Local execution of Lambda functions (RIE) and ECS task definitions (Docker required)");
|
|
44309
|
-
const invoke = new Command("invoke").description("Run a Lambda function locally in a Docker container (RIE-backed). Target accepts a CDK display path (MyStack/MyApi/Handler) or stack-qualified logical ID (MyStack:MyApiHandler1234ABCD). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the Lambda to invoke").addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for IMAGE local-build path; `docker build` does not pull base layers by default")).addOption(new Option("--no-build", "Skip docker build on the IMAGE local-build path (use the previously-built tag). Requires the deterministic tag to already be in the local registry; errors with an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. Compatible with --no-pull.")).addOption(new Option("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(new Option("--container-host <host>", "Host to bind the RIE port to").default("127.0.0.1")).addOption(new Option("--assume-role
|
|
44338
|
+
const invoke = new Command("invoke").description("Run a Lambda function locally in a Docker container (RIE-backed). Target accepts a CDK display path (MyStack/MyApi/Handler) or stack-qualified logical ID (MyStack:MyApiHandler1234ABCD). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the Lambda to invoke").addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for IMAGE local-build path; `docker build` does not pull base layers by default")).addOption(new Option("--no-build", "Skip docker build on the IMAGE local-build path (use the previously-built tag). Requires the deterministic tag to already be in the local registry; errors with an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. Compatible with --no-pull.")).addOption(new Option("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(new Option("--container-host <host>", "Host to bind the RIE port to").default("127.0.0.1")).addOption(new Option("--assume-role [arn]", "Assume the Lambda's deployed execution role and forward STS-issued temp credentials to the container so the handler runs with the deployed function's narrow permissions (closes the \"developer admin / function narrow\" skew). Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) auto-resolves the function's execution role ARN from cdkd state (requires --from-state); (3) `--no-assume-role` explicitly opts out (forces dev creds even with --from-state). Off by default — when omitted, the developer's shell credentials are forwarded unchanged (SAM-compatible default). STS failures degrade to a warn + dev-creds fallback.")).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub in env vars with the deployed physical IDs / attributes. Off by default — keep PR 1 warn-and-drop semantics; turn on for stacks already deployed via cdkd deploy.").default(false)).addOption(new Option("--stack-region <region>", "Region of the cdkd state record to read (used with --from-state when the same stack name has state in multiple regions).")).action(withErrorHandling(localInvokeCommand));
|
|
44310
44339
|
[
|
|
44311
44340
|
...commonOptions,
|
|
44312
44341
|
...appOptions,
|
|
@@ -45594,7 +45623,7 @@ function reorderArgs(argv) {
|
|
|
45594
45623
|
*/
|
|
45595
45624
|
async function main() {
|
|
45596
45625
|
const program = new Command();
|
|
45597
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
45626
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.116.0");
|
|
45598
45627
|
program.addCommand(createBootstrapCommand());
|
|
45599
45628
|
program.addCommand(createSynthCommand());
|
|
45600
45629
|
program.addCommand(createListCommand());
|