Skip to content

Commit eed8194

Browse files
Merge pull request krustlet#610 from tot0/lupickup/expose_manifest_pull
Expose pull_manifest in public API.
2 parents dd3762e + cf4b637 commit eed8194

3 files changed

Lines changed: 104 additions & 9 deletions

File tree

crates/oci-distribution/src/client.rs

Lines changed: 94 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ impl Client {
184184
self.auth(image, auth, &RegistryOperation::Pull).await?;
185185
}
186186

187-
let (manifest, digest) = self.pull_manifest(image).await?;
187+
let (manifest, digest) = self._pull_manifest(image).await?;
188188

189189
self.validate_layers(&manifest, accepted_media_types)
190190
.await?;
@@ -402,11 +402,30 @@ impl Client {
402402
Ok(())
403403
}
404404

405+
/// Pull a manifest from the remote OCI Distribution service.
406+
///
407+
/// The client will check if it's already been authenticated and if
408+
/// not will attempt to do.
409+
///
410+
/// A Tuple is returned containing the [OciManifest](crate::manifest::OciManifest)
411+
/// and the manifest content digest hash.
412+
pub async fn pull_manifest(
413+
&mut self,
414+
image: &Reference,
415+
auth: &RegistryAuth,
416+
) -> anyhow::Result<(OciManifest, String)> {
417+
if !self.tokens.contains_key(image.registry()) {
418+
self.auth(image, auth, &RegistryOperation::Pull).await?;
419+
}
420+
421+
self._pull_manifest(image).await
422+
}
423+
405424
/// Pull a manifest from the remote OCI Distribution service.
406425
///
407426
/// If the connection has already gone through authentication, this will
408427
/// use the bearer token. Otherwise, this will attempt an anonymous pull.
409-
async fn pull_manifest(&self, image: &Reference) -> anyhow::Result<(OciManifest, String)> {
428+
async fn _pull_manifest(&self, image: &Reference) -> anyhow::Result<(OciManifest, String)> {
410429
let url = self.to_v2_manifest_url(image);
411430
debug!("Pulling image manifest from {}", url);
412431
let request = self.client.get(&url);
@@ -467,6 +486,40 @@ impl Client {
467486
Ok(())
468487
}
469488

489+
/// Pull a manifest and its config from the remote OCI Distribution service.
490+
///
491+
/// The client will check if it's already been authenticated and if
492+
/// not will attempt to do.
493+
///
494+
/// A Tuple is returned containing the [OciManifest](crate::manifest::OciManifest),
495+
/// the manifest content digest hash and the contents of the manifests config layer
496+
/// as a String.
497+
pub async fn pull_manifest_and_config(
498+
&mut self,
499+
image: &Reference,
500+
auth: &RegistryAuth,
501+
) -> anyhow::Result<(OciManifest, String, String)> {
502+
if !self.tokens.contains_key(image.registry()) {
503+
self.auth(image, auth, &RegistryOperation::Pull).await?;
504+
}
505+
506+
self._pull_manifest_and_config(image).await
507+
}
508+
509+
async fn _pull_manifest_and_config(
510+
&mut self,
511+
image: &Reference,
512+
) -> anyhow::Result<(OciManifest, String, String)> {
513+
let (manifest, digest) = self._pull_manifest(image).await?;
514+
515+
let mut out: Vec<u8> = Vec::new();
516+
debug!("Pulling config layer");
517+
self.pull_layer(image, &manifest.config.digest, &mut out)
518+
.await?;
519+
520+
Ok((manifest, digest, String::from_utf8(out)?))
521+
}
522+
470523
/// Pull a single layer from an OCI registry.
471524
///
472525
/// This pulls the layer for a particular image that is identified by
@@ -1189,12 +1242,12 @@ mod test {
11891242
}
11901243

11911244
#[tokio::test]
1192-
async fn test_pull_manifest() {
1245+
async fn test_pull_manifest_private() {
11931246
for &image in TEST_IMAGES {
11941247
let reference = Reference::try_from(image).expect("failed to parse reference");
11951248
// Currently, pull_manifest does not perform Authz, so this will fail.
11961249
let c = Client::default();
1197-
c.pull_manifest(&reference)
1250+
c._pull_manifest(&reference)
11981251
.await
11991252
.expect_err("pull manifest should fail");
12001253

@@ -1208,7 +1261,23 @@ mod test {
12081261
.await
12091262
.expect("authenticated");
12101263
let (manifest, _) = c
1211-
.pull_manifest(&reference)
1264+
._pull_manifest(&reference)
1265+
.await
1266+
.expect("pull manifest should not fail");
1267+
1268+
// The test on the manifest checks all fields. This is just a brief sanity check.
1269+
assert_eq!(manifest.schema_version, 2);
1270+
assert!(!manifest.layers.is_empty());
1271+
}
1272+
}
1273+
1274+
#[tokio::test]
1275+
async fn test_pull_manifest_public() {
1276+
for &image in TEST_IMAGES {
1277+
let reference = Reference::try_from(image).expect("failed to parse reference");
1278+
let mut c = Client::default();
1279+
let (manifest, _) = c
1280+
.pull_manifest(&reference, &RegistryAuth::Anonymous)
12121281
.await
12131282
.expect("pull manifest should not fail");
12141283

@@ -1218,6 +1287,23 @@ mod test {
12181287
}
12191288
}
12201289

1290+
#[tokio::test]
1291+
async fn pull_manifest_and_config_public() {
1292+
for &image in TEST_IMAGES {
1293+
let reference = Reference::try_from(image).expect("failed to parse reference");
1294+
let mut c = Client::default();
1295+
let (manifest, _, config) = c
1296+
.pull_manifest_and_config(&reference, &RegistryAuth::Anonymous)
1297+
.await
1298+
.expect("pull manifest and config should not fail");
1299+
1300+
// The test on the manifest checks all fields. This is just a brief sanity check.
1301+
assert_eq!(manifest.schema_version, 2);
1302+
assert!(!manifest.layers.is_empty());
1303+
assert!(!config.is_empty());
1304+
}
1305+
}
1306+
12211307
#[tokio::test]
12221308
async fn test_fetch_digest() {
12231309
let mut c = Client::default();
@@ -1264,7 +1350,7 @@ mod test {
12641350
.await
12651351
.expect("authenticated");
12661352
let (manifest, _) = c
1267-
.pull_manifest(&reference)
1353+
._pull_manifest(&reference)
12681354
.await
12691355
.expect("failed to pull manifest");
12701356

@@ -1485,7 +1571,7 @@ mod test {
14851571
.expect("authenticated");
14861572

14871573
let (manifest, _digest) = c
1488-
.pull_manifest(&image)
1574+
._pull_manifest(&image)
14891575
.await
14901576
.expect("failed to pull manifest");
14911577

@@ -1537,7 +1623,7 @@ mod test {
15371623
.expect("failed to pull pushed image");
15381624

15391625
let (pulled_manifest, _digest) = c
1540-
.pull_manifest(&push_image)
1626+
._pull_manifest(&push_image)
15411627
.await
15421628
.expect("failed to pull pushed image manifest");
15431629

crates/oci-distribution/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub mod secrets;
1111
#[doc(inline)]
1212
pub use client::Client;
1313
#[doc(inline)]
14-
pub use reference::Reference;
14+
pub use reference::{ParseError, Reference};
1515

1616
#[macro_use]
1717
extern crate lazy_static;

crates/oci-distribution/src/reference.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,24 @@ use crate::regexp;
88
/// NAME_TOTAL_LENGTH_MAX is the maximum total number of characters in a repository name.
99
const NAME_TOTAL_LENGTH_MAX: usize = 255;
1010

11+
/// Reasons that parsing a string as a Reference can fail.
1112
#[derive(Debug, PartialEq, Eq)]
1213
pub enum ParseError {
14+
/// Invalid checksum digest format
1315
DigestInvalidFormat,
16+
/// Invalid checksum digest length
1417
DigestInvalidLength,
18+
/// Unsupported digest algorithm
1519
DigestUnsupported,
20+
/// Repository name must be lowercase
1621
NameContainsUppercase,
22+
/// Repository name must have at least one component
1723
NameEmpty,
24+
/// Repository name must not be more than NAME_TOTAL_LENGTH_MAX characters
1825
NameTooLong,
26+
/// Invalid reference format
1927
ReferenceInvalidFormat,
28+
/// Invalid tag format
2029
TagInvalidFormat,
2130
}
2231

0 commit comments

Comments
 (0)