mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 14:29:45 +00:00
Refactor/sdk init (#2947)
* fixes for main * refactor package initialization * fixes from testing * more fixes * beta.21 * do not use instanceof * closes #2921 * beta22 * allow disabling kiosk * migration * fix /etc/shadow * actionRequest -> task * beta.23
This commit is contained in:
@@ -2,11 +2,7 @@ import { DEFAULT_SIGTERM_TIMEOUT } from "."
|
||||
import { NO_TIMEOUT, SIGTERM } from "../../../base/lib/types"
|
||||
|
||||
import * as T from "../../../base/lib/types"
|
||||
import {
|
||||
MountOptions,
|
||||
SubContainerHandle,
|
||||
SubContainer,
|
||||
} from "../util/SubContainer"
|
||||
import { MountOptions, SubContainer } from "../util/SubContainer"
|
||||
import { Drop, splitCommand } from "../util"
|
||||
import * as cp from "child_process"
|
||||
import * as fs from "node:fs/promises"
|
||||
@@ -44,7 +40,7 @@ export class CommandController<Manifest extends T.SDKManifest> extends Drop {
|
||||
) => {
|
||||
try {
|
||||
let commands: string[]
|
||||
if (command instanceof T.UseEntrypoint) {
|
||||
if (T.isUseEntrypoint(command)) {
|
||||
const imageMeta: T.ImageMetadata = await fs
|
||||
.readFile(`/media/startos/images/${subcontainer.imageId}.json`, {
|
||||
encoding: "utf8",
|
||||
@@ -110,13 +106,10 @@ export class CommandController<Manifest extends T.SDKManifest> extends Drop {
|
||||
}
|
||||
}
|
||||
}
|
||||
get subContainerHandle() {
|
||||
return new SubContainerHandle(this.subcontainer)
|
||||
}
|
||||
async wait({ timeout = NO_TIMEOUT, keepSubcontainer = false } = {}) {
|
||||
async wait({ timeout = NO_TIMEOUT } = {}) {
|
||||
if (timeout > 0)
|
||||
setTimeout(() => {
|
||||
this.term({ keepSubcontainer })
|
||||
this.term()
|
||||
}, timeout)
|
||||
try {
|
||||
return await this.runningAnswer
|
||||
@@ -124,14 +117,10 @@ export class CommandController<Manifest extends T.SDKManifest> extends Drop {
|
||||
if (!this.state.exited) {
|
||||
this.process.kill("SIGKILL")
|
||||
}
|
||||
if (!keepSubcontainer) await this.subcontainer.destroy()
|
||||
await this.subcontainer.destroy()
|
||||
}
|
||||
}
|
||||
async term({
|
||||
signal = SIGTERM,
|
||||
timeout = this.sigtermTimeout,
|
||||
keepSubcontainer = false,
|
||||
} = {}) {
|
||||
async term({ signal = SIGTERM, timeout = this.sigtermTimeout } = {}) {
|
||||
try {
|
||||
if (!this.state.exited) {
|
||||
if (signal !== "SIGKILL") {
|
||||
@@ -148,10 +137,10 @@ export class CommandController<Manifest extends T.SDKManifest> extends Drop {
|
||||
|
||||
await this.runningAnswer
|
||||
} finally {
|
||||
if (!keepSubcontainer) await this.subcontainer.destroy()
|
||||
await this.subcontainer.destroy()
|
||||
}
|
||||
}
|
||||
onDrop(): void {
|
||||
this.term({ keepSubcontainer: true }).catch(console.error)
|
||||
this.term().catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import * as T from "../../../base/lib/types"
|
||||
import { asError } from "../../../base/lib/util/asError"
|
||||
import { Drop } from "../util"
|
||||
import { ExecSpawnable, SubContainer } from "../util/SubContainer"
|
||||
import {
|
||||
SubContainer,
|
||||
SubContainerOwned,
|
||||
SubContainerRc,
|
||||
} from "../util/SubContainer"
|
||||
import { CommandController } from "./CommandController"
|
||||
import { Oneshot } from "./Oneshot"
|
||||
|
||||
const TIMEOUT_INCREMENT_MS = 1000
|
||||
const MAX_TIMEOUT_MS = 30000
|
||||
@@ -16,14 +21,15 @@ export class Daemon<Manifest extends T.SDKManifest> extends Drop {
|
||||
private shouldBeRunning = false
|
||||
protected exitedSuccess = false
|
||||
protected constructor(
|
||||
private subcontainer: SubContainer<Manifest>,
|
||||
private startCommand: () => Promise<CommandController<Manifest>>,
|
||||
readonly oneshot: boolean = false,
|
||||
protected onExitSuccessFns: (() => void)[] = [],
|
||||
) {
|
||||
super()
|
||||
}
|
||||
get subContainerHandle(): undefined | ExecSpawnable {
|
||||
return this.commandController?.subContainerHandle
|
||||
isOneshot(): this is Oneshot<Manifest> {
|
||||
return this.oneshot
|
||||
}
|
||||
static of<Manifest extends T.SDKManifest>() {
|
||||
return async (
|
||||
@@ -44,14 +50,15 @@ export class Daemon<Manifest extends T.SDKManifest> extends Drop {
|
||||
sigtermTimeout?: number
|
||||
},
|
||||
) => {
|
||||
if (subcontainer.isOwned()) subcontainer = subcontainer.rc()
|
||||
const startCommand = () =>
|
||||
CommandController.of<Manifest>()(
|
||||
effects,
|
||||
subcontainer,
|
||||
subcontainer.rc(),
|
||||
command,
|
||||
options,
|
||||
)
|
||||
return new Daemon(startCommand)
|
||||
return new Daemon(subcontainer, startCommand)
|
||||
}
|
||||
}
|
||||
async start() {
|
||||
@@ -65,11 +72,11 @@ export class Daemon<Manifest extends T.SDKManifest> extends Drop {
|
||||
while (this.shouldBeRunning) {
|
||||
if (this.commandController)
|
||||
await this.commandController
|
||||
.term({ keepSubcontainer: true })
|
||||
.term({})
|
||||
.catch((err) => console.error(err))
|
||||
this.commandController = await this.startCommand()
|
||||
if (
|
||||
(await this.commandController.wait({ keepSubcontainer: true }).then(
|
||||
(await this.commandController.wait().then(
|
||||
(_) => true,
|
||||
(err) => {
|
||||
console.error(err)
|
||||
@@ -112,6 +119,10 @@ export class Daemon<Manifest extends T.SDKManifest> extends Drop {
|
||||
?.term({ ...termOptions })
|
||||
.catch((e) => console.error(asError(e)))
|
||||
this.commandController = null
|
||||
await this.subcontainer.destroy()
|
||||
}
|
||||
subcontainerRc(): SubContainerRc<Manifest> {
|
||||
return this.subcontainer.rc()
|
||||
}
|
||||
onDrop(): void {
|
||||
this.stop().catch((e) => console.error(asError(e)))
|
||||
|
||||
@@ -5,7 +5,7 @@ import { HealthCheckResult } from "../health/checkFns"
|
||||
import { Trigger } from "../trigger"
|
||||
import * as T from "../../../base/lib/types"
|
||||
import { Mounts } from "./Mounts"
|
||||
import { ExecSpawnable, MountOptions, SubContainer } from "../util/SubContainer"
|
||||
import { MountOptions, SubContainer } from "../util/SubContainer"
|
||||
|
||||
import { promisify } from "node:util"
|
||||
import * as CP from "node:child_process"
|
||||
@@ -17,6 +17,7 @@ import { Daemon } from "./Daemon"
|
||||
import { CommandController } from "./CommandController"
|
||||
import { HealthCheck } from "../health/HealthCheck"
|
||||
import { Oneshot } from "./Oneshot"
|
||||
import { Manifest } from "../test/output.sdk"
|
||||
|
||||
export const cpExec = promisify(CP.exec)
|
||||
export const cpExecFile = promisify(CP.execFile)
|
||||
@@ -38,7 +39,7 @@ export type Ready = {
|
||||
* ```
|
||||
*/
|
||||
fn: (
|
||||
spawnable: ExecSpawnable,
|
||||
subcontainer: SubContainer<Manifest>,
|
||||
) => Promise<HealthCheckResult> | HealthCheckResult
|
||||
/**
|
||||
* A duration in milliseconds to treat a failing health check as "starting"
|
||||
@@ -168,9 +169,14 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
const daemon =
|
||||
"daemon" in options
|
||||
? Promise.resolve(options.daemon)
|
||||
: Daemon.of()(this.effects, options.subcontainer, options.command, {
|
||||
...options,
|
||||
})
|
||||
: Daemon.of<Manifest>()(
|
||||
this.effects,
|
||||
options.subcontainer,
|
||||
options.command,
|
||||
{
|
||||
...options,
|
||||
},
|
||||
)
|
||||
const healthDaemon = new HealthDaemon(
|
||||
daemon,
|
||||
options.requires
|
||||
@@ -212,7 +218,7 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
: Id,
|
||||
options: AddOneshotParams<Manifest, Ids, Id>,
|
||||
) {
|
||||
const daemon = Oneshot.of()(
|
||||
const daemon = Oneshot.of<Manifest>()(
|
||||
this.effects,
|
||||
options.subcontainer,
|
||||
options.command,
|
||||
@@ -220,7 +226,7 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
||||
...options,
|
||||
},
|
||||
)
|
||||
const healthDaemon = new HealthDaemon(
|
||||
const healthDaemon = new HealthDaemon<Manifest>(
|
||||
daemon,
|
||||
options.requires
|
||||
.map((x) => this.ids.indexOf(x))
|
||||
|
||||
@@ -91,8 +91,9 @@ export class HealthDaemon<Manifest extends SDKManifest> {
|
||||
}
|
||||
private async setupHealthCheck() {
|
||||
if (this.ready === "EXIT_SUCCESS") {
|
||||
if (this.daemon instanceof Oneshot) {
|
||||
this.daemon.onExitSuccess(() =>
|
||||
const daemon = await this.daemon
|
||||
if (daemon.isOneshot()) {
|
||||
daemon.onExitSuccess(() =>
|
||||
this.setHealth({ result: "success", message: null }),
|
||||
)
|
||||
}
|
||||
@@ -113,9 +114,9 @@ export class HealthDaemon<Manifest extends SDKManifest> {
|
||||
!res.done;
|
||||
res = await Promise.race([status, trigger.next()])
|
||||
) {
|
||||
const handle = (await this.daemon).subContainerHandle
|
||||
const handle = (await this.daemon).subcontainerRc()
|
||||
|
||||
if (handle) {
|
||||
try {
|
||||
const response: HealthCheckResult = await Promise.resolve(
|
||||
this.ready.fn(handle),
|
||||
).catch((err) => {
|
||||
@@ -132,11 +133,8 @@ export class HealthDaemon<Manifest extends SDKManifest> {
|
||||
this.resolveReady()
|
||||
}
|
||||
await this.setHealth(response)
|
||||
} else {
|
||||
await this.setHealth({
|
||||
result: "failure",
|
||||
message: "Daemon not running",
|
||||
})
|
||||
} finally {
|
||||
await handle.destroy()
|
||||
}
|
||||
}
|
||||
}).catch((err) => console.error(`Daemon ${this.id} failed: ${err}`))
|
||||
@@ -164,7 +162,7 @@ export class HealthDaemon<Manifest extends SDKManifest> {
|
||||
if (
|
||||
result === "failure" &&
|
||||
this.started &&
|
||||
performance.now() - this.started <= (this.ready.gracePeriod ?? 5000)
|
||||
performance.now() - this.started <= (this.ready.gracePeriod ?? 10_000)
|
||||
)
|
||||
result = "starting"
|
||||
await this.effects.setHealth({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as T from "../../../base/lib/types"
|
||||
import { SubContainer } from "../util/SubContainer"
|
||||
import { SubContainer, SubContainerOwned } from "../util/SubContainer"
|
||||
import { CommandController } from "./CommandController"
|
||||
import { Daemon } from "./Daemon"
|
||||
|
||||
@@ -28,14 +28,15 @@ export class Oneshot<Manifest extends T.SDKManifest> extends Daemon<Manifest> {
|
||||
sigtermTimeout?: number
|
||||
},
|
||||
) => {
|
||||
if (subcontainer.isOwned()) subcontainer = subcontainer.rc()
|
||||
const startCommand = () =>
|
||||
CommandController.of<Manifest>()(
|
||||
effects,
|
||||
subcontainer,
|
||||
subcontainer.rc(),
|
||||
command,
|
||||
options,
|
||||
)
|
||||
return new Oneshot(startCommand, true, [])
|
||||
return new Oneshot(subcontainer, startCommand, true, [])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user