1use indexmap::IndexMap;
6use schemars::JsonSchema;
7use serde::Deserialize;
8
9use crate::model::{External, Target, Virtiofs, Winrm};
10
11#[derive(Debug, Clone, PartialEq, Default, Deserialize, JsonSchema)]
13#[serde(deny_unknown_fields)]
14pub struct Overrides {
15 #[serde(default)]
17 pub docker: Option<DockerOverrides>,
18 #[serde(default)]
20 pub kubernetes: Option<KubernetesOverrides>,
21 #[serde(default)]
23 pub vagrant: Option<VagrantOverrides>,
24 #[serde(default)]
26 pub proxmox: Option<ProxmoxOverrides>,
27 #[serde(default)]
29 pub esxi: Option<EsxiOverrides>,
30 #[serde(default)]
32 pub aws: Option<CloudOverrides>,
33 #[serde(default)]
35 pub azure: Option<CloudOverrides>,
36 #[serde(default)]
38 pub gcp: Option<CloudOverrides>,
39 #[serde(default)]
41 pub digitalocean: Option<CloudOverrides>,
42 #[serde(default)]
44 pub linode: Option<CloudOverrides>,
45 #[serde(default)]
47 pub oci: Option<CloudOverrides>,
48 #[serde(default)]
50 pub external: Option<ExternalOverrides>,
51}
52
53#[derive(Debug, Clone, PartialEq, Default, Deserialize, JsonSchema)]
55#[serde(deny_unknown_fields)]
56pub struct DockerOverrides {
57 #[serde(default)]
59 pub networks: IndexMap<String, DockerNetwork>,
60 #[serde(default)]
62 pub volumes: IndexMap<String, ExternalVolume>,
63}
64
65#[derive(Debug, Clone, PartialEq, Default, Deserialize, JsonSchema)]
67#[serde(deny_unknown_fields)]
68pub struct DockerNetwork {
69 #[serde(default)]
73 pub cidr: Option<String>,
74 #[serde(default)]
77 pub external: Option<String>,
78}
79
80#[derive(Debug, Clone, PartialEq, Deserialize, JsonSchema)]
82#[serde(deny_unknown_fields)]
83pub struct ExternalVolume {
84 pub external: String,
87}
88
89#[derive(Debug, Clone, PartialEq, Deserialize, JsonSchema)]
91#[serde(deny_unknown_fields)]
92pub struct ExternalNetwork {
93 pub external: String,
96}
97
98#[derive(Debug, Clone, PartialEq, Default, Deserialize, JsonSchema)]
100#[serde(deny_unknown_fields)]
101pub struct KubernetesOverrides {
102 #[serde(default)]
104 pub volumes: IndexMap<String, ExternalVolume>,
105}
106
107#[derive(Debug, Clone, PartialEq, Default, Deserialize, JsonSchema)]
109#[serde(deny_unknown_fields)]
110pub struct VagrantOverrides {
111 #[serde(default)]
113 pub machines: IndexMap<String, VagrantMachine>,
114 #[serde(default)]
116 pub controller: Option<VagrantBox>,
117}
118
119#[derive(Debug, Clone, PartialEq, Default, Deserialize, JsonSchema)]
121#[serde(deny_unknown_fields)]
122pub struct VagrantMachine {
123 #[serde(default, rename = "box")]
125 pub box_name: Option<String>,
126 #[serde(default)]
128 pub box_version: Option<String>,
129 #[serde(default)]
132 pub box_url: Option<String>,
133 #[serde(default)]
135 pub box_server_url: Option<String>,
136 #[serde(default)]
138 pub boxes: Option<ProviderBoxes>,
139 #[serde(default)]
142 pub winrm: Option<Winrm>,
143 #[serde(default)]
145 pub winssh: bool,
146}
147
148#[derive(Debug, Clone, PartialEq, Default, Deserialize, JsonSchema)]
150#[serde(deny_unknown_fields)]
151pub struct ProviderBoxes {
152 #[serde(default)]
154 pub qemu: Option<String>,
155 #[serde(default)]
157 pub hyperv: Option<String>,
158}
159
160#[derive(Debug, Clone, PartialEq, Default, Deserialize, JsonSchema)]
162#[serde(deny_unknown_fields)]
163pub struct VagrantBox {
164 #[serde(default, rename = "box")]
166 pub box_name: Option<String>,
167 #[serde(default)]
169 pub box_version: Option<String>,
170}
171
172#[derive(Debug, Clone, PartialEq, Default, Deserialize, JsonSchema)]
174#[serde(deny_unknown_fields)]
175pub struct ProxmoxOverrides {
176 #[serde(default)]
178 pub machines: IndexMap<String, ProxmoxMachine>,
179 #[serde(default)]
181 pub volumes: IndexMap<String, ProxmoxVolume>,
182 #[serde(default)]
184 pub networks: IndexMap<String, ExternalNetwork>,
185}
186
187#[derive(Debug, Clone, PartialEq, Default, Deserialize, JsonSchema)]
189#[serde(deny_unknown_fields)]
190pub struct ProxmoxMachine {
191 #[serde(default)]
193 pub image: Option<String>,
194 #[serde(default)]
197 pub lxc: bool,
198}
199
200#[derive(Debug, Clone, PartialEq, Deserialize, JsonSchema)]
202#[serde(deny_unknown_fields)]
203pub struct ProxmoxVolume {
204 pub virtiofs: Virtiofs,
207}
208
209#[derive(Debug, Clone, PartialEq, Default, Deserialize, JsonSchema)]
211#[serde(deny_unknown_fields)]
212pub struct EsxiOverrides {
213 #[serde(default)]
215 pub networks: IndexMap<String, ExternalNetwork>,
216}
217
218#[derive(Debug, Clone, PartialEq, Default, Deserialize, JsonSchema)]
220#[serde(deny_unknown_fields)]
221pub struct CloudOverrides {
222 #[serde(default)]
224 pub machines: IndexMap<String, CloudMachine>,
225}
226
227#[derive(Debug, Clone, PartialEq, Deserialize, JsonSchema)]
229#[serde(deny_unknown_fields)]
230pub struct CloudMachine {
231 pub image: String,
234}
235
236#[derive(Debug, Clone, PartialEq, Default, Deserialize, JsonSchema)]
238#[serde(deny_unknown_fields)]
239pub struct ExternalOverrides {
240 #[serde(default)]
242 pub machines: IndexMap<String, ExternalMachine>,
243}
244
245#[derive(Debug, Clone, PartialEq, Deserialize, JsonSchema)]
247#[serde(deny_unknown_fields)]
248pub struct ExternalMachine {
249 pub ssh: External,
251}
252
253impl Overrides {
254 #[must_use]
256 pub fn clouds(&self) -> [(&'static str, Option<&CloudOverrides>); 6] {
257 [
258 ("aws", self.aws.as_ref()),
259 ("azure", self.azure.as_ref()),
260 ("gcp", self.gcp.as_ref()),
261 ("digitalocean", self.digitalocean.as_ref()),
262 ("linode", self.linode.as_ref()),
263 ("oci", self.oci.as_ref()),
264 ]
265 }
266
267 #[must_use]
269 pub fn targets_of(key: &str) -> &'static [Target] {
270 match key {
271 "docker" => &[Target::Docker, Target::Hosted, Target::DockerVm, Target::CloudDocker],
272 "kubernetes" => &[Target::Kubernetes],
273 "vagrant" => &[Target::Vagrant, Target::Hybrid],
274 "proxmox" => &[Target::Proxmox],
275 "esxi" => &[Target::Esxi],
276 "external" => &[Target::External],
277 "aws" | "azure" | "gcp" | "digitalocean" | "linode" | "oci" => &[Target::CloudVm],
278 _ => &[],
279 }
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 #[test]
288 fn each_override_key_applies_to_its_targets() {
289 assert_eq!(
290 Overrides::targets_of("docker"),
291 [Target::Docker, Target::Hosted, Target::DockerVm, Target::CloudDocker]
292 );
293 assert_eq!(Overrides::targets_of("kubernetes"), [Target::Kubernetes]);
294 assert_eq!(Overrides::targets_of("vagrant"), [Target::Vagrant, Target::Hybrid]);
295 assert_eq!(Overrides::targets_of("proxmox"), [Target::Proxmox]);
296 assert_eq!(Overrides::targets_of("esxi"), [Target::Esxi]);
297 assert_eq!(Overrides::targets_of("external"), [Target::External]);
298 for cloud in ["aws", "azure", "gcp", "digitalocean", "linode", "oci"] {
299 assert_eq!(Overrides::targets_of(cloud), [Target::CloudVm], "{cloud}");
300 }
301 assert_eq!(Overrides::targets_of("podman"), []);
302 }
303
304 #[test]
305 fn each_target_has_its_own_fields() {
306 let o: Overrides = serde_yaml_ng::from_str(
307 "{ docker: { networks: { office: { cidr: 10.70.0.0/24 } } }, vagrant: { machines: { portal: { box: example/debian, box_version: '1' } } }, proxmox: { machines: { status: { lxc: true } }, volumes: { uploads: { virtiofs: {} } } }, aws: { machines: { portal: { image: ami-1 } } } }",
308 )
309 .unwrap();
310 assert_eq!(o.vagrant.as_ref().unwrap().machines["portal"].box_name.as_deref(), Some("example/debian"));
311 assert!(o.proxmox.as_ref().unwrap().machines["status"].lxc);
312 assert_eq!(o.clouds()[0].1.unwrap().machines["portal"].image, "ami-1");
313 }
314
315 #[test]
316 fn a_field_another_target_has_is_refused() {
317 let e = serde_yaml_ng::from_str::<Overrides>("{ docker: { machines: { web: { box: x } } } }").unwrap_err();
318 assert!(e.to_string().contains("unknown field `machines`"));
319 assert!(serde_yaml_ng::from_str::<Overrides>("{ podman: {} }").is_err());
320 }
321}