1use crate::error::OciSpecError;
2
3use std::{
4 fs,
5 io::{BufReader, BufWriter, Write},
6 path::{Path, PathBuf},
7};
8
9use derive_builder::Builder;
10use getset::{Getters, MutGetters, Setters};
11use serde::{Deserialize, Serialize};
12use std::{collections::HashMap, fmt::Display};
13
14#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Deserialize, Serialize)]
16#[serde(rename_all = "lowercase")]
17pub enum ContainerState {
18 Creating,
20
21 Created,
24
25 Running,
28
29 #[default]
32 Stopped,
33}
34
35impl Display for ContainerState {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 match self {
38 ContainerState::Creating => write!(f, "creating"),
39 ContainerState::Created => write!(f, "created"),
40 ContainerState::Running => write!(f, "running"),
41 ContainerState::Stopped => write!(f, "stopped"),
42 }
43 }
44}
45
46#[derive(
48 Builder,
49 Clone,
50 Debug,
51 Default,
52 Deserialize,
53 Eq,
54 Getters,
55 MutGetters,
56 Setters,
57 PartialEq,
58 Serialize,
59)]
60#[serde(rename_all = "camelCase")]
61#[builder(
62 default,
63 pattern = "owned",
64 setter(into, strip_option),
65 build_fn(error = "OciSpecError")
66)]
67#[getset(get_mut = "pub", get = "pub", set = "pub")]
68pub struct State {
69 #[serde(default, rename = "ociVersion")]
71 version: String,
72
73 #[serde(default)]
75 id: String,
76
77 #[serde(default)]
79 status: ContainerState,
80
81 #[serde(default, skip_serializing_if = "Option::is_none")]
83 pid: Option<i32>,
84
85 #[serde(default)]
87 bundle: PathBuf,
88
89 #[serde(default, skip_serializing_if = "Option::is_none")]
91 annotations: Option<HashMap<String, String>>,
92}
93
94impl State {
95 pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, OciSpecError> {
100 let path = path.as_ref();
101 let file = fs::File::open(path)?;
102 let reader = BufReader::new(file);
103 let state = serde_json::from_reader(reader)?;
104 Ok(state)
105 }
106
107 pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), OciSpecError> {
112 let path = path.as_ref();
113 let file = fs::File::create(path)?;
114 let mut writer = BufWriter::new(file);
115 serde_json::to_writer(&mut writer, self)?;
116 writer.flush()?;
117 Ok(())
118 }
119}
120
121pub const SECCOMP_FD_NAME: &str = "seccompFd";
125
126#[derive(
128 Builder,
129 Clone,
130 Debug,
131 Default,
132 Deserialize,
133 Eq,
134 Getters,
135 MutGetters,
136 Setters,
137 PartialEq,
138 Serialize,
139)]
140#[serde(rename_all = "camelCase")]
141#[builder(
142 default,
143 pattern = "owned",
144 setter(into, strip_option),
145 build_fn(error = "OciSpecError")
146)]
147#[getset(get_mut = "pub", get = "pub", set = "pub")]
148pub struct ContainerProcessState {
149 #[serde(default, rename = "ociVersion")]
151 version: String,
152
153 #[serde(default)]
157 fds: Vec<String>,
158
159 #[serde(default)]
161 pid: i32,
162
163 #[serde(default, skip_serializing_if = "Option::is_none")]
165 metadata: Option<String>,
166
167 #[serde(default)]
169 state: State,
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn test_load_save() {
178 let state = State {
179 ..Default::default()
180 };
181 let test_dir = tempfile::tempdir().expect("failed to create tmp test dir");
182 let state_path = test_dir.keep().join("state.json");
183
184 state.save(&state_path).expect("failed to save state");
187 let loaded_state = State::load(&state_path).expect("failed to load state");
188 assert_eq!(
189 state, loaded_state,
190 "The saved state is not the same as the loaded state"
191 );
192 }
193}