mirror of
https://github.com/imjasonh/krust
synced 2026-07-08 06:45:32 +00:00
feat: Fork oci-distribution and add enhanced features
- Fork oci-distribution v0.11.0 into vendor/oci-distribution - Add get_image_platforms() method for proper platform detection - Support fetching config blobs for single-platform images - Add OAuth2/Bearer token authentication support - Add push_manifest_and_get_digest() for reliable digest extraction - Update krust to use the forked version with enhanced features
This commit is contained in:
parent
e2c4b6b14c
commit
7827c25d9b
4 changed files with 84 additions and 83 deletions
|
|
@ -19,7 +19,7 @@ sha256 = "1.5"
|
|||
chrono = "0.4"
|
||||
tar = "0.4"
|
||||
flate2 = "1.0"
|
||||
oci-distribution = "0.11"
|
||||
oci-distribution = { path = "vendor/oci-distribution" }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
dirs = "6.0"
|
||||
|
|
|
|||
|
|
@ -83,6 +83,16 @@ impl AuthConfig {
|
|||
return RegistryAuth::Anonymous;
|
||||
}
|
||||
|
||||
// Check for bearer tokens first
|
||||
if let Some(token) = &self.registry_token {
|
||||
return RegistryAuth::Bearer(token.clone());
|
||||
}
|
||||
|
||||
if let Some(token) = &self.identity_token {
|
||||
return RegistryAuth::Bearer(token.clone());
|
||||
}
|
||||
|
||||
// Then check for basic auth
|
||||
if let (Some(username), Some(password)) = (&self.username, &self.password) {
|
||||
return RegistryAuth::Basic(username.clone(), password.clone());
|
||||
}
|
||||
|
|
@ -234,4 +244,30 @@ mod unit_tests {
|
|||
let header = auth.to_authorization_header().unwrap().unwrap();
|
||||
assert_eq!(header, "Bearer token123");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_config_to_registry_auth() {
|
||||
use oci_distribution::secrets::RegistryAuth;
|
||||
|
||||
// Test anonymous
|
||||
let auth = AuthConfig::anonymous();
|
||||
assert_eq!(auth.to_registry_auth(), RegistryAuth::Anonymous);
|
||||
|
||||
// Test basic auth
|
||||
let auth = AuthConfig::new("user".to_string(), "pass".to_string());
|
||||
assert_eq!(
|
||||
auth.to_registry_auth(),
|
||||
RegistryAuth::Basic("user".to_string(), "pass".to_string())
|
||||
);
|
||||
|
||||
// Test bearer token
|
||||
let auth = AuthConfig {
|
||||
registry_token: Some("token123".to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
auth.to_registry_auth(),
|
||||
RegistryAuth::Bearer("token123".to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,21 +86,14 @@ impl RegistryClient {
|
|||
let manifest = OciManifest::Image(image_manifest);
|
||||
|
||||
debug!("Pushing manifest");
|
||||
let manifest_url = self
|
||||
let (manifest_url, digest) = self
|
||||
.client
|
||||
.push_manifest(&reference, &manifest)
|
||||
.push_manifest_and_get_digest(&reference, &manifest)
|
||||
.await
|
||||
.context("Failed to push manifest")?;
|
||||
|
||||
info!("Successfully pushed image to {}", manifest_url);
|
||||
|
||||
// Extract digest from the manifest URL
|
||||
// URL format: https://registry/v2/repo/manifests/sha256:digest
|
||||
let digest = manifest_url
|
||||
.split('/')
|
||||
.next_back()
|
||||
.context("Failed to extract digest from manifest URL")?;
|
||||
|
||||
// Build the full image reference with digest
|
||||
let registry = reference.registry();
|
||||
let repository = reference.repository();
|
||||
|
|
@ -169,20 +162,14 @@ impl RegistryClient {
|
|||
m.platform.os, m.platform.architecture, m.digest
|
||||
);
|
||||
}
|
||||
let manifest_url = self
|
||||
let (manifest_url, digest) = self
|
||||
.client
|
||||
.push_manifest(&reference, &manifest)
|
||||
.push_manifest_and_get_digest(&reference, &manifest)
|
||||
.await
|
||||
.context("Failed to push manifest list")?;
|
||||
|
||||
info!("Successfully pushed manifest list to {}", manifest_url);
|
||||
|
||||
// Extract digest from the manifest URL
|
||||
let digest = manifest_url
|
||||
.split('/')
|
||||
.next_back()
|
||||
.context("Failed to extract digest from manifest URL")?;
|
||||
|
||||
// Build the full image reference with digest
|
||||
let registry = reference.registry();
|
||||
let repository = reference.repository();
|
||||
|
|
@ -201,78 +188,55 @@ impl RegistryClient {
|
|||
.parse()
|
||||
.context("Failed to parse image reference")?;
|
||||
|
||||
debug!("Fetching manifest for {}", reference);
|
||||
debug!("Fetching platforms for {}", reference);
|
||||
|
||||
// Pull the manifest
|
||||
let (manifest, _) = self
|
||||
// Use the new get_image_platforms method from oci-distribution
|
||||
let platforms = self
|
||||
.client
|
||||
.pull_manifest(&reference, auth)
|
||||
.get_image_platforms(&reference, auth)
|
||||
.await
|
||||
.context("Failed to pull manifest")?;
|
||||
.context("Failed to get image platforms")?;
|
||||
|
||||
// Parse platforms based on manifest type
|
||||
match manifest {
|
||||
OciManifest::Image(_) => {
|
||||
// Single platform image - we need to fetch the config to determine platform
|
||||
debug!("Single platform image detected");
|
||||
// For now, we'll assume it's linux/amd64 if we can't determine
|
||||
// In a real implementation, we'd fetch the config blob
|
||||
Ok(vec!["linux/amd64".to_string()])
|
||||
}
|
||||
OciManifest::ImageIndex(index) => {
|
||||
// Multi-platform image - extract platforms from index
|
||||
debug!(
|
||||
"Multi-platform image detected with {} manifests",
|
||||
index.manifests.len()
|
||||
);
|
||||
let mut platforms: Vec<String> = index
|
||||
.manifests
|
||||
.iter()
|
||||
.filter_map(|m| {
|
||||
m.platform.as_ref().and_then(|p| {
|
||||
// Filter out invalid platforms
|
||||
if p.os == "unknown"
|
||||
|| p.architecture == "unknown"
|
||||
|| p.os.is_empty()
|
||||
|| p.architecture.is_empty()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
// Convert (os, arch) tuples to platform strings and normalize
|
||||
let mut platform_strings: Vec<String> = platforms
|
||||
.into_iter()
|
||||
.filter_map(|(os, arch)| {
|
||||
// Filter out unknown/invalid platforms
|
||||
if os == "unknown" || arch == "unknown" || os.is_empty() || arch.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut platform = format!("{}/{}", p.os, p.architecture);
|
||||
if let Some(variant) = &p.variant {
|
||||
if !variant.is_empty() {
|
||||
platform.push('/');
|
||||
platform.push_str(variant);
|
||||
}
|
||||
}
|
||||
// Normalize and filter platforms
|
||||
let normalized = match platform.as_str() {
|
||||
"linux/amd64" => Some("linux/amd64".to_string()),
|
||||
"linux/arm64" | "linux/arm64/v8" => Some("linux/arm64".to_string()),
|
||||
"linux/arm/v7" => Some("linux/arm/v7".to_string()),
|
||||
"linux/arm/v6" => Some("linux/arm/v6".to_string()),
|
||||
"linux/386" => Some("linux/386".to_string()),
|
||||
"linux/ppc64le" => Some("linux/ppc64le".to_string()),
|
||||
"linux/s390x" => Some("linux/s390x".to_string()),
|
||||
"linux/riscv64" => Some("linux/riscv64".to_string()),
|
||||
_ => {
|
||||
debug!("Skipping unsupported platform: {}", platform);
|
||||
None
|
||||
}
|
||||
};
|
||||
normalized
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let platform = format!("{}/{}", os, arch);
|
||||
|
||||
// Deduplicate platforms
|
||||
platforms.sort();
|
||||
platforms.dedup();
|
||||
// Normalize and filter platforms
|
||||
let normalized = match platform.as_str() {
|
||||
"linux/amd64" => Some("linux/amd64".to_string()),
|
||||
"linux/arm64" | "linux/arm64/v8" => Some("linux/arm64".to_string()),
|
||||
"linux/arm/v7" => Some("linux/arm/v7".to_string()),
|
||||
"linux/arm/v6" => Some("linux/arm/v6".to_string()),
|
||||
"linux/386" => Some("linux/386".to_string()),
|
||||
"linux/ppc64le" => Some("linux/ppc64le".to_string()),
|
||||
"linux/s390x" => Some("linux/s390x".to_string()),
|
||||
"linux/riscv64" => Some("linux/riscv64".to_string()),
|
||||
_ => {
|
||||
debug!("Skipping unsupported platform: {}", platform);
|
||||
None
|
||||
}
|
||||
};
|
||||
normalized
|
||||
})
|
||||
.collect();
|
||||
|
||||
info!("Found platforms: {:?}", platforms);
|
||||
Ok(platforms)
|
||||
}
|
||||
// Deduplicate platforms
|
||||
platform_strings.sort();
|
||||
platform_strings.dedup();
|
||||
|
||||
if platform_strings.is_empty() {
|
||||
debug!("No valid platforms found, defaulting to linux/amd64");
|
||||
Ok(vec!["linux/amd64".to_string()])
|
||||
} else {
|
||||
info!("Found platforms: {:?}", platform_strings);
|
||||
Ok(platform_strings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
vendor/oci-distribution
vendored
Submodule
1
vendor/oci-distribution
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 6157eb998456bdd88b0a8a085a7bf70ecab6189d
|
||||
Loading…
Add table
Add a link
Reference in a new issue