mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-04 14:29:45 +00:00
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:
@@ -1,12 +1,15 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use imbl_value::InternedString;
|
||||
use models::ImageId;
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::disk::mount::filesystem::overlayfs::OverlayGuard;
|
||||
use crate::rpc_continuations::Guid;
|
||||
use crate::service::effects::prelude::*;
|
||||
use crate::util::Invoke;
|
||||
use crate::{
|
||||
disk::mount::filesystem::overlayfs::OverlayGuard, service::persistent_container::Subcontainer,
|
||||
};
|
||||
|
||||
#[cfg(feature = "container-runtime")]
|
||||
mod sync;
|
||||
@@ -38,7 +41,7 @@ pub async fn destroy_subcontainer_fs(
|
||||
.await
|
||||
.remove(&guid)
|
||||
{
|
||||
overlay.unmount(true).await?;
|
||||
overlay.overlay.unmount(true).await?;
|
||||
} else {
|
||||
tracing::warn!("Could not find a subcontainer fs to destroy; assumming that it already is destroyed and will be skipping");
|
||||
}
|
||||
@@ -50,11 +53,13 @@ pub async fn destroy_subcontainer_fs(
|
||||
#[ts(export)]
|
||||
pub struct CreateSubcontainerFsParams {
|
||||
image_id: ImageId,
|
||||
#[ts(type = "string | null")]
|
||||
name: Option<InternedString>,
|
||||
}
|
||||
#[instrument(skip_all)]
|
||||
pub async fn create_subcontainer_fs(
|
||||
context: EffectContext,
|
||||
CreateSubcontainerFsParams { image_id }: CreateSubcontainerFsParams,
|
||||
CreateSubcontainerFsParams { image_id, name }: CreateSubcontainerFsParams,
|
||||
) -> Result<(PathBuf, Guid), Error> {
|
||||
let context = context.deref()?;
|
||||
if let Some(image) = context
|
||||
@@ -87,7 +92,13 @@ pub async fn create_subcontainer_fs(
|
||||
.with_kind(ErrorKind::Incoherent)?,
|
||||
);
|
||||
tracing::info!("Mounting overlay {guid} for {image_id}");
|
||||
let guard = OverlayGuard::mount(image, &mountpoint).await?;
|
||||
let subcontainer_wrapper = Subcontainer {
|
||||
overlay: OverlayGuard::mount(image, &mountpoint).await?,
|
||||
name: name
|
||||
.unwrap_or_else(|| InternedString::intern(format!("subcontainer-{}", image_id))),
|
||||
image_id: image_id.clone(),
|
||||
};
|
||||
|
||||
Command::new("chown")
|
||||
.arg("100000:100000")
|
||||
.arg(&mountpoint)
|
||||
@@ -100,7 +111,7 @@ pub async fn create_subcontainer_fs(
|
||||
.subcontainers
|
||||
.lock()
|
||||
.await
|
||||
.insert(guid.clone(), guard);
|
||||
.insert(guid.clone(), subcontainer_wrapper);
|
||||
Ok((container_mountpoint, guid))
|
||||
} else {
|
||||
Err(Error::new(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::{c_int, OsStr, OsString};
|
||||
use std::fs::File;
|
||||
@@ -12,7 +11,6 @@ use nix::unistd::Pid;
|
||||
use signal_hook::consts::signal::*;
|
||||
use tokio::sync::oneshot;
|
||||
use tty_spawn::TtySpawn;
|
||||
use unshare::Command as NSCommand;
|
||||
|
||||
use crate::service::effects::prelude::*;
|
||||
use crate::service::effects::ContainerCliContext;
|
||||
@@ -50,11 +48,13 @@ fn open_file_read(path: impl AsRef<Path>) -> Result<File, Error> {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Parser)]
|
||||
pub struct ExecParams {
|
||||
#[arg(short = 'e', long = "env")]
|
||||
#[arg(long)]
|
||||
force_tty: bool,
|
||||
#[arg(short, long)]
|
||||
env: Option<PathBuf>,
|
||||
#[arg(short = 'w', long = "workdir")]
|
||||
#[arg(short, long)]
|
||||
workdir: Option<PathBuf>,
|
||||
#[arg(short = 'u', long = "user")]
|
||||
#[arg(short, long)]
|
||||
user: Option<String>,
|
||||
chroot: PathBuf,
|
||||
#[arg(trailing_var_arg = true)]
|
||||
@@ -68,6 +68,7 @@ impl ExecParams {
|
||||
user,
|
||||
chroot,
|
||||
command,
|
||||
..
|
||||
} = self;
|
||||
let Some(([command], args)) = command.split_at_checked(1) else {
|
||||
return Err(Error::new(
|
||||
@@ -88,16 +89,6 @@ impl ExecParams {
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
std::os::unix::fs::chroot(chroot)
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("chroot {chroot:?}")))?;
|
||||
let command = which::which_in(
|
||||
command,
|
||||
env.get("PATH")
|
||||
.copied()
|
||||
.map(Cow::Borrowed)
|
||||
.or_else(|| std::env::var("PATH").ok().map(Cow::Owned))
|
||||
.as_deref(),
|
||||
workdir.as_deref().unwrap_or(Path::new("/")),
|
||||
)
|
||||
.with_kind(ErrorKind::Filesystem)?;
|
||||
let mut cmd = StdCommand::new(command);
|
||||
cmd.args(args);
|
||||
for (k, v) in env {
|
||||
@@ -135,6 +126,7 @@ impl ExecParams {
|
||||
pub fn launch(
|
||||
_: ContainerCliContext,
|
||||
ExecParams {
|
||||
force_tty,
|
||||
env,
|
||||
workdir,
|
||||
user,
|
||||
@@ -142,47 +134,8 @@ pub fn launch(
|
||||
command,
|
||||
}: ExecParams,
|
||||
) -> Result<(), Error> {
|
||||
use unshare::{Namespace, Stdio};
|
||||
|
||||
use crate::service::cli::ContainerCliContext;
|
||||
let mut sig = signal_hook::iterator::Signals::new(FWD_SIGNALS)?;
|
||||
let mut cmd = NSCommand::new("/usr/bin/start-cli");
|
||||
cmd.arg("subcontainer").arg("launch-init");
|
||||
if let Some(env) = env {
|
||||
cmd.arg("--env").arg(env);
|
||||
}
|
||||
if let Some(workdir) = workdir {
|
||||
cmd.arg("--workdir").arg(workdir);
|
||||
}
|
||||
if let Some(user) = user {
|
||||
cmd.arg("--user").arg(user);
|
||||
}
|
||||
cmd.arg(&chroot);
|
||||
cmd.args(&command);
|
||||
cmd.unshare(&[Namespace::Pid, Namespace::Cgroup, Namespace::Ipc]);
|
||||
cmd.stdin(Stdio::piped());
|
||||
cmd.stdout(Stdio::piped());
|
||||
cmd.stderr(Stdio::piped());
|
||||
let (stdin_send, stdin_recv) = oneshot::channel();
|
||||
std::thread::spawn(move || {
|
||||
if let Ok(mut stdin) = stdin_recv.blocking_recv() {
|
||||
std::io::copy(&mut std::io::stdin(), &mut stdin).unwrap();
|
||||
}
|
||||
});
|
||||
let (stdout_send, stdout_recv) = oneshot::channel();
|
||||
std::thread::spawn(move || {
|
||||
if let Ok(mut stdout) = stdout_recv.blocking_recv() {
|
||||
std::io::copy(&mut stdout, &mut std::io::stdout()).unwrap();
|
||||
}
|
||||
});
|
||||
let (stderr_send, stderr_recv) = oneshot::channel();
|
||||
std::thread::spawn(move || {
|
||||
if let Ok(mut stderr) = stderr_recv.blocking_recv() {
|
||||
std::io::copy(&mut stderr, &mut std::io::stderr()).unwrap();
|
||||
}
|
||||
});
|
||||
if chroot.join("proc/1").exists() {
|
||||
let ns_id = procfs::process::Process::new_with_root(chroot.join("proc"))
|
||||
let ns_id = procfs::process::Process::new_with_root(chroot.join("proc/1"))
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, "open subcontainer procfs"))?
|
||||
.namespaces()
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, "read subcontainer pid 1 ns"))?
|
||||
@@ -225,20 +178,92 @@ pub fn launch(
|
||||
nix::mount::umount(&chroot.join("proc"))
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, "unmounting subcontainer procfs"))?;
|
||||
}
|
||||
|
||||
if (std::io::stdin().is_terminal()
|
||||
&& std::io::stdout().is_terminal()
|
||||
&& std::io::stderr().is_terminal())
|
||||
|| force_tty
|
||||
{
|
||||
let mut cmd = TtySpawn::new("/usr/bin/start-cli");
|
||||
cmd.arg("subcontainer").arg("launch-init");
|
||||
if let Some(env) = env {
|
||||
cmd.arg("--env").arg(env);
|
||||
}
|
||||
if let Some(workdir) = workdir {
|
||||
cmd.arg("--workdir").arg(workdir);
|
||||
}
|
||||
if let Some(user) = user {
|
||||
cmd.arg("--user").arg(user);
|
||||
}
|
||||
cmd.arg(&chroot);
|
||||
cmd.args(command.iter());
|
||||
nix::sched::unshare(CloneFlags::CLONE_NEWPID)
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, "unshare pid ns"))?;
|
||||
nix::sched::unshare(CloneFlags::CLONE_NEWCGROUP)
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, "unshare cgroup ns"))?;
|
||||
nix::sched::unshare(CloneFlags::CLONE_NEWIPC)
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, "unshare ipc ns"))?;
|
||||
std::process::exit(cmd.spawn().with_kind(ErrorKind::Filesystem)?);
|
||||
}
|
||||
|
||||
let mut sig = signal_hook::iterator::Signals::new(FWD_SIGNALS)?;
|
||||
let (send_pid, recv_pid) = oneshot::channel();
|
||||
std::thread::spawn(move || {
|
||||
if let Ok(pid) = recv_pid.blocking_recv() {
|
||||
for sig in sig.forever() {
|
||||
nix::sys::signal::kill(
|
||||
Pid::from_raw(pid),
|
||||
Some(nix::sys::signal::Signal::try_from(sig).unwrap()),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
});
|
||||
let mut cmd = StdCommand::new("/usr/bin/start-cli");
|
||||
cmd.arg("subcontainer").arg("launch-init");
|
||||
if let Some(env) = env {
|
||||
cmd.arg("--env").arg(env);
|
||||
}
|
||||
if let Some(workdir) = workdir {
|
||||
cmd.arg("--workdir").arg(workdir);
|
||||
}
|
||||
if let Some(user) = user {
|
||||
cmd.arg("--user").arg(user);
|
||||
}
|
||||
cmd.arg(&chroot);
|
||||
cmd.args(&command);
|
||||
cmd.stdin(Stdio::piped());
|
||||
cmd.stdout(Stdio::piped());
|
||||
cmd.stderr(Stdio::piped());
|
||||
let (stdin_send, stdin_recv) = oneshot::channel();
|
||||
std::thread::spawn(move || {
|
||||
if let Ok(mut stdin) = stdin_recv.blocking_recv() {
|
||||
std::io::copy(&mut std::io::stdin(), &mut stdin).unwrap();
|
||||
}
|
||||
});
|
||||
let (stdout_send, stdout_recv) = oneshot::channel();
|
||||
std::thread::spawn(move || {
|
||||
if let Ok(mut stdout) = stdout_recv.blocking_recv() {
|
||||
std::io::copy(&mut stdout, &mut std::io::stdout()).unwrap();
|
||||
}
|
||||
});
|
||||
let (stderr_send, stderr_recv) = oneshot::channel();
|
||||
std::thread::spawn(move || {
|
||||
if let Ok(mut stderr) = stderr_recv.blocking_recv() {
|
||||
std::io::copy(&mut stderr, &mut std::io::stderr()).unwrap();
|
||||
}
|
||||
});
|
||||
nix::sched::unshare(CloneFlags::CLONE_NEWPID)
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, "unshare pid ns"))?;
|
||||
nix::sched::unshare(CloneFlags::CLONE_NEWCGROUP)
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, "unshare cgroup ns"))?;
|
||||
nix::sched::unshare(CloneFlags::CLONE_NEWIPC)
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, "unshare ipc ns"))?;
|
||||
let mut child = cmd
|
||||
.spawn()
|
||||
.map_err(color_eyre::eyre::Report::msg)
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, "spawning child process"))?;
|
||||
let pid = child.pid();
|
||||
std::thread::spawn(move || {
|
||||
for sig in sig.forever() {
|
||||
nix::sys::signal::kill(
|
||||
Pid::from_raw(pid),
|
||||
Some(nix::sys::signal::Signal::try_from(sig).unwrap()),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
});
|
||||
send_pid.send(child.id() as i32).unwrap_or_default();
|
||||
stdin_send
|
||||
.send(child.stdin.take().unwrap())
|
||||
.unwrap_or_default();
|
||||
@@ -253,16 +278,16 @@ pub fn launch(
|
||||
.wait()
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, "waiting on child process"))?;
|
||||
if let Some(code) = exit.code() {
|
||||
nix::mount::umount(&chroot.join("proc"))
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, "umount procfs"))?;
|
||||
std::process::exit(code);
|
||||
} else if exit.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
if exit.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::new(
|
||||
color_eyre::eyre::Report::msg(exit),
|
||||
ErrorKind::Unknown,
|
||||
))
|
||||
}
|
||||
Err(Error::new(
|
||||
color_eyre::eyre::Report::msg(exit),
|
||||
ErrorKind::Unknown,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,6 +313,7 @@ pub fn launch_init(_: ContainerCliContext, params: ExecParams) -> Result<(), Err
|
||||
pub fn exec(
|
||||
_: ContainerCliContext,
|
||||
ExecParams {
|
||||
force_tty,
|
||||
env,
|
||||
workdir,
|
||||
user,
|
||||
@@ -295,7 +321,11 @@ pub fn exec(
|
||||
command,
|
||||
}: ExecParams,
|
||||
) -> Result<(), Error> {
|
||||
if std::io::stdin().is_terminal() {
|
||||
if (std::io::stdin().is_terminal()
|
||||
&& std::io::stdout().is_terminal()
|
||||
&& std::io::stderr().is_terminal())
|
||||
|| force_tty
|
||||
{
|
||||
let mut cmd = TtySpawn::new("/usr/bin/start-cli");
|
||||
cmd.arg("subcontainer").arg("exec-command");
|
||||
if let Some(env) = env {
|
||||
@@ -407,15 +437,13 @@ pub fn exec(
|
||||
.with_ctx(|_| (ErrorKind::Filesystem, "waiting on child process"))?;
|
||||
if let Some(code) = exit.code() {
|
||||
std::process::exit(code);
|
||||
} else if exit.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
if exit.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::new(
|
||||
color_eyre::eyre::Report::msg(exit),
|
||||
ErrorKind::Unknown,
|
||||
))
|
||||
}
|
||||
Err(Error::new(
|
||||
color_eyre::eyre::Report::msg(exit),
|
||||
ErrorKind::Unknown,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user