1use std::collections::BTreeMap;
4use std::path::PathBuf;
5
6use serde::{Deserialize, Serialize};
7
8use super::CloudSecretsConfig;
9use crate::domain::{
10 DiskImageFormat, HostPermissions, MountOptions, NetworkPolicy, OwnedVolumeStorage, Patch,
11 PullPolicy, Rlimit, RlimitResource, SandboxLogLevel, StatVirtualization, VolumeMount,
12 default_private, default_strict,
13};
14
15#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
24#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
25#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
26#[serde(rename_all = "snake_case")]
27pub enum CloudPullPolicy {
28 #[default]
30 IfMissing,
31 Always,
33 Never,
35}
36
37impl From<PullPolicy> for CloudPullPolicy {
38 fn from(policy: PullPolicy) -> Self {
39 match policy {
40 PullPolicy::IfMissing => Self::IfMissing,
41 PullPolicy::Always => Self::Always,
42 PullPolicy::Never => Self::Never,
43 }
44 }
45}
46
47impl From<CloudPullPolicy> for PullPolicy {
48 fn from(policy: CloudPullPolicy) -> Self {
49 match policy {
50 CloudPullPolicy::IfMissing => Self::IfMissing,
51 CloudPullPolicy::Always => Self::Always,
52 CloudPullPolicy::Never => Self::Never,
53 }
54 }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
60#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
61#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
62#[serde(rename_all = "snake_case")]
63pub enum CloudDiskImageFormat {
64 Qcow2,
66 Raw,
68 Vmdk,
70}
71
72impl From<DiskImageFormat> for CloudDiskImageFormat {
73 fn from(format: DiskImageFormat) -> Self {
74 match format {
75 DiskImageFormat::Qcow2 => Self::Qcow2,
76 DiskImageFormat::Raw => Self::Raw,
77 DiskImageFormat::Vmdk => Self::Vmdk,
78 }
79 }
80}
81
82impl From<CloudDiskImageFormat> for DiskImageFormat {
83 fn from(format: CloudDiskImageFormat) -> Self {
84 match format {
85 CloudDiskImageFormat::Qcow2 => Self::Qcow2,
86 CloudDiskImageFormat::Raw => Self::Raw,
87 CloudDiskImageFormat::Vmdk => Self::Vmdk,
88 }
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
95#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
96#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
97#[serde(rename_all = "snake_case")]
98pub enum CloudRlimitResource {
99 Cpu,
101 Fsize,
103 Data,
105 Stack,
107 Core,
109 Rss,
111 Nproc,
113 Nofile,
115 Memlock,
117 As,
119 Locks,
121 Sigpending,
123 Msgqueue,
125 Nice,
127 Rtprio,
129 Rttime,
131}
132
133impl From<RlimitResource> for CloudRlimitResource {
134 fn from(resource: RlimitResource) -> Self {
135 match resource {
136 RlimitResource::Cpu => Self::Cpu,
137 RlimitResource::Fsize => Self::Fsize,
138 RlimitResource::Data => Self::Data,
139 RlimitResource::Stack => Self::Stack,
140 RlimitResource::Core => Self::Core,
141 RlimitResource::Rss => Self::Rss,
142 RlimitResource::Nproc => Self::Nproc,
143 RlimitResource::Nofile => Self::Nofile,
144 RlimitResource::Memlock => Self::Memlock,
145 RlimitResource::As => Self::As,
146 RlimitResource::Locks => Self::Locks,
147 RlimitResource::Sigpending => Self::Sigpending,
148 RlimitResource::Msgqueue => Self::Msgqueue,
149 RlimitResource::Nice => Self::Nice,
150 RlimitResource::Rtprio => Self::Rtprio,
151 RlimitResource::Rttime => Self::Rttime,
152 }
153 }
154}
155
156impl From<CloudRlimitResource> for RlimitResource {
157 fn from(resource: CloudRlimitResource) -> Self {
158 match resource {
159 CloudRlimitResource::Cpu => Self::Cpu,
160 CloudRlimitResource::Fsize => Self::Fsize,
161 CloudRlimitResource::Data => Self::Data,
162 CloudRlimitResource::Stack => Self::Stack,
163 CloudRlimitResource::Core => Self::Core,
164 CloudRlimitResource::Rss => Self::Rss,
165 CloudRlimitResource::Nproc => Self::Nproc,
166 CloudRlimitResource::Nofile => Self::Nofile,
167 CloudRlimitResource::Memlock => Self::Memlock,
168 CloudRlimitResource::As => Self::As,
169 CloudRlimitResource::Locks => Self::Locks,
170 CloudRlimitResource::Sigpending => Self::Sigpending,
171 CloudRlimitResource::Msgqueue => Self::Msgqueue,
172 CloudRlimitResource::Nice => Self::Nice,
173 CloudRlimitResource::Rtprio => Self::Rtprio,
174 CloudRlimitResource::Rttime => Self::Rttime,
175 }
176 }
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
181#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
182#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
183pub struct CloudRlimit {
184 pub resource: CloudRlimitResource,
186 pub soft: u64,
188 pub hard: u64,
190}
191
192impl From<Rlimit> for CloudRlimit {
193 fn from(rlimit: Rlimit) -> Self {
194 Self {
195 resource: rlimit.resource.into(),
196 soft: rlimit.soft,
197 hard: rlimit.hard,
198 }
199 }
200}
201
202impl From<CloudRlimit> for Rlimit {
203 fn from(rlimit: CloudRlimit) -> Self {
204 Self {
205 resource: rlimit.resource.into(),
206 soft: rlimit.soft,
207 hard: rlimit.hard,
208 }
209 }
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
215#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
216#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
217#[serde(tag = "type", rename_all = "snake_case")]
218pub enum CloudPatch {
219 Text {
221 path: String,
223 content: String,
225 mode: Option<u32>,
227 replace: bool,
229 },
230 File {
232 path: String,
234 content: Vec<u8>,
236 mode: Option<u32>,
238 replace: bool,
240 },
241 CopyFile {
243 #[cfg_attr(feature = "ts", ts(type = "string"))]
245 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
246 src: PathBuf,
247 dst: String,
249 mode: Option<u32>,
251 replace: bool,
253 },
254 CopyDir {
256 #[cfg_attr(feature = "ts", ts(type = "string"))]
258 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
259 src: PathBuf,
260 dst: String,
262 replace: bool,
264 },
265 Symlink {
267 target: String,
269 link: String,
271 replace: bool,
273 },
274 Mkdir {
276 path: String,
278 mode: Option<u32>,
280 },
281 Remove {
283 path: String,
285 },
286 Append {
288 path: String,
290 content: String,
292 },
293}
294
295impl From<Patch> for CloudPatch {
296 fn from(patch: Patch) -> Self {
297 match patch {
298 Patch::Text {
299 path,
300 content,
301 mode,
302 replace,
303 } => Self::Text {
304 path,
305 content,
306 mode,
307 replace,
308 },
309 Patch::File {
310 path,
311 content,
312 mode,
313 replace,
314 } => Self::File {
315 path,
316 content,
317 mode,
318 replace,
319 },
320 Patch::CopyFile {
321 src,
322 dst,
323 mode,
324 replace,
325 } => Self::CopyFile {
326 src,
327 dst,
328 mode,
329 replace,
330 },
331 Patch::CopyDir { src, dst, replace } => Self::CopyDir { src, dst, replace },
332 Patch::Symlink {
333 target,
334 link,
335 replace,
336 } => Self::Symlink {
337 target,
338 link,
339 replace,
340 },
341 Patch::Mkdir { path, mode } => Self::Mkdir { path, mode },
342 Patch::Remove { path } => Self::Remove { path },
343 Patch::Append { path, content } => Self::Append { path, content },
344 }
345 }
346}
347
348impl From<CloudPatch> for Patch {
349 fn from(patch: CloudPatch) -> Self {
350 match patch {
351 CloudPatch::Text {
352 path,
353 content,
354 mode,
355 replace,
356 } => Self::Text {
357 path,
358 content,
359 mode,
360 replace,
361 },
362 CloudPatch::File {
363 path,
364 content,
365 mode,
366 replace,
367 } => Self::File {
368 path,
369 content,
370 mode,
371 replace,
372 },
373 CloudPatch::CopyFile {
374 src,
375 dst,
376 mode,
377 replace,
378 } => Self::CopyFile {
379 src,
380 dst,
381 mode,
382 replace,
383 },
384 CloudPatch::CopyDir { src, dst, replace } => Self::CopyDir { src, dst, replace },
385 CloudPatch::Symlink {
386 target,
387 link,
388 replace,
389 } => Self::Symlink {
390 target,
391 link,
392 replace,
393 },
394 CloudPatch::Mkdir { path, mode } => Self::Mkdir { path, mode },
395 CloudPatch::Remove { path } => Self::Remove { path },
396 CloudPatch::Append { path, content } => Self::Append { path, content },
397 }
398 }
399}
400
401#[derive(Debug, Clone, Serialize, Deserialize)]
408#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
409#[serde(tag = "type", rename_all = "snake_case")]
410pub enum CloudRootfsSource {
411 Bind {
413 #[cfg_attr(feature = "ts", ts(type = "string"))]
415 path: PathBuf,
416 },
417
418 Oci {
420 reference: String,
422 },
423
424 DiskImage {
426 #[cfg_attr(feature = "ts", ts(type = "string"))]
428 path: PathBuf,
429 format: CloudDiskImageFormat,
431 fstype: Option<String>,
433 },
434}
435
436#[derive(Debug, Clone, Serialize, Deserialize)]
439#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
440#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
441#[serde(tag = "type", rename_all = "snake_case")]
442pub enum CloudVolumeMount {
443 Owned {
445 guest: String,
447 storage: OwnedVolumeStorage,
449 options: MountOptions,
451 stat_virtualization: StatVirtualization,
453 host_permissions: HostPermissions,
455 },
456 Bind {
458 #[cfg_attr(feature = "ts", ts(type = "string"))]
460 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
461 host: PathBuf,
462 guest: String,
464 #[serde(default)]
466 options: MountOptions,
467 #[serde(default = "default_strict")]
469 stat_virtualization: StatVirtualization,
470 #[serde(default = "default_private")]
472 host_permissions: HostPermissions,
473 #[serde(default)]
475 quota_mib: Option<u32>,
476 },
477
478 Named {
480 name: String,
482 guest: String,
484 #[serde(default)]
486 options: MountOptions,
487 #[serde(default = "default_strict")]
489 stat_virtualization: StatVirtualization,
490 #[serde(default = "default_private")]
492 host_permissions: HostPermissions,
493 },
494
495 Tmpfs {
497 guest: String,
499 #[serde(default)]
501 size_mib: Option<u32>,
502 #[serde(default)]
504 options: MountOptions,
505 },
506
507 DiskImage {
509 #[cfg_attr(feature = "ts", ts(type = "string"))]
511 #[cfg_attr(feature = "utoipa", schema(value_type = String))]
512 host: PathBuf,
513 guest: String,
515 format: CloudDiskImageFormat,
517 #[serde(default)]
519 fstype: Option<String>,
520 #[serde(default)]
522 options: MountOptions,
523 },
524}
525
526impl From<CloudVolumeMount> for VolumeMount {
527 fn from(m: CloudVolumeMount) -> Self {
528 match m {
529 CloudVolumeMount::Owned {
530 guest,
531 storage,
532 options,
533 stat_virtualization,
534 host_permissions,
535 } => VolumeMount::Owned {
536 guest,
537 storage,
538 options,
539 stat_virtualization,
540 host_permissions,
541 },
542 CloudVolumeMount::Bind {
543 host,
544 guest,
545 options,
546 stat_virtualization,
547 host_permissions,
548 quota_mib,
549 } => VolumeMount::Bind {
550 host,
551 guest,
552 options,
553 stat_virtualization,
554 host_permissions,
555 follow_root_symlinks: false,
558 quota_mib,
559 },
560 CloudVolumeMount::Named {
561 name,
562 guest,
563 options,
564 stat_virtualization,
565 host_permissions,
566 } => VolumeMount::Named {
567 name,
568 guest,
569 create: None,
570 options,
571 stat_virtualization,
572 host_permissions,
573 follow_root_symlinks: false,
574 },
575 CloudVolumeMount::Tmpfs {
576 guest,
577 size_mib,
578 options,
579 } => VolumeMount::Tmpfs {
580 guest,
581 size_mib,
582 options,
583 },
584 CloudVolumeMount::DiskImage {
585 host,
586 guest,
587 format,
588 fstype,
589 options,
590 } => VolumeMount::DiskImage {
591 host,
592 guest,
593 format: format.into(),
594 fstype,
595 options,
596 },
597 }
598 }
599}
600
601impl From<VolumeMount> for CloudVolumeMount {
602 fn from(m: VolumeMount) -> Self {
603 match m {
604 VolumeMount::Owned {
605 guest,
606 storage,
607 options,
608 stat_virtualization,
609 host_permissions,
610 } => CloudVolumeMount::Owned {
611 guest,
612 storage,
613 options,
614 stat_virtualization,
615 host_permissions,
616 },
617 VolumeMount::Bind {
618 host,
619 guest,
620 options,
621 stat_virtualization,
622 host_permissions,
623 follow_root_symlinks: _,
624 quota_mib,
625 } => CloudVolumeMount::Bind {
626 host,
627 guest,
628 options,
629 stat_virtualization,
630 host_permissions,
631 quota_mib,
632 },
633 VolumeMount::Named {
634 name,
635 guest,
636 create: _,
637 options,
638 stat_virtualization,
639 host_permissions,
640 follow_root_symlinks: _,
641 } => CloudVolumeMount::Named {
642 name,
643 guest,
644 options,
645 stat_virtualization,
646 host_permissions,
647 },
648 VolumeMount::Tmpfs {
649 guest,
650 size_mib,
651 options,
652 } => CloudVolumeMount::Tmpfs {
653 guest,
654 size_mib,
655 options,
656 },
657 VolumeMount::DiskImage {
658 host,
659 guest,
660 format,
661 fstype,
662 options,
663 } => CloudVolumeMount::DiskImage {
664 host,
665 guest,
666 format: format.into(),
667 fstype,
668 options,
669 },
670 }
671 }
672}
673
674#[derive(Debug, Clone, Serialize, Deserialize)]
679#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
680#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
681#[serde(default)]
682pub struct CloudNetworkSpec {
683 pub enabled: bool,
685
686 #[serde(skip_serializing_if = "Option::is_none")]
688 pub policy: Option<NetworkPolicy>,
689
690 #[serde(skip_serializing_if = "Option::is_none")]
692 pub secrets: Option<CloudSecretsConfig>,
693
694 pub strict: bool,
696
697 #[serde(default, skip_serializing_if = "Option::is_none")]
699 #[serde(rename = "max_connections", alias = "max_tcp_connections")]
701 pub max_tcp_connections: Option<usize>,
702
703 #[serde(default, skip_serializing_if = "Option::is_none")]
705 pub max_udp_connections: Option<usize>,
706}
707
708impl Default for CloudNetworkSpec {
709 fn default() -> Self {
710 Self {
711 enabled: true,
712 policy: None,
713 secrets: None,
714 strict: false,
715 max_tcp_connections: None,
716 max_udp_connections: None,
717 }
718 }
719}
720
721#[derive(Debug, Clone, Default, Serialize, Deserialize)]
725#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
726#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
727#[serde(default)]
728pub struct CloudSandboxRuntimeOptions {
729 pub workdir: Option<String>,
731
732 pub shell: Option<String>,
734
735 pub scripts: BTreeMap<String, String>,
737
738 pub entrypoint: Option<Vec<String>>,
740
741 pub cmd: Option<Vec<String>>,
743
744 pub user: Option<String>,
746
747 pub log_level: Option<SandboxLogLevel>,
749}