Skip to content

Commit

Permalink
fix: kill subprocess after timeout (#827)
Browse files Browse the repository at this point in the history
When the api fails to stop the process, kill it forcefully
  • Loading branch information
achingbrain authored Apr 19, 2024
1 parent 4569bd2 commit 09c9fcd
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 20 deletions.
19 changes: 11 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export interface Node<API = unknown, Options = NodeOptions, Info extends Record<
cleanup(args?: CleanupArgs): Promise<void>
}

export interface NodeOptions<InitOptions = unknown, StartOptions = unknown> {
export interface NodeOptions<InitOptions = unknown, StartOptions = unknown, StopOptions = unknown, CleanOptions = unknown> {
/**
* The type of controller
*/
Expand Down Expand Up @@ -193,13 +193,6 @@ export interface NodeOptions<InitOptions = unknown, StartOptions = unknown> {
*/
args?: string[]

/**
* How long to wait before force killing a daemon in ms
*
* @default 5000
*/
forceKillTimeout?: number

/**
* Init options
*/
Expand All @@ -209,6 +202,16 @@ export interface NodeOptions<InitOptions = unknown, StartOptions = unknown> {
* Start options
*/
start?: StartOptions

/**
* Stop options
*/
stop?: StopOptions

/**
* Clean options
*/
clean?: CleanOptions
}

export interface NodeOptionsOverrides {
Expand Down
18 changes: 15 additions & 3 deletions src/kubo/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { KuboNode, KuboInfo, KuboInitOptions, KuboOptions, KuboStartOptions } from './index.js'
import type { KuboNode, KuboInfo, KuboInitOptions, KuboOptions, KuboStartOptions, KuboStopOptions } from './index.js'
import type { PeerInfo } from '@libp2p/interface'
import type { KuboRPCClient } from 'kubo-rpc-client'

Expand All @@ -23,6 +23,7 @@ export default class KuboClient implements KuboNode {
private _api?: KuboRPCClient
private readonly initArgs?: KuboInitOptions
private readonly startArgs?: KuboStartOptions
private readonly stopArgs?: KuboStopOptions

constructor (options: KuboClientInit) {
if (options.rpc == null) {
Expand All @@ -43,6 +44,10 @@ export default class KuboClient implements KuboNode {
if (options.start != null && typeof options.start !== 'boolean') {
this.startArgs = options.start
}

if (options.stop != null) {
this.stopArgs = options.stop
}
}

get api (): KuboRPCClient {
Expand Down Expand Up @@ -102,9 +107,16 @@ export default class KuboClient implements KuboNode {
this._api = this.options.rpc(info.api)
}

async stop (): Promise<void> {
async stop (args?: KuboStopOptions): Promise<void> {
const response = await fetch(`${this.endpoint}/stop?${new URLSearchParams({ id: this.id })}`, {
method: 'POST'
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
...(this.stopArgs ?? {}),
...(args ?? {})
})
})

if (!response.ok) {
Expand Down
31 changes: 24 additions & 7 deletions src/kubo/daemon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import mergeOptions from 'merge-options'
import pDefer from 'p-defer'
import waitFor from 'p-wait-for'
import { checkForRunningApi, tmpDir, buildStartArgs, repoExists, buildInitArgs } from './utils.js'
import type { KuboNode, KuboInfo, KuboInitOptions, KuboOptions, KuboStartOptions } from './index.js'
import type { KuboNode, KuboInfo, KuboInitOptions, KuboOptions, KuboStartOptions, KuboStopOptions } from './index.js'
import type { Logger } from '@libp2p/interface'
import type { KuboRPCClient } from 'kubo-rpc-client'

Expand Down Expand Up @@ -34,6 +34,7 @@ export default class KuboDaemon implements KuboNode {
private readonly env: Record<string, string>
private readonly initArgs?: KuboInitOptions
private readonly startArgs?: KuboStartOptions
private readonly stopArgs?: KuboStopOptions

constructor (options: KuboOptions) {
if (options.rpc == null) {
Expand All @@ -59,6 +60,10 @@ export default class KuboDaemon implements KuboNode {
if (options.start != null && typeof options.start !== 'boolean') {
this.startArgs = options.start
}

if (options.stop != null) {
this.stopArgs = options.stop
}
}

get api (): KuboRPCClient {
Expand Down Expand Up @@ -223,20 +228,32 @@ export default class KuboDaemon implements KuboNode {
await deferred.promise
}

async stop (options: { timeout?: number } = {}): Promise<void> {
const timeout = options.timeout ?? 60000
async stop (options?: KuboStopOptions): Promise<void> {
const stopOptions = {
...(this.stopArgs ?? {}),
...(options ?? {})
}
const timeout = stopOptions.forceKillTimeout ?? 1000
const subprocess = this.subprocess

if (subprocess == null || subprocess.exitCode != null || this._api == null) {
return
}

if (!(await this.api.isOnline())) {
return
}

await this.api.stop()

// wait for the subprocess to exit and declare ourselves stopped
await waitFor(() => subprocess.exitCode != null, {
timeout
})
try {
// wait for the subprocess to exit and declare ourselves stopped
await waitFor(() => subprocess.exitCode != null, {
timeout
})
} catch {
subprocess.kill('SIGKILL')
}

if (this.disposable) {
// wait for the cleanup routine to run after the subprocess has exited
Expand Down
13 changes: 11 additions & 2 deletions src/kubo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export interface KuboStartOptions {
args?: string[]
}

export interface KuboOptions extends NodeOptions<boolean | KuboInitOptions, boolean | KuboStartOptions> {
export interface KuboOptions extends NodeOptions<boolean | KuboInitOptions, boolean | KuboStartOptions, KuboStopOptions> {
type: 'kubo'

/**
Expand All @@ -58,6 +58,15 @@ export interface KuboOptions extends NodeOptions<boolean | KuboInitOptions, bool
repo?: string
}

export interface KuboStopOptions {
/**
* How long to wait in ms before sending `SIGKILL` to the process
*
* @default 1000
*/
forceKillTimeout?: number
}

export interface KuboInfo {
pid?: number
version?: string
Expand All @@ -67,6 +76,6 @@ export interface KuboInfo {
repo: string
}

export interface KuboNode extends Node<KuboRPCClient, KuboOptions, KuboInfo, KuboInitOptions, KuboStartOptions> {
export interface KuboNode extends Node<KuboRPCClient, KuboOptions, KuboInfo, KuboInitOptions, KuboStartOptions, KuboStopOptions> {

}

0 comments on commit 09c9fcd

Please sign in to comment.