mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 22:39:46 +00:00
Feature/sdk improvements (#2879)
* sdk improvements * subcontainer fixes, disable wifi on migration if not in use, filterable interfaces
This commit is contained in:
@@ -10,12 +10,13 @@ import {
|
||||
import { Drop, splitCommand } from "../util"
|
||||
import * as cp from "child_process"
|
||||
import * as fs from "node:fs/promises"
|
||||
import { Mounts } from "./Mounts"
|
||||
|
||||
export class CommandController extends Drop {
|
||||
export class CommandController<Manifest extends T.SDKManifest> extends Drop {
|
||||
private constructor(
|
||||
readonly runningAnswer: Promise<unknown>,
|
||||
private state: { exited: boolean },
|
||||
private readonly subcontainer: SubContainer,
|
||||
private readonly subcontainer: SubContainer<Manifest>,
|
||||
private process: cp.ChildProcess,
|
||||
readonly sigtermTimeout: number = DEFAULT_SIGTERM_TIMEOUT,
|
||||
) {
|
||||
@@ -29,13 +30,13 @@ export class CommandController extends Drop {
|
||||
imageId: keyof Manifest["images"] & T.ImageId
|
||||
sharedRun?: boolean
|
||||
}
|
||||
| SubContainer,
|
||||
| SubContainer<Manifest>,
|
||||
command: T.CommandType,
|
||||
options: {
|
||||
subcontainerName?: string
|
||||
// Defaults to the DEFAULT_SIGTERM_TIMEOUT = 30_000ms
|
||||
sigtermTimeout?: number
|
||||
mounts?: { mountpoint: string; options: MountOptions }[]
|
||||
mounts: Mounts<Manifest> | null
|
||||
runAsInit?: boolean
|
||||
env?:
|
||||
| {
|
||||
@@ -65,13 +66,12 @@ export class CommandController extends Drop {
|
||||
: await SubContainer.of(
|
||||
effects,
|
||||
subcontainer,
|
||||
null,
|
||||
options?.subcontainerName || commands.join(" "),
|
||||
)
|
||||
|
||||
try {
|
||||
for (let mount of options.mounts || []) {
|
||||
await subc.mount(mount.options, mount.mountpoint)
|
||||
}
|
||||
if (options.mounts) await subc.mount(options.mounts)
|
||||
|
||||
let childProcess: cp.ChildProcess
|
||||
if (options.runAsInit) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as T from "../../../base/lib/types"
|
||||
import { asError } from "../../../base/lib/util/asError"
|
||||
import { ExecSpawnable, MountOptions, SubContainer } from "../util/SubContainer"
|
||||
import { CommandController } from "./CommandController"
|
||||
import { Mounts } from "./Mounts"
|
||||
|
||||
const TIMEOUT_INCREMENT_MS = 1000
|
||||
const MAX_TIMEOUT_MS = 30000
|
||||
@@ -10,10 +11,12 @@ const MAX_TIMEOUT_MS = 30000
|
||||
* and the others state of running, where it will keep a living running command
|
||||
*/
|
||||
|
||||
export class Daemon {
|
||||
private commandController: CommandController | null = null
|
||||
export class Daemon<Manifest extends T.SDKManifest> {
|
||||
private commandController: CommandController<Manifest> | null = null
|
||||
private shouldBeRunning = false
|
||||
constructor(private startCommand: () => Promise<CommandController>) {}
|
||||
constructor(
|
||||
private startCommand: () => Promise<CommandController<Manifest>>,
|
||||
) {}
|
||||
get subContainerHandle(): undefined | ExecSpawnable {
|
||||
return this.commandController?.subContainerHandle
|
||||
}
|
||||
@@ -25,11 +28,11 @@ export class Daemon {
|
||||
imageId: keyof Manifest["images"] & T.ImageId
|
||||
sharedRun?: boolean
|
||||
}
|
||||
| SubContainer,
|
||||
| SubContainer<Manifest>,
|
||||
command: T.CommandType,
|
||||
options: {
|
||||
subcontainerName?: string
|
||||
mounts?: { mountpoint: string; options: MountOptions }[]
|
||||
mounts: Mounts<Manifest> | null
|
||||
env?:
|
||||
| {
|
||||
[variable: string]: string
|
||||
|
||||
@@ -67,7 +67,7 @@ type DaemonsParams<
|
||||
*/
|
||||
sharedRun?: boolean
|
||||
}
|
||||
| SubContainer
|
||||
| SubContainer<Manifest>
|
||||
/** For mounting the necessary volumes. Syntax: sdk.Mounts.of().addVolume() */
|
||||
mounts: Mounts<Manifest>
|
||||
env?: Record<string, string>
|
||||
@@ -113,9 +113,9 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
private constructor(
|
||||
readonly effects: T.Effects,
|
||||
readonly started: (onTerm: () => PromiseLike<void>) => PromiseLike<null>,
|
||||
readonly daemons: Promise<Daemon>[],
|
||||
readonly daemons: Promise<Daemon<Manifest>>[],
|
||||
readonly ids: Ids[],
|
||||
readonly healthDaemons: HealthDaemon[],
|
||||
readonly healthDaemons: HealthDaemon<Manifest>[],
|
||||
readonly healthChecks: HealthCheck[],
|
||||
) {}
|
||||
/**
|
||||
@@ -164,7 +164,6 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
options.command,
|
||||
{
|
||||
...options,
|
||||
mounts: options.mounts.build(),
|
||||
subcontainerName: id,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@ import { HealthCheckResult } from "../health/checkFns"
|
||||
import { defaultTrigger } from "../trigger/defaultTrigger"
|
||||
import { Ready } from "./Daemons"
|
||||
import { Daemon } from "./Daemon"
|
||||
import { SetHealth, Effects } from "../../../base/lib/types"
|
||||
import { SetHealth, Effects, SDKManifest } from "../../../base/lib/types"
|
||||
import { DEFAULT_SIGTERM_TIMEOUT } from "."
|
||||
import { asError } from "../../../base/lib/util/asError"
|
||||
|
||||
@@ -21,7 +21,7 @@ const oncePromise = <T>() => {
|
||||
* -- Running: Daemon is running and the status is in the health
|
||||
*
|
||||
*/
|
||||
export class HealthDaemon {
|
||||
export class HealthDaemon<Manifest extends SDKManifest> {
|
||||
private _health: HealthCheckResult = { result: "starting", message: null }
|
||||
private healthWatchers: Array<() => unknown> = []
|
||||
private running = false
|
||||
@@ -29,9 +29,9 @@ export class HealthDaemon {
|
||||
private resolveReady: (() => void) | undefined
|
||||
private readyPromise: Promise<void>
|
||||
constructor(
|
||||
private readonly daemon: Promise<Daemon>,
|
||||
private readonly daemon: Promise<Daemon<Manifest>>,
|
||||
readonly daemonIndex: number,
|
||||
private readonly dependencies: HealthDaemon[],
|
||||
private readonly dependencies: HealthDaemon<Manifest>[],
|
||||
readonly id: string,
|
||||
readonly ids: string[],
|
||||
readonly ready: Ready,
|
||||
|
||||
@@ -3,7 +3,13 @@ import { MountOptions } from "../util/SubContainer"
|
||||
|
||||
type MountArray = { mountpoint: string; options: MountOptions }[]
|
||||
|
||||
export class Mounts<Manifest extends T.SDKManifest> {
|
||||
export class Mounts<
|
||||
Manifest extends T.SDKManifest,
|
||||
Backups extends {
|
||||
subpath: string | null
|
||||
mountpoint: string
|
||||
} = never,
|
||||
> {
|
||||
private constructor(
|
||||
readonly volumes: {
|
||||
id: Manifest["volumes"][number]
|
||||
@@ -22,10 +28,11 @@ export class Mounts<Manifest extends T.SDKManifest> {
|
||||
mountpoint: string
|
||||
readonly: boolean
|
||||
}[],
|
||||
readonly backups: Backups[],
|
||||
) {}
|
||||
|
||||
static of<Manifest extends T.SDKManifest>() {
|
||||
return new Mounts<Manifest>([], [], [])
|
||||
return new Mounts<Manifest>([], [], [], [])
|
||||
}
|
||||
|
||||
addVolume(
|
||||
@@ -38,13 +45,20 @@ export class Mounts<Manifest extends T.SDKManifest> {
|
||||
/** Whether or not the volume should be readonly for this daemon */
|
||||
readonly: boolean,
|
||||
) {
|
||||
this.volumes.push({
|
||||
id,
|
||||
subpath,
|
||||
mountpoint,
|
||||
readonly,
|
||||
})
|
||||
return this
|
||||
return new Mounts<Manifest, Backups>(
|
||||
[
|
||||
...this.volumes,
|
||||
{
|
||||
id,
|
||||
subpath,
|
||||
mountpoint,
|
||||
readonly,
|
||||
},
|
||||
],
|
||||
[...this.assets],
|
||||
[...this.dependencies],
|
||||
[...this.backups],
|
||||
)
|
||||
}
|
||||
|
||||
addAssets(
|
||||
@@ -53,11 +67,18 @@ export class Mounts<Manifest extends T.SDKManifest> {
|
||||
/** Where to mount the asset. e.g. /asset */
|
||||
mountpoint: string,
|
||||
) {
|
||||
this.assets.push({
|
||||
subpath,
|
||||
mountpoint,
|
||||
})
|
||||
return this
|
||||
return new Mounts<Manifest, Backups>(
|
||||
[...this.volumes],
|
||||
[
|
||||
...this.assets,
|
||||
{
|
||||
subpath,
|
||||
mountpoint,
|
||||
},
|
||||
],
|
||||
[...this.dependencies],
|
||||
[...this.backups],
|
||||
)
|
||||
}
|
||||
|
||||
addDependency<DependencyManifest extends T.SDKManifest>(
|
||||
@@ -72,14 +93,36 @@ export class Mounts<Manifest extends T.SDKManifest> {
|
||||
/** Whether or not the volume should be readonly for this daemon */
|
||||
readonly: boolean,
|
||||
) {
|
||||
this.dependencies.push({
|
||||
dependencyId,
|
||||
volumeId,
|
||||
subpath,
|
||||
mountpoint,
|
||||
readonly,
|
||||
})
|
||||
return this
|
||||
return new Mounts<Manifest, Backups>(
|
||||
[...this.volumes],
|
||||
[...this.assets],
|
||||
[
|
||||
...this.dependencies,
|
||||
{
|
||||
dependencyId,
|
||||
volumeId,
|
||||
subpath,
|
||||
mountpoint,
|
||||
readonly,
|
||||
},
|
||||
],
|
||||
[...this.backups],
|
||||
)
|
||||
}
|
||||
|
||||
addBackups(subpath: string | null, mountpoint: string) {
|
||||
return new Mounts<
|
||||
Manifest,
|
||||
{
|
||||
subpath: string | null
|
||||
mountpoint: string
|
||||
}
|
||||
>(
|
||||
[...this.volumes],
|
||||
[...this.assets],
|
||||
[...this.dependencies],
|
||||
[...this.backups, { subpath, mountpoint }],
|
||||
)
|
||||
}
|
||||
|
||||
build(): MountArray {
|
||||
@@ -130,3 +173,7 @@ export class Mounts<Manifest extends T.SDKManifest> {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const a = Mounts.of().addBackups(null, "")
|
||||
// @ts-expect-error
|
||||
const m: Mounts<T.SDKManifest, never> = a
|
||||
|
||||
Reference in New Issue
Block a user