Skip to content

Commit 54e63d8

Browse files
committed
feat(client): Add ability to pull an image from a mirror with ns param
You can pull images via a pull-through proxy, this will change the resolved registry for the actual request, and the original registry of the image gets forwarded as a hint for the upstream registry in the "ns" query parameter. Signed-off-by: Benjamin Neff <[email protected]>
1 parent 395caec commit 54e63d8

File tree

2 files changed

+158
-90
lines changed

2 files changed

+158
-90
lines changed

src/client.rs

Lines changed: 72 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,7 +1094,7 @@ impl Client {
10941094
offset: Option<u64>,
10951095
) -> Result<Response> {
10961096
let layer = layer.as_layer_descriptor();
1097-
let url = self.to_v2_blob_url(image.resolve_registry(), image.repository(), layer.digest);
1097+
let url = self.to_v2_blob_url(image, layer.digest);
10981098

10991099
let mut request = RequestBuilderWrapper::from_client(self, |client| client.get(&url))
11001100
.apply_accept(MIME_TYPES_DISTRIBUTION_MANIFEST)?
@@ -1433,11 +1433,10 @@ impl Client {
14331433
) -> Result<String> {
14341434
let lh = location_header.to_str()?;
14351435
if lh.starts_with("/v2/") {
1436+
let registry = image.resolve_registry();
14361437
Ok(format!(
1437-
"{}://{}{}",
1438-
self.config.protocol.scheme_for(image.resolve_registry()),
1439-
image.resolve_registry(),
1440-
lh
1438+
"{scheme}://{registry}{lh}",
1439+
scheme = self.config.protocol.scheme_for(registry)
14411440
))
14421441
} else {
14431442
Ok(lh.to_string())
@@ -1446,57 +1445,52 @@ impl Client {
14461445

14471446
/// Convert a Reference to a v2 manifest URL.
14481447
fn to_v2_manifest_url(&self, reference: &Reference) -> String {
1449-
if let Some(digest) = reference.digest() {
1450-
format!(
1451-
"{}://{}/v2/{}/manifests/{}",
1452-
self.config
1453-
.protocol
1454-
.scheme_for(reference.resolve_registry()),
1455-
reference.resolve_registry(),
1456-
reference.repository(),
1457-
digest,
1458-
)
1459-
} else {
1460-
format!(
1461-
"{}://{}/v2/{}/manifests/{}",
1462-
self.config
1463-
.protocol
1464-
.scheme_for(reference.resolve_registry()),
1465-
reference.resolve_registry(),
1466-
reference.repository(),
1448+
let registry = reference.resolve_registry();
1449+
format!(
1450+
"{scheme}://{registry}/v2/{repository}/manifests/{reference}{ns}",
1451+
scheme = self.config.protocol.scheme_for(registry),
1452+
repository = reference.repository(),
1453+
reference = if let Some(digest) = reference.digest() {
1454+
digest
1455+
} else {
14671456
reference.tag().unwrap_or("latest")
1468-
)
1469-
}
1457+
},
1458+
ns = reference
1459+
.namespace()
1460+
.map(|ns| format!("?ns={ns}"))
1461+
.unwrap_or_default(),
1462+
)
14701463
}
14711464

14721465
/// Convert a Reference to a v2 blob (layer) URL.
1473-
fn to_v2_blob_url(&self, registry: &str, repository: &str, digest: &str) -> String {
1466+
fn to_v2_blob_url(&self, reference: &Reference, digest: &str) -> String {
1467+
let registry = reference.resolve_registry();
14741468
format!(
1475-
"{}://{}/v2/{}/blobs/{}",
1476-
self.config.protocol.scheme_for(registry),
1477-
registry,
1478-
repository,
1479-
digest,
1469+
"{scheme}://{registry}/v2/{repository}/blobs/{digest}{ns}",
1470+
scheme = self.config.protocol.scheme_for(registry),
1471+
repository = reference.repository(),
1472+
ns = reference
1473+
.namespace()
1474+
.map(|ns| format!("?ns={ns}"))
1475+
.unwrap_or_default(),
14801476
)
14811477
}
14821478

14831479
/// Convert a Reference to a v2 blob upload URL.
14841480
fn to_v2_blob_upload_url(&self, reference: &Reference) -> String {
1485-
self.to_v2_blob_url(
1486-
reference.resolve_registry(),
1487-
reference.repository(),
1488-
"uploads/",
1489-
)
1481+
self.to_v2_blob_url(reference, "uploads/")
14901482
}
14911483

14921484
fn to_list_tags_url(&self, reference: &Reference) -> String {
1485+
let registry = reference.resolve_registry();
14931486
format!(
1494-
"{}://{}/v2/{}/tags/list",
1495-
self.config
1496-
.protocol
1497-
.scheme_for(reference.resolve_registry()),
1498-
reference.resolve_registry(),
1499-
reference.repository(),
1487+
"{scheme}://{registry}/v2/{repository}/tags/list{ns}",
1488+
scheme = self.config.protocol.scheme_for(registry),
1489+
repository = reference.repository(),
1490+
ns = reference
1491+
.namespace()
1492+
.map(|ns| format!("?ns={ns}"))
1493+
.unwrap_or_default(),
15001494
)
15011495
}
15021496
}
@@ -1941,6 +1935,7 @@ fn digest_header_value(headers: HeaderMap, body: Option<&[u8]>) -> Result<String
19411935
mod test {
19421936
use super::*;
19431937
use crate::manifest::{self, IMAGE_DOCKER_LAYER_GZIP_MEDIA_TYPE};
1938+
use rstest::rstest;
19441939
use std::convert::TryFrom;
19451940
use std::fs;
19461941
use std::path;
@@ -2075,31 +2070,34 @@ mod test {
20752070

20762071
#[test]
20772072
fn test_to_v2_blob_url() {
2078-
let image = Reference::try_from(HELLO_IMAGE_TAG).expect("failed to parse reference");
2079-
let blob_url = Client::default().to_v2_blob_url(
2080-
image.registry(),
2081-
image.repository(),
2082-
"sha256:deadbeef",
2083-
);
2073+
let mut image = Reference::try_from(HELLO_IMAGE_TAG).expect("failed to parse reference");
2074+
let c = Client::default();
2075+
20842076
assert_eq!(
2085-
blob_url,
2077+
c.to_v2_blob_url(&image, "sha256:deadbeef"),
20862078
"https://webassembly.azurecr.io/v2/hello-wasm/blobs/sha256:deadbeef"
2087-
)
2079+
);
2080+
2081+
image.set_mirror_registry("docker.mirror.io".to_owned());
2082+
assert_eq!(
2083+
c.to_v2_blob_url(&image, "sha256:deadbeef"),
2084+
"https://docker.mirror.io/v2/hello-wasm/blobs/sha256:deadbeef?ns=webassembly.azurecr.io"
2085+
);
20882086
}
20892087

2090-
#[test]
2091-
fn test_to_v2_manifest() {
2088+
#[rstest(image, expected_uri, expected_mirror_uri,
2089+
case(HELLO_IMAGE_NO_TAG, "https://webassembly.azurecr.io/v2/hello-wasm/manifests/latest", "https://docker.mirror.io/v2/hello-wasm/manifests/latest?ns=webassembly.azurecr.io"), // TODO: confirm this is the right translation when no tag
2090+
case(HELLO_IMAGE_TAG, "https://webassembly.azurecr.io/v2/hello-wasm/manifests/v1", "https://docker.mirror.io/v2/hello-wasm/manifests/v1?ns=webassembly.azurecr.io"),
2091+
case(HELLO_IMAGE_DIGEST, "https://webassembly.azurecr.io/v2/hello-wasm/manifests/sha256:51d9b231d5129e3ffc267c9d455c49d789bf3167b611a07ab6e4b3304c96b0e7", "https://docker.mirror.io/v2/hello-wasm/manifests/sha256:51d9b231d5129e3ffc267c9d455c49d789bf3167b611a07ab6e4b3304c96b0e7?ns=webassembly.azurecr.io"),
2092+
case(HELLO_IMAGE_TAG_AND_DIGEST, "https://webassembly.azurecr.io/v2/hello-wasm/manifests/sha256:51d9b231d5129e3ffc267c9d455c49d789bf3167b611a07ab6e4b3304c96b0e7", "https://docker.mirror.io/v2/hello-wasm/manifests/sha256:51d9b231d5129e3ffc267c9d455c49d789bf3167b611a07ab6e4b3304c96b0e7?ns=webassembly.azurecr.io"),
2093+
)]
2094+
fn test_to_v2_manifest(image: &str, expected_uri: &str, expected_mirror_uri: &str) {
2095+
let mut reference = Reference::try_from(image).expect("failed to parse reference");
20922096
let c = Client::default();
2097+
assert_eq!(c.to_v2_manifest_url(&reference), expected_uri);
20932098

2094-
for &(image, expected_uri) in [
2095-
(HELLO_IMAGE_NO_TAG, "https://webassembly.azurecr.io/v2/hello-wasm/manifests/latest"), // TODO: confirm this is the right translation when no tag
2096-
(HELLO_IMAGE_TAG, "https://webassembly.azurecr.io/v2/hello-wasm/manifests/v1"),
2097-
(HELLO_IMAGE_DIGEST, "https://webassembly.azurecr.io/v2/hello-wasm/manifests/sha256:51d9b231d5129e3ffc267c9d455c49d789bf3167b611a07ab6e4b3304c96b0e7"),
2098-
(HELLO_IMAGE_TAG_AND_DIGEST, "https://webassembly.azurecr.io/v2/hello-wasm/manifests/sha256:51d9b231d5129e3ffc267c9d455c49d789bf3167b611a07ab6e4b3304c96b0e7"),
2099-
].iter() {
2100-
let reference = Reference::try_from(image).expect("failed to parse reference");
2101-
assert_eq!(c.to_v2_manifest_url(&reference), expected_uri);
2102-
}
2099+
reference.set_mirror_registry("docker.mirror.io".to_owned());
2100+
assert_eq!(c.to_v2_manifest_url(&reference), expected_mirror_uri);
21032101
}
21042102

21052103
#[test]
@@ -2115,13 +2113,19 @@ mod test {
21152113

21162114
#[test]
21172115
fn test_to_list_tags_url() {
2118-
let image = Reference::try_from(HELLO_IMAGE_TAG).expect("failed to parse reference");
2119-
let blob_url = Client::default().to_list_tags_url(&image);
2116+
let mut image = Reference::try_from(HELLO_IMAGE_TAG).expect("failed to parse reference");
2117+
let c = Client::default();
21202118

21212119
assert_eq!(
2122-
blob_url,
2120+
c.to_list_tags_url(&image),
21232121
"https://webassembly.azurecr.io/v2/hello-wasm/tags/list"
2124-
)
2122+
);
2123+
2124+
image.set_mirror_registry("docker.mirror.io".to_owned());
2125+
assert_eq!(
2126+
c.to_list_tags_url(&image),
2127+
"https://docker.mirror.io/v2/hello-wasm/tags/list?ns=webassembly.azurecr.io"
2128+
);
21252129
}
21262130

21272131
#[test]
@@ -2148,11 +2152,7 @@ mod test {
21482152
.expect("Could not parse reference");
21492153
assert_eq!(
21502154
"http://webassembly.azurecr.io/v2/hello/blobs/sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
2151-
c.to_v2_blob_url(
2152-
reference.registry(),
2153-
reference.repository(),
2154-
reference.digest().unwrap()
2155-
)
2155+
c.to_v2_blob_url(&reference, reference.digest().unwrap())
21562156
);
21572157
}
21582158

@@ -2200,11 +2200,7 @@ mod test {
22002200
.expect("Could not parse reference");
22012201
assert_eq!(
22022202
"https://webassembly.azurecr.io/v2/hello/blobs/sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
2203-
c.to_v2_blob_url(
2204-
reference.registry(),
2205-
reference.repository(),
2206-
reference.digest().unwrap()
2207-
)
2203+
c.to_v2_blob_url(&reference, reference.digest().unwrap())
22082204
);
22092205
}
22102206

@@ -2220,11 +2216,7 @@ mod test {
22202216
.expect("Could not parse reference");
22212217
assert_eq!(
22222218
"http://oci.registry.local/v2/hello/blobs/sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
2223-
c.to_v2_blob_url(
2224-
reference.registry(),
2225-
reference.repository(),
2226-
reference.digest().unwrap()
2227-
)
2219+
c.to_v2_blob_url(&reference, reference.digest().unwrap())
22282220
);
22292221
}
22302222

0 commit comments

Comments
 (0)