mirror of
https://github.com/imjasonh/krust
synced 2026-07-08 06:45:32 +00:00
feat: push platform images by digest only, no tags
Platform-specific images are now pushed without any tags, only by digest. This ensures no platform tags like :platform-linux-amd64 are created in the registry, addressing the core requirement of issue #27. - Added push_image_by_digest() method to registry client - Platform images use digest-only references for manifest list - Only manifest lists can receive explicit tags via --tag flag - Eliminates all unwanted platform-specific tags from registry 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6228bce97b
commit
89e2000c62
2 changed files with 94 additions and 7 deletions
10
src/main.rs
10
src/main.rs
|
|
@ -128,16 +128,12 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
let layers = vec![(layer_data, manifest.layers[0].media_type.clone())];
|
let layers = vec![(layer_data, manifest.layers[0].media_type.clone())];
|
||||||
|
|
||||||
// Create a unique tag for this platform to avoid conflicts
|
|
||||||
// Platform-specific images should not be tagged for external use
|
|
||||||
let platform_tag = format!("platform-{}", platform_str.replace('/', "-"));
|
|
||||||
let platform_ref = format!("{}:{}", base_repo, platform_tag);
|
|
||||||
|
|
||||||
// Get auth for the target registry
|
// Get auth for the target registry
|
||||||
let push_auth = resolve_auth(&platform_ref)?;
|
let push_auth = resolve_auth(&base_repo)?;
|
||||||
|
|
||||||
|
// Push platform image by digest only (no tags)
|
||||||
let (digest_ref, manifest_size) = registry_client
|
let (digest_ref, manifest_size) = registry_client
|
||||||
.push_image(&platform_ref, config_data, layers, &push_auth)
|
.push_image_by_digest(&base_repo, config_data, layers, &push_auth)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Parse platform string
|
// Parse platform string
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,97 @@ impl RegistryClient {
|
||||||
Ok(Self { client })
|
Ok(Self { client })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn push_image_by_digest(
|
||||||
|
&mut self,
|
||||||
|
repository: &str,
|
||||||
|
config_data: Vec<u8>,
|
||||||
|
layers: Vec<(Vec<u8>, String)>,
|
||||||
|
auth: &RegistryAuth,
|
||||||
|
) -> Result<(String, usize)> {
|
||||||
|
// Create a temporary reference for authentication - we'll only use the digest result
|
||||||
|
let temp_ref = format!("{}:temp", repository);
|
||||||
|
let reference: Reference = temp_ref
|
||||||
|
.parse()
|
||||||
|
.context("Failed to parse repository reference")?;
|
||||||
|
|
||||||
|
info!("Pushing image to {} (digest only)", repository);
|
||||||
|
|
||||||
|
// Authenticate with the registry
|
||||||
|
self.client
|
||||||
|
.auth(&reference, auth, oci_distribution::RegistryOperation::Push)
|
||||||
|
.await
|
||||||
|
.context("Failed to authenticate with registry")?;
|
||||||
|
|
||||||
|
// Push config blob
|
||||||
|
let config_digest = format!("sha256:{}", sha256::digest(&config_data));
|
||||||
|
debug!("Pushing config blob: {}", config_digest);
|
||||||
|
|
||||||
|
self.client
|
||||||
|
.push_blob(&reference, &config_data, &config_digest)
|
||||||
|
.await
|
||||||
|
.context("Failed to push config blob")?;
|
||||||
|
|
||||||
|
// Push layers
|
||||||
|
let mut manifest_layers = Vec::new();
|
||||||
|
for (layer_data, media_type) in layers {
|
||||||
|
let digest = format!("sha256:{}", sha256::digest(&layer_data));
|
||||||
|
debug!("Pushing layer: {}", digest);
|
||||||
|
|
||||||
|
self.client
|
||||||
|
.push_blob(&reference, &layer_data, &digest)
|
||||||
|
.await
|
||||||
|
.context("Failed to push layer")?;
|
||||||
|
|
||||||
|
manifest_layers.push(OciDescriptor {
|
||||||
|
media_type: media_type.clone(),
|
||||||
|
digest: digest.clone(),
|
||||||
|
size: layer_data.len() as i64,
|
||||||
|
urls: None,
|
||||||
|
annotations: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and push manifest (this will generate a digest)
|
||||||
|
let image_manifest = OciImageManifest {
|
||||||
|
schema_version: 2,
|
||||||
|
media_type: Some("application/vnd.oci.image.manifest.v1+json".to_string()),
|
||||||
|
artifact_type: None,
|
||||||
|
config: OciDescriptor {
|
||||||
|
media_type: "application/vnd.oci.image.config.v1+json".to_string(),
|
||||||
|
digest: config_digest,
|
||||||
|
size: config_data.len() as i64,
|
||||||
|
urls: None,
|
||||||
|
annotations: None,
|
||||||
|
},
|
||||||
|
layers: manifest_layers,
|
||||||
|
annotations: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrap the image manifest in the OciManifest enum
|
||||||
|
let manifest = OciManifest::Image(image_manifest);
|
||||||
|
|
||||||
|
debug!("Pushing manifest for digest-only image");
|
||||||
|
let (manifest_url, digest) = self
|
||||||
|
.client
|
||||||
|
.push_manifest_and_get_digest(&reference, &manifest)
|
||||||
|
.await
|
||||||
|
.context("Failed to push manifest")?;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Successfully pushed image to {} (digest: {})",
|
||||||
|
manifest_url, digest
|
||||||
|
);
|
||||||
|
|
||||||
|
// Build the full image reference with digest only
|
||||||
|
let registry = reference.registry();
|
||||||
|
let repository = reference.repository();
|
||||||
|
let digest_ref = format!("{}/{}@{}", registry, repository, digest);
|
||||||
|
|
||||||
|
// Return both the digest ref and the actual manifest size
|
||||||
|
let manifest_size = serde_json::to_vec(&manifest)?.len();
|
||||||
|
Ok((digest_ref, manifest_size))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn push_image(
|
pub async fn push_image(
|
||||||
&mut self,
|
&mut self,
|
||||||
image_ref: &str,
|
image_ref: &str,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue