use super::*;
use crate::lifecycle::actor::ComputerSnapshot;
pub(super) fn retained_artifacts(
config: &RuntimeConfig,
id: &ComputerId,
snapshot: &ComputerSnapshot,
) -> RetainedArtifacts {
let preserved = super::preserved_cow_file(config, id);
let live = snapshot
.cow_file
.clone()
.filter(|cow_file| *cow_file != preserved);
RetainedArtifacts {
pause_snapshot_id: snapshot.pause_snapshot_id.clone(),
live_overlay: live,
preserved_overlay: preserved,
parked_rootfs: snapshot.vm_dir.join(super::pause::PAUSED_ROOTFS_FILE),
}
}
pub(super) struct RetainedArtifacts {
pause_snapshot_id: Option<String>,
live_overlay: Option<PathBuf>,
preserved_overlay: PathBuf,
parked_rootfs: PathBuf,
}
impl RetainedArtifacts {
pub(super) fn has_checkpoint(&self) -> bool {
self.pause_snapshot_id.is_some()
}
pub(super) fn storage_bytes(&self, checkpoint_paths: impl FnOnce(&str) -> Vec<PathBuf>) -> u64 {
use std::os::unix::fs::MetadataExt as _;
let allocated = |path: &Path| {
std::fs::metadata(path).map_or(0, |metadata| metadata.blocks().saturating_mul(512))
};
let checkpoint = self
.pause_snapshot_id
.as_deref()
.map(checkpoint_paths)
.unwrap_or_default();
checkpoint
.iter()
.chain(&self.live_overlay)
.chain([&self.preserved_overlay, &self.parked_rootfs])
.fold(0u64, |total, path| total.saturating_add(allocated(path)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lifecycle::actor::{ComputerSnapshot, Deadlines};
fn snapshot(vm_dir: &Path, cow_file: Option<PathBuf>) -> ComputerSnapshot {
ComputerSnapshot {
state: ComputerState::Ready,
agent: None,
handle: None,
error: None,
labels: HashMap::new(),
vcpus: 1,
memory_mib: 128,
lease: None,
vm_dir: vm_dir.to_path_buf(),
created_at: chrono::Utc::now(),
ready_at: None,
last_exited_at: None,
last_exit_status: None,
paused_at: None,
pause_snapshot_id: None,
cow_file,
deadlines: Deadlines::default(),
}
}
fn config_over(data_dir: &Path) -> RuntimeConfig {
let mut config = RuntimeConfig::default();
config.firecracker.data_dir = data_dir.to_string_lossy().into_owned();
config
}
#[test]
fn the_overlay_is_found_on_either_side_of_the_pause_rename() {
let dir = tempfile::tempdir().unwrap();
let cow_dir = dir.path().join("cow");
std::fs::create_dir_all(&cow_dir).unwrap();
let config = config_over(dir.path());
let id = "napper".to_owned();
let slot_keyed = cow_dir.join("arcbox-cow-pool-1234.img");
std::fs::write(super::super::preserved_cow_file(&config, &id), b"overlay").unwrap();
let stale = snapshot(dir.path(), Some(slot_keyed.clone()));
let sized = retained_artifacts(&config, &id, &stale).storage_bytes(|_| Vec::new());
assert!(sized > 0, "the renamed overlay is still counted");
std::fs::rename(super::super::preserved_cow_file(&config, &id), &slot_keyed).unwrap();
let pre = retained_artifacts(&config, &id, &stale).storage_bytes(|_| Vec::new());
assert_eq!(
pre, sized,
"either side of the rename sizes the same overlay"
);
}
#[test]
fn an_id_keyed_overlay_is_counted_once() {
let dir = tempfile::tempdir().unwrap();
std::fs::create_dir_all(dir.path().join("cow")).unwrap();
let config = config_over(dir.path());
let id = "solo".to_owned();
let overlay = super::super::preserved_cow_file(&config, &id);
std::fs::write(&overlay, b"overlay").unwrap();
use std::os::unix::fs::MetadataExt as _;
let allocated = std::fs::metadata(&overlay).unwrap().blocks() * 512;
let live = snapshot(dir.path(), Some(overlay));
let sized = retained_artifacts(&config, &id, &live).storage_bytes(|_| Vec::new());
assert_eq!(sized, allocated, "the shared path is not double-counted");
}
}