mod archive;
mod create;
mod store;
mod verify;
use std::path::{Path, PathBuf};
use crate::MicrosandboxResult;
#[derive(Debug, Clone)]
pub struct Snapshot {
path: PathBuf,
digest: String,
manifest: Manifest,
}
#[derive(Debug, Clone)]
pub enum SnapshotDestination {
Name(String),
Path(PathBuf),
}
#[derive(Debug, Clone)]
pub struct SnapshotConfig {
pub source_sandbox: String,
pub destination: SnapshotDestination,
pub labels: Vec<(String, String)>,
pub force: bool,
pub record_integrity: bool,
}
pub struct SnapshotBuilder {
source_sandbox: String,
destination: Option<SnapshotDestination>,
labels: Vec<(String, String)>,
force: bool,
record_integrity: bool,
}
impl Snapshot {
pub fn builder(source_sandbox: impl Into<String>) -> SnapshotBuilder {
SnapshotBuilder {
source_sandbox: source_sandbox.into(),
destination: None,
labels: Vec::new(),
force: false,
record_integrity: false,
}
}
pub async fn create(config: SnapshotConfig) -> MicrosandboxResult<Self> {
create::create_snapshot(config).await
}
pub async fn open(path_or_name: impl AsRef<str>) -> MicrosandboxResult<Self> {
store::open_snapshot(path_or_name.as_ref()).await
}
pub async fn verify(&self) -> MicrosandboxResult<SnapshotVerifyReport> {
verify::verify_snapshot(self).await
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn digest(&self) -> &str {
&self.digest
}
pub fn manifest(&self) -> &Manifest {
&self.manifest
}
pub fn size_bytes(&self) -> u64 {
self.manifest.upper.size_bytes
}
pub async fn get(name_or_digest: &str) -> MicrosandboxResult<SnapshotHandle> {
store::get_handle(name_or_digest).await
}
pub async fn list() -> MicrosandboxResult<Vec<SnapshotHandle>> {
store::list_indexed().await
}
pub async fn list_dir(dir: impl AsRef<Path>) -> MicrosandboxResult<Vec<Snapshot>> {
store::list_dir(dir.as_ref()).await
}
pub async fn remove(path_or_name: &str, force: bool) -> MicrosandboxResult<()> {
store::remove_snapshot(path_or_name, force).await
}
pub async fn reindex(dir: impl AsRef<Path>) -> MicrosandboxResult<usize> {
store::reindex_dir(dir.as_ref()).await
}
pub async fn export(
name_or_path: &str,
out: &Path,
opts: archive::ExportOpts,
) -> MicrosandboxResult<()> {
archive::export_snapshot(name_or_path, out, opts).await
}
pub async fn import(
archive_path: &Path,
dest: Option<&Path>,
) -> MicrosandboxResult<SnapshotHandle> {
archive::import_snapshot(archive_path, dest).await
}
}
#[derive(Debug, Clone)]
pub struct SnapshotHandle {
pub(crate) digest: String,
pub(crate) name: Option<String>,
pub(crate) parent_digest: Option<String>,
pub(crate) image_ref: String,
pub(crate) format: SnapshotFormat,
pub(crate) size_bytes: Option<u64>,
pub(crate) created_at: chrono::NaiveDateTime,
pub(crate) artifact_path: PathBuf,
}
impl SnapshotHandle {
pub fn digest(&self) -> &str {
&self.digest
}
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn parent_digest(&self) -> Option<&str> {
self.parent_digest.as_deref()
}
pub fn image_ref(&self) -> &str {
&self.image_ref
}
pub fn format(&self) -> SnapshotFormat {
self.format
}
pub fn size_bytes(&self) -> Option<u64> {
self.size_bytes
}
pub fn created_at(&self) -> chrono::NaiveDateTime {
self.created_at
}
pub fn path(&self) -> &Path {
&self.artifact_path
}
pub async fn open(&self) -> MicrosandboxResult<Snapshot> {
Snapshot::open(self.artifact_path.to_string_lossy().as_ref()).await
}
pub async fn remove(&self, force: bool) -> MicrosandboxResult<()> {
Snapshot::remove(&self.digest, force).await
}
}
impl SnapshotBuilder {
pub fn destination(mut self, dest: SnapshotDestination) -> Self {
self.destination = Some(dest);
self
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.destination = Some(SnapshotDestination::Name(name.into()));
self
}
pub fn path(mut self, path: impl Into<PathBuf>) -> Self {
self.destination = Some(SnapshotDestination::Path(path.into()));
self
}
pub fn label(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.labels.push((key.into(), value.into()));
self
}
pub fn force(mut self) -> Self {
self.force = true;
self
}
pub fn record_integrity(mut self) -> Self {
self.record_integrity = true;
self
}
pub fn build(self) -> MicrosandboxResult<SnapshotConfig> {
let destination = self.destination.ok_or_else(|| {
crate::MicrosandboxError::InvalidConfig(
"snapshot builder requires a destination (.name() or .path())".into(),
)
})?;
Ok(SnapshotConfig {
source_sandbox: self.source_sandbox,
destination,
labels: self.labels,
force: self.force,
record_integrity: self.record_integrity,
})
}
pub async fn create(self) -> MicrosandboxResult<Snapshot> {
Snapshot::create(self.build()?).await
}
}
pub use archive::ExportOpts;
pub use microsandbox_image::snapshot::{
ImageRef, MANIFEST_FILENAME, Manifest, SnapshotFormat, UpperIntegrity, UpperLayer,
};
pub use verify::{SnapshotVerifyReport, UpperVerifyStatus};
impl Snapshot {
pub(crate) fn from_parts(path: PathBuf, digest: String, manifest: Manifest) -> Self {
Self {
path,
digest,
manifest,
}
}
}