From 90e767a0db00baa9369b201c2cd50e243e424896 Mon Sep 17 00:00:00 2001 From: m4air Date: Sun, 4 Oct 2026 14:42:02 -0700 Subject: [PATCH] refactor(ssh): one managed-tunnel ownership check and forward bookkeeping --- src/main/ssh/orcad-managed-tunnel-active.ts | 76 +++++++++++++++++++ src/main/ssh/orcad-managed-tunnel-manager.ts | 36 +++------ src/main/ssh/orcad-managed-tunnel-resume.ts | 79 ++++++++------------ src/main/ssh/orcad-managed-tunnel-serving.ts | 3 +- 4 files changed, 122 insertions(+), 72 deletions(-) create mode 100644 src/main/ssh/orcad-managed-tunnel-active.ts diff --git a/src/main/ssh/orcad-managed-tunnel-active.ts b/src/main/ssh/orcad-managed-tunnel-active.ts new file mode 100644 index 000000000000..d04df7c0a730 --- /dev/null +++ b/src/main/ssh/orcad-managed-tunnel-active.ts @@ -0,0 +1,76 @@ +/** The managed tunnels a client holds, and the one rule for whether one still belongs to its server. */ +import { getManagedOrcadFenceEnvironmentId } from '../../shared/managed-orcad-ssh-owner' +import { + getRuntimeSshAccess, + type KnownRuntimeEnvironment, + type RuntimeSshTunnelLink +} from '../../shared/runtime-environments' +import type { SshTarget } from '../../shared/ssh-types' +import type { SshConnection } from './ssh-connection' +import type { PortForwardEntry, SshPortForwardManager } from './ssh-port-forward' + +export type ActiveOrcadTunnel = { + connection: SshConnection + forwardId: string + localPort: number + /** The port orcad bound, which can differ from the persisted (preferred) one. */ + remotePort: number + preferredPort: number + sshTargetGeneration: number + targetId: string + transportGeneration: number +} + +export type ManagedTunnelExpectation = { + environmentId: string + sshTargetId: string + sshTargetGeneration: number + localPort: number + remotePort: number +} + +/** The environment's SSH access while it and its target still name this tunnel; null otherwise. */ +export function managedTunnelAccess( + environment: KnownRuntimeEnvironment | null | undefined, + target: SshTarget | null | undefined, + expected: ManagedTunnelExpectation +): RuntimeSshTunnelLink | null { + const access = environment ? getRuntimeSshAccess(environment) : undefined + return environment?.id === expected.environmentId && + environment.connectionDependency === 'ssh-tunnel' && + access?.sshTargetId === expected.sshTargetId && + access.sshTargetGeneration === expected.sshTargetGeneration && + access.localPort === expected.localPort && + access.remotePort === expected.remotePort && + target?.generation === expected.sshTargetGeneration && + getManagedOrcadFenceEnvironmentId(target) === expected.environmentId + ? access + : null +} + +/** Removes `entry`'s forward, and the record only if nothing replaced it meanwhile. */ +export async function dropActiveOrcadTunnel( + active: Map, + forwards: SshPortForwardManager, + environmentId: string, + entry: ActiveOrcadTunnel +): Promise { + await forwards.removeForwardAndWait(entry.forwardId) + if (active.get(environmentId) === entry) { + active.delete(environmentId) + } +} + +export function recordActiveOrcadTunnel( + active: Map, + environmentId: string, + forward: PortForwardEntry, + tunnel: Omit +): void { + active.set(environmentId, { + ...tunnel, + forwardId: forward.id, + localPort: forward.localPort, + remotePort: forward.remotePort + }) +} diff --git a/src/main/ssh/orcad-managed-tunnel-manager.ts b/src/main/ssh/orcad-managed-tunnel-manager.ts index ce549278cde7..ebc778d4122b 100644 --- a/src/main/ssh/orcad-managed-tunnel-manager.ts +++ b/src/main/ssh/orcad-managed-tunnel-manager.ts @@ -14,12 +14,17 @@ import { SshPortForwardManager } from './ssh-port-forward' import { OrcadManagedTunnelTransportProvider } from './orcad-managed-tunnel-transport' import { OrcadManagedTunnelResumeRecovery, - type ActiveOrcadTunnel, type OrcadManagedServingCheck, type OrcadManagedTunnelProbe, type OrcadManagedTunnelResumeOptions } from './orcad-managed-tunnel-resume' import type { OrcadManagedServing } from './orcad-managed-serving' +import { + dropActiveOrcadTunnel, + managedTunnelAccess, + recordActiveOrcadTunnel, + type ActiveOrcadTunnel +} from './orcad-managed-tunnel-active' import { checkManagedTunnelServing, type OrcadTunnelServing } from './orcad-managed-tunnel-serving' import type { getSshTargetRegistryStore } from './ssh-target-registry' import { @@ -132,11 +137,8 @@ export class OrcadManagedTunnelManager { if (!forward) { throw new Error('Orca SSH tunnel setup was superseded.') } - this.active.set(environmentId, { + recordActiveOrcadTunnel(this.active, environmentId, forward, { connection, - forwardId: forward.id, - localPort: forward.localPort, - remotePort: forward.remotePort, preferredPort: checks.preferredPort ?? remotePort, sshTargetGeneration: target.generation, targetId: target.id, @@ -220,24 +222,16 @@ export class OrcadManagedTunnelManager { const managerGeneration = this.managerGeneration const ownershipGeneration = this.ownershipGenerations.get(environment.id) ?? 0 + const expected = { environmentId: environment.id, ...deployment } const stillOwned = (): boolean => { - const currentTarget = targetStore.getTarget(target.id) const currentEnvironment = resolveCurrent() - const currentAccess = currentEnvironment ? getRuntimeSshAccess(currentEnvironment) : undefined return ( this.managerGeneration === managerGeneration && (this.ownershipGenerations.get(environment.id) ?? 0) === ownershipGeneration && - currentTarget?.generation === target.generation && - getManagedOrcadFenceEnvironmentId(currentTarget) === environment.id && - currentEnvironment?.id === environment.id && - currentEnvironment.runtimeId === environment.runtimeId && + currentEnvironment?.runtimeId === environment.runtimeId && (currentEnvironment.pairingRevision ?? currentEnvironment.createdAt) === (environment.pairingRevision ?? environment.createdAt) && - currentEnvironment.connectionDependency === 'ssh-tunnel' && - currentAccess?.sshTargetId === deployment.sshTargetId && - currentAccess.sshTargetGeneration === deployment.sshTargetGeneration && - currentAccess.localPort === deployment.localPort && - currentAccess.remotePort === deployment.remotePort + managedTunnelAccess(currentEnvironment, targetStore.getTarget(target.id), expected) !== null ) } const connection = await connectionManager.connect(target) @@ -262,10 +256,7 @@ export class OrcadManagedTunnelManager { connection }) if (active && stillOwned()) { - await this.forwards.removeForwardAndWait(active.forwardId) - if (this.active.get(environment.id) === active) { - this.active.delete(environment.id) - } + await dropActiveOrcadTunnel(this.active, this.forwards, environment.id, active) } if (!stillOwned()) { return @@ -283,11 +274,8 @@ export class OrcadManagedTunnelManager { if (!forward) { return } - this.active.set(environment.id, { + recordActiveOrcadTunnel(this.active, environment.id, forward, { connection, - forwardId: forward.id, - localPort: forward.localPort, - remotePort: forward.remotePort, preferredPort: deployment.remotePort, sshTargetGeneration: target.generation, targetId: target.id, diff --git a/src/main/ssh/orcad-managed-tunnel-resume.ts b/src/main/ssh/orcad-managed-tunnel-resume.ts index 203d722b8351..4e5fb21befa0 100644 --- a/src/main/ssh/orcad-managed-tunnel-resume.ts +++ b/src/main/ssh/orcad-managed-tunnel-resume.ts @@ -1,8 +1,6 @@ import { sendRemoteRuntimeRequest } from '../../shared/remote-runtime-client' -import { getManagedOrcadFenceEnvironmentId } from '../../shared/managed-orcad-ssh-owner' import { getPreferredPairingOffer, - getRuntimeSshAccess, type KnownRuntimeEnvironment, type RuntimeSshTunnelLink } from '../../shared/runtime-environments' @@ -12,24 +10,18 @@ import type { OrcadManagedServing } from './orcad-managed-serving' import type { SshConnectionManager } from './ssh-connection-manager' import type { SshPortForwardManager } from './ssh-port-forward' import type { getSshTargetRegistryStore } from './ssh-target-registry' +import { + dropActiveOrcadTunnel, + managedTunnelAccess, + recordActiveOrcadTunnel, + type ActiveOrcadTunnel +} from './orcad-managed-tunnel-active' import { environmentForwardChecks, forwardToVerifiedOrcad, type OrcadManagedTunnelTargeting } from './orcad-managed-tunnel-target' -export type ActiveOrcadTunnel = { - connection: SshConnection - forwardId: string - localPort: number - /** The port orcad bound, which can differ from the persisted (preferred) one. */ - remotePort: number - preferredPort: number - sshTargetGeneration: number - targetId: string - transportGeneration: number -} - export type OrcadManagedTunnelProbe = ( environment: KnownRuntimeEnvironment, timeoutMs: number @@ -120,7 +112,7 @@ export class OrcadManagedTunnelResumeRecovery { if (await this.probeTunnel(resolved.environment, options.timeoutMs)) { return } - if (!this.stillOwned(environmentId, active, ownershipGeneration, managerGeneration, true)) { + if (!this.stillOwned(environmentId, active, ownershipGeneration, managerGeneration)) { return } } @@ -129,7 +121,7 @@ export class OrcadManagedTunnelResumeRecovery { if (pending) { await pending.catch(() => undefined) } - if (!this.stillOwned(environmentId, active, ownershipGeneration, managerGeneration, true)) { + if (!this.stillOwned(environmentId, active, ownershipGeneration, managerGeneration)) { return } const resolved = this.resolveEnvironment(environmentId, active, options) @@ -167,7 +159,7 @@ export class OrcadManagedTunnelResumeRecovery { ): Promise { await connectionManager.reconnect(active.targetId) if ( - !this.stillOwned(environment.id, active, ownershipGeneration, managerGeneration, true) || + !this.stillOwned(environment.id, active, ownershipGeneration, managerGeneration) || connectionManager !== this.dependencies.getConnectionManager() || connectionManager.getConnection(active.targetId) !== active.connection || connectionManager.getState(active.targetId)?.status !== 'connected' || @@ -184,12 +176,14 @@ export class OrcadManagedTunnelResumeRecovery { return } if (currentActive === active) { - await this.dependencies.forwards.removeForwardAndWait(active.forwardId) - if (this.dependencies.active.get(environment.id) === active) { - this.dependencies.active.delete(environment.id) - } + await dropActiveOrcadTunnel( + this.dependencies.active, + this.dependencies.forwards, + environment.id, + active + ) } - if (!this.stillOwned(environment.id, active, ownershipGeneration, managerGeneration, true)) { + if (!this.stillOwned(environment.id, active, ownershipGeneration, managerGeneration)) { return } @@ -217,17 +211,14 @@ export class OrcadManagedTunnelResumeRecovery { ...checks, stillCurrent: () => active.connection.getTransportGeneration() === transportGeneration && - this.stillOwned(environment.id, active, ownershipGeneration, managerGeneration, true) && + this.stillOwned(environment.id, active, ownershipGeneration, managerGeneration) && this.resolveEnvironment(environment.id, active, options) !== null }) if (!forward) { return } - this.dependencies.active.set(environment.id, { + recordActiveOrcadTunnel(this.dependencies.active, environment.id, forward, { connection: active.connection, - forwardId: forward.id, - localPort: forward.localPort, - remotePort: forward.remotePort, preferredPort: deployment.remotePort, sshTargetGeneration: active.sshTargetGeneration, targetId: active.targetId, @@ -243,37 +234,31 @@ export class OrcadManagedTunnelResumeRecovery { options: OrcadManagedTunnelResumeOptions ): ResolvedManagedTunnelEnvironment | null { const environment = options.resolveEnvironment(environmentId) - const deployment = environment ? getRuntimeSshAccess(environment) : undefined - const target = this.dependencies.getTargetStore()?.getTarget(active.targetId) - if ( - !environment || - environment.connectionDependency !== 'ssh-tunnel' || - !deployment || - deployment.sshTargetId !== active.targetId || - deployment.sshTargetGeneration !== active.sshTargetGeneration || - deployment.localPort !== active.localPort || - deployment.remotePort !== active.preferredPort || - !target || - target.generation !== active.sshTargetGeneration || - getManagedOrcadFenceEnvironmentId(target) !== environmentId - ) { - return null - } - return { deployment, environment } + const deployment = managedTunnelAccess( + environment, + this.dependencies.getTargetStore()?.getTarget(active.targetId), + { + environmentId, + sshTargetId: active.targetId, + sshTargetGeneration: active.sshTargetGeneration, + localPort: active.localPort, + remotePort: active.preferredPort + } + ) + return environment && deployment ? { deployment, environment } : null } private stillOwned( environmentId: string, active: ActiveOrcadTunnel, ownershipGeneration: number, - managerGeneration: number, - allowMissingActive = false + managerGeneration: number ): boolean { const current = this.dependencies.active.get(environmentId) return ( this.dependencies.getManagerGeneration() === managerGeneration && (this.dependencies.ownershipGenerations.get(environmentId) ?? 0) === ownershipGeneration && - (current === active || (allowMissingActive && current === undefined)) + (current === active || current === undefined) ) } } diff --git a/src/main/ssh/orcad-managed-tunnel-serving.ts b/src/main/ssh/orcad-managed-tunnel-serving.ts index bd545c753605..a21bfc7392b1 100644 --- a/src/main/ssh/orcad-managed-tunnel-serving.ts +++ b/src/main/ssh/orcad-managed-tunnel-serving.ts @@ -5,7 +5,8 @@ */ import type { KnownRuntimeEnvironment } from '../../shared/runtime-environments' import type { OrcadManagedServing } from './orcad-managed-serving' -import type { ActiveOrcadTunnel, OrcadManagedServingCheck } from './orcad-managed-tunnel-resume' +import type { OrcadManagedServingCheck } from './orcad-managed-tunnel-resume' +import type { ActiveOrcadTunnel } from './orcad-managed-tunnel-active' import type { SshPortForwardManager } from './ssh-port-forward' import type { SshTarget } from '../../shared/ssh-types'