mirror of
https://github.com/rustfs/rustfs.git
synced 2026-09-06 20:19:14 +00:00
test(rpc): observe bootstrap CAS during fresh startup
This commit is contained in:
+1
-1
@@ -68,7 +68,7 @@ license = []
|
||||
io-scheduler-debug = [] # Enable debug information in I/O scheduler
|
||||
tracing-chunk-debug = [] # Enable per-chunk tracing in data plane (high noise, for debugging only)
|
||||
full = ["metrics-gpu", "ftps", "swift", "webdav", "sftp", "pyroscope", "gcs"]
|
||||
e2e-test-hooks = []
|
||||
e2e-test-hooks = ["rustfs-ecstore/e2e-test-hooks"]
|
||||
# Shortens Connect credentials only in debug E2E builds.
|
||||
connect-e2e-short-credentials = []
|
||||
# Builds the dedicated rustfs-cli-e2e target with a build-time public enrollment root.
|
||||
|
||||
@@ -62,6 +62,29 @@ fn emit_fatal_stderr(context: &str, error: impl std::fmt::Display) {
|
||||
}
|
||||
|
||||
async fn async_main() -> Result<()> {
|
||||
#[cfg(feature = "e2e-test-hooks")]
|
||||
if let Ok(nonce) = std::env::var("RUSTFS_E2E_STARTUP_CAS_PROBE") {
|
||||
let nonce = uuid::Uuid::parse_str(&nonce).map_err(Error::other)?;
|
||||
// This precedes CLI parsing and observability, including `--help`.
|
||||
println!(
|
||||
"RUSTFS_E2E_STARTUP_CAS {}",
|
||||
serde_json::json!({
|
||||
"kind": "capability", "schema": "fresh-startup-cas/v1", "nonce": nonce,
|
||||
})
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
#[cfg(feature = "e2e-test-hooks")]
|
||||
if let Ok(nonce) = std::env::var("RUSTFS_E2E_STARTUP_CAS_NONCE") {
|
||||
let nonce = uuid::Uuid::parse_str(&nonce).map_err(Error::other)?;
|
||||
let line = format!(
|
||||
"RUSTFS_E2E_STARTUP_CAS {}\n",
|
||||
serde_json::json!({
|
||||
"kind": "observer-ready", "nonce": nonce, "pid": std::process::id(),
|
||||
})
|
||||
);
|
||||
let _ = std::io::Write::write_all(&mut std::io::stderr().lock(), line.as_bytes());
|
||||
}
|
||||
hotpath::tokio_runtime!();
|
||||
|
||||
// Log container resource detection early in startup
|
||||
@@ -160,6 +183,33 @@ async fn run(config: Config) -> Result<()> {
|
||||
shutdown_token: ctx,
|
||||
} = init_startup_storage_runtime(server_addr, &endpoint_pools, readiness.clone(), instance_ctx).await?;
|
||||
|
||||
#[cfg(feature = "e2e-test-hooks")]
|
||||
if let Ok(nonce) = std::env::var("RUSTFS_E2E_STARTUP_CAS_NONCE") {
|
||||
let nonce = uuid::Uuid::parse_str(&nonce).map_err(Error::other)?;
|
||||
let release = std::path::PathBuf::from(
|
||||
std::env::var_os("RUSTFS_E2E_STARTUP_CAS_RELEASE")
|
||||
.ok_or_else(|| Error::other("startup CAS fixture requires a release path"))?,
|
||||
);
|
||||
if server_ctx.installed_object_store().is_some() {
|
||||
return Err(Error::other("startup CAS gate reached an installed slot"));
|
||||
}
|
||||
let line = format!(
|
||||
"RUSTFS_E2E_STARTUP_CAS {}\n",
|
||||
serde_json::json!({
|
||||
"kind": "gate", "nonce": nonce, "pid": std::process::id(), "slot_installed": false,
|
||||
})
|
||||
);
|
||||
let _ = std::io::Write::write_all(&mut std::io::stderr().lock(), line.as_bytes());
|
||||
tokio::time::timeout(std::time::Duration::from_secs(180), async {
|
||||
while !tokio::fs::try_exists(&release).await? {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(25)).await;
|
||||
}
|
||||
Ok::<_, Error>(())
|
||||
})
|
||||
.await
|
||||
.map_err(|_| Error::other("startup CAS gate release timed out"))??;
|
||||
}
|
||||
|
||||
let capacity_tasks = crate::capacity::capacity_integration::init_capacity_management_managed().await;
|
||||
|
||||
let service_runtime = init_startup_runtime_services(
|
||||
|
||||
@@ -39,6 +39,29 @@ use tonic::{Request, Response, Status};
|
||||
use tracing::debug;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[cfg(feature = "e2e-test-hooks")]
|
||||
fn startup_cas_rename_observation(
|
||||
target: &LocalMutationTarget,
|
||||
request: &RenameDataRequest,
|
||||
file_info: &FileInfo,
|
||||
) -> Option<serde_json::Value> {
|
||||
use sha2::{Digest, Sha256};
|
||||
if request.dst_volume != ".rustfs.sys" || !matches!(request.dst_path.as_str(), "pool.bin" | "pool.bin.identity") {
|
||||
return None;
|
||||
}
|
||||
let nonce = uuid::Uuid::parse_str(&std::env::var("RUSTFS_E2E_STARTUP_CAS_NONCE").ok()?).ok()?;
|
||||
let body = rustfs_protos::canonical_rename_data_request_body(request).ok()?;
|
||||
Some(serde_json::json!({
|
||||
"kind": "receiver", "nonce": nonce, "pid": std::process::id(),
|
||||
"target": match target { LocalMutationTarget::Ready(_) => "ready", LocalMutationTarget::Bootstrap(_) => "bootstrap", LocalMutationTarget::Unbound => "unbound" },
|
||||
"disk": request.disk, "src_volume": request.src_volume, "src_path": request.src_path,
|
||||
"dst_volume": request.dst_volume, "dst_path": request.dst_path,
|
||||
"body_sha256": format!("{:x}", Sha256::digest(body)),
|
||||
"etag": file_info.metadata.get("etag"),
|
||||
"mod_time": file_info.mod_time.map(|time| time.unix_timestamp_nanos().to_string()),
|
||||
}))
|
||||
}
|
||||
|
||||
impl LocalMutationTarget {
|
||||
async fn rename_local_data(
|
||||
&self,
|
||||
@@ -1275,7 +1298,9 @@ impl NodeService {
|
||||
Some(token)
|
||||
};
|
||||
let request_decoded_from_msgpack = decoded_file_info.from_msgpack;
|
||||
match target
|
||||
#[cfg(feature = "e2e-test-hooks")]
|
||||
let observation = startup_cas_rename_observation(&target, &request, &decoded_file_info.value);
|
||||
let result = target
|
||||
.rename_local_data(
|
||||
&request.disk,
|
||||
(&request.src_volume, &request.src_path),
|
||||
@@ -1283,8 +1308,15 @@ impl NodeService {
|
||||
(&request.dst_volume, &request.dst_path),
|
||||
scanner_publication_lease_token,
|
||||
)
|
||||
.await
|
||||
{
|
||||
.await;
|
||||
#[cfg(feature = "e2e-test-hooks")]
|
||||
if let Some(mut observation) = observation {
|
||||
observation["ok"] = serde_json::json!(result.is_ok());
|
||||
observation["error"] = serde_json::json!(result.as_ref().err().map(ToString::to_string));
|
||||
let line = format!("RUSTFS_E2E_STARTUP_CAS {observation}\n");
|
||||
let _ = std::io::Write::write_all(&mut std::io::stderr().lock(), line.as_bytes());
|
||||
}
|
||||
match result {
|
||||
Ok(rename_data_resp) => match encode_rename_data_response_payloads(&rename_data_resp, request_decoded_from_msgpack) {
|
||||
Ok((rename_data_resp, rename_data_resp_bin)) => Ok(Response::new(RenameDataResponse {
|
||||
success: true,
|
||||
|
||||
Reference in New Issue
Block a user