Skip to main content

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};