use indexmap::IndexMap;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use super::{Arch, Dns, Mount, Resources, ScalarSchema, Tmpfs, Virtiofs, VmImage};
#[cfg(doc)]
use super::{Machine, Spec};
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Shared {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub arch: Option<Arch>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub privileged: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub read_only: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tmpfs: Option<Vec<Tmpfs>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shm_size: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dns: Option<Dns>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schemars(with = "Option<IndexMap<String, ScalarSchema>>")]
pub environment: Option<IndexMap<String, serde_json::Value>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schemars(with = "Option<IndexMap<String, ScalarSchema>>")]
pub sysctls: Option<IndexMap<String, serde_json::Value>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ulimits: Option<IndexMap<String, Ulimit>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub inputs: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub resources: Option<Resources>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub depends_on: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub volumes: Option<IndexMap<String, Mount>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub docker: Option<SharedDocker>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub vm: Option<SharedVm>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SharedDocker {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub image: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub build: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub init: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub idle: Option<bool>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SharedVm {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub os: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub provision: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub image: Option<VmImage>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tpm: Option<bool>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Group {
pub members: Vec<String>,
#[serde(flatten)]
pub shared: Shared,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Tool {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub image: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub command: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub port: Option<u16>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub publish: Option<u16>,
}
pub const TOOL_RECIPES: &[&str] = &["shell"];
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct External {
pub address: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub user: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub port: Option<u16>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub key: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SharedVolume {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub owner: Option<String>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub persistent: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schemars(skip)] pub external: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schemars(skip)] pub virtiofs: Option<Virtiofs>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum Ulimit {
Both(u64),
Pair {
soft: u64,
hard: u64,
},
}
impl Ulimit {
#[must_use]
pub fn soft(self) -> u64 {
match self {
Ulimit::Both(n) | Ulimit::Pair { soft: n, .. } => n,
}
}
#[must_use]
pub fn hard(self) -> u64 {
match self {
Ulimit::Both(n) | Ulimit::Pair { hard: n, .. } => n,
}
}
}
pub const ULIMITS: &[&str] = &[
"core",
"cpu",
"data",
"fsize",
"locks",
"memlock",
"msgqueue",
"nice",
"nofile",
"nproc",
"rss",
"rtprio",
"rttime",
"sigpending",
"stack",
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_ulimit_is_one_value_for_both_or_a_soft_and_a_hard_one() -> Result<(), serde_yaml_ng::Error> {
let limits = |yaml| serde_yaml_ng::from_str::<Ulimit>(yaml).map(|u| (u.soft(), u.hard()));
assert_eq!(limits("65536")?, (65_536, 65_536));
assert_eq!(limits("{ soft: 1024, hard: 4096 }")?, (1024, 4096));
Ok(())
}
}