cdk-local 0.54.0 → 0.55.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 +2 -2
- package/dist/cli.js +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{local-list-e7cz-0OZ.js → local-list-DT8qRbKy.js} +187 -39
- package/dist/local-list-DT8qRbKy.js.map +1 -0
- package/package.json +1 -1
- package/dist/local-list-e7cz-0OZ.js.map +0 -1
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ Full flags, precedence, and `--from-cfn-stack` resolution: [docs/cli-reference.m
|
|
|
69
69
|
|
|
70
70
|
### start-service vs start-alb — which one?
|
|
71
71
|
|
|
72
|
-
`start-service` runs just the ECS service's replicas (workers, queue consumers, Service-Connect-only). `start-alb` boots the ECS service(s) behind an ALB **plus** a host-side front-door on each listener port
|
|
72
|
+
`start-service` runs just the ECS service's replicas (workers, queue consumers, Service-Connect-only). `start-alb` boots the ECS service(s) behind an ALB **plus** a host-side front-door on each listener port — HTTP and HTTPS (TLS terminated locally with `--tls-cert` / `--tls-key` or an auto-generated self-signed cert) — so external traffic reaches them the way it does in the cloud. Full resolution model: [docs/cli-reference.md](docs/cli-reference.md#cdkl-start-alb-run-an-alb-fronted-service-locally).
|
|
73
73
|
|
|
74
74
|
## Supported resources
|
|
75
75
|
|
|
@@ -80,7 +80,7 @@ Full flags, precedence, and `--from-cfn-stack` resolution: [docs/cli-reference.m
|
|
|
80
80
|
| ECS task definitions | `run-task` |
|
|
81
81
|
| ECS services | `start-service` |
|
|
82
82
|
| Cloud Map / Service Connect registry | service discovery between local replicas |
|
|
83
|
-
| ALB-fronted ECS / Lambda services | `start-alb` — all six listener-rule conditions, weighted forwards, redirect / fixed-response, mixed ECS + Lambda targets |
|
|
83
|
+
| ALB-fronted ECS / Lambda services | `start-alb` — HTTP / HTTPS listeners, all six listener-rule conditions, weighted forwards, redirect / fixed-response, mixed ECS + Lambda targets |
|
|
84
84
|
| Bedrock AgentCore Runtime agents | `invoke-agentcore` — container + `fromCodeAsset` / `fromS3` artifacts, HTTP + MCP |
|
|
85
85
|
|
|
86
86
|
Lambda runs on every current AWS Lambda runtime — Node.js (18/20/22/24), Python (3.11–3.14), Ruby (3.2/3.3), Java (8.al2/11/17/21), .NET (6/8), and the OS-only `provided.al2` / `provided.al2023`. The retired `go1.x` runtime is rejected with a pointer to migrate to `provided.al2023`.
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { a as createLocalStartApiCommand } from "./cloud-map-resolver-CbSdXQjx.js";
|
|
3
|
-
import { a as createLocalRunTaskCommand, i as createLocalStartServiceCommand, o as createLocalInvokeAgentCoreCommand, r as createLocalStartAlbCommand, s as createLocalInvokeCommand, t as createLocalListCommand } from "./local-list-
|
|
3
|
+
import { a as createLocalRunTaskCommand, i as createLocalStartServiceCommand, o as createLocalInvokeAgentCoreCommand, r as createLocalStartAlbCommand, s as createLocalInvokeCommand, t as createLocalListCommand } from "./local-list-DT8qRbKy.js";
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
//#region src/cli/index.ts
|
|
7
7
|
const program = new Command();
|
|
8
|
-
program.name("cdkl").description("Run AWS CDK stacks locally with Docker.").version("0.
|
|
8
|
+
program.name("cdkl").description("Run AWS CDK stacks locally with Docker.").version("0.55.0");
|
|
9
9
|
program.addCommand(createLocalInvokeCommand());
|
|
10
10
|
program.addCommand(createLocalInvokeAgentCoreCommand());
|
|
11
11
|
program.addCommand(createLocalStartApiCommand());
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/cli/commands/local-invoke.ts","../src/cli/commands/local-invoke-agentcore.ts","../src/cli/commands/local-run-task.ts","../src/local/target-lister.ts","../src/cli/commands/local-start-service.ts","../src/cli/commands/local-start-alb.ts","../src/cli/commands/local-list.ts","../src/local/cfn-local-state-provider.ts"],"mappings":";;;;;UA6IiB,+BAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAi6BA,wBAAA,CAAyB,IAAA,GAAM,+BAAA,GAAuC,OAAA;;;UCz3BrE,wCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAmqCA,iCAAA,CACd,IAAA,GAAM,wCAAA,GACL,OAAA;;;UCrvCc,gCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAmhBA,yBAAA,CAA0B,IAAA,GAAM,gCAAA,GAAwC,OAAA;;;UCjnBvE,WAAA;EAEf,SAAA;EAEA,SAAA;EAKA,WAAA;EAMA,WAAA;EAOA,IAAA;AAAA;AAAA,UAOe,aAAA;EAEf,OAAA,EAAS,WAAA;EAMT,IAAA,EAAM,WAAA;EAEN,WAAA,EAAa,WAAA;EAEb,kBAAA,EAAoB,WAAA;EAEpB,iBAAA,EAAmB,WAAA;EAEnB,aAAA,EAAe,WAAA;AAAA;AAAA,iBAkID,WAAA,CAAY,MAAA,WAAiB,SAAA,KAAc,aAAA;AAAA,iBAsC3C,YAAA,CAAa,OAAA,EAAS,aAAA;;;UC3MrB,qCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAmCA,8BAAA,CACd,IAAA,GAAM,qCAAA,GACL,OAAA;;;UChCc,iCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/cli/commands/local-invoke.ts","../src/cli/commands/local-invoke-agentcore.ts","../src/cli/commands/local-run-task.ts","../src/local/target-lister.ts","../src/cli/commands/local-start-service.ts","../src/cli/commands/local-start-alb.ts","../src/cli/commands/local-list.ts","../src/local/cfn-local-state-provider.ts"],"mappings":";;;;;UA6IiB,+BAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAi6BA,wBAAA,CAAyB,IAAA,GAAM,+BAAA,GAAuC,OAAA;;;UCz3BrE,wCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAmqCA,iCAAA,CACd,IAAA,GAAM,wCAAA,GACL,OAAA;;;UCrvCc,gCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAmhBA,yBAAA,CAA0B,IAAA,GAAM,gCAAA,GAAwC,OAAA;;;UCjnBvE,WAAA;EAEf,SAAA;EAEA,SAAA;EAKA,WAAA;EAMA,WAAA;EAOA,IAAA;AAAA;AAAA,UAOe,aAAA;EAEf,OAAA,EAAS,WAAA;EAMT,IAAA,EAAM,WAAA;EAEN,WAAA,EAAa,WAAA;EAEb,kBAAA,EAAoB,WAAA;EAEpB,iBAAA,EAAmB,WAAA;EAEnB,aAAA,EAAe,WAAA;AAAA;AAAA,iBAkID,WAAA,CAAY,MAAA,WAAiB,SAAA,KAAc,aAAA;AAAA,iBAsC3C,YAAA,CAAa,OAAA,EAAS,aAAA;;;UC3MrB,qCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAmCA,8BAAA,CACd,IAAA,GAAM,qCAAA,GACL,OAAA;;;UChCc,iCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBA2QA,0BAAA,CAA2B,IAAA,GAAM,iCAAA,GAAyC,OAAA;;;UCvQzE,6BAAA;EAEf,WAAA,GAAc,mBAAA;AAAA;AAAA,UAuCC,0BAAA;EAOf,IAAA;AAAA;AAAA,iBAYc,mBAAA,CACd,OAAA,EAAS,aAAA,EACT,OAAA,UACA,OAAA,GAAS,0BAAA;AAAA,iBAuEK,sBAAA,CAAuB,IAAA,GAAM,6BAAA,GAAqC,OAAA;;;UC/FjE,4BAAA;EAOf,YAAA;EAKA,MAAA;EAiBA,OAAA;AAAA;AAAA,cAGW,qBAAA,YAAiC,kBAAA;EAAA,SAG5B,KAAA;EAAA,iBACC,YAAA;EAAA,iBACA,MAAA;EAAA,QAKT,MAAA;EAAA,QAKA,YAAA;EAAA,QAKA,SAAA;EAAA,iBACS,aAAA;EAAA,QAQT,QAAA;cAEI,IAAA,EAAM,4BAAA;EAAA,QAOV,SAAA;EAAA,QAmBA,eAAA;EAAA,QAaA,YAAA;EA6BK,4BAAA,CACX,QAAA,EAAU,sBAAA,GACT,OAAA,CAAQ,qBAAA;EAsBE,0BAAA,CACX,kBAAA,WACC,OAAA,CAAQ,MAAA;EAmCE,IAAA,CACX,UAAA,UACA,YAAA,uBACC,OAAA,CAAQ,gBAAA;EA2DE,uBAAA,CACX,eAAA,WACC,OAAA,CAAQ,kBAAA;EA8DJ,OAAA,CAAA;AAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as getEmbedConfig, l as resetEmbedConfig, u as setEmbedConfig } from "./docker-cmd-voNPrcRh.js";
|
|
2
2
|
import { Cn as listTargets, Sn as countTargets, a as createLocalStartApiCommand, cn as createLocalStateProvider, dn as resolveCfnFallbackRegion, fn as resolveCfnRegion, gn as resolveSsmParameters, hn as collectSsmParameterRefs, in as substituteEnvVarsFromStateAsync, ln as isCfnFlagPresent, mn as CfnLocalStateProvider, nn as substituteAgainstStateAsync, pn as resolveCfnStackName, rn as substituteEnvVarsFromState, sn as LocalStateSourceError, tn as substituteAgainstState, un as rejectExplicitCfnStackWithMultipleStacks } from "./cloud-map-resolver-CbSdXQjx.js";
|
|
3
|
-
import { a as createLocalRunTaskCommand, i as createLocalStartServiceCommand, n as formatTargetListing, o as createLocalInvokeAgentCoreCommand, r as createLocalStartAlbCommand, s as createLocalInvokeCommand, t as createLocalListCommand } from "./local-list-
|
|
3
|
+
import { a as createLocalRunTaskCommand, i as createLocalStartServiceCommand, n as formatTargetListing, o as createLocalInvokeAgentCoreCommand, r as createLocalStartAlbCommand, s as createLocalInvokeCommand, t as createLocalListCommand } from "./local-list-DT8qRbKy.js";
|
|
4
4
|
|
|
5
5
|
export { CfnLocalStateProvider, LocalStateSourceError, collectSsmParameterRefs, countTargets, createLocalInvokeAgentCoreCommand, createLocalInvokeCommand, createLocalListCommand, createLocalRunTaskCommand, createLocalStartAlbCommand, createLocalStartApiCommand, createLocalStartServiceCommand, createLocalStateProvider, formatTargetListing, getEmbedConfig, isCfnFlagPresent, listTargets, rejectExplicitCfnStackWithMultipleStacks, resetEmbedConfig, resolveCfnFallbackRegion, resolveCfnRegion, resolveCfnStackName, resolveSsmParameters, setEmbedConfig, substituteAgainstState, substituteAgainstStateAsync, substituteEnvVarsFromState, substituteEnvVarsFromStateAsync };
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { a as runDockerStreaming, c as getEmbedConfig, r as getDockerCmd, s as getLogger, u as setEmbedConfig } from "./docker-cmd-voNPrcRh.js";
|
|
2
2
|
import { $n as parseContextOptions, $t as resolveEcsTaskTarget, At as parseEcrUri, Bn as matchStacks, Bt as removeContainer, Cn as listTargets, Ct as singleFlight, Dt as waitForRieReady, Et as invokeRie, Fn as resolveAgentCoreTarget, Ft as appendEnvFlags, Gn as LocalInvokeBuildError, Gt as resolveRuntimeImage, Hn as readCdkPathOrUndefined, Ht as streamLogs, In as resolveLambdaTarget, It as ensureDockerAvailable, Jn as applyRoleArnIfSet, Jt as applyCrossStackResolverToTask, Kn as LocalStartServiceError, Kt as EcsTaskResolutionError, Ln as derivePseudoParametersFromRegion, Lt as execEnvForSecrets, Mt as buildDockerImage, Nt as DockerRunnerError, Ot as architectureToPlatform, Pn as pickAgentCoreCandidateStack, Pt as SENSITIVE_ENV_KEYS, Q as verifyJwtViaDiscovery, Qn as deprecatedRegionOption, Qt as parseEcsTarget, Rt as pickFreePort, Sn as countTargets, St as writeProfileCredentialsFile, Tt as getDockerImageBySourceHash, Un as resolveCdkPathToLogicalIds, Ut as resolveRuntimeCodeMountPath, Vn as buildCdkPathIndex, Vt as runDetached, Wn as CdkLocalError, Wt as resolveRuntimeFileExtension, Xn as commonOptions, Xt as derivePartitionAndUrlSuffix, Y as createJwksCache, Yn as appOptions, Yt as checkVolumeHostPath, Zn as contextOptions, Zt as detectEcsImageResolutionNeeds, _n as resolveApp, an as resolveEnvVars, at as invokeAgentCoreWs, bn as resolveMultiTarget, c as resolveProfileCredentials$1, cn as createLocalStateProvider, dn as resolveCfnFallbackRegion, en as applyDeployedEnvFallback, er as warnIfDeprecatedRegion, ft as signAgentCoreInvocation, gt as downloadAndExtractS3Bundle, ht as waitForAgentCorePing, i as getPublishedHostPort, in as substituteEnvVarsFromStateAsync, jn as AGENTCORE_MCP_PROTOCOL, jt as pullEcrImage, kt as buildContainerImage, lt as mcpInvokeOnce, mt as invokeAgentCore, n as CloudMapRegistry, nn as substituteAgainstStateAsync, on as materializeLayerFromArn, ot as MCP_CONTAINER_PORT, qn as withErrorHandling, qt as TASK_ROLE_ACCOUNT_PLACEHOLDER, r as getContainerNetworkIp, st as MCP_PATH, t as buildCloudMapIndex, un as rejectExplicitCfnStackWithMultipleStacks, vt as buildAgentCoreCodeImage, wt as AssetManifestLoader, xn as resolveSingleTarget, yn as Synthesizer, zt as pullImage } from "./cloud-map-resolver-CbSdXQjx.js";
|
|
3
|
-
import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
3
|
+
import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { homedir, tmpdir } from "node:os";
|
|
5
5
|
import * as path from "node:path";
|
|
6
|
-
import { dirname } from "node:path";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
7
|
import { Command, Option } from "commander";
|
|
8
8
|
import { GetParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
|
|
9
9
|
import { execFile, spawn } from "node:child_process";
|
|
10
10
|
import { promisify } from "node:util";
|
|
11
|
-
import { randomBytes, randomUUID } from "node:crypto";
|
|
11
|
+
import { X509Certificate, randomBytes, randomUUID } from "node:crypto";
|
|
12
12
|
import { createServer, request } from "node:http";
|
|
13
|
+
import { createServer as createServer$1 } from "node:https";
|
|
13
14
|
import graphlib from "graphlib";
|
|
14
15
|
import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
|
|
15
16
|
|
|
@@ -1329,7 +1330,7 @@ function createLocalInvokeAgentCoreCommand(opts = {}) {
|
|
|
1329
1330
|
|
|
1330
1331
|
//#endregion
|
|
1331
1332
|
//#region src/local/ecs-network.ts
|
|
1332
|
-
const execFileAsync$
|
|
1333
|
+
const execFileAsync$2 = promisify(execFile);
|
|
1333
1334
|
/**
|
|
1334
1335
|
* Docker network + AWS-published metadata-endpoints sidecar lifecycle for
|
|
1335
1336
|
* `cdkl run-task`. The sidecar (a small Go binary maintained by
|
|
@@ -1453,7 +1454,7 @@ async function sweepOrphanedSvcNetworks(prefix) {
|
|
|
1453
1454
|
const filter = `${prefix}-svc-`;
|
|
1454
1455
|
let names;
|
|
1455
1456
|
try {
|
|
1456
|
-
const { stdout } = await execFileAsync$
|
|
1457
|
+
const { stdout } = await execFileAsync$2(getDockerCmd(), [
|
|
1457
1458
|
"network",
|
|
1458
1459
|
"ls",
|
|
1459
1460
|
"--filter",
|
|
@@ -1471,7 +1472,7 @@ async function sweepOrphanedSvcNetworks(prefix) {
|
|
|
1471
1472
|
for (const name of names) {
|
|
1472
1473
|
let attached;
|
|
1473
1474
|
try {
|
|
1474
|
-
const { stdout } = await execFileAsync$
|
|
1475
|
+
const { stdout } = await execFileAsync$2(getDockerCmd(), [
|
|
1475
1476
|
"network",
|
|
1476
1477
|
"inspect",
|
|
1477
1478
|
name,
|
|
@@ -1506,7 +1507,7 @@ async function createNetworkAndSidecar(args) {
|
|
|
1506
1507
|
await pullImage(METADATA_ENDPOINT_IMAGE, skipPull);
|
|
1507
1508
|
logger.info(`Creating docker network ${networkName} (subnet ${cidr})...`);
|
|
1508
1509
|
try {
|
|
1509
|
-
await execFileAsync$
|
|
1510
|
+
await execFileAsync$2(getDockerCmd(), [
|
|
1510
1511
|
"network",
|
|
1511
1512
|
"create",
|
|
1512
1513
|
"--driver",
|
|
@@ -1541,7 +1542,7 @@ async function createNetworkAndSidecar(args) {
|
|
|
1541
1542
|
sidecarArgs.push(METADATA_ENDPOINT_IMAGE);
|
|
1542
1543
|
logger.info(`Starting ECS local-container-endpoints sidecar at ${sidecarIp}...`);
|
|
1543
1544
|
try {
|
|
1544
|
-
const { stdout } = await execFileAsync$
|
|
1545
|
+
const { stdout } = await execFileAsync$2(getDockerCmd(), sidecarArgs, {
|
|
1545
1546
|
maxBuffer: 10 * 1024 * 1024,
|
|
1546
1547
|
...execEnvForSecrets(sidecarPassthroughEnv)
|
|
1547
1548
|
});
|
|
@@ -1612,7 +1613,7 @@ async function destroyNetworkOnly(networkName) {
|
|
|
1612
1613
|
if (!networkName) return;
|
|
1613
1614
|
const logger = getLogger().child("ecs-network");
|
|
1614
1615
|
try {
|
|
1615
|
-
await execFileAsync$
|
|
1616
|
+
await execFileAsync$2(getDockerCmd(), [
|
|
1616
1617
|
"network",
|
|
1617
1618
|
"rm",
|
|
1618
1619
|
networkName
|
|
@@ -1751,7 +1752,7 @@ async function resolveSsm(entry, shape, client) {
|
|
|
1751
1752
|
|
|
1752
1753
|
//#endregion
|
|
1753
1754
|
//#region src/local/ecs-task-runner.ts
|
|
1754
|
-
const execFileAsync = promisify(execFile);
|
|
1755
|
+
const execFileAsync$1 = promisify(execFile);
|
|
1755
1756
|
/**
|
|
1756
1757
|
* Top-level orchestrator for `cdkl run-task`. Coordinates image
|
|
1757
1758
|
* preparation, secret resolution, docker-network bring-up, container
|
|
@@ -1811,7 +1812,7 @@ async function cleanupEcsRun(state, options) {
|
|
|
1811
1812
|
if (state.network && !state.network.ownedByCaller) await destroyTaskNetwork(state.network);
|
|
1812
1813
|
state.network = void 0;
|
|
1813
1814
|
for (const v of state.dockerVolumeNames) try {
|
|
1814
|
-
await execFileAsync(getDockerCmd(), [
|
|
1815
|
+
await execFileAsync$1(getDockerCmd(), [
|
|
1815
1816
|
"volume",
|
|
1816
1817
|
"rm",
|
|
1817
1818
|
v
|
|
@@ -1892,7 +1893,7 @@ async function runEcsTask(task, options, state) {
|
|
|
1892
1893
|
logger.info(`Starting container '${container.name}' (image=${imagePlan.get(container.name)})`);
|
|
1893
1894
|
let id;
|
|
1894
1895
|
try {
|
|
1895
|
-
const { stdout } = await execFileAsync(getDockerCmd(), args, {
|
|
1896
|
+
const { stdout } = await execFileAsync$1(getDockerCmd(), args, {
|
|
1896
1897
|
maxBuffer: 10 * 1024 * 1024,
|
|
1897
1898
|
...execEnvForSecrets(sensitiveEnv)
|
|
1898
1899
|
});
|
|
@@ -2004,7 +2005,7 @@ async function waitForContainerHealthy(containerId, displayName) {
|
|
|
2004
2005
|
let lastStatus = "";
|
|
2005
2006
|
while (Date.now() < deadline) {
|
|
2006
2007
|
try {
|
|
2007
|
-
const { stdout } = await execFileAsync(getDockerCmd(), [
|
|
2008
|
+
const { stdout } = await execFileAsync$1(getDockerCmd(), [
|
|
2008
2009
|
"inspect",
|
|
2009
2010
|
"--format",
|
|
2010
2011
|
"{{.State.Health.Status}}",
|
|
@@ -2027,7 +2028,7 @@ async function waitForContainerHealthy(containerId, displayName) {
|
|
|
2027
2028
|
}
|
|
2028
2029
|
async function waitForContainerExit(containerId) {
|
|
2029
2030
|
try {
|
|
2030
|
-
const { stdout } = await execFileAsync(getDockerCmd(), ["wait", containerId], { maxBuffer: 1024 * 1024 });
|
|
2031
|
+
const { stdout } = await execFileAsync$1(getDockerCmd(), ["wait", containerId], { maxBuffer: 1024 * 1024 });
|
|
2031
2032
|
const code = Number.parseInt(stdout.trim(), 10);
|
|
2032
2033
|
return Number.isFinite(code) ? code : 1;
|
|
2033
2034
|
} catch (err) {
|
|
@@ -2037,7 +2038,7 @@ async function waitForContainerExit(containerId) {
|
|
|
2037
2038
|
}
|
|
2038
2039
|
async function stopContainer(containerId, graceSeconds) {
|
|
2039
2040
|
try {
|
|
2040
|
-
await execFileAsync(getDockerCmd(), [
|
|
2041
|
+
await execFileAsync$1(getDockerCmd(), [
|
|
2041
2042
|
"stop",
|
|
2042
2043
|
"-t",
|
|
2043
2044
|
String(graceSeconds),
|
|
@@ -2163,7 +2164,7 @@ async function realizeDockerVolumes(volumes, state) {
|
|
|
2163
2164
|
const dockerVolumeName = `${getEmbedConfig().resourceNamePrefix}-${v.name}-${randHex(4)}`;
|
|
2164
2165
|
args.push(dockerVolumeName);
|
|
2165
2166
|
try {
|
|
2166
|
-
await execFileAsync(getDockerCmd(), args);
|
|
2167
|
+
await execFileAsync$1(getDockerCmd(), args);
|
|
2167
2168
|
state.dockerVolumeNames.push(dockerVolumeName);
|
|
2168
2169
|
logger.debug(`Created docker volume ${dockerVolumeName} for task volume '${v.name}'`);
|
|
2169
2170
|
} catch (err) {
|
|
@@ -3707,12 +3708,17 @@ const DEFAULT_UPSTREAM_TIMEOUT_MS = 3e4;
|
|
|
3707
3708
|
async function startFrontDoorServer(opts) {
|
|
3708
3709
|
const logger = getLogger().child("front-door");
|
|
3709
3710
|
const host = opts.host ?? "127.0.0.1";
|
|
3710
|
-
const
|
|
3711
|
+
const requestHandler = (req, res) => {
|
|
3711
3712
|
handleProxyRequest(req, res, opts).catch((err) => {
|
|
3712
3713
|
logger.debug(`front-door request error: ${err instanceof Error ? err.message : String(err)}`);
|
|
3713
3714
|
if (!res.headersSent) writeError(res, 502, "Bad Gateway");
|
|
3714
3715
|
});
|
|
3715
|
-
}
|
|
3716
|
+
};
|
|
3717
|
+
const server = opts.tls ? createServer$1({
|
|
3718
|
+
cert: opts.tls.certPem,
|
|
3719
|
+
key: opts.tls.keyPem
|
|
3720
|
+
}, requestHandler) : createServer(requestHandler);
|
|
3721
|
+
const scheme = opts.tls ? "https" : "http";
|
|
3716
3722
|
server.on("connection", (socket) => socket.setNoDelay(true));
|
|
3717
3723
|
const boundPort = await new Promise((resolve, reject) => {
|
|
3718
3724
|
server.once("error", reject);
|
|
@@ -3729,6 +3735,7 @@ async function startFrontDoorServer(opts) {
|
|
|
3729
3735
|
return {
|
|
3730
3736
|
port: boundPort,
|
|
3731
3737
|
host,
|
|
3738
|
+
scheme,
|
|
3732
3739
|
server,
|
|
3733
3740
|
close: async () => {
|
|
3734
3741
|
if (closed) return;
|
|
@@ -3769,7 +3776,7 @@ function handleProxyRequest(req, res, opts) {
|
|
|
3769
3776
|
if (!action) return reply404(req, res, opts);
|
|
3770
3777
|
if (action.kind === "redirect" || action.kind === "fixed-response") {
|
|
3771
3778
|
req.resume();
|
|
3772
|
-
if (action.kind === "redirect") writeRedirect(res, action, req, opts.listenerPort);
|
|
3779
|
+
if (action.kind === "redirect") writeRedirect(res, action, req, opts.listenerPort, opts.tls ? "https" : "http");
|
|
3773
3780
|
else writeFixedResponse(res, action);
|
|
3774
3781
|
return Promise.resolve();
|
|
3775
3782
|
}
|
|
@@ -3807,7 +3814,7 @@ function handlePoolRequest(req, res, pool, opts) {
|
|
|
3807
3814
|
};
|
|
3808
3815
|
const headers = { ...req.headers };
|
|
3809
3816
|
stripHopByHopHeaders(headers);
|
|
3810
|
-
appendForwardedHeaders(headers, req, opts.listenerPort);
|
|
3817
|
+
appendForwardedHeaders(headers, req, opts.listenerPort, opts.tls ? "https" : "http");
|
|
3811
3818
|
const proxyReq = request({
|
|
3812
3819
|
host: endpoint.host,
|
|
3813
3820
|
port: endpoint.port,
|
|
@@ -3874,8 +3881,8 @@ function pickWeightedTarget(targets) {
|
|
|
3874
3881
|
* `#{query}` placeholders filled from the incoming request. We resolve those
|
|
3875
3882
|
* placeholders against the request the front-door received.
|
|
3876
3883
|
*/
|
|
3877
|
-
function writeRedirect(res, action, req, listenerPort) {
|
|
3878
|
-
const location = buildRedirectLocation(action, req, listenerPort);
|
|
3884
|
+
function writeRedirect(res, action, req, listenerPort, scheme) {
|
|
3885
|
+
const location = buildRedirectLocation(action, req, listenerPort, scheme);
|
|
3879
3886
|
res.writeHead(action.statusCode, {
|
|
3880
3887
|
location,
|
|
3881
3888
|
"content-type": "text/plain; charset=utf-8",
|
|
@@ -3883,15 +3890,20 @@ function writeRedirect(res, action, req, listenerPort) {
|
|
|
3883
3890
|
});
|
|
3884
3891
|
res.end();
|
|
3885
3892
|
}
|
|
3886
|
-
/**
|
|
3887
|
-
|
|
3893
|
+
/**
|
|
3894
|
+
* Build the `Location` URL for a redirect action, resolving ALB `#{...}`
|
|
3895
|
+
* placeholders. `scheme` is the receiving listener's protocol — it sets the
|
|
3896
|
+
* default for `#{protocol}` so an HTTPS listener redirects to `https://...`
|
|
3897
|
+
* by default, matching what a real ALB does.
|
|
3898
|
+
*/
|
|
3899
|
+
function buildRedirectLocation(action, req, listenerPort, scheme = "http") {
|
|
3888
3900
|
const url = req.url ?? "/";
|
|
3889
3901
|
const qIndex = url.indexOf("?");
|
|
3890
3902
|
const reqPath = qIndex === -1 ? url : url.slice(0, qIndex);
|
|
3891
3903
|
const reqQuery = qIndex === -1 ? "" : url.slice(qIndex + 1);
|
|
3892
3904
|
const rawHost = req.headers["host"];
|
|
3893
3905
|
const placeholders = {
|
|
3894
|
-
protocol:
|
|
3906
|
+
protocol: scheme,
|
|
3895
3907
|
host: ((Array.isArray(rawHost) ? rawHost[0] : rawHost) ?? "").split(":")[0] ?? "",
|
|
3896
3908
|
port: String(listenerPort),
|
|
3897
3909
|
path: reqPath.replace(/^\//, ""),
|
|
@@ -3958,7 +3970,7 @@ function handleLambdaRequest(req, res, lambda, opts) {
|
|
|
3958
3970
|
const body = Buffer.concat(chunks);
|
|
3959
3971
|
const forwardHeaders = { ...req.headers };
|
|
3960
3972
|
stripHopByHopHeaders(forwardHeaders);
|
|
3961
|
-
appendForwardedHeaders(forwardHeaders, req, opts.listenerPort);
|
|
3973
|
+
appendForwardedHeaders(forwardHeaders, req, opts.listenerPort, opts.tls ? "https" : "http");
|
|
3962
3974
|
const snapshot = snapshotFromIncoming(req, body);
|
|
3963
3975
|
for (const [name, value] of Object.entries(forwardHeaders)) {
|
|
3964
3976
|
if (value === void 0) continue;
|
|
@@ -4020,14 +4032,15 @@ function stripHopByHopHeaders(headers) {
|
|
|
4020
4032
|
/**
|
|
4021
4033
|
* Inject the ALB-style forwarding headers a downstream app may read. Appends
|
|
4022
4034
|
* the client IP to any existing `X-Forwarded-For` chain (ALB appends rather
|
|
4023
|
-
* than replaces) and stamps the scheme / listener port.
|
|
4035
|
+
* than replaces) and stamps the scheme / listener port. `scheme` follows the
|
|
4036
|
+
* listener's protocol so an HTTPS listener stamps `x-forwarded-proto: https`.
|
|
4024
4037
|
*/
|
|
4025
|
-
function appendForwardedHeaders(headers, req, listenerPort) {
|
|
4038
|
+
function appendForwardedHeaders(headers, req, listenerPort, scheme) {
|
|
4026
4039
|
const clientIp = req.socket.remoteAddress ?? "";
|
|
4027
4040
|
const existing = headers["x-forwarded-for"];
|
|
4028
4041
|
const chain = Array.isArray(existing) ? existing.join(", ") : existing;
|
|
4029
4042
|
headers["x-forwarded-for"] = chain ? `${chain}, ${clientIp}` : clientIp;
|
|
4030
|
-
headers["x-forwarded-proto"] =
|
|
4043
|
+
headers["x-forwarded-proto"] = scheme;
|
|
4031
4044
|
headers["x-forwarded-port"] = String(listenerPort);
|
|
4032
4045
|
}
|
|
4033
4046
|
function writeError(res, statusCode, message) {
|
|
@@ -4345,6 +4358,129 @@ function matchBitPrefix(a, b, prefixLength) {
|
|
|
4345
4358
|
return (a[fullBytes] & mask) === (b[fullBytes] & mask);
|
|
4346
4359
|
}
|
|
4347
4360
|
|
|
4361
|
+
//#endregion
|
|
4362
|
+
//#region src/local/front-door-tls.ts
|
|
4363
|
+
const execFileAsync = promisify(execFile);
|
|
4364
|
+
/**
|
|
4365
|
+
* Resolve a single global cert/key pair for HTTPS front-door listeners. When
|
|
4366
|
+
* BOTH `certPath` and `keyPath` are supplied, those PEM files are read from
|
|
4367
|
+
* disk. When neither is supplied, a long-lived self-signed cert is cached
|
|
4368
|
+
* under `$XDG_CACHE_HOME/cdk-local/alb-https/` (defaulting to
|
|
4369
|
+
* `~/.cache/cdk-local/alb-https/`) and reused across boots; it is regenerated
|
|
4370
|
+
* when missing or within `regenerateWithinDays` of expiry. Pairing is enforced
|
|
4371
|
+
* — supplying exactly one of `--tls-cert` / `--tls-key` is rejected with a
|
|
4372
|
+
* clear error before the server starts.
|
|
4373
|
+
*
|
|
4374
|
+
* The self-signed cert path subprocesses `openssl req -x509 ...`. `openssl`
|
|
4375
|
+
* is on macOS / Linux dev boxes and Docker base images by default; absence
|
|
4376
|
+
* surfaces as an actionable error pointing at the BYO recipe.
|
|
4377
|
+
*/
|
|
4378
|
+
async function resolveFrontDoorTlsMaterials(opts) {
|
|
4379
|
+
const hasCert = opts.certPath !== void 0 && opts.certPath !== "";
|
|
4380
|
+
const hasKey = opts.keyPath !== void 0 && opts.keyPath !== "";
|
|
4381
|
+
if (hasCert !== hasKey) throw new Error(`${hasCert ? "--tls-cert" : "--tls-key"} is set but ${hasCert ? "--tls-key" : "--tls-cert"} is missing. Both --tls-cert and --tls-key must be set together, or both left unset to use an auto-generated self-signed cert.`);
|
|
4382
|
+
if (hasCert && hasKey) return {
|
|
4383
|
+
certPem: readPemOrThrow(opts.certPath, "--tls-cert"),
|
|
4384
|
+
keyPem: readPemOrThrow(opts.keyPath, "--tls-key")
|
|
4385
|
+
};
|
|
4386
|
+
return ensureSelfSignedCert({
|
|
4387
|
+
cacheDir: opts.cacheDir ?? defaultCacheDir(),
|
|
4388
|
+
regenerateWithinDays: opts.regenerateWithinDays ?? 30,
|
|
4389
|
+
validityDays: opts.validityDays ?? 825
|
|
4390
|
+
});
|
|
4391
|
+
}
|
|
4392
|
+
/** Default cache dir for the auto-generated self-signed cert. */
|
|
4393
|
+
function defaultCacheDir() {
|
|
4394
|
+
const xdg = process.env["XDG_CACHE_HOME"];
|
|
4395
|
+
return join(xdg && xdg !== "" ? xdg : join(homedir(), ".cache"), "cdk-local", "alb-https");
|
|
4396
|
+
}
|
|
4397
|
+
const CERT_FILENAME = "cert.pem";
|
|
4398
|
+
const KEY_FILENAME = "key.pem";
|
|
4399
|
+
async function ensureSelfSignedCert(opts) {
|
|
4400
|
+
const logger = getLogger().child("front-door-tls");
|
|
4401
|
+
const certPath = join(opts.cacheDir, CERT_FILENAME);
|
|
4402
|
+
const keyPath = join(opts.cacheDir, KEY_FILENAME);
|
|
4403
|
+
if (cachedPairIsFresh(certPath, keyPath, opts.regenerateWithinDays)) return {
|
|
4404
|
+
certPem: readFileSync(certPath),
|
|
4405
|
+
keyPem: readFileSync(keyPath)
|
|
4406
|
+
};
|
|
4407
|
+
mkdirSync(opts.cacheDir, { recursive: true });
|
|
4408
|
+
try {
|
|
4409
|
+
unlinkSync(certPath);
|
|
4410
|
+
} catch {}
|
|
4411
|
+
try {
|
|
4412
|
+
unlinkSync(keyPath);
|
|
4413
|
+
} catch {}
|
|
4414
|
+
try {
|
|
4415
|
+
await runOpenssl([
|
|
4416
|
+
"req",
|
|
4417
|
+
"-x509",
|
|
4418
|
+
"-newkey",
|
|
4419
|
+
"rsa:2048",
|
|
4420
|
+
"-nodes",
|
|
4421
|
+
"-keyout",
|
|
4422
|
+
keyPath,
|
|
4423
|
+
"-out",
|
|
4424
|
+
certPath,
|
|
4425
|
+
"-subj",
|
|
4426
|
+
"/CN=localhost",
|
|
4427
|
+
"-days",
|
|
4428
|
+
String(opts.validityDays),
|
|
4429
|
+
"-addext",
|
|
4430
|
+
"subjectAltName=DNS:localhost,IP:127.0.0.1"
|
|
4431
|
+
]);
|
|
4432
|
+
} catch (err) {
|
|
4433
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4434
|
+
throw new Error(`Failed to auto-generate a self-signed cert via openssl: ${msg}. Install openssl, or supply --tls-cert <path> + --tls-key <path> with your own PEM files. Recipe: openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -subj "/CN=localhost" -days 365.`);
|
|
4435
|
+
}
|
|
4436
|
+
logger.info(`ALB front-door: generated self-signed cert at ${certPath} (valid ${opts.validityDays} days)`);
|
|
4437
|
+
return {
|
|
4438
|
+
certPem: readFileSync(certPath),
|
|
4439
|
+
keyPem: readFileSync(keyPath)
|
|
4440
|
+
};
|
|
4441
|
+
}
|
|
4442
|
+
function cachedPairIsFresh(certPath, keyPath, regenWithinDays) {
|
|
4443
|
+
try {
|
|
4444
|
+
statSync(certPath);
|
|
4445
|
+
statSync(keyPath);
|
|
4446
|
+
} catch {
|
|
4447
|
+
return false;
|
|
4448
|
+
}
|
|
4449
|
+
try {
|
|
4450
|
+
const notAfter = readCertNotAfter(certPath);
|
|
4451
|
+
if (notAfter === void 0) return false;
|
|
4452
|
+
const renewAt = notAfter.getTime() - regenWithinDays * 864e5;
|
|
4453
|
+
return Date.now() < renewAt;
|
|
4454
|
+
} catch {
|
|
4455
|
+
return false;
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4458
|
+
/**
|
|
4459
|
+
* Read a cert's `notAfter` expiry timestamp. Used to decide whether to
|
|
4460
|
+
* regenerate the cached self-signed cert proactively. Returns `undefined`
|
|
4461
|
+
* when the cert is unreadable (caller then regenerates).
|
|
4462
|
+
*/
|
|
4463
|
+
function readCertNotAfter(certPath) {
|
|
4464
|
+
try {
|
|
4465
|
+
const cert = new X509Certificate(readFileSync(certPath));
|
|
4466
|
+
const parsed = new Date(cert.validTo);
|
|
4467
|
+
return Number.isNaN(parsed.getTime()) ? void 0 : parsed;
|
|
4468
|
+
} catch {
|
|
4469
|
+
return;
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
async function runOpenssl(args) {
|
|
4473
|
+
await execFileAsync("openssl", args, { timeout: 3e4 });
|
|
4474
|
+
}
|
|
4475
|
+
function readPemOrThrow(path, flagName) {
|
|
4476
|
+
try {
|
|
4477
|
+
return readFileSync(path);
|
|
4478
|
+
} catch (err) {
|
|
4479
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4480
|
+
throw new Error(`${flagName}: cannot read PEM file at '${path}': ${msg}`);
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
|
|
4348
4484
|
//#endregion
|
|
4349
4485
|
//#region src/local/front-door-lambda-runner.ts
|
|
4350
4486
|
/** Forward the dev shell's AWS credential / region env into the Lambda container. */
|
|
@@ -4829,6 +4965,10 @@ async function buildFrontDoor(plan, options, logger) {
|
|
|
4829
4965
|
...action.messageBody !== void 0 && { messageBody: action.messageBody }
|
|
4830
4966
|
};
|
|
4831
4967
|
};
|
|
4968
|
+
const tlsMaterials = plan.listeners.some((l) => l.protocol === "HTTPS") ? await resolveFrontDoorTlsMaterials({
|
|
4969
|
+
certPath: options.tlsCert,
|
|
4970
|
+
keyPath: options.tlsKey
|
|
4971
|
+
}) : void 0;
|
|
4832
4972
|
try {
|
|
4833
4973
|
for (const listener of plan.listeners) {
|
|
4834
4974
|
const defaultRoute = listener.defaultAction ? toRouteAction(listener.defaultAction) : void 0;
|
|
@@ -4843,15 +4983,17 @@ async function buildFrontDoor(plan, options, logger) {
|
|
|
4843
4983
|
target: toRouteAction(r.action)
|
|
4844
4984
|
}));
|
|
4845
4985
|
const route = (req) => matchAlbPathRule(req, ruleRoutes) ?? defaultRoute;
|
|
4986
|
+
const tls = listener.protocol === "HTTPS" ? tlsMaterials : void 0;
|
|
4846
4987
|
const server = await startFrontDoorServer({
|
|
4847
4988
|
route,
|
|
4848
4989
|
port: listener.hostPort,
|
|
4849
4990
|
host: containerHost,
|
|
4850
4991
|
listenerPort: listener.listenerPort,
|
|
4851
|
-
label: `listener port ${listener.listenerPort}
|
|
4992
|
+
label: `listener port ${listener.listenerPort}`,
|
|
4993
|
+
...tls ? { tls } : {}
|
|
4852
4994
|
});
|
|
4853
4995
|
servers.push(server);
|
|
4854
|
-
logger.info(`ALB front-door:
|
|
4996
|
+
logger.info(`ALB front-door: ${server.scheme}://${server.host}:${server.port} (listener port ${listener.listenerPort})`);
|
|
4855
4997
|
if (listener.defaultAction) logger.info(` default -> ${describeAction(listener.defaultAction)}`);
|
|
4856
4998
|
for (const r of [...listener.rules].sort((a, b) => a.priority - b.priority)) logger.info(` ${describeConditions(r)} (priority ${r.priority}) -> ${describeAction(r.action)}`);
|
|
4857
4999
|
if (!listener.defaultAction) logger.info(" (no default action: unmatched requests return 404)");
|
|
@@ -5153,8 +5295,12 @@ function createLocalStartServiceCommand(opts = {}) {
|
|
|
5153
5295
|
* functions (`TargetType:"lambda"` target groups — #123: the TG -> backing
|
|
5154
5296
|
* `AWS::Lambda::Function` is resolved and the front-door invokes it locally per
|
|
5155
5297
|
* request); `redirect` / `fixed-response` actions. A single weighted forward may
|
|
5156
|
-
* mix ECS and Lambda targets.
|
|
5157
|
-
*
|
|
5298
|
+
* mix ECS and Lambda targets. HTTPS listeners are served — local TLS
|
|
5299
|
+
* termination uses a user-supplied or auto-generated self-signed cert/key
|
|
5300
|
+
* pair (the deployed `Listener.Certificates[]` ACM ARNs are not fetched,
|
|
5301
|
+
* because ACM private keys are not retrievable by design). Skipped with a
|
|
5302
|
+
* warning: TLS listeners (NLB-style, not ALB) and `authenticate-cognito` /
|
|
5303
|
+
* `authenticate-oidc` actions.
|
|
5158
5304
|
*/
|
|
5159
5305
|
const ALB_TYPE = "AWS::ElasticLoadBalancingV2::LoadBalancer";
|
|
5160
5306
|
const LISTENER_TYPE = "AWS::ElasticLoadBalancingV2::Listener";
|
|
@@ -5181,10 +5327,11 @@ function resolveAlbFrontDoor(stack, albLogicalId) {
|
|
|
5181
5327
|
const port = parsePort(props["Port"]);
|
|
5182
5328
|
if (port === void 0) continue;
|
|
5183
5329
|
const protocol = typeof props["Protocol"] === "string" ? props["Protocol"] : "HTTP";
|
|
5184
|
-
if (protocol !== "HTTP") {
|
|
5185
|
-
warnings.push(`Listener '${listenerLogicalId}' on port ${port} uses protocol ${protocol}; the local ALB front-door supports HTTP listeners only (TLS
|
|
5330
|
+
if (protocol !== "HTTP" && protocol !== "HTTPS") {
|
|
5331
|
+
warnings.push(`Listener '${listenerLogicalId}' on port ${port} uses protocol ${protocol}; the local ALB front-door supports HTTP and HTTPS listeners only (TLS / NLB-style listeners are not served). Skipping it.`);
|
|
5186
5332
|
continue;
|
|
5187
5333
|
}
|
|
5334
|
+
if (protocol === "HTTPS" && Array.isArray(props["Certificates"]) && props["Certificates"].length > 0) warnings.push(`Listener '${listenerLogicalId}' on port ${port} declares ACM Certificates which are not retrievable locally. The front-door terminates TLS with --tls-cert/--tls-key or an auto-generated self-signed cert.`);
|
|
5188
5335
|
const defaultAction = resolveAction(props["DefaultActions"], resources, tgToService, stackName, `Listener '${listenerLogicalId}' (port ${port}) default action`, warnings);
|
|
5189
5336
|
const rules = [];
|
|
5190
5337
|
for (const { ruleLogicalId, ruleProps } of rulesByListener.get(listenerLogicalId) ?? []) {
|
|
@@ -5210,7 +5357,7 @@ function resolveAlbFrontDoor(stack, albLogicalId) {
|
|
|
5210
5357
|
if (!defaultAction && rules.length === 0) continue;
|
|
5211
5358
|
listeners.push({
|
|
5212
5359
|
listenerPort: port,
|
|
5213
|
-
listenerProtocol:
|
|
5360
|
+
listenerProtocol: protocol,
|
|
5214
5361
|
listenerLogicalId,
|
|
5215
5362
|
...defaultAction ? { defaultAction } : {},
|
|
5216
5363
|
rules
|
|
@@ -5800,6 +5947,7 @@ function albStrategy(options) {
|
|
|
5800
5947
|
listeners.push({
|
|
5801
5948
|
listenerPort: listener.listenerPort,
|
|
5802
5949
|
hostPort,
|
|
5950
|
+
protocol: listener.listenerProtocol,
|
|
5803
5951
|
...listener.defaultAction ? { defaultAction: qualify(listener.defaultAction) } : {},
|
|
5804
5952
|
rules: listener.rules.map((r) => ({
|
|
5805
5953
|
priority: r.priority,
|
|
@@ -5838,7 +5986,7 @@ function albStrategy(options) {
|
|
|
5838
5986
|
*/
|
|
5839
5987
|
function createLocalStartAlbCommand(opts = {}) {
|
|
5840
5988
|
setEmbedConfig(opts.embedConfig);
|
|
5841
|
-
return addCommonEcsServiceOptions(new Command("start-alb").description("Run an Application Load Balancer locally: name the ALB, and cdk-local boots the ECS service(s) behind its
|
|
5989
|
+
return addCommonEcsServiceOptions(new Command("start-alb").description("Run an Application Load Balancer locally: name the ALB, and cdk-local boots the ECS service(s) behind its listeners and stands up a local front-door on each listener port that round-robins across the running replicas and routes its listener rules across the backing services — a stable host endpoint, like behind a real load balancer. The symmetric ALB counterpart of `start-api`. Each <target> accepts a CDK display path (MyStack/MyAlb) or stack-qualified logical ID; single-stack apps may omit the stack prefix. Supports HTTP and HTTPS listeners (TLS terminated locally with --tls-cert/--tls-key or an auto-generated self-signed cert); all six ALB rule-condition fields (path-pattern / host-header / http-header / http-request-method / query-string / source-ip); forward (single and weighted), redirect, and fixed-response actions; and ECS or Lambda targets (a Lambda target group is invoked locally via the Lambda RIE). authenticate-cognito / authenticate-oidc actions are skipped with a warning. Omit <targets> in an interactive terminal to multi-select the load balancers from a list.").argument("[targets...]", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ElasticLoadBalancingV2::LoadBalancer resources to run (omit to multi-select interactively in a TTY)").addOption(new Option("--lb-port <listenerPort=hostPort...>", "Bind the local front-door on a specific host port (e.g. 80=8080); repeatable. Default: host port == ALB listener port. Use this on macOS to remap a privileged listener port (< 1024) to a non-privileged host port.")).addOption(new Option("--tls-cert <path>", "PEM-encoded server certificate for HTTPS front-door listeners. Must be set together with --tls-key. Omit both flags to auto-generate a self-signed cert (cached under $XDG_CACHE_HOME/cdk-local/alb-https/, default ~/.cache/cdk-local/alb-https/); requires openssl on PATH. The deployed Listener Certificates[] are NOT fetched (ACM private keys are not retrievable by design). The auto-generated cert lists DNS:localhost,IP:127.0.0.1 as SubjectAltName, so a client validating a non-loopback --container-host will fail the SAN check — pass --tls-cert / --tls-key with a SAN covering that host instead.")).addOption(new Option("--tls-key <path>", "PEM-encoded server private key matching --tls-cert. Must be set together with --tls-cert.")).action(withErrorHandling(async (targets, options) => {
|
|
5842
5990
|
await runEcsServiceEmulator(targets, options, albStrategy(options), opts.extraStateProviders);
|
|
5843
5991
|
})));
|
|
5844
5992
|
}
|
|
@@ -5915,4 +6063,4 @@ function createLocalListCommand(opts = {}) {
|
|
|
5915
6063
|
|
|
5916
6064
|
//#endregion
|
|
5917
6065
|
export { createLocalRunTaskCommand as a, createLocalStartServiceCommand as i, formatTargetListing as n, createLocalInvokeAgentCoreCommand as o, createLocalStartAlbCommand as r, createLocalInvokeCommand as s, createLocalListCommand as t };
|
|
5918
|
-
//# sourceMappingURL=local-list-
|
|
6066
|
+
//# sourceMappingURL=local-list-DT8qRbKy.js.map
|