Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -102,22 +102,38 @@ describe('reopening a managed tunnel after orcad restarted elsewhere', () => {
return saved ? getPreferredPairingOffer(saved) : null
},
deployAndLaunch: async () => ({
outcome: 'ready', distro: 'Ubuntu', distroId: null, fullVersion: '1', pid: null,
holder: 'adopted', port: Number(new URL(server.getWebSocketEndpoint()!).port),
readiness: { runtimeId: server['runtime'].getRuntimeId(),
outcome: 'ready',
distro: 'Ubuntu',
distroId: null,
fullVersion: '1',
pid: null,
holder: 'adopted',
port: Number(new URL(server.getWebSocketEndpoint()!).port),
readiness: {
runtimeId: server['runtime'].getRuntimeId(),
managedWslCliReconciliation: 'settled',
...await buildServePairingReadiness({ pairingAddress: '127.0.0.1' }, server) }
...(await buildServePairingReadiness({ pairingAddress: '127.0.0.1' }, server))
}
}),
listen: async (_transport, { remotePort }) => ({
entry: { id: 'bridge', connectionId: 'wsl:ubuntu', localPort: remotePort,
remoteHost: '127.0.0.1', remotePort },
close: async () => {}, dispose: () => {}
entry: {
id: 'bridge',
connectionId: 'wsl:ubuntu',
localPort: remotePort,
remoteHost: '127.0.0.1',
remotePort
},
close: async () => {},
dispose: () => {}
})
})
const dispatch = vi.spyOn(server['dispatcher'], 'dispatchStreaming')
try {
const connection = await manager.ensure('Ubuntu')
return { connection, caller: dispatch.mock.calls.at(-1)?.[2]?.authenticatedCallerFingerprint }
return {
connection,
caller: dispatch.mock.calls.at(-1)?.[2]?.authenticatedCallerFingerprint
}
} finally {
dispatch.mockRestore()
await manager.dispose()
Expand All @@ -126,9 +142,15 @@ describe('reopening a managed tunnel after orcad restarted elsewhere', () => {
const first = await connect()
expect(first.caller).toBeTruthy()
addWslHostEnvironment(desktopProfile, {
id: 'wsl:ubuntu', name: 'Ubuntu', pairingCode: first.connection.pairingOffer,
record: { kind: 'wsl', distro: 'Ubuntu',
localPort: first.connection.localPort, remotePort: first.connection.remotePort }
id: 'wsl:ubuntu',
name: 'Ubuntu',
pairingCode: first.connection.pairingOffer,
record: {
kind: 'wsl',
distro: 'Ubuntu',
localPort: first.connection.localPort,
remotePort: first.connection.remotePort
}
})
await server.stop()
server = await startOrcad(hostProfile)
Expand All @@ -139,7 +161,8 @@ describe('reopening a managed tunnel after orcad restarted elsewhere', () => {
await expect(connect()).rejects.toThrow('orcad_identity_mismatch')
refreshWslHostPairing(desktopProfile, 'wsl:ubuntu', {
pairingCode: encodePairingOffer(pairingOffer(server)),
localPort: restarted.connection.localPort, remotePort: restarted.connection.remotePort
localPort: restarted.connection.localPort,
remotePort: restarted.connection.remotePort
})
const rePaired = await connect()
expect(rePaired.caller).toBeTruthy()
Expand Down
3 changes: 2 additions & 1 deletion src/main/wsl/wsl-host-first-connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { errorMessage } from '../../shared/error-message'
import type { WslHostAddResult } from '../../shared/wsl-host-types'
import { OrcadHostUnsupportedError } from '../orcad/orcad-host-unavailable'
import type { WslHostConnector } from './wsl-host-service'
import { WslOrcadLaunchDeferredError, type WslOrcadConnection } from './wsl-orcad-connection'
import type { WslOrcadConnection } from './wsl-orcad-connection'
import { WslOrcadLaunchDeferredError } from './wsl-orcad-connection-errors'

type WslHostAddRefusal = Extract<WslHostAddResult, { ok: false }>
type AddConnector = Pick<WslHostConnector, 'connect' | 'disconnect' | 'releaseHolder'>
Expand Down
3 changes: 2 additions & 1 deletion src/main/wsl/wsl-host-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { getPreferredPairingOffer } from '../../shared/runtime-environments'
import type { WslHostServerStop, WslHostStatus } from '../../shared/wsl-host-types'
import type { WslHostDistroCandidate } from './wsl-host-distro-list'
import { WslHostService, type WslHostConnector } from './wsl-host-service'
import { WslOrcadLaunchDeferredError, type WslOrcadConnection } from './wsl-orcad-connection'
import type { WslOrcadConnection } from './wsl-orcad-connection'
import { WslOrcadLaunchDeferredError } from './wsl-orcad-connection-errors'

const KEY = Buffer.alloc(32, 3).toString('base64')
const UBUNTU_ID = '0e3a7f2c-1b4d-4c5e-9f60-7a8b9c0d1e2f'
Expand Down
18 changes: 18 additions & 0 deletions src/main/wsl/wsl-orcad-connection-errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { WslOrcadLaunch } from './wsl-orcad-deploy'

export type WslOrcadDeferredLaunch = Extract<WslOrcadLaunch, { outcome: 'deferred' }>

/** orcad installed an update it may not activate yet (live terminals); nothing is listening. */
export class WslOrcadLaunchDeferredError extends Error {
constructor(readonly launch: WslOrcadDeferredLaunch) {
super(launch.reason)
this.name = 'WslOrcadLaunchDeferredError'
}
}

export class WslOrcadConnectionSupersededError extends Error {
constructor(distro: string) {
super(`The connection to the WSL distro ${distro} was closed while it was being opened.`)
this.name = 'WslOrcadConnectionSupersededError'
}
}
6 changes: 4 additions & 2 deletions src/main/wsl/wsl-orcad-connection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import { startEchoServer } from '../managed-server/lifecycle/orcad-stdio-bridge-
import { fakeWslBridgeChildren, posixOrcadStdioBridge } from './wsl-orcad-bridge-test-child'
import {
WslOrcadConnectionManager,
WslOrcadConnectionSupersededError,
WslOrcadLaunchDeferredError,
type WslOrcadConnection,
type WslOrcadConnectionDependencies
} from './wsl-orcad-connection'
import {
WslOrcadConnectionSupersededError,
WslOrcadLaunchDeferredError
} from './wsl-orcad-connection-errors'
import type { WslOrcadLaunch } from './wsl-orcad-deploy'
import { createWslOrcadHostTransport } from './wsl-orcad-host-transport'
import { listenWslOrcadStdioBridge } from './wsl-orcad-stdio-bridge'
Expand Down
52 changes: 22 additions & 30 deletions src/main/wsl/wsl-orcad-connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ import type { WslOrcadHostTarget } from './wsl-orcad-host-target'
import { createWslOrcadHostTransport } from './wsl-orcad-host-transport'
import { resolveWslOrcadPairing } from './wsl-orcad-pairing-identity'
import { trackWslOrcadHostEnvironment } from './wsl-orcad-host-environments'
import {
WslOrcadConnectionSupersededError,
WslOrcadLaunchDeferredError
} from './wsl-orcad-connection-errors'
import {
listenWslOrcadStdioBridge,
type WslOrcadStdioBridgeOptions
Expand All @@ -46,23 +50,6 @@ export type WslOrcadConnection = {
close(): Promise<void>
}

type WslOrcadDeferredLaunch = Extract<WslOrcadLaunch, { outcome: 'deferred' }>

/** orcad installed an update it may not activate yet (live terminals); nothing is listening. */
export class WslOrcadLaunchDeferredError extends Error {
constructor(readonly launch: WslOrcadDeferredLaunch) {
super(launch.reason)
this.name = 'WslOrcadLaunchDeferredError'
}
}

export class WslOrcadConnectionSupersededError extends Error {
constructor(distro: string) {
super(`The connection to the WSL distro ${distro} was closed while it was being opened.`)
this.name = 'WslOrcadConnectionSupersededError'
}
}

export type WslOrcadConnectOptions = {
environmentId?: string
label?: string
Expand Down Expand Up @@ -201,12 +188,9 @@ export class WslOrcadConnectionManager {
const generation = this.generation(key)
const environmentId = options.environmentId ?? defaultWslOrcadEnvironmentId(distro)
const label = options.label ?? distro
const transport = (this.dependencies.createTransport ?? defaultTransport)({
kind: 'wsl',
id: environmentId,
distro,
label
})
const target: WslOrcadHostTarget = { kind: 'wsl', id: environmentId, distro, label }
const transport =
this.dependencies.createTransport?.(target) ?? createWslOrcadHostTransport({ target })
// Every call relaunches a stopped orcad, so a distro restart is recovered here.
const launch = await (this.dependencies.deployAndLaunch ?? deployAndLaunchWslOrcad)(distro, {
environmentId,
Expand Down Expand Up @@ -234,12 +218,21 @@ export class WslOrcadConnectionManager {
current.connection.environmentId === environmentId
) {
const pairingOffer = await resolveWslOrcadPairing(
launch, current.connection.localPort, paired, this.dependencies.verifyIdentity
launch,
current.connection.localPort,
paired,
this.dependencies.verifyIdentity
)
if (this.generation(key) !== generation) {
throw new WslOrcadConnectionSupersededError(distro)
}
return this.refreshOffer(key, current, launch, pairingOffer, options.status ? status.ready : null)
return this.refreshOffer(
key,
current,
launch,
pairingOffer,
options.status ? status.ready : null
)
}
// orcad moved ports (or this is the first connect): the old bridge dials a dead port.
const preferredLocalPort = current?.connection.localPort ?? options.localPort ?? 0
Expand All @@ -260,7 +253,10 @@ export class WslOrcadConnectionManager {
throw new WslOrcadConnectionSupersededError(distro)
}
const pairingOffer = await resolveWslOrcadPairing(
launch, forward.entry.localPort, paired, this.dependencies.verifyIdentity
launch,
forward.entry.localPort,
paired,
this.dependencies.verifyIdentity
)
if (this.generation(key) !== generation) {
throw new WslOrcadConnectionSupersededError(distro)
Expand Down Expand Up @@ -341,7 +337,3 @@ export class WslOrcadConnectionManager {
}
}
}

function defaultTransport(target: WslOrcadHostTarget): OrcadHostTransport {
return createWslOrcadHostTransport({ target })
}
4 changes: 3 additions & 1 deletion src/main/wsl/wsl-orcad-pairing-identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export async function resolveWslOrcadPairing(
throw new OrcadManagedIdentityError(launch.port, identity.detail)
}
if (identity.verdict === 'unreachable') {
console.warn(`[wsl] Orca server in ${launch.distro} did not answer its bridge: ${identity.detail}`)
console.warn(
`[wsl] Orca server in ${launch.distro} did not answer its bridge: ${identity.detail}`
)
}
return pairingOffer
}
Expand Down
Loading