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