fix(hwlab): allow direct ssh git mirror egress (#807)

Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
Lyon
2026-06-24 09:40:30 +08:00
committed by GitHub
parent 2a72892f70
commit fc7047d467
3 changed files with 90 additions and 70 deletions
+28 -17
View File
@@ -274,17 +274,19 @@ type NodeRuntimeGitMirrorGithubTransportSpec =
tokenSourceKey: string;
};
interface NodeRuntimeGitMirrorEgressProxySpec {
mode: "k8s-service-cluster-ip";
clientName: string;
namespace: string;
serviceName: string;
port: number;
sourceRef: string;
sourceKey: string;
sourceType: "subscription-url";
noProxy: string[];
}
type NodeRuntimeGitMirrorEgressProxySpec =
| { mode: "direct"; required: false }
| {
mode: "k8s-service-cluster-ip";
clientName: string;
namespace: string;
serviceName: string;
port: number;
sourceRef: string;
sourceKey: string;
sourceType: "subscription-url";
noProxy: string[];
};
const MASTER_ADMIN_API_KEY_KEY = "api-key";
const BOOTSTRAP_ADMIN_PASSWORD_HASH_KEY = "password-hash";
@@ -3386,6 +3388,7 @@ function nodeRuntimeGitMirrorRetryableFailure(
): Record<string, unknown> | null {
const text = `${result.stdout}\n${result.stderr}`;
const proxyConnectFailure = /hwlab git-mirror proxy-connect:/iu.test(text);
const sshProxyBannerFailure = mirror.egressProxy.mode !== "direct" && /Connection timed out during banner exchange|Connection to UNKNOWN port 65535 timed out|kex_exchange_identification|Connection closed by remote host/iu.test(text);
const waitTimeoutFailure = result.exitCode === 45
|| result.exitCode === 124
|| /UNIDESK_SSH_RUNTIME_TIMEOUT|ssh-runtime-timeout|ssh\/tran operation exceeded|job,pod/iu.test(text);
@@ -3437,6 +3440,7 @@ function nodeRuntimeGitMirrorRetryableFailure(
}
const retryable = partialSuccess !== null
|| proxyConnectFailure
|| sshProxyBannerFailure
|| waitTimeoutFailure
|| /kex_exchange_identification|Connection closed by remote host|Could not read from remote repository|ssh.github.com|github.com|fetch-pack|early EOF/iu.test(text);
if (!retryable) return null;
@@ -3455,8 +3459,8 @@ function nodeRuntimeGitMirrorRetryableFailure(
? "GitOps push appears to have succeeded, but the post-push fetch/recheck failed. Standard git-mirror stops without host workspace fallback."
: waitTimeoutFailure
? "Git mirror job wait exceeded the controlled short-connection budget. Standard git-mirror retries with exponential backoff and stops when retry budget is exhausted."
: proxyConnectFailure
? "Git mirror job hit a retryable YAML-first proxy CONNECT failure. Standard git-mirror keeps using the configured node-global proxy and stops when retry budget is exhausted."
: proxyConnectFailure || sshProxyBannerFailure
? "Git mirror job hit a retryable YAML-first SSH-over-proxy failure. Standard git-mirror keeps using the configured node-global proxy and stops when retry budget is exhausted."
: `Git mirror job hit a retryable upstream GitHub ${mirror.githubTransport.mode} transport/fetch failure. Standard git-mirror stops without host workspace fallback.`,
refSources: nodeRuntimeGitMirrorRefSources(scoped, mirror),
githubTransport: nodeRuntimeGitMirrorGithubTransportSummary(mirror),
@@ -3860,6 +3864,7 @@ function nodeRuntimeGitMirrorJobManifest(mirror: NodeRuntimeGitMirrorTargetSpec,
function nodeRuntimeGitMirrorProxyEnv(mirror: NodeRuntimeGitMirrorTargetSpec): Record<string, unknown>[] {
const proxy = mirror.egressProxy;
if (proxy.mode === "direct") return [];
const proxyUrl = `http://${proxy.serviceName}.${proxy.namespace}.svc.cluster.local:${proxy.port}`;
const noProxy = proxy.noProxy.join(",");
return [
@@ -5930,7 +5935,10 @@ function nodeRuntimeRenderOverlay(spec: HwlabRuntimeLaneSpec): Record<string, un
const gitMirror = nodeRuntimeGitMirrorTarget(spec);
const renderGitMirror = {
...gitMirror,
egressProxy: {
egressProxy: gitMirror.egressProxy.mode === "direct" ? {
mode: "direct",
required: false,
} : {
...gitMirror.egressProxy,
mode: "node-global",
required: true,
@@ -6948,14 +6956,17 @@ function nodeRuntimeGitMirrorTarget(spec: HwlabRuntimeLaneSpec): NodeRuntimeGitM
const parsed = record(Bun.YAML.parse(readFileSync(configPath, "utf8")) as unknown);
const nodes = record(parsed.nodes);
const node = record(nodes[spec.nodeId]);
const nodeEgressProxy = nodeRuntimeGitMirrorEgressProxySpec(record(node.egressProxy), `nodes.${spec.nodeId}.egressProxy`);
const targets = Array.isArray(parsed.targets) ? parsed.targets : [];
const target = targets.map((item) => record(item)).find((item) => item.node === spec.nodeId && item.lane === spec.lane);
if (target === undefined) throw new Error(`no gitMirror target for node=${spec.nodeId} lane=${spec.lane} in ${HWLAB_NODE_CONTROL_PLANE_CONFIG_PATH}`);
const gitMirror = record(target.gitMirror);
const gitMirrorEgressProxy = record(gitMirror.egressProxy);
if (stringValue(gitMirrorEgressProxy.mode, "gitMirror.egressProxy.mode") !== "node-global") throw new Error(`gitMirror.egressProxy.mode must be node-global for node=${spec.nodeId} lane=${spec.lane}`);
if (gitMirrorEgressProxy.required !== true) throw new Error(`gitMirror.egressProxy.required must be true for node=${spec.nodeId} lane=${spec.lane}`);
const gitMirrorEgressMode = stringValue(gitMirrorEgressProxy.mode, "gitMirror.egressProxy.mode");
if (gitMirrorEgressMode !== "node-global" && gitMirrorEgressMode !== "direct") throw new Error(`gitMirror.egressProxy.mode must be node-global or direct for node=${spec.nodeId} lane=${spec.lane}`);
const nodeEgressProxy = gitMirrorEgressMode === "direct"
? { mode: "direct" as const, required: false as const }
: nodeRuntimeGitMirrorEgressProxySpec(record(node.egressProxy), `nodes.${spec.nodeId}.egressProxy`);
if (gitMirrorEgressMode === "node-global" && gitMirrorEgressProxy.required !== true) throw new Error(`gitMirror.egressProxy.required must be true for node=${spec.nodeId} lane=${spec.lane}`);
const githubTransport = nodeRuntimeGitMirrorGithubTransportSpec(record(gitMirror.githubTransport), "gitMirror.githubTransport");
const source = record(target.source);
const gitops = record(target.gitops);