feat: split k3s ssh routes from operations

This commit is contained in:
Codex
2026-05-25 06:24:17 +00:00
parent 64c82b5be8
commit 61492b4db5
5 changed files with 463 additions and 70 deletions
+46 -9
View File
@@ -8,6 +8,17 @@ function assertCondition(condition: unknown, message: string, detail: unknown =
if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
}
function assertThrows(fn: () => unknown, pattern: RegExp, message: string): void {
try {
fn();
} catch (error) {
const text = error instanceof Error ? error.message : String(error);
assertCondition(pattern.test(text), message, { error: text });
return;
}
throw new Error(`${message}: expected throw`);
}
export function runSshArgvGuidanceContract(): JsonRecord {
const argv = parseSshArgs(["argv", "true"]);
assertCondition(argv.invocationKind === "argv", "argv subcommand must be classified as argv", argv);
@@ -32,18 +43,40 @@ export function runSshArgvGuidanceContract(): JsonRecord {
assertCondition(k3sExec.invocationKind === "helper", "k3s exec must be classified as helper", k3sExec);
assertCondition(k3sExec.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'node' '-e' 'console.log(process.version)'", "k3s exec must assemble kubectl argv without nested shell quoting", k3sExec);
const routeKubectl = parseSshInvocation("D601:k3s:kubectl", ["get", "pods", "-n", "hwlab-dev"]);
const routeKubectl = parseSshInvocation("D601:k3s", ["kubectl", "get", "pods", "-n", "hwlab-dev"]);
assertCondition(routeKubectl.providerId === "D601", "route must preserve provider id", routeKubectl);
assertCondition(routeKubectl.route.plane === "k3s" && routeKubectl.route.entry === "kubectl", "route must parse k3s kubectl entry", routeKubectl);
assertCondition(routeKubectl.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'get' 'pods' '-n' 'hwlab-dev'", "D601:k3s:kubectl must map to kubectl argv", routeKubectl);
assertCondition(routeKubectl.route.plane === "k3s" && routeKubectl.route.entry === null, "route must keep kubectl as an operation, not as a route entry", routeKubectl);
assertCondition(routeKubectl.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'get' 'pods' '-n' 'hwlab-dev'", "D601:k3s kubectl must map to kubectl argv", routeKubectl);
const routeTarget = parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api", ["node", "-e", "console.log(process.version)"]);
assertCondition(routeTarget.route.namespace === "hwlab-dev" && routeTarget.route.resource === "hwlab-cloud-api", "route target must parse namespace and workload", routeTarget);
assertCondition(routeTarget.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'node' '-e' 'console.log(process.version)'", "D601:k3s:<namespace>:<workload> must default to deployment exec", routeTarget);
const routeScript = parseSshInvocation("D601:k3s:script:hwlab-dev:hwlab-cloud-api", ["--shell", "bash", "--", "arg"]);
assertCondition(routeScript.parsed.requiresStdin === true, "k3s script route must stream local stdin", routeScript);
assertCondition(routeScript.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-i' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'bash' '-s' '--' 'arg'", "D601:k3s:script:<namespace>:<workload> must map stdin to shell -s", routeScript);
const routeScript = parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api", ["script", "--shell", "bash", "--", "arg"]);
assertCondition(routeScript.parsed.requiresStdin === true, "k3s script operation must stream local stdin", routeScript);
assertCondition(routeScript.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-i' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'bash' '-s' '--' 'arg'", "D601:k3s:<namespace>:<workload> script must map stdin to shell -s", routeScript);
const routeControlScript = parseSshInvocation("D601:k3s", ["script", "--shell", "bash", "--", "arg"]);
assertCondition(routeControlScript.parsed.requiresStdin === true, "k3s control-plane script operation must stream local stdin", routeControlScript);
assertCondition(routeControlScript.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'bash' '-s' '--' 'arg'", "D601:k3s script must inject native kubeconfig without manual export", routeControlScript);
const routeApplyPatch = parseSshInvocation("D601:k3s:hwlab-dev:hwlab-cloud-api", ["apply-patch"]);
assertCondition(routeApplyPatch.parsed.requiresStdin === true, "k3s apply-patch operation must stream local patch stdin", routeApplyPatch);
assertCondition(routeApplyPatch.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-i' '-n' 'hwlab-dev' 'deployment/hwlab-cloud-api' '--' 'sh' '-s' '--'", "D601:k3s:<namespace>:<workload> apply-patch must enter pod with stdin", routeApplyPatch);
assertCondition(routeApplyPatch.parsed.stdinPrefix?.includes("apply_patch") && routeApplyPatch.parsed.stdinPrefix.includes("__UNIDESK_APPLY_PATCH_PAYLOAD__"), "k3s apply-patch operation must inject pod helper before patch stdin", routeApplyPatch);
assertCondition(!routeApplyPatch.parsed.stdinPrefix?.includes("python3") && !routeApplyPatch.parsed.stdinPrefix?.includes("node "), "k3s apply-patch operation must use the sh-only pod helper", routeApplyPatch);
assertCondition(routeApplyPatch.parsed.stdinSuffix === "\n__UNIDESK_APPLY_PATCH_PAYLOAD__\n", "k3s apply-patch operation must terminate patch heredoc", routeApplyPatch);
assertThrows(
() => parseSshInvocation("D601:k3s:kubectl", ["get", "pods"]),
/route must locate a target only.*ssh D601:k3s kubectl/u,
"operation names must not be accepted as k3s route segments",
);
assertThrows(
() => parseSshInvocation("D601:k3s:apply-patch:hwlab-dev:hwlab-cloud-api", []),
/route must locate a target only.*apply-patch/u,
"pod apply-patch must be an operation after the route",
);
const routePodTarget = parseSshInvocation("D601:k3s:hwlab-dev:pod/hwlab-cloud-api-abc:api", ["printenv", "HOSTNAME"]);
assertCondition(routePodTarget.parsed.remoteCommand === "'env' 'KUBECONFIG=/etc/rancher/k3s/k3s.yaml' 'kubectl' 'exec' '-n' 'hwlab-dev' 'pod/hwlab-cloud-api-abc' '-c' 'api' '--' 'printenv' 'HOSTNAME'", "pod route with container must preserve explicit pod kind", routePodTarget);
@@ -62,8 +95,10 @@ export function runSshArgvGuidanceContract(): JsonRecord {
const helpText = JSON.stringify(sshHelp());
assertCondition(helpText.includes("ssh <providerId> script [--shell sh|bash] [script-args...] <<'SCRIPT'"), "ssh help must recommend stdin script passthrough for shell scripts", helpText);
assertCondition(helpText.includes("ssh D601:k3s:kubectl get pods -n hwlab-dev"), "ssh help must document k3s kubectl route", helpText);
assertCondition(helpText.includes("ssh D601:k3s:script:hwlab-dev:hwlab-cloud-api <<'SCRIPT'"), "ssh help must document k3s script route", helpText);
assertCondition(helpText.includes("ssh D601:k3s kubectl get pods -n hwlab-dev"), "ssh help must document k3s kubectl operation", helpText);
assertCondition(helpText.includes("ssh D601:k3s script <<'SCRIPT'"), "ssh help must document k3s control-plane script operation", helpText);
assertCondition(helpText.includes("ssh D601:k3s:hwlab-dev:hwlab-cloud-api apply-patch <<'PATCH'"), "ssh help must document k3s pod apply-patch operation", helpText);
assertCondition(helpText.includes("ssh D601:k3s:hwlab-dev:hwlab-cloud-api script <<'SCRIPT'"), "ssh help must document k3s script operation", helpText);
assertCondition(helpText.includes("UNIDESK_SSH_HINT"), "ssh help must document structured failure hint", helpText);
const crossChecks = providerTriageRecommendedCrossChecks("D601");
@@ -74,7 +109,9 @@ export function runSshArgvGuidanceContract(): JsonRecord {
checks: [
"argv form is classified and quoted as the success path for non-interactive commands",
"stdin script form removes shell-command strings for host and k3s workload scripts",
"k3s route fixes native kubeconfig and assembles kubectl exec as argv",
"pod apply-patch operation injects helper and forwards patch stdin",
"legacy operation-in-route forms are rejected with canonical route-plus-operation guidance",
"k3s route stays location-only while operations fix native kubeconfig and assemble kubectl exec as argv",
"ssh-like timeout/kex failures emit one structured argv retry hint",
"help text documents stdin script passthrough and UNIDESK_SSH_HINT",
"provider triage recommendedCrossChecks keeps ssh D601 argv true",