mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 18:17:36 +00:00
fix(wsl-host): a failed removal no longer leaves the host stuck as "being removed" (#29921)
remove() marked the host as being removed, then awaited disconnect and releaseHolder with no cleanup on failure. A throw (EBUSY/EPERM on Windows) left every later connect and runtime call refused. Clear the mark in a finally, keep the record and its visible work (dropped only on success), and rethrow so the user sees the error and can retry.
This commit is contained in:
@@ -352,6 +352,26 @@ describe('WslHostService', () => {
|
||||
expect(listEnvironments(userDataPath)).toEqual([])
|
||||
})
|
||||
|
||||
it('keeps a host whose removal failed usable and removable again', async () => {
|
||||
const connector = makeConnector([UBUNTU])
|
||||
const service = makeService(connector)
|
||||
await service.add({ distro: 'Ubuntu' })
|
||||
service.setVisibleWork(['wsl:ubuntu'])
|
||||
connector.releaseHolder.mockRejectedValueOnce(
|
||||
Object.assign(new Error('EBUSY: resource busy or locked'), { code: 'EBUSY' })
|
||||
)
|
||||
|
||||
await expect(service.remove('wsl:ubuntu')).rejects.toThrow('EBUSY')
|
||||
|
||||
expect(listEnvironments(userDataPath)).toHaveLength(1)
|
||||
expect(removed).not.toHaveBeenCalled()
|
||||
await expect(service.ensureForRuntimeCall('wsl:ubuntu')).resolves.toBeUndefined()
|
||||
await expect(service.connect('wsl:ubuntu')).resolves.toBeUndefined()
|
||||
await expect(service.remove('wsl:ubuntu')).resolves.toEqual({ stopped: true })
|
||||
expect(listEnvironments(userDataPath)).toEqual([])
|
||||
expect(removed).toHaveBeenCalledWith('wsl:ubuntu')
|
||||
})
|
||||
|
||||
it('turning the experiment off closes every connection and releases holders; on resumes lazily', async () => {
|
||||
const connector = makeConnector([UBUNTU])
|
||||
const service = makeService(connector)
|
||||
|
||||
@@ -139,19 +139,23 @@ export class WslHostService {
|
||||
}
|
||||
// First: nothing may reconnect it while it is being stopped (open work, liveness, calls).
|
||||
this.removing.add(environmentId)
|
||||
this.visible.delete(environmentId)
|
||||
const connector = this.connector
|
||||
let serverStop: WslHostServerStop = { stopped: true }
|
||||
if (connector) {
|
||||
serverStop = await connector
|
||||
.stopServer(record.distro, environmentId)
|
||||
.catch((error: unknown) => ({ stopped: false, reason: errorMessage(error) }))
|
||||
await connector.disconnect(record.distro)
|
||||
// Without its holder the distro idles out.
|
||||
await connector.releaseHolder(record.distro)
|
||||
try {
|
||||
if (connector) {
|
||||
serverStop = await connector
|
||||
.stopServer(record.distro, environmentId)
|
||||
.catch((error: unknown) => ({ stopped: false, reason: errorMessage(error) }))
|
||||
await connector.disconnect(record.distro)
|
||||
// Without its holder the distro idles out.
|
||||
await connector.releaseHolder(record.distro)
|
||||
}
|
||||
removeWslHostEnvironment(this.deps.userDataPath(), environmentId)
|
||||
this.visible.delete(environmentId)
|
||||
} finally {
|
||||
// Why: a failed removal (e.g. EBUSY on Windows) must leave the host usable and removable.
|
||||
this.removing.delete(environmentId)
|
||||
}
|
||||
removeWslHostEnvironment(this.deps.userDataPath(), environmentId)
|
||||
this.removing.delete(environmentId)
|
||||
this.statuses.delete(environmentId)
|
||||
await this.deps.removed(environmentId)
|
||||
return serverStop
|
||||
@@ -236,7 +240,8 @@ export class WslHostService {
|
||||
hasUserVisibleWork(distro: string): boolean {
|
||||
const record = this.recordForDistro(distro)
|
||||
// Why the gate: liveness relaunches only for visible work, and none may run while off.
|
||||
return record ? this.canRun() && this.visible.has(record.environmentId) : false
|
||||
const id = record?.environmentId
|
||||
return id ? this.canRun() && this.visible.has(id) && !this.removing.has(id) : false
|
||||
}
|
||||
|
||||
/** Startup: keep the holders a previous run left, without booting anything. */
|
||||
|
||||
Reference in New Issue
Block a user