feat: Create RunDriver (#196)

This will be used for running containers for various tasks. There will
be a way to take all output from the process and a way to display output
from a running container like our builds have.
This commit is contained in:
Gerald Pinder 2024-07-05 19:20:38 -04:00 committed by GitHub
parent 1a348f8137
commit 784be9869a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 510 additions and 98 deletions

View file

@ -32,6 +32,7 @@ indicatif.workspace = true
indicatif-log-bridge.workspace = true
log.workspace = true
once_cell.workspace = true
tempdir.workspace = true
serde.workspace = true
serde_yaml.workspace = true
serde_json.workspace = true

View file

@ -3,7 +3,7 @@ use std::{
fs::OpenOptions,
io::{BufRead, BufReader, Result, Write as IoWrite},
path::{Path, PathBuf},
process::{Command, ExitStatus},
process::{Command, ExitStatus, Stdio},
sync::{Arc, Mutex},
thread,
time::Duration,
@ -199,7 +199,9 @@ impl CommandLogging for Command {
let log_prefix = Arc::new(log_header(short_name));
let (reader, writer) = os_pipe::pipe()?;
self.stdout(writer.try_clone()?).stderr(writer);
self.stdout(writer.try_clone()?)
.stderr(writer)
.stdin(Stdio::piped());
let progress = Logger::multi_progress()
.add(ProgressBar::new_spinner().with_message(format!("{} {name}", message.as_ref())));

View file

@ -1,10 +1,12 @@
use std::{
process,
fs,
path::PathBuf,
process::{self, Command},
sync::{atomic::AtomicBool, Arc, Mutex},
thread,
};
use log::{error, trace, warn};
use log::{debug, error, trace, warn};
use nix::{
libc::{SIGABRT, SIGCONT, SIGHUP, SIGTSTP},
sys::signal::{kill, Signal},
@ -20,7 +22,31 @@ use signal_hook::{
use crate::logging::Logger;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ContainerId {
cid_path: PathBuf,
requires_sudo: bool,
crt: String,
}
impl ContainerId {
pub fn new<P, S>(cid_path: P, container_runtime: S, requires_sudo: bool) -> Self
where
P: Into<PathBuf>,
S: Into<String>,
{
let cid_path = cid_path.into();
let crt = container_runtime.into();
Self {
cid_path,
requires_sudo,
crt,
}
}
}
static PID_LIST: Lazy<Arc<Mutex<Vec<i32>>>> = Lazy::new(|| Arc::new(Mutex::new(vec![])));
static CID_LIST: Lazy<Arc<Mutex<Vec<ContainerId>>>> = Lazy::new(|| Arc::new(Mutex::new(vec![])));
/// Initialize Ctrl-C handler. This should be done at the start
/// of a binary.
@ -57,10 +83,36 @@ where
warn!("Received termination signal, cleaning up...");
trace!("{info:#?}");
Logger::multi_progress().clear().unwrap();
Logger::multi_progress()
.clear()
.expect("Should clear multi_progress");
send_signal_processes(termsig);
let cid_list = CID_LIST.clone();
let cid_list = cid_list.lock().expect("Should lock mutex");
cid_list.iter().for_each(|cid| {
if let Ok(id) = fs::read_to_string(&cid.cid_path) {
let id = id.trim();
debug!("Killing container {id}");
let status = if cid.requires_sudo {
Command::new("sudo")
.arg(&cid.crt)
.arg("stop")
.arg(id)
.status()
} else {
Command::new(&cid.crt).arg("stop").arg(id).status()
};
if let Err(e) = status {
error!("Failed to kill container {id}: Error {e}");
}
}
});
drop(cid_list);
process::exit(1);
}
SIGTSTP => {
@ -86,8 +138,12 @@ where
fn send_signal_processes(sig: i32) {
let pid_list = PID_LIST.clone();
let pid_list = pid_list.lock().expect("Should lock mutex");
pid_list.iter().for_each(|pid| {
if let Err(e) = kill(Pid::from_raw(*pid), Signal::try_from(sig).unwrap()) {
if let Err(e) = kill(
Pid::from_raw(*pid),
Signal::try_from(sig).expect("Should be valid signal"),
) {
error!("Failed to kill process {pid}: Error {e}");
} else {
trace!("Killed process {pid}");
@ -130,3 +186,28 @@ where
}
}
}
/// Add a cid to the list to kill when the program
/// recieves a kill signal.
///
/// # Panics
/// Will panic if the mutex cannot be locked.
pub fn add_cid(cid: &ContainerId) {
let mut cid_list = CID_LIST.lock().expect("Should lock cid_list");
if !cid_list.contains(cid) {
cid_list.push(cid.clone());
}
}
/// Remove a cid from the list of pids to kill.
///
/// # Panics
/// Will panic if the mutex cannot be locked.
pub fn remove_cid(cid: &ContainerId) {
let mut cid_list = CID_LIST.lock().expect("Should lock cid_list");
if let Some(index) = cid_list.iter().position(|val| *val == *cid) {
cid_list.swap_remove(index);
}
}