use std::{ fs::DirBuilder, io, path::{Path, PathBuf}, process, }; use nix::{ mount::{self, MsFlags}, sched::{self, CloneFlags}, unistd, }; use serde::{Deserialize, Serialize}; use sha2::Digest; use tee_readwrite::TeeWriter; use crate::{ types::*, unshare, util::{self, ToIOResult}, }; use super::spec; #[derive(Debug, Deserialize, Serialize)] pub enum Error { Code(i32), String(String), } impl From for Error { fn from(error: io::Error) -> Self { match error.raw_os_error() { Some(code) => Error::Code(code), None => Error::String(error.to_string()), } } } impl From for io::Error { fn from(error: Error) -> Self { match error { Error::Code(code) => io::Error::from_raw_os_error(code), Error::String(string) => io::Error::new(io::ErrorKind::Other, string), } } } fn init_task() -> Result<(), Error> { sched::unshare(CloneFlags::CLONE_NEWNS).to_io_result()?; mount::mount::<_, _, _, str>( Some("runc"), "build/tmp/runc", Some("tmpfs"), MsFlags::empty(), None, ) .to_io_result()?; let workdir = "build/tmp/runc/workdir"; DirBuilder::new().create(workdir)?; unistd::chown(workdir, Some(unshare::BUILD_UID), Some(unshare::BUILD_GID)).to_io_result()?; Ok(()) } fn output_filename(task: TaskRef) -> PathBuf { Path::new("build/state").join(format!("{}.tar", task)) } fn collect_output(task: TaskRef, task_def: TaskDef) -> Result { let file = util::unix::create_as( output_filename(task), Some(unshare::BUILD_UID), Some(unshare::BUILD_GID), )?; let hasher = OutputHasher::default(); let writer = TeeWriter::new(file, hasher); let writer = util::tar::pack(writer, "build/tmp/runc/workdir", task_def.output.iter())?; let (file, hasher) = writer.into_inner(); file.sync_all()?; Ok(StringHash(hasher.finalize().into())) } pub fn handle_task(task: TaskRef, task_def: TaskDef) -> Result { init_task()?; spec::generate_spec(task_def.run.as_str()) .save("build/tmp/runc/config.json") .expect("Saving runtime spec failed"); let output = process::Command::new("runc") .arg("--root") .arg("build/tmp/runc/state") .arg("run") .arg("rebel") .current_dir("build/tmp/runc") .output()?; if !output.status.success() { println!( "{}:\n{}", task, String::from_utf8_lossy(output.stderr.as_slice()), ); return Err(Error::String("Task failed".to_string())); } println!( "{}:\n{}", task, String::from_utf8_lossy(output.stdout.as_slice()), ); Ok(collect_output(task, task_def)?) }