@go-to-k/cdkd 0.147.0 → 0.147.2
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/cli.js +121 -30
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-DjnWyAAc.js → deploy-engine-CRxNgcqJ.js} +47 -2
- package/dist/deploy-engine-CRxNgcqJ.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-DjnWyAAc.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { _ as withSkipPrefix, a as runDockerStreaming, c as getLogger, d as getLiveRenderer, f as PATTERN_B_NAME_PROPERTIES, g as generateResourceNameWithFallback, h as generateResourceName, i as runDockerForeground, n as formatDockerLoginError, p as PATTERN_B_RESOURCE_TYPES, r as getDockerCmd, u as runStackBuffered, v as withStackName } from "./docker-cmd-EtWSTAje.js";
|
|
3
|
-
import { A as AssetPublisher, B as resolveStateBucketWithDefault, C as applyRoleArnIfSet, D as LockManager, E as TemplateParser, F as getDefaultStateBucketName, G as MIGRATE_TMP_PREFIX, H as warnDeprecatedNoPrefixCliFlag, I as getLegacyStateBucketName, J as AssemblyReader, K as findLargeInlineResources, L as resolveApp, M as WorkGraph, N as buildDockerImage, O as S3StateBackend, P as Synthesizer, Q as CdkdError, R as resolveCaptureObservedState, S as IntrinsicFunctionResolver, T as DagBuilder, U as CFN_TEMPLATE_BODY_LIMIT, V as resolveStateBucketWithDefaultAndSource, W as CFN_TEMPLATE_URL_LIMIT, X as resolveBucketRegion, _ as normalizeAwsTagsToCfn,
|
|
3
|
+
import { A as AssetPublisher, B as resolveStateBucketWithDefault, C as applyRoleArnIfSet, D as LockManager, E as TemplateParser, F as getDefaultStateBucketName, G as MIGRATE_TMP_PREFIX, H as warnDeprecatedNoPrefixCliFlag, I as getLegacyStateBucketName, J as AssemblyReader, K as findLargeInlineResources, L as resolveApp, M as WorkGraph, N as buildDockerImage, O as S3StateBackend, P as Synthesizer, Q as CdkdError, R as resolveCaptureObservedState, S as IntrinsicFunctionResolver, T as DagBuilder, U as CFN_TEMPLATE_BODY_LIMIT, V as resolveStateBucketWithDefaultAndSource, W as CFN_TEMPLATE_URL_LIMIT, X as resolveBucketRegion, _ as normalizeAwsTagsToCfn, a as withRetry, at as MissingCdkCliError, b as CloudControlProvider, c as cyan, ct as ProvisioningError, d as red, dt as RouteDiscoveryError, f as yellow, ft as StackHasActiveImportsError, g as matchesCdkPath, h as CDK_PATH_TAG, i as withResourceDeadline, j as stringifyValue, k as shouldRetainResource, l as gray, lt as ResourceTimeoutError, m as collectInlinePolicyNamesManagedBySiblings, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as LocalMigrateError, o as IMPLICIT_DELETE_DEPENDENCIES, ot as NestedStackChildDirectDestroyError, p as IAMRoleProvider, pt as StackTerminationProtectionError, q as uploadCfnTemplate, r as DeployEngine, rt as LocalStartServiceError, s as bold, st as PartialFailureError, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as LocalInvokeBuildError, u as green, ut as ResourceUpdateNotSupportedError, v as resolveExplicitPhysicalId, vt as normalizeAwsError, w as DiffCalculator, x as assertRegionMatch, y as ProviderRegistry, yt as withErrorHandling, z as resolveSkipPrefix } from "./deploy-engine-CRxNgcqJ.js";
|
|
4
4
|
import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-BF03Alpe.js";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { createHash, createHmac, createPublicKey, createVerify, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
@@ -33592,6 +33592,7 @@ async function destroyCommand(stackArgs, options) {
|
|
|
33592
33592
|
}
|
|
33593
33593
|
} else throw new Error("Could not determine which stacks belong to this app. Specify stack names explicitly, use --all, or ensure --app / cdk.json is configured.");
|
|
33594
33594
|
const stackPatterns = stackArgs.length > 0 ? stackArgs : options.stack ? [options.stack] : [];
|
|
33595
|
+
let totalErrors = 0;
|
|
33595
33596
|
let stackNames;
|
|
33596
33597
|
if (options.all) stackNames = candidateStacks.map((s) => s.stackName);
|
|
33597
33598
|
else if (stackPatterns.length > 0) stackNames = matchStacks(candidateStacks, stackPatterns).map((s) => s.stackName);
|
|
@@ -33601,6 +33602,21 @@ async function destroyCommand(stackArgs, options) {
|
|
|
33601
33602
|
return;
|
|
33602
33603
|
} else throw new Error(`Multiple stacks found: ${candidateStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all`);
|
|
33603
33604
|
if (stackNames.length === 0) {
|
|
33605
|
+
if (stackPatterns.length > 0) {
|
|
33606
|
+
const allStateNamesSet = new Set(allStateRefs.map((r) => r.stackName));
|
|
33607
|
+
for (const pattern of stackPatterns) {
|
|
33608
|
+
if (pattern.includes("*") || pattern.includes("?") || pattern.includes("/")) continue;
|
|
33609
|
+
if (!allStateNamesSet.has(pattern)) continue;
|
|
33610
|
+
const refRegion = allStateRefs.find((r) => r.stackName === pattern)?.region ?? region;
|
|
33611
|
+
const stateOnlyResult = await stateBackend.getState(pattern, refRegion);
|
|
33612
|
+
if (stateOnlyResult?.state.parentStack) {
|
|
33613
|
+
const err = new NestedStackChildDirectDestroyError(pattern, stateOnlyResult.state.parentStack, stateOnlyResult.state.parentLogicalId);
|
|
33614
|
+
logger.error(` ✗ ${err.message}`);
|
|
33615
|
+
totalErrors++;
|
|
33616
|
+
}
|
|
33617
|
+
}
|
|
33618
|
+
if (totalErrors > 0) throw new PartialFailureError(`Destroy completed with ${totalErrors} resource error(s). State preserved — inspect 'cdkd state show <stack>' and re-run 'cdkd destroy' to retry.`);
|
|
33619
|
+
}
|
|
33604
33620
|
logger.info("No matching stacks found in state");
|
|
33605
33621
|
return;
|
|
33606
33622
|
}
|
|
@@ -33612,7 +33628,6 @@ async function destroyCommand(stackArgs, options) {
|
|
|
33612
33628
|
arr.push(ref);
|
|
33613
33629
|
stateRefsByName.set(ref.stackName, arr);
|
|
33614
33630
|
}
|
|
33615
|
-
let totalErrors = 0;
|
|
33616
33631
|
for (const stackName of stackNames) {
|
|
33617
33632
|
logger.info(`\nPreparing to destroy stack: ${stackName}`);
|
|
33618
33633
|
const refs = stateRefsByName.get(stackName) ?? [];
|
|
@@ -33643,6 +33658,12 @@ async function destroyCommand(stackArgs, options) {
|
|
|
33643
33658
|
logger.warn(`No state found for stack ${stackName}, skipping`);
|
|
33644
33659
|
continue;
|
|
33645
33660
|
}
|
|
33661
|
+
if (stateResult.state.parentStack) {
|
|
33662
|
+
const err = new NestedStackChildDirectDestroyError(stackName, stateResult.state.parentStack, stateResult.state.parentLogicalId);
|
|
33663
|
+
logger.error(` ✗ ${err.message}`);
|
|
33664
|
+
totalErrors++;
|
|
33665
|
+
continue;
|
|
33666
|
+
}
|
|
33646
33667
|
const result = await withNestedStackContext({
|
|
33647
33668
|
stateBackend,
|
|
33648
33669
|
lockManager,
|
|
@@ -36626,9 +36647,14 @@ async function injectRetainPoliciesRecursiveInternal(templateBody, cfnStackName,
|
|
|
36626
36647
|
};
|
|
36627
36648
|
}
|
|
36628
36649
|
/**
|
|
36629
|
-
*
|
|
36630
|
-
* the full
|
|
36631
|
-
* `
|
|
36650
|
+
* Walk a CloudFormation stack and every nested child recursively, returning
|
|
36651
|
+
* the full {@link CfnStackResourceTree} rooted at `rootStackName`. Used by
|
|
36652
|
+
* `cdkd import --migrate-from-cloudformation` to drive BOTH per-child state
|
|
36653
|
+
* writes AND the recursive `injectRetainPoliciesRecursive` walk — both
|
|
36654
|
+
* consumers share this single `DescribeStackResources` tree so an
|
|
36655
|
+
* arbitrarily-deep nesting only costs one round-trip per stack.
|
|
36656
|
+
*
|
|
36657
|
+
* For each `AWS::CloudFormation::Stack` row in the root's resources, recursively
|
|
36632
36658
|
* calls `DescribeStackResources(<child ARN>)` (AWS accepts the ARN as
|
|
36633
36659
|
* `StackName`) to populate the child node, and so on to arbitrary depth.
|
|
36634
36660
|
*
|
|
@@ -39477,6 +39503,7 @@ function extractTaskDefinitionProperties(stack, logicalId, resource, context) {
|
|
|
39477
39503
|
if (rawNetworkMode === "bridge" || rawNetworkMode === "awsvpc" || rawNetworkMode === "host" || rawNetworkMode === "none") networkMode = rawNetworkMode;
|
|
39478
39504
|
else throw new EcsTaskResolutionError(`Task definition '${logicalId}' has unsupported NetworkMode '${rawNetworkMode}'. Supported values: bridge / awsvpc / host / none.`);
|
|
39479
39505
|
if (networkMode === "awsvpc") warnings.push(`NetworkMode 'awsvpc' on '${logicalId}' is mapped to docker bridge locally — docker cannot emulate ENI-per-task. AWS SDK calls still reach public endpoints via the developer network.`);
|
|
39506
|
+
if (props["ProxyConfiguration"]) warnings.push(`Task definition '${logicalId}' declares 'ProxyConfiguration' (custom Envoy / App Mesh bootstrap), which is NOT honored by 'cdkd local start-service' / 'cdkd local run-task' in v1. Local execution will run without the configured proxy. See design doc § 2 for the rationale.`);
|
|
39480
39507
|
const resources = stack.template.Resources ?? {};
|
|
39481
39508
|
const subContext = buildSubstitutionContextFromImageContext(context);
|
|
39482
39509
|
const taskRoleArn = resolveRoleArn(props["TaskRoleArn"], resources, subContext);
|
|
@@ -52045,7 +52072,7 @@ function extractServiceProperties(stack, serviceLogicalId, resource, stacks, con
|
|
|
52045
52072
|
desiredCount,
|
|
52046
52073
|
healthCheckGracePeriodSeconds,
|
|
52047
52074
|
task,
|
|
52048
|
-
serviceRegistries: extractServiceRegistries(props["ServiceRegistries"]),
|
|
52075
|
+
serviceRegistries: extractServiceRegistries(props["ServiceRegistries"], serviceLogicalId, warnings),
|
|
52049
52076
|
warnings
|
|
52050
52077
|
};
|
|
52051
52078
|
if (serviceConnect) out.serviceConnect = serviceConnect;
|
|
@@ -52118,8 +52145,16 @@ function extractServiceConnect(raw, task) {
|
|
|
52118
52145
|
* canonical `Fn::GetAtt: [<CloudMapServiceLogicalId>, 'Arn']` shape;
|
|
52119
52146
|
* cdkd surfaces the logical id (the AWS-side ARN is irrelevant
|
|
52120
52147
|
* locally — the registry is in-process).
|
|
52148
|
+
*
|
|
52149
|
+
* Issue #544 — entries with a literal-string `RegistryArn` (rare
|
|
52150
|
+
* locally — would imply the user bound to an existing Cloud Map
|
|
52151
|
+
* service deployed out-of-band) are skipped with a warning, since the
|
|
52152
|
+
* in-process registry cannot resolve an external Cloud Map service
|
|
52153
|
+
* back to its `(namespace, name)` pair. Pre-fix this was a silent
|
|
52154
|
+
* `continue` and the user got no feedback about why the registration
|
|
52155
|
+
* didn't show up.
|
|
52121
52156
|
*/
|
|
52122
|
-
function extractServiceRegistries(raw) {
|
|
52157
|
+
function extractServiceRegistries(raw, serviceLogicalId, warnings) {
|
|
52123
52158
|
if (!Array.isArray(raw)) return [];
|
|
52124
52159
|
const out = [];
|
|
52125
52160
|
for (const entry of raw) {
|
|
@@ -52127,7 +52162,10 @@ function extractServiceRegistries(raw) {
|
|
|
52127
52162
|
const e = entry;
|
|
52128
52163
|
const registryArn = e["RegistryArn"];
|
|
52129
52164
|
let cloudMapServiceLogicalId;
|
|
52130
|
-
if (typeof registryArn === "string")
|
|
52165
|
+
if (typeof registryArn === "string") {
|
|
52166
|
+
warnings.push(`ECS Service '${serviceLogicalId}' ServiceRegistries[] entry has a literal-string RegistryArn ('${registryArn}'); cdkd cannot resolve external Cloud Map services locally. Skipping this registration; peer services will not discover this endpoint through the in-process registry. Use Fn::GetAtt: [<CloudMapServiceLogicalId>, "Arn"] instead so cdkd can resolve the namespace + service name from the synthesized template.`);
|
|
52167
|
+
continue;
|
|
52168
|
+
}
|
|
52131
52169
|
if (registryArn && typeof registryArn === "object" && !Array.isArray(registryArn)) {
|
|
52132
52170
|
const getAtt = registryArn["Fn::GetAtt"];
|
|
52133
52171
|
if (Array.isArray(getAtt) && typeof getAtt[0] === "string") cloudMapServiceLogicalId = getAtt[0];
|
|
@@ -52308,6 +52346,38 @@ function backoffDelayMs(restartCount) {
|
|
|
52308
52346
|
return Math.min(1e3 * Math.pow(2, Math.min(restartCount, 10)), 3e4);
|
|
52309
52347
|
}
|
|
52310
52348
|
/**
|
|
52349
|
+
* Maximum number of replica indices the per-replica subnet allocator
|
|
52350
|
+
* can serve without modulo-wrap collision. The allocator below walks
|
|
52351
|
+
* the link-local /24 range `169.254.170.0..169.254.253.0` (84 octets)
|
|
52352
|
+
* and **skips 171** because that octet is owned by the shared-service
|
|
52353
|
+
* network in design § 5 Option A (see `SHARED_SVC_SUBNET_OCTET`), so
|
|
52354
|
+
* the usable count is 83. The CLI's `--max-tasks` parser enforces this
|
|
52355
|
+
* cap before any boot work fires.
|
|
52356
|
+
*/
|
|
52357
|
+
const SUBNET_ALLOCATOR_RANGE = 83;
|
|
52358
|
+
/**
|
|
52359
|
+
* Defensive per-replica subnet octet allocator (Issue #544). Only used
|
|
52360
|
+
* when callers bypass the CLI's `sharedNetwork` construction — i.e.
|
|
52361
|
+
* test paths that hand-build `ServiceRunnerOptions.discovery` without
|
|
52362
|
+
* `sharedNetwork`, or the bare `cdkd local run-task`-shaped path that
|
|
52363
|
+
* runs one network per task. Production `cdkd local start-service`
|
|
52364
|
+
* runs always go through the shared network (design § 5 Option A) so
|
|
52365
|
+
* this allocator is unreachable in the standard path.
|
|
52366
|
+
*
|
|
52367
|
+
* Returns the second-from-last octet of the per-replica /24 (170 →
|
|
52368
|
+
* `169.254.170.0/24`). Walks the 83-slot output range
|
|
52369
|
+
* `[170, 172, 173, ..., 253]` — 171 is intentionally **skipped**
|
|
52370
|
+
* because it's reserved for the shared-service network sidecar
|
|
52371
|
+
* (`SHARED_SVC_SUBNET_OCTET`), and assigning a per-replica network
|
|
52372
|
+
* the same /24 would have docker reject the duplicate-subnet
|
|
52373
|
+
* `network create` with the cryptic "Pool overlaps with other one on
|
|
52374
|
+
* this address space" error.
|
|
52375
|
+
*/
|
|
52376
|
+
function pickSubnetOctet(index) {
|
|
52377
|
+
const candidate = 170 + (index % 83 + 83) % 83;
|
|
52378
|
+
return candidate < 171 ? candidate : candidate + 1;
|
|
52379
|
+
}
|
|
52380
|
+
/**
|
|
52311
52381
|
* Decide whether a replica that just exited should restart. Pure-
|
|
52312
52382
|
* functional so the watcher loop's policy is easy to unit-test.
|
|
52313
52383
|
*/
|
|
@@ -52486,7 +52556,7 @@ async function bootReplica(service, options, instance) {
|
|
|
52486
52556
|
...options.taskOptions,
|
|
52487
52557
|
cluster: perReplicaCluster,
|
|
52488
52558
|
detach: true,
|
|
52489
|
-
...sharedNetwork ? { existingNetwork: sharedNetwork } : { subnetOctet:
|
|
52559
|
+
...sharedNetwork ? { existingNetwork: sharedNetwork } : { subnetOctet: pickSubnetOctet(instance.index) },
|
|
52490
52560
|
...addHostFlags.length > 0 ? { addHostFlags } : {},
|
|
52491
52561
|
...networkAliasesByContainer.size > 0 ? { networkAliasesByContainer } : {}
|
|
52492
52562
|
};
|
|
@@ -52688,8 +52758,13 @@ var CloudMapRegistry = class {
|
|
|
52688
52758
|
/**
|
|
52689
52759
|
* Map<alias, fqdn> — secondary index for ClientAlias short forms.
|
|
52690
52760
|
* Multiple aliases can point at the same fqdn; one alias only points
|
|
52691
|
-
* at one fqdn
|
|
52692
|
-
*
|
|
52761
|
+
* at one fqdn. **First-wins on collision** — consistent with design
|
|
52762
|
+
* §O6's namespace-level first-wins rule for `PrivateDnsNamespace`. A
|
|
52763
|
+
* collision (two services declaring the same `ClientAlias.DnsName`)
|
|
52764
|
+
* emits a `logger.warn` so users can debug "why does
|
|
52765
|
+
* `wget http://api/` reach service B instead of service A" without
|
|
52766
|
+
* silently shadowing the prior binding. Idempotent re-register of
|
|
52767
|
+
* the same `(alias, targetFqdn)` pair is a no-op and does NOT warn.
|
|
52693
52768
|
*/
|
|
52694
52769
|
aliasIndex = /* @__PURE__ */ new Map();
|
|
52695
52770
|
/**
|
|
@@ -52725,8 +52800,13 @@ var CloudMapRegistry = class {
|
|
|
52725
52800
|
* suffix). Cloud Map / Service Connect does NOT auto-create such
|
|
52726
52801
|
* aliases — they're populated by `ClientAliases[].DnsName` entries in
|
|
52727
52802
|
* the consumer service's `ServiceConnectConfiguration`. Aliases are
|
|
52728
|
-
* scoped per-CLI-invocation and
|
|
52729
|
-
*
|
|
52803
|
+
* scoped per-CLI-invocation and **first-wins on collision** —
|
|
52804
|
+
* consistent with design §O6's namespace-level first-wins rule. The
|
|
52805
|
+
* first registration sticks; later attempts to bind the same alias
|
|
52806
|
+
* to a different fqdn are ignored and a `logger.warn` is emitted so
|
|
52807
|
+
* users can debug "why does `wget http://api/` reach service B
|
|
52808
|
+
* instead of service A". Re-registering the same `(alias,
|
|
52809
|
+
* targetFqdn)` pair is idempotent and does NOT warn.
|
|
52730
52810
|
*
|
|
52731
52811
|
* @param alias The bare discovery name (e.g. `orders` for an alias to
|
|
52732
52812
|
* `orders.cdkd-local.local`).
|
|
@@ -52736,6 +52816,12 @@ var CloudMapRegistry = class {
|
|
|
52736
52816
|
registerAlias(alias, targetFqdn) {
|
|
52737
52817
|
if (!alias) throw new Error("CloudMapRegistry.registerAlias: alias must be a non-empty string.");
|
|
52738
52818
|
if (!targetFqdn) throw new Error("CloudMapRegistry.registerAlias: targetFqdn must be a non-empty string.");
|
|
52819
|
+
const existing = this.aliasIndex.get(alias);
|
|
52820
|
+
if (existing !== void 0) {
|
|
52821
|
+
if (existing === targetFqdn) return;
|
|
52822
|
+
getLogger().child("cloud-map-registry").warn(`ClientAlias DnsName collision: '${alias}' was already mapped to '${existing}'; keeping first-wins binding and ignoring new mapping to '${targetFqdn}'. Likely cause: two Service Connect services declared the same ClientAlias.DnsName.`);
|
|
52823
|
+
return;
|
|
52824
|
+
}
|
|
52739
52825
|
this.aliasIndex.set(alias, targetFqdn);
|
|
52740
52826
|
}
|
|
52741
52827
|
/**
|
|
@@ -52981,6 +53067,7 @@ async function localStartServiceCommand(targets, options) {
|
|
|
52981
53067
|
const logger = getLogger();
|
|
52982
53068
|
if (options.verbose) logger.setLevel("debug");
|
|
52983
53069
|
warnIfDeprecatedRegion(options);
|
|
53070
|
+
const skipPull = options.pull === false;
|
|
52984
53071
|
if (!targets || targets.length === 0) throw new LocalStartServiceError("cdkd local start-service requires at least one <target>. Pass one or more service paths like 'Stack/Orders' 'Stack/Frontend'.");
|
|
52985
53072
|
const perTarget = targets.map((t) => ({
|
|
52986
53073
|
target: t,
|
|
@@ -53037,7 +53124,7 @@ async function localStartServiceCommand(targets, options) {
|
|
|
53037
53124
|
try {
|
|
53038
53125
|
sharedNetwork = await createSharedSvcNetwork({
|
|
53039
53126
|
prefix: options.cluster,
|
|
53040
|
-
skipPull
|
|
53127
|
+
skipPull,
|
|
53041
53128
|
cluster: options.cluster
|
|
53042
53129
|
});
|
|
53043
53130
|
} catch (err) {
|
|
@@ -53059,7 +53146,7 @@ async function localStartServiceCommand(targets, options) {
|
|
|
53059
53146
|
};
|
|
53060
53147
|
process.on("SIGINT", sigintHandler);
|
|
53061
53148
|
process.on("SIGTERM", sigintHandler);
|
|
53062
|
-
for (const pt of perTarget) pt.controller = await bootOneTarget(pt.target, pt.runState, stacks, options, discovery);
|
|
53149
|
+
for (const pt of perTarget) pt.controller = await bootOneTarget(pt.target, pt.runState, stacks, options, discovery, skipPull);
|
|
53063
53150
|
const summary = perTarget.map((pt) => `${pt.controller.service.serviceName} (${pt.controller.activeReplicaCount()} replica(s))`).join(", ");
|
|
53064
53151
|
logger.info(`Service(s) running: ${summary}. Press ^C to shut down.`);
|
|
53065
53152
|
await Promise.all(perTarget.map((pt) => pt.controller.waitForShutdown()));
|
|
@@ -53077,7 +53164,7 @@ async function localStartServiceCommand(targets, options) {
|
|
|
53077
53164
|
* options) is scoped locally. Returns the started controller for the
|
|
53078
53165
|
* outer code to wait + tear down.
|
|
53079
53166
|
*/
|
|
53080
|
-
async function bootOneTarget(target, runState, stacks, options, discovery) {
|
|
53167
|
+
async function bootOneTarget(target, runState, stacks, options, discovery, skipPull) {
|
|
53081
53168
|
const logger = getLogger();
|
|
53082
53169
|
const imageContext = await buildEcsImageResolutionContext(target, stacks, options);
|
|
53083
53170
|
const service = resolveEcsServiceTarget(target, stacks, imageContext);
|
|
@@ -53120,7 +53207,7 @@ async function bootOneTarget(target, runState, stacks, options, discovery) {
|
|
|
53120
53207
|
const taskOpts = {
|
|
53121
53208
|
cluster: options.cluster,
|
|
53122
53209
|
containerHost: options.containerHost,
|
|
53123
|
-
skipPull
|
|
53210
|
+
skipPull,
|
|
53124
53211
|
keepRunning: false,
|
|
53125
53212
|
detach: true
|
|
53126
53213
|
};
|
|
@@ -53256,22 +53343,26 @@ function parsePositiveInt(raw, flagName) {
|
|
|
53256
53343
|
}
|
|
53257
53344
|
/**
|
|
53258
53345
|
* Hard cap on `--max-tasks` driven by the per-replica subnet allocator
|
|
53259
|
-
* in `ecs-service-runner.ts:
|
|
53260
|
-
*
|
|
53261
|
-
*
|
|
53262
|
-
*
|
|
53263
|
-
*
|
|
53264
|
-
*
|
|
53346
|
+
* in `ecs-service-runner.ts:pickSubnetOctet`. The allocator walks the
|
|
53347
|
+
* link-local /24 range `169.254.170.0..169.254.253.0` and **skips 171**
|
|
53348
|
+
* because that octet is owned by the shared-service network
|
|
53349
|
+
* (`SHARED_SVC_SUBNET_OCTET`) — assigning a per-replica network the
|
|
53350
|
+
* same /24 would have docker reject the duplicate-subnet `network
|
|
53351
|
+
* create`. The usable count is therefore 83 (Issue #544); beyond
|
|
53352
|
+
* that, the modulo-wrap collapses replica N's `/24` onto replica 0's
|
|
53353
|
+
* allocation and docker rejects the duplicate-subnet network creation
|
|
53354
|
+
* with a cryptic "Pool overlaps with other one on this address space"
|
|
53355
|
+
* error 30s into the boot — by which time some early replicas may
|
|
53356
|
+
* have spent docker-run budget. Reject at parse time so the user
|
|
53265
53357
|
* gets an actionable error before any boot work fires.
|
|
53266
53358
|
*
|
|
53267
|
-
*
|
|
53268
|
-
*
|
|
53269
|
-
* this requires extending the allocator to walk a different IP range.
|
|
53359
|
+
* Raising this requires extending the allocator to walk a different
|
|
53360
|
+
* IP range.
|
|
53270
53361
|
*/
|
|
53271
|
-
const MAX_TASKS_SUBNET_RANGE_CAP =
|
|
53362
|
+
const MAX_TASKS_SUBNET_RANGE_CAP = 83;
|
|
53272
53363
|
function parseMaxTasks(raw) {
|
|
53273
53364
|
const parsed = parsePositiveInt(raw, "--max-tasks");
|
|
53274
|
-
if (parsed >
|
|
53365
|
+
if (parsed > 83) throw new LocalStartServiceError(`--max-tasks ${parsed} exceeds the per-replica link-local /24 subnet allocator's range (${83}). The allocator in ecs-service-runner.ts assigns each replica its own 169.254.x.0/24 from the range 169.254.170.0..169.254.253.0; replica indices >= ${83} would collide with earlier replicas via modulo wrap. Lower --max-tasks to <= ${83}, or accept reduced local concurrency for high-DesiredCount services.`);
|
|
53275
53366
|
return parsed;
|
|
53276
53367
|
}
|
|
53277
53368
|
function parseRestartPolicy(raw) {
|
|
@@ -53279,7 +53370,7 @@ function parseRestartPolicy(raw) {
|
|
|
53279
53370
|
throw new LocalStartServiceError(`--restart-policy must be one of 'on-failure', 'always', or 'none' (got '${raw}').`);
|
|
53280
53371
|
}
|
|
53281
53372
|
function createLocalStartServiceCommand() {
|
|
53282
|
-
const cmd = new Command("start-service").description("Run one or more AWS::ECS::Service resources locally as a long-running emulator. Spins up DesiredCount task replicas per service (clamped by --max-tasks) using the same per-task docker network + metadata sidecar pattern as `cdkd local run-task`, then keeps each replica running and restarts it on exit per --restart-policy. ^C tears every replica + sidecar + network down. Each <target> accepts a CDK display path (MyStack/MyService) or stack-qualified logical ID (MyStack:MyServiceXYZ); single-stack apps may omit the stack prefix. When two or more <target>s are supplied, every service is booted into a shared Cloud Map / Service Connect registry so peer services discover each other via docker --add-host overlay (Issue #460).").argument("<targets...>", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ECS::Service resources to run").addOption(new Option("--cluster <name>", "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix").default("cdkd-local")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})")).addOption(new Option("--container-host <ip>", "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)").default("127.0.0.1")).addOption(new Option("--assume-task-role [arn]", "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed task role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.")).addOption(new Option("--no-pull", "Skip docker pull for every container image and the metadata sidecar")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries.")).addOption(new Option("--platform <platform>", "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture")).addOption(new Option("--max-tasks <n>", `Hard cap on local replica count. Caps the template DesiredCount so local dev machines don't run an unbounded number of containers. Cannot exceed ${
|
|
53373
|
+
const cmd = new Command("start-service").description("Run one or more AWS::ECS::Service resources locally as a long-running emulator. Spins up DesiredCount task replicas per service (clamped by --max-tasks) using the same per-task docker network + metadata sidecar pattern as `cdkd local run-task`, then keeps each replica running and restarts it on exit per --restart-policy. ^C tears every replica + sidecar + network down. Each <target> accepts a CDK display path (MyStack/MyService) or stack-qualified logical ID (MyStack:MyServiceXYZ); single-stack apps may omit the stack prefix. When two or more <target>s are supplied, every service is booted into a shared Cloud Map / Service Connect registry so peer services discover each other via docker --add-host overlay (Issue #460).").argument("<targets...>", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ECS::Service resources to run").addOption(new Option("--cluster <name>", "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix").default("cdkd-local")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})")).addOption(new Option("--container-host <ip>", "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)").default("127.0.0.1")).addOption(new Option("--assume-task-role [arn]", "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed task role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.")).addOption(new Option("--no-pull", "Skip docker pull for every container image and the metadata sidecar")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries.")).addOption(new Option("--platform <platform>", "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture")).addOption(new Option("--max-tasks <n>", `Hard cap on local replica count. Caps the template DesiredCount so local dev machines don't run an unbounded number of containers. Cannot exceed ${83} due to the per-replica link-local /24 subnet allocator's range.`).default(3).argParser(parseMaxTasks)).addOption(new Option("--restart-policy <policy>", "How to react when an essential container exits. 'on-failure' (default) restarts only on non-zero exit; 'always' restarts on every exit; 'none' shuts the replica down and runs the service degraded.").default("on-failure").argParser(parseRestartPolicy)).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Fn::Sub / Fn::GetAtt / Fn::ImportValue / Fn::GetStackOutput intrinsics in container images, environment variables, secrets, role ARNs, and volumes.").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(localStartServiceCommand));
|
|
53283
53374
|
[
|
|
53284
53375
|
...commonOptions,
|
|
53285
53376
|
...appOptions,
|
|
@@ -56593,7 +56684,7 @@ function reorderArgs(argv) {
|
|
|
56593
56684
|
*/
|
|
56594
56685
|
async function main() {
|
|
56595
56686
|
const program = new Command();
|
|
56596
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.147.
|
|
56687
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.147.2");
|
|
56597
56688
|
program.addCommand(createBootstrapCommand());
|
|
56598
56689
|
program.addCommand(createSynthCommand());
|
|
56599
56690
|
program.addCommand(createListCommand());
|