mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
fix: Container runtime actions (#2723)
Actions where running in a race condition that they sometimes didn't wait for the container to be started and the issue was the exec that was then run after would have an issue.
This commit is contained in:
@@ -3,9 +3,10 @@ import * as T from "../types"
|
||||
import * as cp from "child_process"
|
||||
import { promisify } from "util"
|
||||
import { Buffer } from "node:buffer"
|
||||
import { once } from "./once"
|
||||
export const execFile = promisify(cp.execFile)
|
||||
const WORKDIR = (imageId: string) => `/media/startos/images/${imageId}/`
|
||||
|
||||
const False = () => false
|
||||
type ExecResults = {
|
||||
exitCode: number | null
|
||||
exitSignal: NodeJS.Signals | null
|
||||
@@ -17,6 +18,8 @@ export type ExecOptions = {
|
||||
input?: string | Buffer
|
||||
}
|
||||
|
||||
const TIMES_TO_WAIT_FOR_PROC = 100
|
||||
|
||||
/**
|
||||
* This is the type that is going to describe what an subcontainer could do. The main point of the
|
||||
* subcontainer is to have commands that run in a chrooted environment. This is useful for running
|
||||
@@ -44,6 +47,7 @@ export interface ExecSpawnable {
|
||||
export class SubContainer implements ExecSpawnable {
|
||||
private leader: cp.ChildProcess
|
||||
private leaderExited: boolean = false
|
||||
private waitProc: () => Promise<void>
|
||||
private constructor(
|
||||
readonly effects: T.Effects,
|
||||
readonly imageId: T.ImageId,
|
||||
@@ -58,6 +62,26 @@ export class SubContainer implements ExecSpawnable {
|
||||
this.leader.on("exit", () => {
|
||||
this.leaderExited = true
|
||||
})
|
||||
this.waitProc = once(
|
||||
() =>
|
||||
new Promise(async (resolve, reject) => {
|
||||
let count = 0
|
||||
while (
|
||||
!(await fs.stat(`${this.rootfs}/proc/1`).then((x) => !!x, False))
|
||||
) {
|
||||
if (count++ > TIMES_TO_WAIT_FOR_PROC) {
|
||||
console.debug("Failed to start subcontainer", {
|
||||
guid: this.guid,
|
||||
imageId: this.imageId,
|
||||
rootfs: this.rootfs,
|
||||
})
|
||||
reject(new Error(`Failed to start subcontainer ${this.imageId}`))
|
||||
}
|
||||
await wait(1)
|
||||
}
|
||||
resolve()
|
||||
}),
|
||||
)
|
||||
}
|
||||
static async of(
|
||||
effects: T.Effects,
|
||||
@@ -184,6 +208,7 @@ export class SubContainer implements ExecSpawnable {
|
||||
stdout: string | Buffer
|
||||
stderr: string | Buffer
|
||||
}> {
|
||||
await this.waitProc()
|
||||
const imageMeta: T.ImageMetadata = await fs
|
||||
.readFile(`/media/startos/images/${this.imageId}.json`, {
|
||||
encoding: "utf8",
|
||||
@@ -266,6 +291,7 @@ export class SubContainer implements ExecSpawnable {
|
||||
command: string[],
|
||||
options?: CommandOptions,
|
||||
): Promise<cp.ChildProcessWithoutNullStreams> {
|
||||
await this.waitProc()
|
||||
const imageMeta: any = await fs
|
||||
.readFile(`/media/startos/images/${this.imageId}.json`, {
|
||||
encoding: "utf8",
|
||||
@@ -307,6 +333,7 @@ export class SubContainer implements ExecSpawnable {
|
||||
command: string[],
|
||||
options?: CommandOptions,
|
||||
): Promise<cp.ChildProcessWithoutNullStreams> {
|
||||
await this.waitProc()
|
||||
const imageMeta: any = await fs
|
||||
.readFile(`/media/startos/images/${this.imageId}.json`, {
|
||||
encoding: "utf8",
|
||||
@@ -402,3 +429,6 @@ export type MountOptionsBackup = {
|
||||
type: "backup"
|
||||
subpath: string | null
|
||||
}
|
||||
function wait(time: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, time))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user