mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
addHealthCheck instead of additionalHealthChecks for Daemons (#2962)
* addHealthCheck on Daemons * fix bug that prevents domains without protocols from being deleted * fixes from testing * version bump * add sdk version to UI * fix useEntrypoint * fix dependency health check error display * minor fixes * beta.29 * fixes from testing * beta.30 * set /etc/os-release (#2918) * remove check-monitor from kiosk (#2059) * add units for progress (#2693) * use new progress type * alpha.7 * fix up pwa stuff * fix wormhole-squashfs and prune boot (#2964) * don't exit on expected errors * use bash --------- Co-authored-by: Matt Hill <mattnine@protonmail.com>
This commit is contained in:
@@ -37,9 +37,7 @@ export type Ready = {
|
||||
})
|
||||
* ```
|
||||
*/
|
||||
fn: (
|
||||
subcontainer: SubContainer<Manifest>,
|
||||
) => Promise<HealthCheckResult> | HealthCheckResult
|
||||
fn: () => Promise<HealthCheckResult> | HealthCheckResult
|
||||
/**
|
||||
* A duration in milliseconds to treat a failing health check as "starting"
|
||||
*
|
||||
@@ -65,30 +63,40 @@ export type ExecCommandOptions = {
|
||||
onStderr?: (chunk: Buffer | string | any) => void
|
||||
}
|
||||
|
||||
export type ExecFnOptions = {
|
||||
export type ExecFnOptions<
|
||||
Manifest extends T.SDKManifest,
|
||||
C extends SubContainer<Manifest> | null,
|
||||
> = {
|
||||
fn: (
|
||||
subcontainer: SubContainer<Manifest>,
|
||||
subcontainer: C,
|
||||
abort: AbortController,
|
||||
) => Promise<ExecCommandOptions | null>
|
||||
) => Promise<C extends null ? null : ExecCommandOptions | null>
|
||||
// Defaults to the DEFAULT_SIGTERM_TIMEOUT = 30_000ms
|
||||
sigtermTimeout?: number
|
||||
}
|
||||
|
||||
export type DaemonCommandType = ExecCommandOptions | ExecFnOptions
|
||||
export type DaemonCommandType<
|
||||
Manifest extends T.SDKManifest,
|
||||
C extends SubContainer<Manifest> | null,
|
||||
> = ExecFnOptions<Manifest, C> | (C extends null ? never : ExecCommandOptions)
|
||||
|
||||
type NewDaemonParams<Manifest extends T.SDKManifest> = {
|
||||
type NewDaemonParams<
|
||||
Manifest extends T.SDKManifest,
|
||||
C extends SubContainer<Manifest> | null,
|
||||
> = {
|
||||
/** What to run as the daemon: either an async fn or a commandline command to run in the subcontainer */
|
||||
exec: DaemonCommandType | null
|
||||
/** Information about the subcontainer in which the daemon runs */
|
||||
subcontainer: SubContainer<Manifest>
|
||||
exec: DaemonCommandType<Manifest, C>
|
||||
/** The subcontainer in which the daemon runs */
|
||||
subcontainer: C
|
||||
}
|
||||
|
||||
type AddDaemonParams<
|
||||
Manifest extends T.SDKManifest,
|
||||
Ids extends string,
|
||||
Id extends string,
|
||||
C extends SubContainer<Manifest> | null,
|
||||
> = (
|
||||
| NewDaemonParams<Manifest>
|
||||
| NewDaemonParams<Manifest, C>
|
||||
| {
|
||||
daemon: Daemon<Manifest>
|
||||
}
|
||||
@@ -102,8 +110,15 @@ type AddOneshotParams<
|
||||
Manifest extends T.SDKManifest,
|
||||
Ids extends string,
|
||||
Id extends string,
|
||||
> = NewDaemonParams<Manifest> & {
|
||||
exec: DaemonCommandType
|
||||
C extends SubContainer<Manifest> | null,
|
||||
> = NewDaemonParams<Manifest, C> & {
|
||||
exec: DaemonCommandType<Manifest, C>
|
||||
/** An array of IDs of prior daemons whose successful initializations are required before this daemon will initialize */
|
||||
requires: Exclude<Ids, Id>[]
|
||||
}
|
||||
|
||||
type AddHealthCheckParams<Ids extends string, Id extends string> = {
|
||||
ready: Ready
|
||||
/** An array of IDs of prior daemons whose successful initializations are required before this daemon will initialize */
|
||||
requires: Exclude<Ids, Id>[]
|
||||
}
|
||||
@@ -111,7 +126,7 @@ type AddOneshotParams<
|
||||
type ErrorDuplicateId<Id extends string> = `The id '${Id}' is already used`
|
||||
|
||||
export const runCommand = <Manifest extends T.SDKManifest>() =>
|
||||
CommandController.of<Manifest>()
|
||||
CommandController.of<Manifest, SubContainer<Manifest>>()
|
||||
|
||||
/**
|
||||
* A class for defining and controlling the service daemons
|
||||
@@ -141,11 +156,12 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
{
|
||||
private constructor(
|
||||
readonly effects: T.Effects,
|
||||
readonly started: (onTerm: () => PromiseLike<void>) => PromiseLike<null>,
|
||||
readonly started:
|
||||
| ((onTerm: () => PromiseLike<void>) => PromiseLike<null>)
|
||||
| null,
|
||||
readonly daemons: Promise<Daemon<Manifest>>[],
|
||||
readonly ids: Ids[],
|
||||
readonly healthDaemons: HealthDaemon<Manifest>[],
|
||||
readonly healthChecks: HealthCheck[],
|
||||
) {}
|
||||
/**
|
||||
* Returns an empty new Daemons class with the provided inputSpec.
|
||||
@@ -154,13 +170,18 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
*
|
||||
* Daemons run in the order they are defined, with latter daemons being capable of
|
||||
* depending on prior daemons
|
||||
* @param options
|
||||
*
|
||||
* @param effects
|
||||
*
|
||||
* @param started
|
||||
* @returns
|
||||
*/
|
||||
static of<Manifest extends T.SDKManifest>(options: {
|
||||
effects: T.Effects
|
||||
started: (onTerm: () => PromiseLike<void>) => PromiseLike<null>
|
||||
healthChecks: HealthCheck[]
|
||||
/**
|
||||
* A closure to run once the system is launched. If you are in main, provide the `started` argument you receive from the function arguments
|
||||
*/
|
||||
started: ((onTerm: () => PromiseLike<void>) => PromiseLike<null>) | null
|
||||
}) {
|
||||
return new Daemons<Manifest, never>(
|
||||
options.effects,
|
||||
@@ -168,7 +189,6 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
options.healthChecks,
|
||||
)
|
||||
}
|
||||
/**
|
||||
@@ -177,19 +197,19 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
* @param options
|
||||
* @returns a new Daemons object
|
||||
*/
|
||||
addDaemon<Id extends string>(
|
||||
addDaemon<Id extends string, C extends SubContainer<Manifest> | null>(
|
||||
// prettier-ignore
|
||||
id:
|
||||
"" extends Id ? never :
|
||||
ErrorDuplicateId<Id> extends Id ? never :
|
||||
Id extends Ids ? ErrorDuplicateId<Id> :
|
||||
Id,
|
||||
options: AddDaemonParams<Manifest, Ids, Id>,
|
||||
options: AddDaemonParams<Manifest, Ids, Id, C>,
|
||||
) {
|
||||
const daemon =
|
||||
"daemon" in options
|
||||
? Promise.resolve(options.daemon)
|
||||
: Daemon.of<Manifest>()(
|
||||
: Daemon.of<Manifest>()<C>(
|
||||
this.effects,
|
||||
options.subcontainer,
|
||||
options.exec,
|
||||
@@ -201,11 +221,10 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
.filter((x) => x >= 0)
|
||||
.map((id) => this.healthDaemons[id]),
|
||||
id,
|
||||
this.ids,
|
||||
options.ready,
|
||||
this.effects,
|
||||
)
|
||||
const daemons = this.daemons.concat(daemon)
|
||||
const daemons = [...this.daemons, daemon]
|
||||
const ids = [...this.ids, id] as (Ids | Id)[]
|
||||
const healthDaemons = [...this.healthDaemons, healthDaemon]
|
||||
return new Daemons<Manifest, Ids | Id>(
|
||||
@@ -214,7 +233,6 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
daemons,
|
||||
ids,
|
||||
healthDaemons,
|
||||
this.healthChecks,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -225,7 +243,7 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
* @param options
|
||||
* @returns a new Daemons object
|
||||
*/
|
||||
addOneshot<Id extends string>(
|
||||
addOneshot<Id extends string, C extends SubContainer<Manifest> | null>(
|
||||
id: "" extends Id
|
||||
? never
|
||||
: ErrorDuplicateId<Id> extends Id
|
||||
@@ -233,9 +251,9 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
: Id extends Ids
|
||||
? ErrorDuplicateId<Id>
|
||||
: Id,
|
||||
options: AddOneshotParams<Manifest, Ids, Id>,
|
||||
options: AddOneshotParams<Manifest, Ids, Id, C>,
|
||||
) {
|
||||
const daemon = Oneshot.of<Manifest>()(
|
||||
const daemon = Oneshot.of<Manifest>()<C>(
|
||||
this.effects,
|
||||
options.subcontainer,
|
||||
options.exec,
|
||||
@@ -247,11 +265,10 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
.filter((x) => x >= 0)
|
||||
.map((id) => this.healthDaemons[id]),
|
||||
id,
|
||||
this.ids,
|
||||
"EXIT_SUCCESS",
|
||||
this.effects,
|
||||
)
|
||||
const daemons = this.daemons.concat(daemon)
|
||||
const daemons = [...this.daemons, daemon]
|
||||
const ids = [...this.ids, id] as (Ids | Id)[]
|
||||
const healthDaemons = [...this.healthDaemons, healthDaemon]
|
||||
return new Daemons<Manifest, Ids | Id>(
|
||||
@@ -260,13 +277,95 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
daemons,
|
||||
ids,
|
||||
healthDaemons,
|
||||
this.healthChecks,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete list of daemons, including a new HealthCheck defined here
|
||||
* @param id
|
||||
* @param options
|
||||
* @returns a new Daemons object
|
||||
*/
|
||||
addHealthCheck<Id extends string>(
|
||||
id: "" extends Id
|
||||
? never
|
||||
: ErrorDuplicateId<Id> extends Id
|
||||
? never
|
||||
: Id extends Ids
|
||||
? ErrorDuplicateId<Id>
|
||||
: Id,
|
||||
options: AddHealthCheckParams<Ids, Id>,
|
||||
) {
|
||||
const healthDaemon = new HealthDaemon<Manifest>(
|
||||
null,
|
||||
options.requires
|
||||
.map((x) => this.ids.indexOf(x))
|
||||
.filter((x) => x >= 0)
|
||||
.map((id) => this.healthDaemons[id]),
|
||||
id,
|
||||
options.ready,
|
||||
this.effects,
|
||||
)
|
||||
const daemons = [...this.daemons]
|
||||
const ids = [...this.ids, id] as (Ids | Id)[]
|
||||
const healthDaemons = [...this.healthDaemons, healthDaemon]
|
||||
return new Daemons<Manifest, Ids | Id>(
|
||||
this.effects,
|
||||
this.started,
|
||||
daemons,
|
||||
ids,
|
||||
healthDaemons,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the entire system until all daemons have returned `ready`.
|
||||
* @param id
|
||||
* @param options
|
||||
* @returns a new Daemons object
|
||||
*/
|
||||
async runUntilSuccess(timeout: number | null) {
|
||||
let resolve = (_: void) => {}
|
||||
const res = new Promise<void>((res, rej) => {
|
||||
resolve = res
|
||||
if (timeout)
|
||||
setTimeout(() => {
|
||||
const notReady = this.healthDaemons
|
||||
.filter((d) => !d.isReady)
|
||||
.map((d) => d.id)
|
||||
rej(new Error(`Timed out waiting for ${notReady}`))
|
||||
}, timeout)
|
||||
})
|
||||
const daemon = Oneshot.of()(this.effects, null, {
|
||||
fn: async () => {
|
||||
resolve()
|
||||
return null
|
||||
},
|
||||
})
|
||||
const healthDaemon = new HealthDaemon<Manifest>(
|
||||
daemon,
|
||||
[...this.healthDaemons],
|
||||
"__RUN_UNTIL_SUCCESS",
|
||||
"EXIT_SUCCESS",
|
||||
this.effects,
|
||||
)
|
||||
const daemons = await new Daemons<Manifest, Ids>(
|
||||
this.effects,
|
||||
this.started,
|
||||
[...this.daemons, daemon],
|
||||
this.ids,
|
||||
[...this.healthDaemons, healthDaemon],
|
||||
).build()
|
||||
try {
|
||||
await res
|
||||
} finally {
|
||||
await daemons.term()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
async term() {
|
||||
try {
|
||||
this.healthChecks.forEach((health) => health.stop())
|
||||
for (let result of await Promise.allSettled(
|
||||
this.healthDaemons.map((x) => x.term()),
|
||||
)) {
|
||||
@@ -283,10 +382,7 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
for (const daemon of this.healthDaemons) {
|
||||
await daemon.init()
|
||||
}
|
||||
for (const health of this.healthChecks) {
|
||||
health.start()
|
||||
}
|
||||
this.started(() => this.term())
|
||||
this.started?.(() => this.term())
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user