mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-05 06:01:32 +00:00
fix: implement image urls more faithfully
This commit is contained in:
@ -217,6 +217,13 @@ impl OciImageFetcher {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(ref digest) = image.digest {
|
||||||
|
if digest != manifest.digest() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
found = Some(manifest);
|
found = Some(manifest);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -240,7 +247,7 @@ impl OciImageFetcher {
|
|||||||
|
|
||||||
let mut client = OciRegistryClient::new(image.registry_url()?, self.platform.clone())?;
|
let mut client = OciRegistryClient::new(image.registry_url()?, self.platform.clone())?;
|
||||||
let (manifest, descriptor, digest) = client
|
let (manifest, descriptor, digest) = client
|
||||||
.get_manifest_with_digest(&image.name, &image.reference)
|
.get_manifest_with_digest(&image.name, image.reference.as_ref(), image.digest.as_ref())
|
||||||
.await?;
|
.await?;
|
||||||
let descriptor = descriptor.unwrap_or_else(|| {
|
let descriptor = descriptor.unwrap_or_else(|| {
|
||||||
DescriptorBuilder::default()
|
DescriptorBuilder::default()
|
||||||
|
@ -2,33 +2,39 @@ use anyhow::Result;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
const DOCKER_HUB_MIRROR: &str = "mirror.gcr.io";
|
|
||||||
const DEFAULT_IMAGE_TAG: &str = "latest";
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct ImageName {
|
pub struct ImageName {
|
||||||
pub hostname: String,
|
pub hostname: String,
|
||||||
pub port: Option<u16>,
|
pub port: Option<u16>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub reference: String,
|
pub reference: Option<String>,
|
||||||
|
pub digest: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ImageName {
|
impl fmt::Display for ImageName {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
if DOCKER_HUB_MIRROR == self.hostname && self.port.is_none() {
|
let mut suffix = String::new();
|
||||||
|
|
||||||
|
if let Some(ref reference) = self.reference {
|
||||||
|
suffix.push(':');
|
||||||
|
suffix.push_str(reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref digest) = self.digest {
|
||||||
|
suffix.push('@');
|
||||||
|
suffix.push_str(digest);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ImageName::DOCKER_HUB_MIRROR == self.hostname && self.port.is_none() {
|
||||||
if self.name.starts_with("library/") {
|
if self.name.starts_with("library/") {
|
||||||
write!(f, "{}:{}", &self.name[8..], self.reference)
|
write!(f, "{}{}", &self.name[8..], suffix)
|
||||||
} else {
|
} else {
|
||||||
write!(f, "{}:{}", self.name, self.reference)
|
write!(f, "{}{}", self.name, suffix)
|
||||||
}
|
}
|
||||||
} else if let Some(port) = self.port {
|
} else if let Some(port) = self.port {
|
||||||
write!(
|
write!(f, "{}:{}/{}{}", self.hostname, port, self.name, suffix)
|
||||||
f,
|
|
||||||
"{}:{}/{}:{}",
|
|
||||||
self.hostname, port, self.name, self.reference
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
write!(f, "{}/{}:{}", self.hostname, self.name, self.reference)
|
write!(f, "{}/{}{}", self.hostname, self.name, suffix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,13 +47,21 @@ impl Default for ImageName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ImageName {
|
impl ImageName {
|
||||||
|
pub const DOCKER_HUB_MIRROR: &'static str = "registry.docker.io";
|
||||||
|
pub const DEFAULT_IMAGE_TAG: &'static str = "latest";
|
||||||
|
|
||||||
pub fn parse(name: &str) -> Result<Self> {
|
pub fn parse(name: &str) -> Result<Self> {
|
||||||
let full_name = name.to_string();
|
let full_name = name.to_string();
|
||||||
let name = full_name.clone();
|
let name = full_name.clone();
|
||||||
let (mut hostname, mut name) = name
|
let (mut hostname, mut name) = name
|
||||||
.split_once('/')
|
.split_once('/')
|
||||||
.map(|x| (x.0.to_string(), x.1.to_string()))
|
.map(|x| (x.0.to_string(), x.1.to_string()))
|
||||||
.unwrap_or_else(|| (DOCKER_HUB_MIRROR.to_string(), format!("library/{}", name)));
|
.unwrap_or_else(|| {
|
||||||
|
(
|
||||||
|
ImageName::DOCKER_HUB_MIRROR.to_string(),
|
||||||
|
format!("library/{}", name),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
// heuristic to find any docker hub image formats
|
// heuristic to find any docker hub image formats
|
||||||
// that may be in the hostname format. for example:
|
// that may be in the hostname format. for example:
|
||||||
@ -55,7 +69,7 @@ impl ImageName {
|
|||||||
// and neither will abc/hello/xyz:latest
|
// and neither will abc/hello/xyz:latest
|
||||||
if !hostname.contains('.') && full_name.chars().filter(|x| *x == '/').count() == 1 {
|
if !hostname.contains('.') && full_name.chars().filter(|x| *x == '/').count() == 1 {
|
||||||
name = format!("{}/{}", hostname, name);
|
name = format!("{}/{}", hostname, name);
|
||||||
hostname = DOCKER_HUB_MIRROR.to_string();
|
hostname = ImageName::DOCKER_HUB_MIRROR.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
let (hostname, port) = if let Some((hostname, port)) = hostname
|
let (hostname, port) = if let Some((hostname, port)) = hostname
|
||||||
@ -66,15 +80,54 @@ impl ImageName {
|
|||||||
} else {
|
} else {
|
||||||
(hostname, None)
|
(hostname, None)
|
||||||
};
|
};
|
||||||
let (name, reference) = name
|
|
||||||
.split_once(':')
|
let name_has_digest = if name.contains('@') {
|
||||||
.map(|x| (x.0.to_string(), x.1.to_string()))
|
let digest_start = name.chars().position(|c| c == '@');
|
||||||
.unwrap_or((name.to_string(), DEFAULT_IMAGE_TAG.to_string()));
|
let ref_start = name.chars().position(|c| c == ':');
|
||||||
|
if let (Some(digest_start), Some(ref_start)) = (digest_start, ref_start) {
|
||||||
|
digest_start < ref_start
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
let (name, digest) = if name_has_digest {
|
||||||
|
name.split_once('@')
|
||||||
|
.map(|(name, digest)| (name.to_string(), Some(digest.to_string())))
|
||||||
|
.unwrap_or_else(|| (name, None))
|
||||||
|
} else {
|
||||||
|
(name, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
let (name, reference) = if name.contains(':') {
|
||||||
|
name.split_once(':')
|
||||||
|
.map(|(name, reference)| (name.to_string(), Some(reference.to_string())))
|
||||||
|
.unwrap_or((name, None))
|
||||||
|
} else {
|
||||||
|
(name, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
let (reference, digest) = if let Some(reference) = reference {
|
||||||
|
if let Some(digest) = digest {
|
||||||
|
(Some(reference), Some(digest))
|
||||||
|
} else {
|
||||||
|
reference
|
||||||
|
.split_once('@')
|
||||||
|
.map(|(reff, digest)| (Some(reff.to_string()), Some(digest.to_string())))
|
||||||
|
.unwrap_or_else(|| (Some(reference), None))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(None, digest)
|
||||||
|
};
|
||||||
|
|
||||||
Ok(ImageName {
|
Ok(ImageName {
|
||||||
hostname,
|
hostname,
|
||||||
port,
|
port,
|
||||||
name,
|
name,
|
||||||
reference,
|
reference,
|
||||||
|
digest,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +159,9 @@ impl OciPackerCache {
|
|||||||
let image_name = packed.name.to_string();
|
let image_name = packed.name.to_string();
|
||||||
annotations.insert(ANNOTATION_IMAGE_NAME.to_string(), image_name);
|
annotations.insert(ANNOTATION_IMAGE_NAME.to_string(), image_name);
|
||||||
let image_ref = packed.name.reference.clone();
|
let image_ref = packed.name.reference.clone();
|
||||||
annotations.insert(ANNOTATION_REF_NAME.to_string(), image_ref);
|
if let Some(image_ref) = image_ref {
|
||||||
|
annotations.insert(ANNOTATION_REF_NAME.to_string(), image_ref);
|
||||||
|
}
|
||||||
descriptor.set_annotations(Some(annotations));
|
descriptor.set_annotations(Some(annotations));
|
||||||
manifests.push(descriptor.clone());
|
manifests.push(descriptor.clone());
|
||||||
index.set_manifests(manifests);
|
index.set_manifests(manifests);
|
||||||
|
@ -61,6 +61,10 @@ impl OciPackerService {
|
|||||||
digest: &str,
|
digest: &str,
|
||||||
format: OciPackedFormat,
|
format: OciPackedFormat,
|
||||||
) -> Result<Option<OciPackedImage>> {
|
) -> Result<Option<OciPackedImage>> {
|
||||||
|
if digest.contains('/') || digest.contains('\\') || digest.contains("..") {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
self.cache
|
self.cache
|
||||||
.recall(ImageName::parse("cached:latest")?, digest, format)
|
.recall(ImageName::parse("cached:latest")?, digest, format)
|
||||||
.await
|
.await
|
||||||
|
@ -7,7 +7,7 @@ use reqwest::{Client, RequestBuilder, Response, StatusCode};
|
|||||||
use tokio::{fs::File, io::AsyncWriteExt};
|
use tokio::{fs::File, io::AsyncWriteExt};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::{progress::OciBoundProgress, schema::OciSchema};
|
use crate::{name::ImageName, progress::OciBoundProgress, schema::OciSchema};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct OciPlatform {
|
pub struct OciPlatform {
|
||||||
@ -176,7 +176,7 @@ impl OciRegistryClient {
|
|||||||
let url = self.url.join(&format!(
|
let url = self.url.join(&format!(
|
||||||
"/v2/{}/manifests/{}",
|
"/v2/{}/manifests/{}",
|
||||||
name.as_ref(),
|
name.as_ref(),
|
||||||
reference.as_ref()
|
reference.as_ref(),
|
||||||
))?;
|
))?;
|
||||||
let accept = format!(
|
let accept = format!(
|
||||||
"{}, {}, {}, {}",
|
"{}, {}, {}, {}",
|
||||||
@ -202,13 +202,20 @@ impl OciRegistryClient {
|
|||||||
pub async fn get_manifest_with_digest<N: AsRef<str>, R: AsRef<str>>(
|
pub async fn get_manifest_with_digest<N: AsRef<str>, R: AsRef<str>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: N,
|
name: N,
|
||||||
reference: R,
|
reference: Option<R>,
|
||||||
|
digest: Option<N>,
|
||||||
) -> Result<(OciSchema<ImageManifest>, Option<Descriptor>, String)> {
|
) -> Result<(OciSchema<ImageManifest>, Option<Descriptor>, String)> {
|
||||||
let url = self.url.join(&format!(
|
let what = digest
|
||||||
"/v2/{}/manifests/{}",
|
.as_ref()
|
||||||
name.as_ref(),
|
.map(|x| x.as_ref().to_string())
|
||||||
reference.as_ref()
|
.unwrap_or_else(|| {
|
||||||
))?;
|
reference
|
||||||
|
.map(|x| x.as_ref().to_string())
|
||||||
|
.unwrap_or_else(|| ImageName::DEFAULT_IMAGE_TAG.to_string())
|
||||||
|
});
|
||||||
|
let url = self
|
||||||
|
.url
|
||||||
|
.join(&format!("/v2/{}/manifests/{}", name.as_ref(), what,))?;
|
||||||
let accept = format!(
|
let accept = format!(
|
||||||
"{}, {}, {}, {}",
|
"{}, {}, {}, {}",
|
||||||
MediaType::ImageManifest.to_docker_v2s2()?,
|
MediaType::ImageManifest.to_docker_v2s2()?,
|
||||||
@ -239,9 +246,10 @@ impl OciRegistryClient {
|
|||||||
let digest = response
|
let digest = response
|
||||||
.headers()
|
.headers()
|
||||||
.get("Docker-Content-Digest")
|
.get("Docker-Content-Digest")
|
||||||
.ok_or_else(|| anyhow!("fetching manifest did not yield a content digest"))?
|
.and_then(|x| x.to_str().ok())
|
||||||
.to_str()?
|
.map(|x| x.to_string())
|
||||||
.to_string();
|
.or_else(|| digest.map(|x: N| x.as_ref().to_string()))
|
||||||
|
.ok_or_else(|| anyhow!("fetching manifest did not yield a content digest"))?;
|
||||||
let bytes = response.bytes().await?;
|
let bytes = response.bytes().await?;
|
||||||
let manifest = serde_json::from_slice(&bytes)?;
|
let manifest = serde_json::from_slice(&bytes)?;
|
||||||
Ok((OciSchema::new(bytes.to_vec(), manifest), None, digest))
|
Ok((OciSchema::new(bytes.to_vec(), manifest), None, digest))
|
||||||
|
Reference in New Issue
Block a user