use std::path::{Path, PathBuf};
use std::time::Duration;
use chrono::{DateTime, Utc};
use super::error::Error;
use super::sync::WaitableCell;
use crate::sys::signals::*;
#[derive(Clone)]
pub struct InstanceConfig<Engine: Send + Sync + Clone> {
engine: Engine,
stdin: PathBuf,
stdout: PathBuf,
stderr: PathBuf,
bundle: PathBuf,
namespace: String,
containerd_address: String,
}
impl<Engine: Send + Sync + Clone> InstanceConfig<Engine> {
pub fn new(
engine: Engine,
namespace: impl AsRef<str>,
containerd_address: impl AsRef<str>,
) -> Self {
let namespace = namespace.as_ref().to_string();
let containerd_address = containerd_address.as_ref().to_string();
Self {
engine,
namespace,
containerd_address,
stdin: PathBuf::default(),
stdout: PathBuf::default(),
stderr: PathBuf::default(),
bundle: PathBuf::default(),
}
}
pub fn set_stdin(&mut self, stdin: impl AsRef<Path>) -> &mut Self {
self.stdin = stdin.as_ref().to_path_buf();
self
}
pub fn get_stdin(&self) -> &Path {
&self.stdin
}
pub fn set_stdout(&mut self, stdout: impl AsRef<Path>) -> &mut Self {
self.stdout = stdout.as_ref().to_path_buf();
self
}
pub fn get_stdout(&self) -> &Path {
&self.stdout
}
pub fn set_stderr(&mut self, stderr: impl AsRef<Path>) -> &mut Self {
self.stderr = stderr.as_ref().to_path_buf();
self
}
pub fn get_stderr(&self) -> &Path {
&self.stderr
}
pub fn set_bundle(&mut self, bundle: impl AsRef<Path>) -> &mut Self {
self.bundle = bundle.as_ref().to_path_buf();
self
}
pub fn get_bundle(&self) -> &Path {
&self.bundle
}
pub fn get_engine(&self) -> Engine {
self.engine.clone()
}
pub fn get_namespace(&self) -> String {
self.namespace.clone()
}
pub fn get_containerd_address(&self) -> String {
self.containerd_address.clone()
}
}
pub trait Instance: 'static {
type Engine: Send + Sync + Clone;
fn new(id: String, cfg: Option<&InstanceConfig<Self::Engine>>) -> Result<Self, Error>
where
Self: Sized;
fn start(&self) -> Result<u32, Error>;
fn kill(&self, signal: u32) -> Result<(), Error>;
fn delete(&self) -> Result<(), Error>;
#[cfg_attr(feature = "tracing", tracing::instrument(parent = tracing::Span::current(), skip_all, level = "Info"))]
fn wait(&self) -> (u32, DateTime<Utc>) {
self.wait_timeout(None).unwrap()
}
fn wait_timeout(&self, t: impl Into<Option<Duration>>) -> Option<(u32, DateTime<Utc>)>;
}
pub struct Nop {
exit_code: WaitableCell<(u32, DateTime<Utc>)>,
}
impl Instance for Nop {
type Engine = ();
fn new(_id: String, _cfg: Option<&InstanceConfig<Self::Engine>>) -> Result<Self, Error> {
Ok(Nop {
exit_code: WaitableCell::new(),
})
}
fn start(&self) -> Result<u32, Error> {
Ok(std::process::id())
}
fn kill(&self, signal: u32) -> Result<(), Error> {
let code = match signal as i32 {
SIGKILL => 137,
SIGINT | SIGTERM => 0,
s => {
return Err(Error::InvalidArgument(format!("unsupported signal: {}", s)));
}
};
let _ = self.exit_code.set((code, Utc::now()));
Ok(())
}
fn delete(&self) -> Result<(), Error> {
Ok(())
}
fn wait_timeout(&self, t: impl Into<Option<Duration>>) -> Option<(u32, DateTime<Utc>)> {
self.exit_code.wait_timeout(t).copied()
}
}
#[cfg(test)]
mod noptests {
use std::time::Duration;
use super::*;
#[test]
fn test_nop_kill_sigkill() -> Result<(), Error> {
let nop = Nop::new("".to_string(), None)?;
nop.kill(SIGKILL as u32)?;
let ec = nop.wait_timeout(Duration::from_secs(3)).unwrap();
assert_eq!(ec.0, 137);
Ok(())
}
#[test]
fn test_nop_kill_sigterm() -> Result<(), Error> {
let nop = Nop::new("".to_string(), None)?;
nop.kill(SIGTERM as u32)?;
let ec = nop.wait_timeout(Duration::from_secs(3)).unwrap();
assert_eq!(ec.0, 0);
Ok(())
}
#[test]
fn test_nop_kill_sigint() -> Result<(), Error> {
let nop = Nop::new("".to_string(), None)?;
nop.kill(SIGINT as u32)?;
let ec = nop.wait_timeout(Duration::from_secs(3)).unwrap();
assert_eq!(ec.0, 0);
Ok(())
}
#[test]
fn test_nop_delete_after_create() -> Result<(), Error> {
let nop = Nop::new("".to_string(), None)?;
nop.delete()?;
Ok(())
}
}