add support for remote attaching to container (#2732)

* add support for remote attaching to container

* feature: Add in the subcontainer searching

* feat: Add in the name/ imageId filtering

* Feat: Fix the env and the workdir

* chore: Make the sigkill first?

* add some extra guard on term

* fix: Health during error doesnt return what we need

* chore: Cleanup for pr

* fix build

* fix build

* Update startos-iso.yaml

* Update startos-iso.yaml

* Update startos-iso.yaml

* Update startos-iso.yaml

* Update startos-iso.yaml

* Update startos-iso.yaml

* Update startos-iso.yaml

* check status during build

---------

Co-authored-by: J H <dragondef@gmail.com>
This commit is contained in:
Aiden McClelland
2024-09-20 15:38:16 -06:00
committed by GitHub
parent 24c6cd235b
commit eec5cf6b65
31 changed files with 852 additions and 269 deletions

View File

@@ -782,6 +782,7 @@ export async function runCommand<Manifest extends T.Manifest>(
effects,
image,
options.mounts || [],
commands.join(" "),
(subcontainer) => subcontainer.exec(commands),
)
}

View File

@@ -31,6 +31,7 @@ export class CommandController {
| SubContainer,
command: T.CommandType,
options: {
subcontainerName?: string
// Defaults to the DEFAULT_SIGTERM_TIMEOUT = 30_000ms
sigtermTimeout?: number
mounts?: { path: string; options: MountOptions }[]
@@ -51,7 +52,11 @@ export class CommandController {
subcontainer instanceof SubContainer
? subcontainer
: await (async () => {
const subc = await SubContainer.of(effects, subcontainer)
const subc = await SubContainer.of(
effects,
subcontainer,
options?.subcontainerName || commands.join(" "),
)
for (let mount of options.mounts || []) {
await subc.mount(mount.options, mount.path)
}
@@ -119,6 +124,11 @@ export class CommandController {
async term({ signal = SIGTERM, timeout = this.sigtermTimeout } = {}) {
try {
if (!this.state.exited) {
if (signal !== "SIGKILL") {
setTimeout(() => {
if (!this.state.exited) this.process.kill("SIGKILL")
}, timeout)
}
if (!this.process.kill(signal)) {
console.error(
`failed to send signal ${signal} to pid ${this.process.pid}`,
@@ -126,11 +136,6 @@ export class CommandController {
}
}
if (signal !== "SIGKILL") {
setTimeout(() => {
this.process.kill("SIGKILL")
}, timeout)
}
await this.runningAnswer
} finally {
await this.subcontainer.destroy?.()

View File

@@ -28,6 +28,7 @@ export class Daemon {
| SubContainer,
command: T.CommandType,
options: {
subcontainerName?: string
mounts?: { path: string; options: MountOptions }[]
env?:
| {

View File

@@ -127,6 +127,7 @@ export class Daemons<Manifest extends T.Manifest, Ids extends string> {
const daemon = Daemon.of()(this.effects, options.image, options.command, {
...options,
mounts: options.mounts.build(),
subcontainerName: id,
})
const healthDaemon = new HealthDaemon(
daemon,

View File

@@ -1,4 +1,7 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ImageId } from "./ImageId"
export type CreateSubcontainerFsParams = { imageId: ImageId }
export type CreateSubcontainerFsParams = {
imageId: ImageId
name: string | null
}

View File

@@ -1,4 +1,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Sessions } from "./Sessions"
export type SessionList = { current: string; sessions: Sessions }
export type SessionList = { current: string | null; sessions: Sessions }

View File

@@ -366,7 +366,10 @@ export type Effects = {
// subcontainer
subcontainer: {
/** A low level api used by SubContainer */
createFs(options: { imageId: string }): Promise<[string, string]>
createFs(options: {
imageId: string
name: string | null
}): Promise<[string, string]>
/** A low level api used by SubContainer */
destroyFs(options: { guid: string }): Promise<void>
}

View File

@@ -86,10 +86,12 @@ export class SubContainer implements ExecSpawnable {
static async of(
effects: T.Effects,
image: { id: T.ImageId; sharedRun?: boolean },
name: string,
) {
const { id, sharedRun } = image
const [rootfs, guid] = await effects.subcontainer.createFs({
imageId: id as string,
name,
})
const shared = ["dev", "sys"]
@@ -115,9 +117,10 @@ export class SubContainer implements ExecSpawnable {
effects: T.Effects,
image: { id: T.ImageId; sharedRun?: boolean },
mounts: { options: MountOptions; path: string }[],
name: string,
fn: (subContainer: SubContainer) => Promise<T>,
): Promise<T> {
const subContainer = await SubContainer.of(effects, image)
const subContainer = await SubContainer.of(effects, image, name)
try {
for (let mount of mounts) {
await subContainer.mount(mount.options, mount.path)
@@ -179,10 +182,12 @@ export class SubContainer implements ExecSpawnable {
}
return new Promise<void>((resolve, reject) => {
try {
let timeout = setTimeout(() => this.leader.kill("SIGKILL"), 30000)
this.leader.on("exit", () => {
clearTimeout(timeout)
resolve()
})
if (!this.leader.kill("SIGKILL")) {
if (!this.leader.kill("SIGTERM")) {
reject(new Error("kill(2) failed"))
}
} catch (e) {