196 lines
9.1 KiB
TypeScript
196 lines
9.1 KiB
TypeScript
import { HealthCheckResult } from '../health/checkFns';
|
|
import { Trigger } from '../trigger';
|
|
import * as T from '../../../base/lib/types';
|
|
import { SubContainer } from '../util/SubContainer';
|
|
import * as CP from 'node:child_process';
|
|
export { Daemon } from './Daemon';
|
|
export { CommandController } from './CommandController';
|
|
import { HealthDaemon } from './HealthDaemon';
|
|
import { Daemon } from './Daemon';
|
|
import { CommandController } from './CommandController';
|
|
/** Promisified version of `child_process.exec` */
|
|
export declare const cpExec: typeof CP.exec.__promisify__;
|
|
/** Promisified version of `child_process.execFile` */
|
|
export declare const cpExecFile: typeof CP.execFile.__promisify__;
|
|
/**
|
|
* Configuration for a daemon's health-check readiness probe.
|
|
*
|
|
* Determines how the system knows when a daemon is healthy and ready to serve.
|
|
*/
|
|
export type Ready = {
|
|
/** A human-readable display name for the health check. If null, the health check itself will be from the UI */
|
|
display: string | null;
|
|
/**
|
|
* @description The function to determine the health status of the daemon
|
|
*
|
|
* The SDK provides some built-in health checks. To see them, type sdk.healthCheck.
|
|
*
|
|
* @example
|
|
* ```
|
|
fn: () =>
|
|
sdk.healthCheck.checkPortListening(effects, 80, {
|
|
successMessage: 'service listening on port 80',
|
|
errorMessage: 'service is unreachable',
|
|
})
|
|
* ```
|
|
*/
|
|
fn: () => Promise<HealthCheckResult> | HealthCheckResult;
|
|
/**
|
|
* A duration in milliseconds to treat a failing health check as "starting"
|
|
*
|
|
* defaults to 5000
|
|
*/
|
|
gracePeriod?: number;
|
|
trigger?: Trigger;
|
|
};
|
|
/**
|
|
* Options for running a daemon as a shell command inside a subcontainer.
|
|
* Includes the command to run, optional signal/timeout, environment, user, and stdio callbacks.
|
|
*/
|
|
export type ExecCommandOptions = {
|
|
command: T.CommandType;
|
|
sigtermTimeout?: number;
|
|
runAsInit?: boolean;
|
|
env?: {
|
|
[variable in string]?: string;
|
|
} | undefined;
|
|
cwd?: string | undefined;
|
|
user?: string | undefined;
|
|
onStdout?: (chunk: Buffer | string | any) => void;
|
|
onStderr?: (chunk: Buffer | string | any) => void;
|
|
};
|
|
/**
|
|
* Options for running a daemon via an async function that may optionally return
|
|
* a command to execute in the subcontainer. The function receives an `AbortSignal`
|
|
* for cooperative cancellation.
|
|
*/
|
|
export type ExecFnOptions<Manifest extends T.SDKManifest, C extends SubContainer<Manifest> | null> = {
|
|
fn: (subcontainer: C, abort: AbortSignal) => Promise<C extends null ? null : ExecCommandOptions | null>;
|
|
sigtermTimeout?: number;
|
|
};
|
|
/**
|
|
* The execution specification for a daemon: either an {@link ExecFnOptions} (async function)
|
|
* or an {@link ExecCommandOptions} (shell command, only valid when a subcontainer is provided).
|
|
*/
|
|
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, 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<Manifest, C>;
|
|
/** The subcontainer in which the daemon runs */
|
|
subcontainer: C;
|
|
};
|
|
type OptionalParamSync<T> = T | (() => T | null);
|
|
type OptionalParamAsync<T> = () => Promise<T | null>;
|
|
type AddDaemonParams<Manifest extends T.SDKManifest, Ids extends string, Id extends string, C extends SubContainer<Manifest> | null> = (NewDaemonParams<Manifest, C> | {
|
|
daemon: Daemon<Manifest>;
|
|
}) & {
|
|
ready: Ready;
|
|
/** An array of IDs of prior daemons whose successful initializations are required before this daemon will initialize */
|
|
requires: Exclude<Ids, Id>[];
|
|
};
|
|
type AddOneshotParams<Manifest extends T.SDKManifest, Ids extends string, Id extends string, 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>[];
|
|
};
|
|
type ErrorDuplicateId<Id extends string> = `The id '${Id}' is already used`;
|
|
export declare const runCommand: <Manifest extends T.SDKManifest>() => (effects: T.Effects, subcontainer: SubContainer<Manifest, T.Effects>, exec: DaemonCommandType<Manifest, SubContainer<Manifest, T.Effects>>) => Promise<CommandController<Manifest, SubContainer<Manifest, T.Effects>>>;
|
|
/**
|
|
* A class for defining and controlling the service daemons
|
|
```ts
|
|
Daemons.of({
|
|
effects,
|
|
started,
|
|
interfaceReceipt, // Provide the interfaceReceipt to prove it was completed
|
|
healthReceipts, // Provide the healthReceipts or [] to prove they were at least considered
|
|
}).addDaemon('webui', {
|
|
command: 'hello-world', // The command to start the daemon
|
|
ready: {
|
|
display: 'Web Interface',
|
|
// The function to run to determine the health status of the daemon
|
|
fn: () =>
|
|
checkPortListening(effects, 80, {
|
|
successMessage: 'The web interface is ready',
|
|
errorMessage: 'The web interface is not ready',
|
|
}),
|
|
},
|
|
requires: [],
|
|
})
|
|
```
|
|
*/
|
|
export declare class Daemons<Manifest extends T.SDKManifest, Ids extends string> implements T.DaemonBuildable {
|
|
readonly effects: T.Effects;
|
|
readonly ids: Ids[];
|
|
readonly healthDaemons: HealthDaemon<Manifest>[];
|
|
private termPromise;
|
|
private constructor();
|
|
/**
|
|
* Returns an empty new Daemons class with the provided inputSpec.
|
|
*
|
|
* Call .addDaemon() on the returned class to add a daemon.
|
|
*
|
|
* Daemons run in the order they are defined, with latter daemons being capable of
|
|
* depending on prior daemons
|
|
*
|
|
* @param effects
|
|
*
|
|
* @param started
|
|
* @returns
|
|
*/
|
|
static of<Manifest extends T.SDKManifest>(options: {
|
|
effects: T.Effects;
|
|
}): Daemons<Manifest, never>;
|
|
private addDaemonImpl;
|
|
/**
|
|
* Returns the complete list of daemons, including the one defined here
|
|
* @param id
|
|
* @param options
|
|
* @returns a new Daemons object
|
|
*/
|
|
addDaemon<Id extends string, C extends SubContainer<Manifest> | null>(id: "" extends Id ? never : ErrorDuplicateId<Id> extends Id ? never : Id extends Ids ? ErrorDuplicateId<Id> : Id, options: OptionalParamSync<AddDaemonParams<Manifest, Ids, Id, C>>): Daemons<Manifest, Ids | Id>;
|
|
addDaemon<Id extends string, C extends SubContainer<Manifest> | null>(id: "" extends Id ? never : ErrorDuplicateId<Id> extends Id ? never : Id extends Ids ? ErrorDuplicateId<Id> : Id, options: OptionalParamAsync<AddDaemonParams<Manifest, Ids, Id, C>>): Promise<Daemons<Manifest, Ids | Id>>;
|
|
/**
|
|
* Returns the complete list of daemons, including a "oneshot" daemon one defined here
|
|
* a oneshot daemon is a command that executes once when started, and is considered "running" once it exits successfully
|
|
* @param id
|
|
* @param options
|
|
* @returns a new Daemons object
|
|
*/
|
|
addOneshot<Id extends string, C extends SubContainer<Manifest> | null>(id: "" extends Id ? never : ErrorDuplicateId<Id> extends Id ? never : Id extends Ids ? ErrorDuplicateId<Id> : Id, options: OptionalParamSync<AddOneshotParams<Manifest, Ids, Id, C>>): Daemons<Manifest, Ids | Id>;
|
|
addOneshot<Id extends string, C extends SubContainer<Manifest> | null>(id: "" extends Id ? never : ErrorDuplicateId<Id> extends Id ? never : Id extends Ids ? ErrorDuplicateId<Id> : Id, options: OptionalParamAsync<AddOneshotParams<Manifest, Ids, Id, C>>): Promise<Daemons<Manifest, Ids | Id>>;
|
|
/**
|
|
* 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: OptionalParamSync<AddHealthCheckParams<Ids, Id>>): Daemons<Manifest, Ids | Id>;
|
|
addHealthCheck<Id extends string>(id: "" extends Id ? never : ErrorDuplicateId<Id> extends Id ? never : Id extends Ids ? ErrorDuplicateId<Id> : Id, options: OptionalParamAsync<AddHealthCheckParams<Ids, Id>>): Promise<Daemons<Manifest, Ids | Id>>;
|
|
/**
|
|
* Runs the entire system until all daemons have returned `ready`.
|
|
* @param id
|
|
* @param options
|
|
* @returns a new Daemons object
|
|
*/
|
|
runUntilSuccess(timeout: number | null): Promise<null>;
|
|
/**
|
|
* Gracefully terminate all daemons in reverse dependency order.
|
|
*
|
|
* Daemons with no remaining dependents are shut down first, proceeding
|
|
* until all daemons have been terminated. Falls back to a bulk shutdown
|
|
* if a dependency cycle is detected.
|
|
*/
|
|
term(): Promise<void>;
|
|
private _term;
|
|
/**
|
|
* Start all registered daemons and their health checks.
|
|
* @returns This `Daemons` instance, now running
|
|
*/
|
|
build(): Promise<this>;
|
|
}
|