Skip to main content

microvms_core/
ledger.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Outstanding run records, with the local file store composed for CLI callers.
3
4use std::path::Path;
5use std::sync::Arc;
6use std::sync::atomic::{AtomicU64, Ordering};
7
8pub use microvms_app::ledger::{Ledger, Record};
9pub use microvms_app::run_store::{RunStore, StoredLedger};
10pub use microvms_edges::run_store::FileRunStore;
11
12/// Starts a run using the historic state directory layout.
13///
14/// The first key is `<epoch>-<pid>` as before. Further runs in the same process add
15/// a sequence suffix so one application's concurrent runs cannot overwrite each other.
16pub fn new(region: &str, root: &Path) -> Ledger {
17    use crate::clock::Clock as _;
18    let epoch = crate::clock::SystemClock::new().unix_now().as_secs();
19    new_at(region, root, epoch)
20}
21
22fn new_at(region: &str, root: &Path, epoch: u64) -> Ledger {
23    static NEXT_RUN: AtomicU64 = AtomicU64::new(0);
24    let sequence = NEXT_RUN.fetch_add(1, Ordering::Relaxed);
25    let run_id = if sequence == 0 {
26        format!("{epoch}-{}", std::process::id())
27    } else {
28        format!("{epoch}-{}-{sequence:020}", std::process::id())
29    };
30    Ledger::from_store(region, run_id, Arc::new(FileRunStore::new(root)))
31}
32
33/// Reopens every run for a VM, newest first.
34pub fn open_all_for_vm(root: &Path, microvm_id: &str) -> Vec<Ledger> {
35    Ledger::open_all_for_vm_with_store(Arc::new(FileRunStore::new(root)), microvm_id)
36}
37
38/// Reads outstanding runs from the shared local state directory.
39pub fn read_all(root: &Path) -> Vec<microvms_app::ledger::Value> {
40    microvms_app::ledger::read_all(&FileRunStore::new(root))
41}
42
43/// Removes a run after verifying that its key cannot escape the ledger namespace.
44pub fn remove(root: &Path, run_id: &str) -> std::io::Result<()> {
45    microvms_app::ledger::remove(&FileRunStore::new(root), run_id)
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn runs_started_in_one_process_and_second_keep_separate_records() {
54        let root = tempfile::tempdir().unwrap();
55        let mut first = new_at("us-east-1", root.path(), 1700000000);
56        let mut second = new_at("us-east-1", root.path(), 1700000000);
57        assert_ne!(first.record.run_id, second.record.run_id);
58        first.record_microvm("vm-first");
59        first.mark_outstanding();
60        second.record_microvm("vm-second");
61        second.mark_outstanding();
62        let records = read_all(root.path());
63        assert_eq!(records.len(), 2);
64        assert_eq!(records[0]["microvmId"], "vm-first");
65        assert_eq!(records[1]["microvmId"], "vm-second");
66        first.mark_deleted(&[]);
67        first.clear();
68        assert_eq!(read_all(root.path())[0]["microvmId"], "vm-second");
69    }
70}