mirror of
https://github.com/rustfs/rustfs.git
synced 2026-09-03 18:55:39 +00:00
fix(ecstore): avoid warm tier init panics (#2144)
This commit is contained in:
@@ -155,6 +155,47 @@ fn load_root_store_from_tls_path() -> Option<rustls::RootCertStore> {
|
|||||||
Some(store)
|
Some(store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn panic_payload_to_message(payload: Box<dyn std::any::Any + Send>) -> String {
|
||||||
|
if let Some(message) = payload.downcast_ref::<String>() {
|
||||||
|
return message.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(message) = payload.downcast_ref::<&'static str>() {
|
||||||
|
return (*message).to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
"unknown panic payload".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_rustls_init_guard<T, F>(build: F) -> Result<T, std::io::Error>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> Result<T, std::io::Error>,
|
||||||
|
{
|
||||||
|
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(build)) {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(payload) => {
|
||||||
|
let panic_message = panic_payload_to_message(payload);
|
||||||
|
Err(std::io::Error::other(format!(
|
||||||
|
"failed to initialize rustls crypto provider: {panic_message}. Ensure exactly one rustls crypto provider feature is enabled (aws-lc-rs or ring), or install one with CryptoProvider::install_default()"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_tls_config() -> Result<rustls::ClientConfig, std::io::Error> {
|
||||||
|
with_rustls_init_guard(|| {
|
||||||
|
let config = if let Some(store) = load_root_store_from_tls_path() {
|
||||||
|
rustls::ClientConfig::builder()
|
||||||
|
.with_root_certificates(store)
|
||||||
|
.with_no_client_auth()
|
||||||
|
} else {
|
||||||
|
rustls::ClientConfig::builder().with_native_roots()?.with_no_client_auth()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(config)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
impl TransitionClient {
|
impl TransitionClient {
|
||||||
pub async fn new(endpoint: &str, opts: Options, tier_type: &str) -> Result<TransitionClient, std::io::Error> {
|
pub async fn new(endpoint: &str, opts: Options, tier_type: &str) -> Result<TransitionClient, std::io::Error> {
|
||||||
let clnt = Self::private_new(endpoint, opts, tier_type).await?;
|
let clnt = Self::private_new(endpoint, opts, tier_type).await?;
|
||||||
@@ -165,15 +206,8 @@ impl TransitionClient {
|
|||||||
async fn private_new(endpoint: &str, opts: Options, tier_type: &str) -> Result<TransitionClient, std::io::Error> {
|
async fn private_new(endpoint: &str, opts: Options, tier_type: &str) -> Result<TransitionClient, std::io::Error> {
|
||||||
let endpoint_url = get_endpoint_url(endpoint, opts.secure)?;
|
let endpoint_url = get_endpoint_url(endpoint, opts.secure)?;
|
||||||
|
|
||||||
let scheme = endpoint_url.scheme();
|
|
||||||
let client;
|
let client;
|
||||||
let tls = if let Some(store) = load_root_store_from_tls_path() {
|
let tls = build_tls_config()?;
|
||||||
rustls::ClientConfig::builder()
|
|
||||||
.with_root_certificates(store)
|
|
||||||
.with_no_client_auth()
|
|
||||||
} else {
|
|
||||||
rustls::ClientConfig::builder().with_native_roots()?.with_no_client_auth()
|
|
||||||
};
|
|
||||||
|
|
||||||
let https = hyper_rustls::HttpsConnectorBuilder::new()
|
let https = hyper_rustls::HttpsConnectorBuilder::new()
|
||||||
.with_tls_config(tls)
|
.with_tls_config(tls)
|
||||||
@@ -1278,3 +1312,24 @@ pub struct CreateBucketConfiguration {
|
|||||||
#[serde(rename = "LocationConstraint")]
|
#[serde(rename = "LocationConstraint")]
|
||||||
pub location_constraint: String,
|
pub location_constraint: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{build_tls_config, with_rustls_init_guard};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rustls_guard_converts_panics_to_io_errors() {
|
||||||
|
let err = with_rustls_init_guard(|| -> Result<(), std::io::Error> { panic!("missing provider") })
|
||||||
|
.expect_err("panic should be converted into an io::Error");
|
||||||
|
assert!(
|
||||||
|
err.to_string().contains("missing provider"),
|
||||||
|
"expected panic message to be preserved, got: {err}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_tls_config_returns_result_without_panicking() {
|
||||||
|
let outcome = std::panic::catch_unwind(build_tls_config);
|
||||||
|
assert!(outcome.is_ok(), "TLS config creation should not panic");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -72,12 +72,10 @@ impl WarmBackendRustFS {
|
|||||||
};
|
};
|
||||||
let scheme = u.scheme();
|
let scheme = u.scheme();
|
||||||
let default_port = if scheme == "https" { 443 } else { 80 };
|
let default_port = if scheme == "https" { 443 } else { 80 };
|
||||||
let client = TransitionClient::new(
|
let host = u
|
||||||
&format!("{}:{}", u.host_str().expect("err"), u.port().unwrap_or(default_port)),
|
.host_str()
|
||||||
opts,
|
.ok_or_else(|| std::io::Error::other("endpoint URL must include a host"))?;
|
||||||
"rustfs",
|
let client = TransitionClient::new(&format!("{host}:{}", u.port().unwrap_or(default_port)), opts, "rustfs").await?;
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let client = Arc::new(client);
|
let client = Arc::new(client);
|
||||||
let core = TransitionCore(Arc::clone(&client));
|
let core = TransitionCore(Arc::clone(&client));
|
||||||
@@ -158,3 +156,35 @@ fn optimal_part_size(object_size: i64) -> Result<i64, std::io::Error> {
|
|||||||
}
|
}
|
||||||
Ok(part_size)
|
Ok(part_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use futures::FutureExt;
|
||||||
|
use std::panic::AssertUnwindSafe;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn rustfs_tier(endpoint: &str) -> TierRustFS {
|
||||||
|
TierRustFS {
|
||||||
|
endpoint: endpoint.to_string(),
|
||||||
|
access_key: "access".to_string(),
|
||||||
|
secret_key: "secret".to_string(),
|
||||||
|
bucket: "bucket".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn new_returns_error_when_endpoint_has_no_host() {
|
||||||
|
let conf = rustfs_tier("rustfs://");
|
||||||
|
|
||||||
|
let outcome = AssertUnwindSafe(WarmBackendRustFS::new(&conf, "tier")).catch_unwind().await;
|
||||||
|
|
||||||
|
let result = outcome.expect("initialization should return an error instead of panicking");
|
||||||
|
let err = match result {
|
||||||
|
Ok(_) => panic!("endpoint without host must be rejected"),
|
||||||
|
Err(err) => err,
|
||||||
|
};
|
||||||
|
assert!(err.to_string().contains("host"), "expected host validation error, got: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user