squib_api/schemas/
config_file.rs1use serde::Deserialize;
13
14use super::{
15 balloon::RawBalloonConfig, boot_source::RawBootSourceConfig, cpu_config::RawCpuConfig,
16 drive::RawDriveConfig, entropy::RawEntropyConfig, logger::RawLoggerConfig,
17 machine_config::RawMachineConfig, metrics::RawMetricsConfig, mmds::RawMmdsConfig,
18 network::RawNetworkInterfaceConfig, pmem::RawPmemConfig, serial::RawSerialConfig,
19 vsock::RawVsockConfig,
20};
21
22#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Deserialize)]
24#[serde(rename_all = "lowercase")]
25pub enum SquibNetworkMode {
26 #[default]
28 Shared,
29 Bridged,
31 Host,
33 Userspace,
35}
36
37#[derive(Debug, Clone, Default, Deserialize)]
39#[serde(deny_unknown_fields)]
40pub struct SquibExtension {
41 #[serde(default)]
43 pub network: Option<SquibNetworkMode>,
44 #[serde(default)]
46 pub vsock_tsi: bool,
47 #[serde(default)]
49 pub gvproxy_path: Option<String>,
50 #[serde(default)]
52 pub macos_sandbox_profile: Option<String>,
53}
54
55#[derive(Debug, Clone, Default, Deserialize)]
63#[serde(rename_all = "kebab-case")]
64pub struct ConfigFile {
65 #[serde(default)]
69 pub boot_source: Option<RawBootSourceConfig>,
70 #[serde(default)]
72 pub drives: Vec<RawDriveConfig>,
73 #[serde(default)]
75 pub machine_config: Option<RawMachineConfig>,
76 #[serde(default)]
78 pub cpu_config: Option<RawCpuConfig>,
79 #[serde(default)]
81 pub network_interfaces: Vec<RawNetworkInterfaceConfig>,
82 #[serde(default)]
84 pub vsock: Option<RawVsockConfig>,
85 #[serde(default)]
87 pub mmds_config: Option<RawMmdsConfig>,
88 #[serde(default)]
90 pub mmds: Option<serde_json::Value>,
91 #[serde(default)]
93 pub balloon: Option<RawBalloonConfig>,
94 #[serde(default)]
96 pub entropy: Option<RawEntropyConfig>,
97 #[serde(default)]
99 pub serial: Option<RawSerialConfig>,
100 #[serde(default)]
102 pub pmem: Vec<RawPmemConfig>,
103 #[serde(default)]
105 pub hotplug_memory: Option<super::hotplug_memory::RawHotplugMemoryConfig>,
106 #[serde(default)]
108 pub logger: Option<RawLoggerConfig>,
109 #[serde(default)]
111 pub metrics: Option<RawMetricsConfig>,
112 #[serde(default)]
114 pub squib: SquibExtension,
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn test_should_parse_minimal_config_file() {
123 let json = r#"{"boot-source":{"kernel_image_path":"/tmp/k"}}"#;
124 let cfg: ConfigFile = serde_json::from_str(json).unwrap();
125 assert!(cfg.boot_source.is_some());
126 assert!(cfg.drives.is_empty());
127 }
128
129 #[test]
130 fn test_should_tolerate_unknown_top_level_keys() {
131 let json = r#"{"boot-source":{"kernel_image_path":"/tmp/k"},"future-key":42}"#;
132 let cfg: ConfigFile = serde_json::from_str(json).unwrap();
133 assert!(cfg.boot_source.is_some());
134 }
135
136 #[test]
137 fn test_should_reject_unknown_keys_inside_squib_extension() {
138 let json = r#"{"squib":{"unknown":1}}"#;
139 let res: Result<ConfigFile, _> = serde_json::from_str(json);
140 assert!(res.is_err());
141 }
142
143 #[test]
144 fn test_should_parse_squib_network_mode() {
145 let json = r#"{"squib":{"network":"shared"}}"#;
146 let cfg: ConfigFile = serde_json::from_str(json).unwrap();
147 assert_eq!(cfg.squib.network, Some(SquibNetworkMode::Shared));
148 }
149
150 #[test]
151 fn test_should_parse_full_envelope() {
152 let json = r#"{
153 "boot-source": {"kernel_image_path":"/tmp/k","boot_args":"console=ttyAMA0"},
154 "machine-config": {"vcpu_count":2,"mem_size_mib":256},
155 "drives": [{"drive_id":"rootfs","path_on_host":"/tmp/r.img","is_root_device":true}],
156 "network-interfaces": [{"iface_id":"eth0","host_dev_name":"tap0"}],
157 "logger": {"log_path":"/tmp/squib.log"},
158 "squib": {"network":"userspace","vsock_tsi":false}
159 }"#;
160 let cfg: ConfigFile = serde_json::from_str(json).unwrap();
161 assert_eq!(cfg.machine_config.as_ref().unwrap().vcpu_count, 2);
162 assert_eq!(cfg.drives.len(), 1);
163 assert_eq!(cfg.network_interfaces.len(), 1);
164 assert_eq!(cfg.squib.network, Some(SquibNetworkMode::Userspace));
165 }
166}