mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
feature: Adding in the stopping state (#2677)
* feature: Adding in the stopping state * chore: Deal with timeout in the sigterm for main * chore: Update the timeout * Update web/projects/ui/src/app/pages/apps-routes/app-list/app-list-pkg/app-list-pkg.component.ts Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> * Update web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> --------- Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { DEFAULT_SIGTERM_TIMEOUT } from "."
|
||||
import { NO_TIMEOUT, SIGKILL, SIGTERM } from "../StartSdk"
|
||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||
import { Effects, ImageId, ValidIfNoStupidEscape } from "../types"
|
||||
@@ -10,6 +11,7 @@ export class CommandController {
|
||||
readonly runningAnswer: Promise<unknown>,
|
||||
readonly overlay: Overlay,
|
||||
readonly pid: number | undefined,
|
||||
readonly sigtermTimeout: number = DEFAULT_SIGTERM_TIMEOUT,
|
||||
) {}
|
||||
static of<Manifest extends SDKManifest>() {
|
||||
return async <A extends string>(
|
||||
@@ -20,6 +22,8 @@ export class CommandController {
|
||||
},
|
||||
command: ValidIfNoStupidEscape<A> | [string, ...string[]],
|
||||
options: {
|
||||
// Defaults to the DEFAULT_SIGTERM_TIMEOUT = 30_000ms
|
||||
sigtermTimeout?: number
|
||||
mounts?: { path: string; options: MountOptions }[]
|
||||
overlay?: Overlay
|
||||
env?:
|
||||
@@ -67,10 +71,14 @@ export class CommandController {
|
||||
|
||||
const pid = childProcess.pid
|
||||
|
||||
return new CommandController(answer, overlay, pid)
|
||||
return new CommandController(answer, overlay, pid, options.sigtermTimeout)
|
||||
}
|
||||
}
|
||||
async wait() {
|
||||
async wait(timeout: number = NO_TIMEOUT) {
|
||||
if (timeout > 0)
|
||||
setTimeout(() => {
|
||||
this.term()
|
||||
}, timeout)
|
||||
try {
|
||||
return await this.runningAnswer
|
||||
} finally {
|
||||
@@ -82,7 +90,7 @@ export class CommandController {
|
||||
await this.overlay.destroy().catch((_) => {})
|
||||
}
|
||||
}
|
||||
async term({ signal = SIGTERM, timeout = NO_TIMEOUT } = {}) {
|
||||
async term({ signal = SIGTERM, timeout = this.sigtermTimeout } = {}) {
|
||||
if (this.pid === undefined) return
|
||||
try {
|
||||
await cpExecFile("pkill", [
|
||||
|
||||
@@ -34,6 +34,7 @@ export class Daemon {
|
||||
user?: string | undefined
|
||||
onStdout?: (x: Buffer) => void
|
||||
onStderr?: (x: Buffer) => void
|
||||
sigtermTimeout?: number
|
||||
},
|
||||
) => {
|
||||
const startCommand = () =>
|
||||
|
||||
@@ -44,6 +44,7 @@ type DaemonsParams<
|
||||
env?: Record<string, string>
|
||||
ready: Ready
|
||||
requires: Exclude<Ids, Id>[]
|
||||
sigtermTimeout?: number
|
||||
}
|
||||
|
||||
type ErrorDuplicateId<Id extends string> = `The id '${Id}' is already used`
|
||||
@@ -136,6 +137,7 @@ export class Daemons<Manifest extends SDKManifest, Ids extends string> {
|
||||
this.ids,
|
||||
options.ready,
|
||||
this.effects,
|
||||
options.sigtermTimeout,
|
||||
)
|
||||
const daemons = this.daemons.concat(daemon)
|
||||
const ids = [...this.ids, id] as (Ids | Id)[]
|
||||
|
||||
@@ -3,6 +3,7 @@ import { defaultTrigger } from "../trigger/defaultTrigger"
|
||||
import { Ready } from "./Daemons"
|
||||
import { Daemon } from "./Daemon"
|
||||
import { Effects } from "../types"
|
||||
import { DEFAULT_SIGTERM_TIMEOUT } from "."
|
||||
|
||||
const oncePromise = <T>() => {
|
||||
let resolve: (value: T) => void
|
||||
@@ -32,6 +33,7 @@ export class HealthDaemon {
|
||||
readonly ids: string[],
|
||||
readonly ready: Ready,
|
||||
readonly effects: Effects,
|
||||
readonly sigtermTimeout: number = DEFAULT_SIGTERM_TIMEOUT,
|
||||
) {
|
||||
this.updateStatus()
|
||||
this.dependencies.forEach((d) => d.addWatcher(() => this.updateStatus()))
|
||||
@@ -46,7 +48,12 @@ export class HealthDaemon {
|
||||
this.#running = false
|
||||
this.#healthCheckCleanup?.()
|
||||
|
||||
await this.daemon.then((d) => d.stop(termOptions))
|
||||
await this.daemon.then((d) =>
|
||||
d.stop({
|
||||
timeout: this.sigtermTimeout,
|
||||
...termOptions,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
/** Want to add another notifier that the health might have changed */
|
||||
|
||||
@@ -7,6 +7,7 @@ import "./Daemons"
|
||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||
import { MainEffects } from "../StartSdk"
|
||||
|
||||
export const DEFAULT_SIGTERM_TIMEOUT = 30_000
|
||||
/**
|
||||
* Used to ensure that the main function is running with the valid proofs.
|
||||
* We first do the folowing order of things
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { Duration } from "./Duration"
|
||||
import type { HealthCheckId } from "./HealthCheckId"
|
||||
import type { HealthCheckResult } from "./HealthCheckResult"
|
||||
|
||||
@@ -7,7 +6,7 @@ export type MainStatus =
|
||||
| { status: "stopped" }
|
||||
| { status: "restarting" }
|
||||
| { status: "restoring" }
|
||||
| { status: "stopping"; timeout: Duration }
|
||||
| { status: "stopping" }
|
||||
| { status: "starting" }
|
||||
| {
|
||||
status: "running"
|
||||
|
||||
34
sdk/lib/util/inMs.test.ts
Normal file
34
sdk/lib/util/inMs.test.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { inMs } from "./inMs"
|
||||
|
||||
describe("inMs", () => {
|
||||
test("28.001s", () => {
|
||||
expect(inMs("28.001s")).toBe(28001)
|
||||
})
|
||||
test("28.123s", () => {
|
||||
expect(inMs("28.123s")).toBe(28123)
|
||||
})
|
||||
test(".123s", () => {
|
||||
expect(inMs(".123s")).toBe(123)
|
||||
})
|
||||
test("123ms", () => {
|
||||
expect(inMs("123ms")).toBe(123)
|
||||
})
|
||||
test("1h", () => {
|
||||
expect(inMs("1h")).toBe(3600000)
|
||||
})
|
||||
test("1m", () => {
|
||||
expect(inMs("1m")).toBe(60000)
|
||||
})
|
||||
test("1m", () => {
|
||||
expect(inMs("1d")).toBe(1000 * 60 * 60 * 24)
|
||||
})
|
||||
test("123", () => {
|
||||
expect(() => inMs("123")).toThrowError("Invalid time format: 123")
|
||||
})
|
||||
test("123 as number", () => {
|
||||
expect(inMs(123)).toBe(123)
|
||||
})
|
||||
test.only("undefined", () => {
|
||||
expect(inMs(undefined)).toBe(undefined)
|
||||
})
|
||||
})
|
||||
31
sdk/lib/util/inMs.ts
Normal file
31
sdk/lib/util/inMs.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { DEFAULT_SIGTERM_TIMEOUT } from "../mainFn"
|
||||
|
||||
const matchTimeRegex = /^\s*(\d+)?(\.\d+)?\s*(ms|s|m|h|d)/
|
||||
|
||||
const unitMultiplier = (unit?: string) => {
|
||||
if (!unit) return 1
|
||||
if (unit === "ms") return 1
|
||||
if (unit === "s") return 1000
|
||||
if (unit === "m") return 1000 * 60
|
||||
if (unit === "h") return 1000 * 60 * 60
|
||||
if (unit === "d") return 1000 * 60 * 60 * 24
|
||||
throw new Error(`Invalid unit: ${unit}`)
|
||||
}
|
||||
const digitsMs = (digits: string | null, multiplier: number) => {
|
||||
if (!digits) return 0
|
||||
const value = parseInt(digits.slice(1))
|
||||
const divideBy = multiplier / Math.pow(10, digits.length - 1)
|
||||
return Math.round(value * divideBy)
|
||||
}
|
||||
export const inMs = (time?: string | number) => {
|
||||
if (typeof time === "number") return time
|
||||
if (!time) return undefined
|
||||
const matches = time.match(matchTimeRegex)
|
||||
if (!matches) throw new Error(`Invalid time format: ${time}`)
|
||||
const [_, leftHandSide, digits, unit] = matches
|
||||
const multiplier = unitMultiplier(unit)
|
||||
const firstValue = parseInt(leftHandSide || "0") * multiplier
|
||||
const secondValue = digitsMs(digits, multiplier)
|
||||
|
||||
return firstValue + secondValue
|
||||
}
|
||||
@@ -12,3 +12,4 @@ export { addressHostToUrl } from "./getServiceInterface"
|
||||
export { hostnameInfoToAddress } from "./Hostname"
|
||||
export * from "./typeHelpers"
|
||||
export { getDefaultString } from "./getDefaultString"
|
||||
export { inMs } from "./inMs"
|
||||
|
||||
Reference in New Issue
Block a user