From 42cfd69463acd1bad3648700c6d4a9eb41197c38 Mon Sep 17 00:00:00 2001 From: Remco Ros Date: Fri, 25 Oct 2024 00:29:12 +0200 Subject: [PATCH] sdk: fix piping stdio of Daemons, support onStdOut/onStderr (#2762) --- .../DockerProcedureContainer.ts | 2 +- sdk/package/lib/mainFn/CommandController.ts | 13 ++++++++---- sdk/package/lib/mainFn/Daemon.ts | 4 ++-- sdk/package/lib/mainFn/Daemons.ts | 2 ++ sdk/package/lib/util/SubContainer.ts | 20 +++++++++++-------- 5 files changed, 26 insertions(+), 15 deletions(-) diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts index 26e184cef..26e4dd8bf 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts @@ -151,7 +151,7 @@ export class DockerProcedureContainer { } } - async spawn(commands: string[]): Promise { + async spawn(commands: string[]): Promise { return await this.subcontainer.spawn(commands) } } diff --git a/sdk/package/lib/mainFn/CommandController.ts b/sdk/package/lib/mainFn/CommandController.ts index 27089ea86..1aefc854f 100644 --- a/sdk/package/lib/mainFn/CommandController.ts +++ b/sdk/package/lib/mainFn/CommandController.ts @@ -17,7 +17,7 @@ export class CommandController { readonly runningAnswer: Promise, private state: { exited: boolean }, private readonly subcontainer: SubContainer, - private process: cp.ChildProcessWithoutNullStreams, + private process: cp.ChildProcess, readonly sigtermTimeout: number = DEFAULT_SIGTERM_TIMEOUT, ) {} static of() { @@ -43,8 +43,8 @@ export class CommandController { | undefined cwd?: string | undefined user?: string | undefined - onStdout?: (x: Buffer) => null - onStderr?: (x: Buffer) => null + onStdout?: (chunk: Buffer | string | any) => void + onStderr?: (chunk: Buffer | string | any) => void }, ) => { const commands = splitCommand(command) @@ -62,7 +62,7 @@ export class CommandController { } return subc })() - let childProcess: cp.ChildProcessWithoutNullStreams + let childProcess: cp.ChildProcess if (options.runAsInit) { childProcess = await subc.launch(commands, { env: options.env, @@ -70,8 +70,13 @@ export class CommandController { } else { childProcess = await subc.spawn(commands, { env: options.env, + stdio: options.onStdout || options.onStderr ? "pipe" : "inherit", }) } + + if (options.onStdout) childProcess.stdout?.on("data", options.onStdout) + if (options.onStderr) childProcess.stderr?.on("data", options.onStderr) + const state = { exited: false } const answer = new Promise((resolve, reject) => { childProcess.on("exit", (code) => { diff --git a/sdk/package/lib/mainFn/Daemon.ts b/sdk/package/lib/mainFn/Daemon.ts index 7877dce08..d9db89b6a 100644 --- a/sdk/package/lib/mainFn/Daemon.ts +++ b/sdk/package/lib/mainFn/Daemon.ts @@ -37,8 +37,8 @@ export class Daemon { | undefined cwd?: string | undefined user?: string | undefined - onStdout?: (x: Buffer) => null - onStderr?: (x: Buffer) => null + onStdout?: (chunk: Buffer | string | any) => void + onStderr?: (chunk: Buffer | string | any) => void sigtermTimeout?: number }, ) => { diff --git a/sdk/package/lib/mainFn/Daemons.ts b/sdk/package/lib/mainFn/Daemons.ts index 4ad573b0b..cefa06698 100644 --- a/sdk/package/lib/mainFn/Daemons.ts +++ b/sdk/package/lib/mainFn/Daemons.ts @@ -39,6 +39,8 @@ type DaemonsParams< ready: Ready requires: Exclude[] sigtermTimeout?: number + onStdout?: (chunk: Buffer | string | any) => void + onStderr?: (chunk: Buffer | string | any) => void } type ErrorDuplicateId = `The id '${Id}' is already used` diff --git a/sdk/package/lib/util/SubContainer.ts b/sdk/package/lib/util/SubContainer.ts index be1491050..785c6fea0 100644 --- a/sdk/package/lib/util/SubContainer.ts +++ b/sdk/package/lib/util/SubContainer.ts @@ -35,8 +35,8 @@ export interface ExecSpawnable { ): Promise spawn( command: string[], - options?: CommandOptions, - ): Promise + options?: CommandOptions & StdioOptions, + ): Promise } /** * Want to limit what we can do in a container, so we want to launch a container with a specific image and the mounts. @@ -332,8 +332,8 @@ export class SubContainer implements ExecSpawnable { async spawn( command: string[], - options?: CommandOptions, - ): Promise { + options: CommandOptions & StdioOptions = { stdio: "inherit" }, + ): Promise { await this.waitProc() const imageMeta: any = await fs .readFile(`/media/startos/images/${this.imageId}.json`, { @@ -342,12 +342,12 @@ export class SubContainer implements ExecSpawnable { .catch(() => "{}") .then(JSON.parse) let extra: string[] = [] - if (options?.user) { + if (options.user) { extra.push(`--user=${options.user}`) delete options.user } let workdir = imageMeta.workdir || "/" - if (options?.cwd) { + if (options.cwd) { workdir = options.cwd delete options.cwd } @@ -387,8 +387,8 @@ export class SubContainerHandle implements ExecSpawnable { } spawn( command: string[], - options?: CommandOptions, - ): Promise { + options: CommandOptions & StdioOptions = { stdio: "inherit" }, + ): Promise { return this.subContainer.spawn(command, options) } } @@ -399,6 +399,10 @@ export type CommandOptions = { user?: string } +export type StdioOptions = { + stdio?: cp.IOType +} + export type MountOptions = | MountOptionsVolume | MountOptionsAssets