1
0
Fork 0
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:
Jason Hall 2025-06-08 19:50:30 -04:00
parent 6228bce97b
commit 89e2000c62
Failed to extract signature
2 changed files with 94 additions and 7 deletions

View file

@ -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

View file

@ -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,