Skip to content

Commit fcfee2a

Browse files
committed
fix(switch): don't attempt to parse non-oci image ref formats
Fixes #1308 Introduced in 10b66fb was a bug where oci-archive and dir image ref formats were attempted to be parsed, to which an error was returned. This changes the behaviour to only attempt to parse image refs from an allowlisted selection of transports. Signed-off-by: Robert Sturla <[email protected]>
1 parent 84e7e2e commit fcfee2a

File tree

1 file changed

+65
-18
lines changed

1 file changed

+65
-18
lines changed

lib/src/spec.rs

Lines changed: 65 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use std::fmt::Display;
44

5+
use anyhow::Result;
56
use ostree_ext::oci_spec::image::Digest;
67
use ostree_ext::{container::OstreeImageReference, oci_spec};
78
use schemars::JsonSchema;
@@ -91,20 +92,35 @@ pub struct ImageReference {
9192

9293
impl ImageReference {
9394
/// Returns a canonicalized version of this image reference, preferring the digest over the tag if both are present.
94-
pub fn canonicalize(self) -> Result<Self, anyhow::Error> {
95-
let reference: oci_spec::distribution::Reference = self.image.parse()?;
96-
97-
if reference.digest().is_some() && reference.tag().is_some() {
98-
let registry = reference.registry();
99-
let repository = reference.repository();
100-
let digest = reference.digest().expect("digest is present");
101-
return Ok(ImageReference {
102-
image: format!("{registry}/{repository}@{digest}"),
103-
..self
104-
});
95+
pub fn canonicalize(self) -> Result<Self> {
96+
match self.transport.as_str() {
97+
"containers-storage" | "registry" | "oci" => {
98+
let reference: oci_spec::distribution::Reference = self.image.parse()?;
99+
100+
// No tag? Just pass through.
101+
if reference.tag().is_none() {
102+
return Ok(self);
103+
}
104+
105+
// No digest? Also pass through.
106+
let Some(digest) = reference.digest() else {
107+
return Ok(self);
108+
};
109+
110+
let registry = reference.registry();
111+
let repository = reference.repository();
112+
let r = ImageReference {
113+
image: format!("{registry}/{repository}@{digest}"),
114+
transport: self.transport.clone(),
115+
signature: self.signature.clone(),
116+
};
117+
return Ok(r);
118+
}
119+
_ => {
120+
// For other transports, we don't do any canonicalization
121+
Ok(self)
122+
}
105123
}
106-
107-
Ok(self)
108124
}
109125
}
110126

@@ -276,53 +292,84 @@ mod tests {
276292
fn test_image_reference_canonicalize() {
277293
let sample_digest =
278294
"sha256:5db6d8b5f34d3cbdaa1e82ed0152a5ac980076d19317d4269db149cbde057bb2";
295+
279296
let test_cases = [
280297
// When both a tag and digest are present, the digest should be used
281298
(
282299
format!("quay.io/example/someimage:latest@{}", sample_digest),
283300
format!("quay.io/example/someimage@{}", sample_digest),
301+
"registry",
302+
),
303+
(
304+
format!("quay.io/example/someimage:latest@{}", sample_digest),
305+
format!("quay.io/example/someimage@{}", sample_digest),
306+
"containers-storage",
307+
),
308+
(
309+
format!("quay.io/example/someimage:latest@{}", sample_digest),
310+
format!("quay.io/example/someimage@{}", sample_digest),
311+
"oci",
284312
),
285313
// When only a digest is present, it should be used
286314
(
287315
format!("quay.io/example/someimage@{}", sample_digest),
288316
format!("quay.io/example/someimage@{}", sample_digest),
317+
"registry",
289318
),
290319
// When only a tag is present, it should be preserved
291320
(
292321
"quay.io/example/someimage:latest".to_string(),
293322
"quay.io/example/someimage:latest".to_string(),
323+
"registry",
294324
),
295325
// When no tag or digest is present, preserve the original image name
296326
(
297327
"quay.io/example/someimage".to_string(),
298328
"quay.io/example/someimage".to_string(),
329+
"registry",
299330
),
300331
// When used with a local image (i.e. from containers-storage), the functionality should
301332
// be the same as previous cases
302333
(
303334
"localhost/someimage:latest".to_string(),
304335
"localhost/someimage:latest".to_string(),
336+
"registry",
305337
),
306338
(
307339
format!("localhost/someimage:latest@{sample_digest}"),
308340
format!("localhost/someimage@{sample_digest}"),
341+
"registry",
342+
),
343+
// OCI Archive / Dir should be preserved, and canonicalize should be a no-op
344+
(
345+
"/tmp/repo".to_string(),
346+
"/tmp/repo".to_string(),
347+
"oci-archive",
348+
),
349+
(
350+
"/tmp/image-dir".to_string(),
351+
"/tmp/image-dir".to_string(),
352+
"dir",
309353
),
310354
];
311355

312-
for (initial, expected) in test_cases {
356+
for (initial, expected, transport) in test_cases {
313357
let imgref = ImageReference {
314358
image: initial.to_string(),
315-
transport: "registry".to_string(),
359+
transport: transport.to_string(),
316360
signature: None,
317361
};
318362

319363
let canonicalized = imgref.canonicalize();
320364
if let Err(e) = canonicalized {
321-
panic!("Failed to canonicalize {initial}: {e}");
365+
panic!("Failed to canonicalize {initial} with transport {transport}: {e}");
322366
}
323367
let canonicalized = canonicalized.unwrap();
324-
assert_eq!(canonicalized.image, expected);
325-
assert_eq!(canonicalized.transport, "registry");
368+
assert_eq!(
369+
canonicalized.image, expected,
370+
"Mismatch for transport {transport}"
371+
);
372+
assert_eq!(canonicalized.transport, transport);
326373
assert_eq!(canonicalized.signature, None);
327374
}
328375
}

0 commit comments

Comments
 (0)