1use indexmap::IndexMap;
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7
8use super::{Arch, Dns, Mount, Resources, ScalarSchema, Tmpfs, Virtiofs, VmImage};
9#[cfg(doc)]
10use super::{Machine, Spec};
11
12#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
15#[serde(deny_unknown_fields)]
16pub struct Shared {
17 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub arch: Option<Arch>,
20 #[serde(default, skip_serializing_if = "Option::is_none")]
22 pub privileged: Option<bool>,
23 #[serde(default, skip_serializing_if = "Option::is_none")]
25 pub read_only: Option<bool>,
26 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub tmpfs: Option<Vec<Tmpfs>>,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub shm_size: Option<String>,
32 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub dns: Option<Dns>,
35 #[serde(default, skip_serializing_if = "Option::is_none")]
37 #[schemars(with = "Option<IndexMap<String, ScalarSchema>>")]
38 pub environment: Option<IndexMap<String, serde_json::Value>>,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
41 #[schemars(with = "Option<IndexMap<String, ScalarSchema>>")]
42 pub sysctls: Option<IndexMap<String, serde_json::Value>>,
43 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub ulimits: Option<IndexMap<String, Ulimit>>,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
48 pub inputs: Option<Vec<String>>,
49 #[serde(default, skip_serializing_if = "Option::is_none")]
51 pub resources: Option<Resources>,
52 #[serde(default, skip_serializing_if = "Option::is_none")]
54 pub depends_on: Option<Vec<String>>,
55 #[serde(default, skip_serializing_if = "Option::is_none")]
57 pub volumes: Option<IndexMap<String, Mount>>,
58 #[serde(default, skip_serializing_if = "Option::is_none")]
60 pub docker: Option<SharedDocker>,
61 #[serde(default, skip_serializing_if = "Option::is_none")]
63 pub vm: Option<SharedVm>,
64}
65
66#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
68#[serde(deny_unknown_fields)]
69pub struct SharedDocker {
70 #[serde(default, skip_serializing_if = "Option::is_none")]
72 pub image: Option<String>,
73 #[serde(default, skip_serializing_if = "Option::is_none")]
75 pub build: Option<String>,
76 #[serde(default, skip_serializing_if = "Option::is_none")]
78 pub init: Option<Vec<String>>,
79 #[serde(default, skip_serializing_if = "Option::is_none")]
81 pub idle: Option<bool>,
82}
83
84#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
86#[serde(deny_unknown_fields)]
87pub struct SharedVm {
88 #[serde(default, skip_serializing_if = "Option::is_none")]
90 pub os: Option<String>,
91 #[serde(default, skip_serializing_if = "Option::is_none")]
93 pub provision: Option<Vec<String>>,
94 #[serde(default, skip_serializing_if = "Option::is_none")]
96 pub image: Option<VmImage>,
97 #[serde(default, skip_serializing_if = "Option::is_none")]
99 pub tpm: Option<bool>,
100}
101
102#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
104#[serde(deny_unknown_fields)]
105pub struct Group {
106 pub members: Vec<String>,
108 #[serde(flatten)]
110 pub shared: Shared,
111}
112
113#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
117#[serde(deny_unknown_fields)]
118pub struct Tool {
119 #[serde(default, skip_serializing_if = "Option::is_none")]
121 pub image: Option<String>,
122 #[serde(default, skip_serializing_if = "Vec::is_empty")]
124 pub command: Vec<String>,
125 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub port: Option<u16>,
128 #[serde(default, skip_serializing_if = "Option::is_none")]
131 pub publish: Option<u16>,
132}
133
134pub const TOOL_RECIPES: &[&str] = &["shell"];
136
137#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
139#[serde(deny_unknown_fields)]
140pub struct External {
141 pub address: String,
143 #[serde(default, skip_serializing_if = "Option::is_none")]
145 pub user: Option<String>,
146 #[serde(default, skip_serializing_if = "Option::is_none")]
148 pub port: Option<u16>,
149 #[serde(default, skip_serializing_if = "Option::is_none")]
151 pub key: Option<String>,
152}
153
154#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
160#[serde(deny_unknown_fields)]
161pub struct SharedVolume {
162 #[serde(default, skip_serializing_if = "Option::is_none")]
165 pub owner: Option<String>,
166 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
170 pub persistent: bool,
171 #[serde(default, skip_serializing_if = "Option::is_none")]
174 #[schemars(skip)] pub external: Option<String>,
176 #[serde(default, skip_serializing_if = "Option::is_none")]
180 #[schemars(skip)] pub virtiofs: Option<Virtiofs>,
182}
183
184#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
186#[serde(untagged)]
187pub enum Ulimit {
188 Both(u64),
190 Pair {
192 soft: u64,
194 hard: u64,
196 },
197}
198
199impl Ulimit {
200 #[must_use]
202 pub fn soft(self) -> u64 {
203 match self {
204 Ulimit::Both(n) | Ulimit::Pair { soft: n, .. } => n,
205 }
206 }
207
208 #[must_use]
210 pub fn hard(self) -> u64 {
211 match self {
212 Ulimit::Both(n) | Ulimit::Pair { hard: n, .. } => n,
213 }
214 }
215}
216
217pub const ULIMITS: &[&str] = &[
219 "core",
220 "cpu",
221 "data",
222 "fsize",
223 "locks",
224 "memlock",
225 "msgqueue",
226 "nice",
227 "nofile",
228 "nproc",
229 "rss",
230 "rtprio",
231 "rttime",
232 "sigpending",
233 "stack",
234];
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 #[test]
241 fn a_ulimit_is_one_value_for_both_or_a_soft_and_a_hard_one() -> Result<(), serde_yaml_ng::Error> {
242 let limits = |yaml| serde_yaml_ng::from_str::<Ulimit>(yaml).map(|u| (u.soft(), u.hard()));
243 assert_eq!(limits("65536")?, (65_536, 65_536));
244 assert_eq!(limits("{ soft: 1024, hard: 4096 }")?, (1024, 4096));
245 Ok(())
246 }
247}