Replace lexical Rust fragments with real modules
This commit is contained in:
parent
f8849cdb5f
commit
247819250b
2 changed files with 3302 additions and 2671 deletions
|
|
@ -827,4 +827,5 @@ fn hex(bytes: &[u8]) -> String {
|
|||
)
|
||||
}
|
||||
|
||||
include!("transport/tests.rs");
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
|||
|
|
@ -1,193 +1,190 @@
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpListener;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::Duration;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpListener;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use oci_client::client::ClientProtocol;
|
||||
use oci_client::client::ClientProtocol;
|
||||
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn atomic_layout_write_accepts_a_root_level_destination() {
|
||||
#[test]
|
||||
fn atomic_layout_write_accepts_a_root_level_destination() {
|
||||
let tree = tempfile::tempdir().unwrap();
|
||||
let root = File::open(tree.path()).unwrap();
|
||||
write_relative_atomic(&root, Path::new("oci-layout"), b"layout").unwrap();
|
||||
assert_eq!(
|
||||
std::fs::read(tree.path().join("oci-layout")).unwrap(),
|
||||
b"layout"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn confined_read_open_uses_an_empty_creation_mode() {
|
||||
let tree = tempfile::tempdir().unwrap();
|
||||
let root = File::open(tree.path()).unwrap();
|
||||
let error = open_relative_file(&root, Path::new("absent"), OFlags::RDONLY).unwrap_err();
|
||||
assert_eq!(error.kind(), std::io::ErrorKind::NotFound);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preexisting_transport_parents_require_exact_private_mode() {
|
||||
for name in ["image-transports", "blobs", "sha256"] {
|
||||
let tree = tempfile::tempdir().unwrap();
|
||||
let root = File::open(tree.path()).unwrap();
|
||||
write_relative_atomic(&root, Path::new("oci-layout"), b"layout").unwrap();
|
||||
assert_eq!(
|
||||
std::fs::read(tree.path().join("oci-layout")).unwrap(),
|
||||
b"layout"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn confined_read_open_uses_an_empty_creation_mode() {
|
||||
let tree = tempfile::tempdir().unwrap();
|
||||
let root = File::open(tree.path()).unwrap();
|
||||
let error = open_relative_file(&root, Path::new("absent"), OFlags::RDONLY).unwrap_err();
|
||||
assert_eq!(error.kind(), std::io::ErrorKind::NotFound);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preexisting_transport_parents_require_exact_private_mode() {
|
||||
for name in ["image-transports", "blobs", "sha256"] {
|
||||
let tree = tempfile::tempdir().unwrap();
|
||||
std::fs::create_dir(tree.path().join(name)).unwrap();
|
||||
std::fs::set_permissions(
|
||||
tree.path().join(name),
|
||||
std::fs::Permissions::from_mode(0o777),
|
||||
)
|
||||
.unwrap();
|
||||
let parent = File::open(tree.path()).unwrap();
|
||||
assert!(
|
||||
create_directory(&parent, name).is_err(),
|
||||
"accepted wrong mode for {name}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refuses_mismatched_and_incomplete_platforms() {
|
||||
std::fs::create_dir(tree.path().join(name)).unwrap();
|
||||
std::fs::set_permissions(
|
||||
tree.path().join(name),
|
||||
std::fs::Permissions::from_mode(0o777),
|
||||
)
|
||||
.unwrap();
|
||||
let parent = File::open(tree.path()).unwrap();
|
||||
assert!(
|
||||
verify_platform(
|
||||
r#"{"os":"linux","architecture":"amd64"}"#,
|
||||
&Platform::linux("amd64")
|
||||
)
|
||||
.is_ok()
|
||||
);
|
||||
assert!(
|
||||
verify_platform(
|
||||
r#"{"os":"linux","architecture":"arm64"}"#,
|
||||
&Platform::linux("amd64")
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
verify_platform(
|
||||
r#"{"os":"linux","architecture":"amd64","variant":"v2"}"#,
|
||||
&Platform::linux("amd64")
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
assert!(verify_platform(r#"{"architecture":"amd64"}"#, &Platform::linux("amd64")).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn incomplete_authentication_refuses_without_layout_or_secret_disclosure() {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let result = runtime.block_on(OciRegistryTransport.pull_with_config(
|
||||
"registry.invalid/private/image:latest",
|
||||
&RegistryCredentials::Basic {
|
||||
username: "developer".to_owned(),
|
||||
password: String::new(),
|
||||
},
|
||||
&Platform::linux("amd64"),
|
||||
&temp.path().join("layout"),
|
||||
ClientConfig::default(),
|
||||
));
|
||||
let error = result.unwrap_err();
|
||||
assert!(matches!(error, PreparedLowerError::Authentication { .. }));
|
||||
let error = error.to_string();
|
||||
assert!(error.contains("credentials are incomplete"));
|
||||
assert!(!error.contains("developer"));
|
||||
assert!(!temp.path().join("layout").exists());
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{:?}",
|
||||
RegistryCredentials::Bearer("not-a-real-token".to_owned())
|
||||
),
|
||||
"Bearer(\"[redacted]\")"
|
||||
create_directory(&parent, name).is_err(),
|
||||
"accepted wrong mode for {name}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registry_authentication_failure_refuses_before_layout_creation() {
|
||||
let listener = match TcpListener::bind("127.0.0.1:0") {
|
||||
Ok(listener) => listener,
|
||||
Err(error) if error.kind() == std::io::ErrorKind::PermissionDenied => return,
|
||||
Err(error) => panic!("binding test registry: {error}"),
|
||||
};
|
||||
let address = listener.local_addr().unwrap();
|
||||
listener.set_nonblocking(true).unwrap();
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
let server_stop = Arc::clone(&stop);
|
||||
let server = std::thread::spawn(move || {
|
||||
while !server_stop.load(Ordering::Relaxed) {
|
||||
match listener.accept() {
|
||||
Ok((mut stream, _)) => {
|
||||
let mut request = [0; 4096];
|
||||
assert!(stream.read(&mut request).unwrap() > 0);
|
||||
stream
|
||||
#[test]
|
||||
fn refuses_mismatched_and_incomplete_platforms() {
|
||||
assert!(
|
||||
verify_platform(
|
||||
r#"{"os":"linux","architecture":"amd64"}"#,
|
||||
&Platform::linux("amd64")
|
||||
)
|
||||
.is_ok()
|
||||
);
|
||||
assert!(
|
||||
verify_platform(
|
||||
r#"{"os":"linux","architecture":"arm64"}"#,
|
||||
&Platform::linux("amd64")
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
verify_platform(
|
||||
r#"{"os":"linux","architecture":"amd64","variant":"v2"}"#,
|
||||
&Platform::linux("amd64")
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
assert!(verify_platform(r#"{"architecture":"amd64"}"#, &Platform::linux("amd64")).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn incomplete_authentication_refuses_without_layout_or_secret_disclosure() {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let result = runtime.block_on(OciRegistryTransport.pull_with_config(
|
||||
"registry.invalid/private/image:latest",
|
||||
&RegistryCredentials::Basic {
|
||||
username: "developer".to_owned(),
|
||||
password: String::new(),
|
||||
},
|
||||
&Platform::linux("amd64"),
|
||||
&temp.path().join("layout"),
|
||||
ClientConfig::default(),
|
||||
));
|
||||
let error = result.unwrap_err();
|
||||
assert!(matches!(error, PreparedLowerError::Authentication { .. }));
|
||||
let error = error.to_string();
|
||||
assert!(error.contains("credentials are incomplete"));
|
||||
assert!(!error.contains("developer"));
|
||||
assert!(!temp.path().join("layout").exists());
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{:?}",
|
||||
RegistryCredentials::Bearer("not-a-real-token".to_owned())
|
||||
),
|
||||
"Bearer(\"[redacted]\")"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registry_authentication_failure_refuses_before_layout_creation() {
|
||||
let listener = match TcpListener::bind("127.0.0.1:0") {
|
||||
Ok(listener) => listener,
|
||||
Err(error) if error.kind() == std::io::ErrorKind::PermissionDenied => return,
|
||||
Err(error) => panic!("binding test registry: {error}"),
|
||||
};
|
||||
let address = listener.local_addr().unwrap();
|
||||
listener.set_nonblocking(true).unwrap();
|
||||
let stop = Arc::new(AtomicBool::new(false));
|
||||
let server_stop = Arc::clone(&stop);
|
||||
let server = std::thread::spawn(move || {
|
||||
while !server_stop.load(Ordering::Relaxed) {
|
||||
match listener.accept() {
|
||||
Ok((mut stream, _)) => {
|
||||
let mut request = [0; 4096];
|
||||
assert!(stream.read(&mut request).unwrap() > 0);
|
||||
stream
|
||||
.write_all(
|
||||
b"HTTP/1.1 401 Unauthorized\r\nContent-Length: 0\r\nConnection: close\r\n\r\n",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {
|
||||
std::thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
Err(error) => panic!("accepting test registry request: {error}"),
|
||||
}
|
||||
Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => {
|
||||
std::thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
Err(error) => panic!("accepting test registry request: {error}"),
|
||||
}
|
||||
});
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let result = runtime.block_on(OciRegistryTransport.pull_with_config(
|
||||
&format!("{address}/private/image:latest"),
|
||||
&RegistryCredentials::Basic {
|
||||
username: "wrong".to_owned(),
|
||||
password: "wrong".to_owned(),
|
||||
},
|
||||
&Platform::linux("amd64"),
|
||||
&temp.path().join("layout"),
|
||||
ClientConfig {
|
||||
protocol: ClientProtocol::Http,
|
||||
..ClientConfig::default()
|
||||
},
|
||||
));
|
||||
assert!(
|
||||
matches!(result, Err(PreparedLowerError::Authentication { .. })),
|
||||
"{result:?}"
|
||||
);
|
||||
assert!(!temp.path().join("layout").exists());
|
||||
stop.store(true, Ordering::Relaxed);
|
||||
server.join().unwrap();
|
||||
}
|
||||
}
|
||||
});
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let result = runtime.block_on(OciRegistryTransport.pull_with_config(
|
||||
&format!("{address}/private/image:latest"),
|
||||
&RegistryCredentials::Basic {
|
||||
username: "wrong".to_owned(),
|
||||
password: "wrong".to_owned(),
|
||||
},
|
||||
&Platform::linux("amd64"),
|
||||
&temp.path().join("layout"),
|
||||
ClientConfig {
|
||||
protocol: ClientProtocol::Http,
|
||||
..ClientConfig::default()
|
||||
},
|
||||
));
|
||||
assert!(
|
||||
matches!(result, Err(PreparedLowerError::Authentication { .. })),
|
||||
"{result:?}"
|
||||
);
|
||||
assert!(!temp.path().join("layout").exists());
|
||||
stop.store(true, Ordering::Relaxed);
|
||||
server.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registry_failures_map_to_stable_caller_categories() {
|
||||
assert!(matches!(
|
||||
distribution_error(
|
||||
OciDistributionError::AuthenticationFailure("denied".into()),
|
||||
"pull"
|
||||
),
|
||||
PreparedLowerError::Authentication { .. }
|
||||
));
|
||||
assert!(matches!(
|
||||
distribution_error(
|
||||
OciDistributionError::ImageIndexParsingNoPlatformResolverError,
|
||||
"pull"
|
||||
),
|
||||
PreparedLowerError::PlatformSelection { .. }
|
||||
));
|
||||
assert!(matches!(
|
||||
distribution_error(
|
||||
OciDistributionError::ManifestParsingError("invalid".into()),
|
||||
"pull"
|
||||
),
|
||||
PreparedLowerError::ManifestSelection { .. }
|
||||
));
|
||||
assert!(matches!(
|
||||
distribution_error(OciDistributionError::GenericError(None), "pull"),
|
||||
PreparedLowerError::Transport { .. }
|
||||
));
|
||||
}
|
||||
#[test]
|
||||
fn registry_failures_map_to_stable_caller_categories() {
|
||||
assert!(matches!(
|
||||
distribution_error(
|
||||
OciDistributionError::AuthenticationFailure("denied".into()),
|
||||
"pull"
|
||||
),
|
||||
PreparedLowerError::Authentication { .. }
|
||||
));
|
||||
assert!(matches!(
|
||||
distribution_error(
|
||||
OciDistributionError::ImageIndexParsingNoPlatformResolverError,
|
||||
"pull"
|
||||
),
|
||||
PreparedLowerError::PlatformSelection { .. }
|
||||
));
|
||||
assert!(matches!(
|
||||
distribution_error(
|
||||
OciDistributionError::ManifestParsingError("invalid".into()),
|
||||
"pull"
|
||||
),
|
||||
PreparedLowerError::ManifestSelection { .. }
|
||||
));
|
||||
assert!(matches!(
|
||||
distribution_error(OciDistributionError::GenericError(None), "pull"),
|
||||
PreparedLowerError::Transport { .. }
|
||||
));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue