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, or waive required backing.
67/// When the restore operation explicitly allows an unmapped filesystem, it remains
68/// unavailable under either policy; backend operations return EIO.
69#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
70#[serde(rename_all = "lowercase")]
71pub enum ExternalMountRestorePolicy {
72 /// Refuse activation when a supplied mapping or its captured objects cannot be reconstructed.
73 #[default]
74 Strict,
75 /// Accept supported mapping mismatches with warnings and errors for stale or unavailable objects.
76 Relaxed,
77}
78
79/// An unmapped external filesystem or a mismatch accepted during relaxed full restore.
80#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
81pub struct ExternalMountWarning {
82 /// Guest-visible mount path.
83 pub guest_path: String,
84 /// Actionable reason the resource could not be reconstructed.
85 pub reason: String,
86 /// Permanently invalid captured node IDs; empty when the whole export is unavailable.
87 pub stale_inodes: Vec<u64>,
88}
89
90//--------------------------------------------------------------------------------------------------
91// Tests
92//--------------------------------------------------------------------------------------------------
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn compaction_targets_have_closed_wire_shapes() {
100 for (target, json) in [
101 (DiskCompactionTarget::All, r#"{"kind":"all"}"#),
102 (DiskCompactionTarget::Root, r#"{"kind":"root"}"#),
103 (
104 DiskCompactionTarget::Disk {
105 guest_path: "/data".into(),
106 },
107 r#"{"kind":"disk","guest_path":"/data"}"#,
108 ),
109 ] {
110 assert_eq!(serde_json::to_string(&target).unwrap(), json);
111 assert_eq!(
112 serde_json::from_str::<DiskCompactionTarget>(json).unwrap(),
113 target
114 );
115 }
116 assert!(serde_json::from_str::<DiskCompactionTarget>(r#"{"kind":"disk"}"#).is_err());
117 assert!(
118 serde_json::from_str::<DiskCompactionTarget>(
119 r#"{"kind":"disk","guest_path":"/data","external":true}"#
120 )
121 .is_err()
122 );
123 }
124
125 #[test]
126 fn compaction_result_preserves_per_disk_metrics() {
127 let result = DiskCompactionResult {
128 dry_run: true,
129 disks: vec![DiskCompactionDiskResult {
130 guest_path: "/data".into(),
131 input_layers: 1,
132 output_layers: 1,
133 ..Default::default()
134 }],
135 ..Default::default()
136 };
137 let encoded = serde_json::to_string(&result).unwrap();
138 let decoded: DiskCompactionResult = serde_json::from_str(&encoded).unwrap();
139 assert!(decoded.dry_run);
140 assert_eq!(decoded.disks[0].guest_path, "/data");
141 assert_eq!(decoded.disks[0].selected_layers, 0);
142 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());
143 }
144}
145
146/// Released cloud descriptor wire contract.
147pub mod cloud_manifest;
148/// Pure disk generation descriptors.
149pub mod disk;
150/// Existing legacy descriptor identity and cloud projection rules.
151pub mod legacy;
152/// Canonical portable snapshot descriptor.
153pub mod manifest;
154/// Pure owned-storage snapshot inventory.
155pub mod owned;
156mod restore_defaults;
157
158pub use manifest::*;
159pub use owned::{
160 OWNED_VOLUMES_EXTENSION, OwnedDirectoryPayload, OwnedMountSnapshot, OwnedVolumeCapture,
161 OwnedVolumeData, validate_owned_volumes,
162};
163pub use restore_defaults::{RESTORE_DEFAULTS_EXTENSION, RestoreDefaults};