use async_trait::async_trait;
use pf_core::cas::BlobStore;
use pf_core::digest::Digest256;
use pf_core::manifest::Manifest;
use crate::image_ref::ImageRef;
#[derive(Debug, thiserror::Error)]
pub enum RegistryError {
#[error("unsupported scheme: {0}")]
UnsupportedScheme(String),
#[error("image ref: {0}")]
BadRef(#[from] crate::image_ref::ImageRefError),
#[error("core: {0}")]
Core(#[from] pf_core::Error),
#[error("backend: {0}")]
Backend(String),
#[error("signature verify failed: {0}")]
SignatureVerify(String),
}
#[derive(Debug)]
pub struct LayerSet {
pub manifest: Manifest,
pub blobs: Vec<(Digest256, Vec<u8>)>,
}
#[async_trait]
pub trait Registry: Send + Sync {
async fn push(
&self,
target: &ImageRef,
manifest: &Manifest,
blobs: &dyn BlobStore,
) -> Result<(), RegistryError>;
async fn pull(&self, source: &ImageRef) -> Result<LayerSet, RegistryError>;
async fn exists(&self, source: &ImageRef) -> Result<bool, RegistryError>;
}
#[must_use]
pub fn manifest_blob_digests(m: &Manifest) -> Vec<Digest256> {
vec![
m.model.base.clone(),
m.model.diff.clone(),
m.cache.manifest.clone(),
m.world.fs.clone(),
m.world.env.clone(),
m.world.procs.clone(),
m.effects.ledger.clone(),
m.trace.messages.clone(),
]
}
pub fn transitive_blob_digests(
m: &Manifest,
blobs: &dyn BlobStore,
) -> Result<Vec<Digest256>, RegistryError> {
use std::collections::BTreeSet;
let mut seen: BTreeSet<String> = BTreeSet::new();
let mut order: Vec<Digest256> = Vec::new();
for d in manifest_blob_digests(m) {
if seen.insert(d.as_str().to_owned()) {
order.push(d);
}
}
if let Ok(fs_bytes) = blobs.get(&m.world.fs) {
if let Ok(tree) = serde_json::from_slice::<serde_json::Value>(&fs_bytes) {
if tree.get("kind").and_then(|v| v.as_str()) == Some("fs.tree.v1") {
if let Some(entries) = tree.get("entries").and_then(|v| v.as_array()) {
for e in entries {
if let Some(blob) = e.get("blob").and_then(|v| v.as_str()) {
if let Ok(d) = Digest256::parse(blob) {
if seen.insert(d.as_str().to_owned()) {
order.push(d);
}
}
}
}
}
}
}
}
if let Ok(pm_bytes) = blobs.get(&m.cache.manifest) {
if let Ok(pm) = serde_json::from_slice::<serde_json::Value>(&pm_bytes) {
if let Some(pages) = pm.get("pages").and_then(|v| v.as_array()) {
for p in pages {
for key in ["k", "v"] {
if let Some(s) = p.get(key).and_then(|v| v.as_str()) {
if let Ok(d) = Digest256::parse(s) {
if seen.insert(d.as_str().to_owned()) {
order.push(d);
}
}
}
}
}
}
}
}
Ok(order)
}