use crate::error::OciSpecError;
use std::{
fs,
io::{BufReader, BufWriter, Write},
path::{Path, PathBuf},
};
use derive_builder::Builder;
use getset::{Getters, MutGetters, Setters};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fmt::Display};
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum ContainerState {
Creating,
Created,
Running,
#[default]
Stopped,
}
impl Display for ContainerState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ContainerState::Creating => write!(f, "creating"),
ContainerState::Created => write!(f, "created"),
ContainerState::Running => write!(f, "running"),
ContainerState::Stopped => write!(f, "stopped"),
}
}
}
#[derive(
Builder,
Clone,
Debug,
Default,
Deserialize,
Eq,
Getters,
MutGetters,
Setters,
PartialEq,
Serialize,
)]
#[serde(rename_all = "camelCase")]
#[builder(
default,
pattern = "owned",
setter(into, strip_option),
build_fn(error = "OciSpecError")
)]
#[getset(get_mut = "pub", get = "pub", set = "pub")]
pub struct State {
#[serde(default, rename = "ociVersion")]
version: String,
#[serde(default)]
id: String,
#[serde(default)]
status: ContainerState,
#[serde(default, skip_serializing_if = "Option::is_none")]
pid: Option<i32>,
#[serde(default)]
bundle: PathBuf,
#[serde(default, skip_serializing_if = "Option::is_none")]
annotations: Option<HashMap<String, String>>,
}
impl State {
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, OciSpecError> {
let path = path.as_ref();
let file = fs::File::open(path)?;
let reader = BufReader::new(file);
let state = serde_json::from_reader(reader)?;
Ok(state)
}
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), OciSpecError> {
let path = path.as_ref();
let file = fs::File::create(path)?;
let mut writer = BufWriter::new(file);
serde_json::to_writer(&mut writer, self)?;
writer.flush()?;
Ok(())
}
}
pub const SECCOMP_FD_NAME: &str = "seccompFd";
#[derive(
Builder,
Clone,
Debug,
Default,
Deserialize,
Eq,
Getters,
MutGetters,
Setters,
PartialEq,
Serialize,
)]
#[serde(rename_all = "camelCase")]
#[builder(
default,
pattern = "owned",
setter(into, strip_option),
build_fn(error = "OciSpecError")
)]
#[getset(get_mut = "pub", get = "pub", set = "pub")]
pub struct ContainerProcessState {
#[serde(default, rename = "ociVersion")]
version: String,
#[serde(default)]
fds: Vec<String>,
#[serde(default)]
pid: i32,
#[serde(default, skip_serializing_if = "Option::is_none")]
metadata: Option<String>,
#[serde(default)]
state: State,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_load_save() {
let state = State {
..Default::default()
};
let test_dir = tempfile::tempdir().expect("failed to create tmp test dir");
let state_path = test_dir.keep().join("state.json");
state.save(&state_path).expect("failed to save state");
let loaded_state = State::load(&state_path).expect("failed to load state");
assert_eq!(
state, loaded_state,
"The saved state is not the same as the loaded state"
);
}
}