use std::path::PathBuf;
use std::sync::Arc;
use chrono::{DateTime, Utc};
use super::blob_source::{BlobSource, LocalBundleBlobSource, StoreBlobSource};
use super::object::ImageObject;
use crate::db::Database;
use crate::images::store::{ImageStore, SharedImageStore};
use crate::runtime::options::ImageRegistry;
use crate::runtime::types::ImageInfo;
use boxlite_shared::errors::BoxliteResult;
use oci_client::Reference;
use std::str::FromStr;
#[derive(Debug, Clone)]
pub(super) struct ImageManifest {
pub(super) manifest_digest: String,
pub(super) layers: Vec<LayerInfo>,
pub(super) config_digest: String,
pub(super) diff_ids: Vec<String>,
}
#[derive(Debug, Clone)]
pub(super) struct LayerInfo {
pub(super) digest: String,
pub(super) media_type: String,
pub(super) size: i64,
}
#[derive(Clone)]
pub struct ImageManager {
store: SharedImageStore,
}
impl std::fmt::Debug for ImageManager {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ImageManager").finish()
}
}
impl ImageManager {
pub fn new(
images_dir: PathBuf,
db: Database,
image_registries: Vec<ImageRegistry>,
) -> BoxliteResult<Self> {
let store = Arc::new(ImageStore::new(images_dir, db, image_registries)?);
Ok(Self { store })
}
pub async fn pull(&self, image_ref: &str) -> BoxliteResult<ImageObject> {
let manifest = self.store.pull(image_ref).await?;
let storage = self.store.storage().await;
let blob_source = BlobSource::Store(StoreBlobSource::new(storage));
Ok(ImageObject::new(
image_ref.to_string(),
manifest,
blob_source,
))
}
pub async fn list(&self) -> BoxliteResult<Vec<ImageInfo>> {
let raw_images = self.store.list().await?;
let mut images = Vec::with_capacity(raw_images.len());
for (reference, cached) in raw_images {
let cached_at = DateTime::parse_from_rfc3339(&cached.cached_at)
.map(|dt| dt.with_timezone(&Utc))
.unwrap_or_else(|e| {
tracing::warn!("Invalid cached_at timestamp: {}, using epoch", e);
DateTime::<Utc>::from(std::time::SystemTime::UNIX_EPOCH)
});
let (repository, tag) = match Reference::from_str(&reference) {
Ok(r) => (
r.repository().to_string(),
r.tag().unwrap_or("latest").to_string(),
),
Err(_) => {
(reference.clone(), "<none>".to_string())
}
};
images.push(ImageInfo {
reference,
repository,
tag,
id: cached.manifest_digest,
cached_at,
size: None, });
}
Ok(images)
}
pub async fn load_from_local(
&self,
path: std::path::PathBuf,
reference: String,
) -> BoxliteResult<ImageObject> {
let manifest = self.store.load_from_local(path.clone()).await?;
let cache_dir = self
.store
.local_bundle_cache_dir(&path, &manifest.manifest_digest)
.await;
let blob_source = BlobSource::LocalBundle(LocalBundleBlobSource::new(path, cache_dir));
Ok(ImageObject::new(reference, manifest, blob_source))
}
}