Skip to content

Commit 5f9e050

Browse files
committed
fix(kwctl): normalize registry:// URIs to include explicit tag
When a policy URI without a tag is used (e.g. registry://ghcr.io/kubewarden/policies/pod-privileged), the pull step stores the policy under a path with ':latest' appended, but subsequent commands (scaffold, inspect, rm) looked up the URI without the tag, causing them to fail with 'Cannot find policy with uri'. Fix by normalizing registry:// URIs in normalize_uri via Reference::from_str, which defaults to ':latest' when no tag is specified. This makes the lookup key consistent with the store path. Signed-off-by: José Guilherme Vanz <jguilhermevanz@suse.com> Assisted-by: Github Copilot
1 parent 2dda15b commit 5f9e050

2 files changed

Lines changed: 66 additions & 10 deletions

File tree

crates/kwctl/src/main.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ async fn main() -> Result<()> {
119119
Some("info") => info::info(),
120120
Some("pull") => {
121121
if let Some(matches) = matches.subcommand_matches("pull") {
122-
let uri = matches.get_one::<String>("uri").unwrap();
122+
let uri = crate::utils::normalize_uri(matches.get_one::<String>("uri").unwrap());
123+
let uri = &uri;
123124
let destination = matches
124125
.get_one::<String>("output-path")
125126
.map(|output| PathBuf::from_str(output).unwrap());
@@ -133,7 +134,8 @@ async fn main() -> Result<()> {
133134
}
134135
Some("verify") => {
135136
if let Some(matches) = matches.subcommand_matches("verify") {
136-
let uri = matches.get_one::<String>("uri").unwrap();
137+
let uri = crate::utils::normalize_uri(matches.get_one::<String>("uri").unwrap());
138+
let uri = uri.as_str();
137139
let sources = remote_server_options(matches)?;
138140
let verification_options = build_verification_options(matches)?
139141
.ok_or_else(|| anyhow!("could not retrieve sigstore options"))?;
@@ -193,8 +195,10 @@ async fn main() -> Result<()> {
193195
}
194196
Some("rm") => {
195197
if let Some(matches) = matches.subcommand_matches("rm") {
196-
let uri_or_sha_prefix = matches.get_one::<String>("uri_or_sha_prefix").unwrap();
197-
rm::rm(uri_or_sha_prefix)?;
198+
let uri_or_sha_prefix = crate::utils::normalize_uri(
199+
matches.get_one::<String>("uri_or_sha_prefix").unwrap(),
200+
);
201+
rm::rm(&uri_or_sha_prefix)?;
198202
}
199203
Ok(())
200204
}
@@ -233,7 +237,9 @@ async fn main() -> Result<()> {
233237
}
234238
Some("inspect") => {
235239
if let Some(matches) = matches.subcommand_matches("inspect") {
236-
let uri_or_sha_prefix = matches.get_one::<String>("uri_or_sha_prefix").unwrap();
240+
let uri_or_sha_prefix = crate::utils::normalize_uri(
241+
matches.get_one::<String>("uri_or_sha_prefix").unwrap(),
242+
);
237243
let output = inspect::OutputType::try_from(
238244
matches.get_one::<String>("output").map(|s| s.as_str()),
239245
)?;
@@ -242,7 +248,7 @@ async fn main() -> Result<()> {
242248
.get_one::<bool>("show-signatures")
243249
.unwrap_or(&false)
244250
.to_owned();
245-
inspect::inspect(uri_or_sha_prefix, output, sources, no_color, no_signatures)
251+
inspect::inspect(&uri_or_sha_prefix, output, sources, no_color, no_signatures)
246252
.await?;
247253
};
248254
Ok(())
@@ -446,7 +452,9 @@ async fn pull_command(
446452
* This function will pull the policy if it is not already present in the local store.
447453
*/
448454
async fn scaffold_manifest_command(matches: &ArgMatches) -> Result<()> {
449-
let uri_or_sha_prefix = matches.get_one::<String>("uri_or_sha_prefix").unwrap();
455+
let uri_or_sha_prefix =
456+
crate::utils::normalize_uri(matches.get_one::<String>("uri_or_sha_prefix").unwrap());
457+
let uri_or_sha_prefix = uri_or_sha_prefix.as_str();
450458

451459
pull_if_needed(uri_or_sha_prefix, matches).await?;
452460

crates/kwctl/src/utils.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,23 @@ pub(crate) enum LookupError {
2424
IoError(#[from] std::io::Error),
2525
}
2626

27+
/// Normalizes a `registry://` URI to include an explicit tag.
28+
/// If no tag is specified, defaults to `:latest`, making the URI consistent
29+
/// with how the local store paths are keyed.
30+
/// Non-registry URIs (e.g. `file://`, `http://`) are returned unchanged.
31+
pub(crate) fn normalize_uri(uri: &str) -> String {
32+
if let Some(image) = uri.strip_prefix("registry://")
33+
&& let Ok(reference) = Reference::from_str(image)
34+
{
35+
return format!("registry://{}", reference.whole());
36+
}
37+
uri.to_string()
38+
}
39+
2740
pub(crate) fn map_path_to_uri(uri_or_sha_prefix: &str) -> std::result::Result<String, LookupError> {
2841
let uri_has_schema = Regex::new(r"^\w+://").unwrap();
2942
if uri_has_schema.is_match(uri_or_sha_prefix) {
30-
return Ok(String::from(uri_or_sha_prefix));
43+
return Ok(normalize_uri(uri_or_sha_prefix));
3144
}
3245

3346
let path = PathBuf::from(uri_or_sha_prefix);
@@ -46,8 +59,9 @@ pub(crate) fn map_path_to_uri(uri_or_sha_prefix: &str) -> std::result::Result<St
4659
}
4760

4861
pub(crate) fn get_uri(uri_or_sha_prefix: &String) -> std::result::Result<String, LookupError> {
49-
map_path_to_uri(uri_or_sha_prefix).or_else(|_| {
50-
Reference::from_str(uri_or_sha_prefix)
62+
let normalized = normalize_uri(uri_or_sha_prefix);
63+
map_path_to_uri(&normalized).or_else(|_| {
64+
Reference::from_str(&normalized)
5165
.map(|oci_reference| format!("registry://{}", oci_reference.whole()))
5266
.map_err(|_| LookupError::PolicyMissing(uri_or_sha_prefix.to_string()))
5367
})
@@ -105,6 +119,40 @@ mod tests {
105119

106120
use super::*;
107121

122+
#[test]
123+
fn test_normalize_uri_registry_without_tag_defaults_to_latest() {
124+
assert_eq!(
125+
normalize_uri("registry://ghcr.io/kubewarden/policies/pod-privileged"),
126+
"registry://ghcr.io/kubewarden/policies/pod-privileged:latest",
127+
);
128+
}
129+
130+
#[test]
131+
fn test_normalize_uri_registry_with_tag_unchanged() {
132+
assert_eq!(
133+
normalize_uri("registry://ghcr.io/kubewarden/policies/pod-privileged:v0.2.2"),
134+
"registry://ghcr.io/kubewarden/policies/pod-privileged:v0.2.2",
135+
);
136+
}
137+
138+
#[test]
139+
fn test_normalize_uri_registry_with_digest_unchanged() {
140+
let uri = "registry://ghcr.io/kubewarden/policies/pod-privileged@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
141+
assert_eq!(normalize_uri(uri), uri);
142+
}
143+
144+
#[test]
145+
fn test_normalize_uri_non_registry_unchanged() {
146+
assert_eq!(
147+
normalize_uri("file:///some/path/policy.wasm"),
148+
"file:///some/path/policy.wasm",
149+
);
150+
assert_eq!(
151+
normalize_uri("https://example.com/policy.wasm"),
152+
"https://example.com/policy.wasm",
153+
);
154+
}
155+
108156
#[test]
109157
fn test_map_path_to_uri_remote_scheme() -> Result<()> {
110158
assert_eq!(

0 commit comments

Comments
 (0)