Repository navigation
fix(link): raise Child preflight output cap and name the limit #8380
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import { SSH_MAX_OUTPUT_BYTES } from "../../link/ssh-runner"; | ||
|
|
||
| /** | ||
| * Home enrollment reads one Child command before it may restart that Child: `ocx config show | ||
| * --json`, for the effective `port`, `runtimeRole` and (ephemeral) `ephemeralChild` fields. | ||
| * | ||
| * There is no smaller command that answers on every Child this Home supports. Full `ocx` has | ||
| * `config get <dot.path>` since 2.66.0, but resolving only fields that happen to be persisted | ||
| * cannot tell an absent `runtimeRole` (standalone by default) from an unreadable one, and it | ||
| * fails outright for an absent key. The lightweight `ocx-child` binary answers only `config show | ||
| * --json`. A Child is therefore allowed the runner's maximum capture here: a saved configuration | ||
| * of ordinary size stays well under it, and the 64 KiB default rejected a large-but-valid one | ||
| * before preflight could read the two fields it needs (#8356). | ||
| */ | ||
| export const CHILD_PREFLIGHT_MAX_OUTPUT_BYTES = SSH_MAX_OUTPUT_BYTES; | ||
|
|
||
| export const CHILD_CONFIG_TOO_LARGE_MESSAGE = "The Child configuration is too large for the Remote Link preflight."; | ||
|
|
||
| /** The operator-facing hint for a Child whose config output exceeded even the raised cap. */ | ||
| export const CHILD_CONFIG_TOO_LARGE_HINT = | ||
| `ocx config show --json on the Child exceeded ${CHILD_PREFLIGHT_MAX_OUTPUT_BYTES} bytes; trim the Child's saved configuration and retry`; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ import { withConfigMutationLockSync } from "../../config/mutation-lock"; | |
| import type { ManagementContext } from "./context"; | ||
| import { readManagementJsonBodyOr } from "./body"; | ||
| import { fail, exactBody, port, isRecord, runnerFor, readStoreFor, writeStoreFor, knownHostsFile, putKnownHost, issueKey, compensateNewLink, type LinkRouteState } from "./link-route-state"; | ||
| import { CHILD_CONFIG_TOO_LARGE_HINT, CHILD_CONFIG_TOO_LARGE_MESSAGE, CHILD_PREFLIGHT_MAX_OUTPUT_BYTES } from "./link-child-preflight"; | ||
|
|
||
| const PROBE_TTL_MS = 5 * 60_000; | ||
| const MIN_REMOTE_OCX = [2, 66, 0] as const; | ||
|
|
@@ -285,7 +286,7 @@ export async function apply(ctx: ManagementContext, state: LinkRouteState): Prom | |
| const remotePort = parsePort(chosen.stdout); | ||
| if (!remotePort) return await failed(fail("remote_port_failed", "The remote link port could not be determined.", 502)); | ||
| phase = "child_preflight"; | ||
| const configured = await run(ocx(["config", "show", "--json"])); | ||
| const configured = await run(ocx(["config", "show", "--json"]), budget, CHILD_PREFLIGHT_MAX_OUTPUT_BYTES); | ||
| let childConfig: unknown = null; | ||
| try { if (configured.code === 0) childConfig = JSON.parse(configured.stdout); } catch { /* fail closed */ } | ||
| if (!isRecord(childConfig) || !port(childConfig.port)) return await failed(fail("child_preflight_failed", "The Child effective configuration could not be read.", 502)); | ||
|
|
@@ -350,6 +351,9 @@ export async function apply(ctx: ManagementContext, state: LinkRouteState): Prom | |
| return Response.json({ linkId: id }, { status: 202 }); | ||
| } finally { unsubscribe(); } | ||
| } catch (error) { | ||
| if (phase === "child_preflight" && error instanceof SshRunnerError && error.code === "output_limit") { | ||
| return await failed(fail("child_preflight_output_limit", CHILD_CONFIG_TOO_LARGE_MESSAGE, 502, CHILD_CONFIG_TOO_LARGE_HINT)); | ||
|
Comment on lines
353
to
+355
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
| return await failed(fail(phase + (error instanceof SshRunnerError && error.code === "timeout" ? "_timeout" : "_failed"), "Remote Link failed during " + phase.replaceAll("_", " ") + ".", 502, sshRunnerErrorHint(error))); | ||
| } finally { input?.fill(0); rmSync(dir, { recursive: true, force: true }); } | ||
| }); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
Repository: lidge-jun/opencodex
Length of output: 14195
Script executed:
Repository: lidge-jun/opencodex
Length of output: 42444
Script executed:
Repository: lidge-jun/opencodex
Length of output: 40092
Script executed:
Repository: lidge-jun/opencodex
Length of output: 19932
Script executed:
Repository: lidge-jun/opencodex
Length of output: 31990
Web query:
OpenSSH sshd manual SSH_ORIGINAL_COMMAND forced command stderr ~/.ssh/rc noninteractive commandResult:
Script executed:
Repository: lidge-jun/opencodex
Length of output: 18438
Show purge guidance when rollback fails.
When preflight exceeds the SSH output limit after
--plantand purge fails, this branch hides the residual state and tells the user to retry. Show purge recovery guidance whenresidualis true.