microsandbox_types/snapshot.rs
1//! Snapshot maintenance results shared by SDK and runtime without linking a VM runner.
2
3use serde::{Deserialize, Serialize};
4
5//--------------------------------------------------------------------------------------------------
6// Types
7//--------------------------------------------------------------------------------------------------
8
9/// Disks eligible for explicit maintenance. Named and external disks are never included.
10#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
11#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
12pub enum DiskCompactionTarget {
13 /// The managed/flat root, when present, and every sandbox-owned data disk.
14 #[default]
15 All,
16 /// Only the managed or flat root disk.
17 Root,
18 /// Only the sandbox-owned data disk mounted at this guest path.
19 Disk {
20 /// Canonical absolute guest mount path; `/` selects the root.
21 guest_path: String,
22 },
23}
24
25/// Per-disk outcome; a selected count below two means the chain was unchanged.
26#[derive(Clone, Debug, Default, Serialize, Deserialize)]
27pub struct DiskCompactionDiskResult {
28 /// Guest mount path; `/` identifies the root disk.
29 pub guest_path: String,
30 /// Physical layers before compaction, including the writable head.
31 pub input_layers: usize,
32 /// Oldest sealed physical layers selected, including the base.
33 pub selected_layers: usize,
34 /// Physical layers after compaction, including the writable head.
35 pub output_layers: usize,
36 /// Guest bytes materialized; not reclaimed disk space.
37 pub materialized_bytes: u64,
38 /// This disk's preparation/materialization duration in microseconds, excluding journal
39 /// adoption and backend switching. Those shared phases are included in the aggregate timing.
40 pub total_us: u64,
41}
42
43/// Aggregate outcome or dry-run projection of explicit disk compaction.
44#[derive(Clone, Debug, Default, Serialize, Deserialize)]
45pub struct DiskCompactionResult {
46 /// Whether only selection was performed.
47 pub dry_run: bool,
48 /// Sum of physical layers before compaction, including each writable head.
49 pub input_layers: usize,
50 /// Sum of selected oldest sealed layers, including each base, excluding writable heads.
51 pub selected_layers: usize,
52 /// Sum of physical layers after compaction, including each writable head.
53 pub output_layers: usize,
54 /// Guest bytes materialized; not a disk-space saving estimate.
55 pub materialized_bytes: u64,
56 /// Total operation duration in microseconds.
57 pub total_us: u64,
58 /// Measured VM pause through resume, zero for stopped sources and dry runs.
59 pub pause_us: u64,
60 /// Individual selected disks, including unchanged chains with fewer than two sealed layers.
61 pub disks: Vec<DiskCompactionDiskResult>,
62}
63
64/// How full restore validates authorized external filesystem mappings and captured objects.
65///
66/// This policy does not authorize or inherit host resources. Intentionally unmapped
67/// filesystems remain unavailable under either policy; backend operations return EIO.
68#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
69#[serde(rename_all = "lowercase")]
70pub enum ExternalMountRestorePolicy {
71 /// Refuse activation when a supplied mapping or its captured objects cannot be reconstructed.
72 #[default]
73 Strict,
74 /// Accept supported mapping mismatches with warnings and errors for stale or unavailable objects.
75 Relaxed,
76}
77
78/// An unmapped external filesystem or a mismatch accepted during relaxed full restore.
79#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
80pub struct ExternalMountWarning {
81 /// Guest-visible mount path.
82 pub guest_path: String,
83 /// Actionable reason the resource could not be reconstructed.
84 pub reason: String,
85 /// Permanently invalid captured node IDs; empty when the whole export is unavailable.
86 pub stale_inodes: Vec<u64>,
87}
88
89//--------------------------------------------------------------------------------------------------
90// Tests
91//--------------------------------------------------------------------------------------------------
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn compaction_targets_have_closed_wire_shapes() {
99 for (target, json) in [
100 (DiskCompactionTarget::All, r#"{"kind":"all"}"#),
101 (DiskCompactionTarget::Root, r#"{"kind":"root"}"#),
102 (
103 DiskCompactionTarget::Disk {
104 guest_path: "/data".into(),
105 },
106 r#"{"kind":"disk","guest_path":"/data"}"#,
107 ),
108 ] {
109 assert_eq!(serde_json::to_string(&target).unwrap(), json);
110 assert_eq!(
111 serde_json::from_str::<DiskCompactionTarget>(json).unwrap(),
112 target
113 );
114 }
115 assert!(serde_json::from_str::<DiskCompactionTarget>(r#"{"kind":"disk"}"#).is_err());
116 assert!(
117 serde_json::from_str::<DiskCompactionTarget>(
118 r#"{"kind":"disk","guest_path":"/data","external":true}"#
119 )
120 .is_err()
121 );
122 }
123
124 #[test]
125 fn compaction_result_preserves_per_disk_metrics() {
126 let result = DiskCompactionResult {
127 dry_run: true,
128 disks: vec![DiskCompactionDiskResult {
129 guest_path: "/data".into(),
130 input_layers: 1,
131 output_layers: 1,
132 ..Default::default()
133 }],
134 ..Default::default()
135 };
136 let encoded = serde_json::to_string(&result).unwrap();
137 let decoded: DiskCompactionResult = serde_json::from_str(&encoded).unwrap();
138 assert!(decoded.dry_run);
139 assert_eq!(decoded.disks[0].guest_path, "/data");
140 assert_eq!(decoded.disks[0].selected_layers, 0);
141 assert!(serde_json::from_str::<DiskCompactionResult>(r#"{"dry_run":false,"input_layers":1,"selected_layers":0,"output_layers":1,"materialized_bytes":0,"total_us":0,"pause_us":0}"#).is_err());
142 }
143}
144
145/// Released cloud descriptor wire contract.
146pub mod cloud_manifest;
147/// Pure disk generation descriptors.
148pub mod disk;
149/// Existing legacy descriptor identity and cloud projection rules.
150pub mod legacy;
151/// Canonical portable snapshot descriptor.
152pub mod manifest;
153/// Pure owned-storage snapshot inventory.
154pub mod owned;
155mod restore_defaults;
156
157pub use manifest::*;
158pub use owned::{
159 OWNED_VOLUMES_EXTENSION, OwnedDirectoryPayload, OwnedMountSnapshot, OwnedVolumeCapture,
160 OwnedVolumeData, validate_owned_volumes,
161};
162pub use restore_defaults::{RESTORE_DEFAULTS_EXTENSION, RestoreDefaults};