use oci_distribution::client::Client;
use oci_distribution::manifest::{OciImageIndex, OciImageManifest, OciManifest};
use oci_distribution::secrets::RegistryAuth;
use oci_distribution::Reference;
use serde_json::Value;
use tracing::debug;
pub async fn pull_and_parse_config(
image_ref: &str,
) -> Result<(OciImageManifest, String), Box<dyn std::error::Error + Send + Sync>> {
let client = Client::default();
let reference: Reference = image_ref.parse()?;
client
.auth(
&reference,
&RegistryAuth::Anonymous,
oci_distribution::RegistryOperation::Pull,
)
.await?;
let (manifest_enum, top_digest) = client
.pull_manifest(&reference, &RegistryAuth::Anonymous)
.await?;
let pinned_reference = match manifest_enum {
OciManifest::Image(_image_manifest) => {
debug!("Got a single-arch image manifest, digest={}", top_digest);
Reference::with_digest(
reference.registry().to_string(),
reference.repository().to_string(),
top_digest,
)
}
OciManifest::ImageIndex(OciImageIndex { manifests, .. }) => {
if let Some(first_entry) = manifests.first() {
debug!(
"Got a multi-arch index with {} entries. Picking digest={}",
manifests.len(),
first_entry.digest
);
Reference::with_digest(
reference.registry().to_string(),
reference.repository().to_string(),
first_entry.digest.clone(),
)
} else {
return Err("No sub-manifests found in multi-arch index".into());
}
}
};
let (manifest_enum2, pinned_digest) = client
.pull_manifest(&pinned_reference, &RegistryAuth::Anonymous)
.await?;
let image_manifest = match manifest_enum2 {
OciManifest::Image(img) => {
debug!("Sub-manifest pin succeeded, digest={}", pinned_digest);
img
}
OciManifest::ImageIndex(_) => {
return Err("Still got an index?! No valid single-arch manifest found.".into());
}
};
let mut config_bytes = Vec::new();
debug!("Pulling config layer from pinned reference...");
client
.pull_blob(&pinned_reference, &image_manifest.config, &mut config_bytes)
.await?;
let config_str = String::from_utf8(config_bytes)?;
debug!("Config JSON:\n{}", config_str);
let config_json: Value = serde_json::from_str(&config_str)?;
let user = config_json
.get("config")
.and_then(|c| c.get("User"))
.and_then(|u| u.as_str())
.unwrap_or("root");
debug!("Found user={user}");
Ok((image_manifest, user.to_owned()))
}