mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-18 18:46:17 +00:00
fix(rpc): authenticate non-disk mutation bodies (#5425)
This commit is contained in:
@@ -443,6 +443,16 @@ pub fn set_tonic_canonical_body_digest<T>(request: &mut tonic::Request<T>, canon
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_tonic_mutation_body_digest<T: rustfs_protos::CanonicalMutationBody>(
|
||||
request: &mut tonic::Request<T>,
|
||||
) -> std::io::Result<()> {
|
||||
let canonical_body = request
|
||||
.get_ref()
|
||||
.canonical_body()
|
||||
.map_err(|_| std::io::Error::other("RPC mutation body length cannot be represented"))?;
|
||||
set_tonic_canonical_body_digest(request, &canonical_body)
|
||||
}
|
||||
|
||||
pub fn verify_tonic_canonical_body_digest<T>(request: &tonic::Request<T>, canonical_body: &[u8]) -> std::io::Result<()> {
|
||||
let version = request
|
||||
.metadata()
|
||||
@@ -466,7 +476,7 @@ pub fn verify_tonic_canonical_body_digest<T>(request: &tonic::Request<T>, canoni
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verify a mutating disk RPC's canonical body digest with a rolling-upgrade fallback.
|
||||
/// Verify a mutating RPC's canonical body digest with a rolling-upgrade fallback.
|
||||
///
|
||||
/// When the request carries a real (non-`UNSIGNED-PAYLOAD`) content SHA-256 it is verified exactly
|
||||
/// like [`verify_tonic_canonical_body_digest`]. The digest value is a member of the signed v2
|
||||
@@ -497,7 +507,7 @@ fn verify_tonic_mutation_body_digest_with_strictness<T>(
|
||||
Some(digest) if digest != UNSIGNED_PAYLOAD => verify_tonic_canonical_body_digest(request, canonical_body),
|
||||
_ => {
|
||||
// RUSTFS_COMPAT_TODO(disk-mutation-body-digest): accept digestless peers during rolling upgrades. Remove after the
|
||||
// minimum supported RustFS peer version body-binds every mutating disk RPC.
|
||||
// minimum supported RustFS peer version body-binds every mutating RPC.
|
||||
if strict {
|
||||
return Err(std::io::Error::other("RPC mutation requires a body-bound v2 signature"));
|
||||
}
|
||||
@@ -677,11 +687,28 @@ mod tests {
|
||||
use crate::cluster::rpc::context_propagation::REQUEST_ID_HEADER;
|
||||
use crate::runtime::sources as runtime_sources;
|
||||
use http::{HeaderMap, Method};
|
||||
use rustfs_protos::{
|
||||
CanonicalMutationBody as _, PEER_RESTDRY_RUN, PEER_RESTSIGNAL, PEER_RESTSUB_SYS,
|
||||
proto_gen::node_service::{Mss, SignalServiceRequest},
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::io::{self, Write};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use time::OffsetDateTime;
|
||||
use tracing_subscriber::fmt::MakeWriter;
|
||||
|
||||
fn signal_service_request(signal: &str, sub_system: &str, dry_run: &str) -> SignalServiceRequest {
|
||||
SignalServiceRequest {
|
||||
vars: Some(Mss {
|
||||
value: HashMap::from([
|
||||
(PEER_RESTSIGNAL.to_string(), signal.to_string()),
|
||||
(PEER_RESTSUB_SYS.to_string(), sub_system.to_string()),
|
||||
(PEER_RESTDRY_RUN.to_string(), dry_run.to_string()),
|
||||
]),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
struct CapturedLogs {
|
||||
buffer: Arc<Mutex<Vec<u8>>>,
|
||||
@@ -1596,6 +1623,72 @@ mod tests {
|
||||
assert_eq!(stripped.to_string(), "RPC content SHA-256 mismatch");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signal_service_mutation_contract_rejects_tampering_and_replay() {
|
||||
ensure_test_rpc_secret();
|
||||
let body = signal_service_request("2", "scanner", "false")
|
||||
.canonical_body()
|
||||
.expect("small signal request should encode");
|
||||
let mut request = tonic::Request::new(());
|
||||
set_tonic_canonical_body_digest(&mut request, &body).expect("canonical body digest should be attached");
|
||||
let content_sha256 = request
|
||||
.metadata()
|
||||
.get(RPC_CONTENT_SHA256_HEADER)
|
||||
.and_then(|value| value.to_str().ok());
|
||||
let headers = gen_tonic_signature_headers("node-a:9000", "node_service.NodeService", "SignalService", content_sha256)
|
||||
.expect("body-bound auth headers should build");
|
||||
request.metadata_mut().as_mut().extend(headers.clone());
|
||||
|
||||
assert!(
|
||||
verify_tonic_rpc_signature("node-a:9000", "/node_service.NodeService/SignalService", &headers).is_ok(),
|
||||
"the first body-bound signal request must authenticate"
|
||||
);
|
||||
assert!(verify_tonic_mutation_body_digest(&request, &body).is_ok());
|
||||
|
||||
let tampered = signal_service_request("1", "scanner", "false")
|
||||
.canonical_body()
|
||||
.expect("small signal request should encode");
|
||||
let error = verify_tonic_mutation_body_digest(&request, &tampered)
|
||||
.expect_err("changing the signal must invalidate the signed digest");
|
||||
assert_eq!(error.to_string(), "RPC content SHA-256 mismatch");
|
||||
|
||||
let replay = verify_tonic_rpc_signature("node-a:9000", "/node_service.NodeService/SignalService", &headers)
|
||||
.expect_err("reusing the signal nonce must fail");
|
||||
assert_eq!(replay.to_string(), "RPC request replay detected");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial(rpc_body_digest_fallback_counter)]
|
||||
fn signal_service_mutation_contract_preserves_rollout_fallback_and_strictness() {
|
||||
let body = signal_service_request("2", "scanner", "false")
|
||||
.canonical_body()
|
||||
.expect("small signal request should encode");
|
||||
let before = global_internode_metrics().snapshot().body_digest_fallback_total;
|
||||
let digestless = tonic::Request::new(());
|
||||
|
||||
assert!(
|
||||
verify_tonic_mutation_body_digest_with_strictness(&digestless, &body, false).is_ok(),
|
||||
"old peers must remain compatible while the rollout gate is open"
|
||||
);
|
||||
assert_eq!(
|
||||
global_internode_metrics().snapshot().body_digest_fallback_total,
|
||||
before + 1,
|
||||
"accepted digestless signal requests must be visible in the fallback metric"
|
||||
);
|
||||
|
||||
let error = verify_tonic_mutation_body_digest_with_strictness(&digestless, &body, true)
|
||||
.expect_err("strict mode must reject a digestless signal request");
|
||||
assert_eq!(error.to_string(), "RPC mutation requires a body-bound v2 signature");
|
||||
|
||||
let mut bound = tonic::Request::new(());
|
||||
set_tonic_canonical_body_digest(&mut bound, &body).expect("canonical body digest should be attached");
|
||||
bound
|
||||
.metadata_mut()
|
||||
.as_mut()
|
||||
.insert(RPC_AUTH_VERSION_HEADER, HeaderValue::from_static(RPC_AUTH_VERSION_V2));
|
||||
assert!(verify_tonic_mutation_body_digest_with_strictness(&bound, &body, true).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nonce_cache_rejects_replay_after_wall_clock_regression() {
|
||||
let now = Instant::now();
|
||||
|
||||
@@ -30,9 +30,9 @@ pub use client::{
|
||||
};
|
||||
pub use http_auth::{
|
||||
TONIC_RPC_PREFIX, build_auth_headers, gen_signature_headers, gen_tonic_signature_headers, normalize_tonic_rpc_audience,
|
||||
set_tonic_canonical_body_digest, sign_ns_scanner_capability, sign_tonic_rpc_response_proof, verify_ns_scanner_capability,
|
||||
verify_rpc_signature, verify_tonic_canonical_body_digest, verify_tonic_mutation_body_digest, verify_tonic_rpc_response_proof,
|
||||
verify_tonic_rpc_signature,
|
||||
set_tonic_canonical_body_digest, set_tonic_mutation_body_digest, sign_ns_scanner_capability, sign_tonic_rpc_response_proof,
|
||||
verify_ns_scanner_capability, verify_rpc_signature, verify_tonic_canonical_body_digest, verify_tonic_mutation_body_digest,
|
||||
verify_tonic_rpc_response_proof, verify_tonic_rpc_signature,
|
||||
};
|
||||
#[cfg(test)]
|
||||
pub(crate) use internode_data_transport::TcpHttpInternodeDataTransport;
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::cluster::rpc::client::{
|
||||
TonicInterceptor, embedded_tonic_status, gen_tonic_signature_interceptor, heal_control_time_out_client,
|
||||
is_network_like_status, message_has_network_needle, node_service_time_out_client, tier_mutation_control_time_out_client,
|
||||
};
|
||||
use crate::cluster::rpc::{set_tonic_canonical_body_digest, verify_tonic_rpc_response_proof};
|
||||
use crate::cluster::rpc::{set_tonic_canonical_body_digest, set_tonic_mutation_body_digest, verify_tonic_rpc_response_proof};
|
||||
use crate::error::{Error, Result};
|
||||
use crate::storage_api_contracts::internode::{
|
||||
SCANNER_ACTIVITY_LEGACY_PROTOCOL_VERSION, SCANNER_ACTIVITY_PREVIOUS_PROTOCOL_VERSION, SCANNER_ACTIVITY_PROTOCOL_VERSION,
|
||||
@@ -50,6 +50,7 @@ use rustfs_protos::proto_gen::node_service::{
|
||||
TierMutationPeerState, TierMutationPrepareRequest, node_service_client::NodeServiceClient,
|
||||
tier_mutation_control_service_client::TierMutationControlServiceClient,
|
||||
};
|
||||
pub use rustfs_protos::{PEER_RESTDRY_RUN, PEER_RESTSIGNAL, PEER_RESTSUB_SYS};
|
||||
use rustfs_protos::{TierMutationRpcPhase, evict_failed_connection};
|
||||
use rustfs_utils::XHost;
|
||||
use serde::{Deserialize, Serialize as _};
|
||||
@@ -69,9 +70,6 @@ use tonic::transport::Channel;
|
||||
use tracing::{debug, info, warn};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub const PEER_RESTSIGNAL: &str = "signal";
|
||||
pub const PEER_RESTSUB_SYS: &str = "sub-sys";
|
||||
pub const PEER_RESTDRY_RUN: &str = "dry-run";
|
||||
pub const SERVICE_SIGNAL_REFRESH_CONFIG: u64 = 1;
|
||||
pub const SERVICE_SIGNAL_RELOAD_DYNAMIC: u64 = 2;
|
||||
const BACKGROUND_HEAL_STATUS_MAX_MESSAGE_SIZE: usize = 64 * 1024;
|
||||
@@ -1160,10 +1158,11 @@ impl PeerRestClient {
|
||||
self.finalize_result(
|
||||
async {
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(LoadBucketMetadataRequest {
|
||||
let mut request = Request::new(LoadBucketMetadataRequest {
|
||||
bucket: bucket.to_string(),
|
||||
scanner_maintenance_change,
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.load_bucket_metadata(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1183,9 +1182,10 @@ impl PeerRestClient {
|
||||
self.finalize_result(
|
||||
async {
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(DeleteBucketMetadataRequest {
|
||||
let mut request = Request::new(DeleteBucketMetadataRequest {
|
||||
bucket: bucket.to_string(),
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.delete_bucket_metadata(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1205,9 +1205,10 @@ impl PeerRestClient {
|
||||
self.finalize_result(
|
||||
async {
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(DeletePolicyRequest {
|
||||
let mut request = Request::new(DeletePolicyRequest {
|
||||
policy_name: policy.to_string(),
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.delete_policy(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1227,9 +1228,10 @@ impl PeerRestClient {
|
||||
self.finalize_result(
|
||||
async {
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(LoadPolicyRequest {
|
||||
let mut request = Request::new(LoadPolicyRequest {
|
||||
policy_name: policy.to_string(),
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.load_policy(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1249,11 +1251,12 @@ impl PeerRestClient {
|
||||
self.finalize_result(
|
||||
async {
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(LoadPolicyMappingRequest {
|
||||
let mut request = Request::new(LoadPolicyMappingRequest {
|
||||
user_or_group: user_or_group.to_string(),
|
||||
user_type,
|
||||
is_group,
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.load_policy_mapping(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1273,9 +1276,10 @@ impl PeerRestClient {
|
||||
self.finalize_result(
|
||||
async {
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(DeleteUserRequest {
|
||||
let mut request = Request::new(DeleteUserRequest {
|
||||
access_key: access_key.to_string(),
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.delete_user(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1295,9 +1299,10 @@ impl PeerRestClient {
|
||||
self.finalize_result(
|
||||
async {
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(DeleteServiceAccountRequest {
|
||||
let mut request = Request::new(DeleteServiceAccountRequest {
|
||||
access_key: access_key.to_string(),
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.delete_service_account(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1317,10 +1322,11 @@ impl PeerRestClient {
|
||||
self.finalize_result(
|
||||
async {
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(LoadUserRequest {
|
||||
let mut request = Request::new(LoadUserRequest {
|
||||
access_key: access_key.to_string(),
|
||||
temp,
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.load_user(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1340,9 +1346,10 @@ impl PeerRestClient {
|
||||
self.finalize_result(
|
||||
async {
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(LoadServiceAccountRequest {
|
||||
let mut request = Request::new(LoadServiceAccountRequest {
|
||||
access_key: access_key.to_string(),
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.load_service_account(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1362,9 +1369,10 @@ impl PeerRestClient {
|
||||
self.finalize_result(
|
||||
async {
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(LoadGroupRequest {
|
||||
let mut request = Request::new(LoadGroupRequest {
|
||||
group: group.to_string(),
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.load_group(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1384,7 +1392,8 @@ impl PeerRestClient {
|
||||
self.finalize_result(
|
||||
async {
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(ReloadSiteReplicationConfigRequest {});
|
||||
let mut request = Request::new(ReloadSiteReplicationConfigRequest {});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.reload_site_replication_config(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1408,9 +1417,10 @@ impl PeerRestClient {
|
||||
vars.insert(PEER_RESTSIGNAL.to_string(), sig.to_string());
|
||||
vars.insert(PEER_RESTSUB_SYS.to_string(), sub_sys.to_string());
|
||||
vars.insert(PEER_RESTDRY_RUN.to_string(), dry_run.to_string());
|
||||
let request = Request::new(SignalServiceRequest {
|
||||
let mut request = Request::new(SignalServiceRequest {
|
||||
vars: Some(Mss { value: vars }),
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.signal_service(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1479,7 +1489,8 @@ impl PeerRestClient {
|
||||
self.finalize_result(
|
||||
async {
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(ReloadPoolMetaRequest {});
|
||||
let mut request = Request::new(ReloadPoolMetaRequest {});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.reload_pool_meta(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1500,9 +1511,10 @@ impl PeerRestClient {
|
||||
self.finalize_result(
|
||||
async {
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(StopRebalanceRequest {
|
||||
let mut request = Request::new(StopRebalanceRequest {
|
||||
expected_rebalance_id: expected_rebalance_id.unwrap_or_default().to_string(),
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.stop_rebalance(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1523,7 +1535,8 @@ impl PeerRestClient {
|
||||
self.finalize_result(
|
||||
async {
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(LoadRebalanceMetaRequest { start_rebalance });
|
||||
let mut request = Request::new(LoadRebalanceMetaRequest { start_rebalance });
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.load_rebalance_meta(request).await?.into_inner();
|
||||
|
||||
@@ -1562,7 +1575,8 @@ impl PeerRestClient {
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(StartDecommissionRequest { pool_indices });
|
||||
let mut request = Request::new(StartDecommissionRequest { pool_indices });
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.start_decommission(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1585,7 +1599,8 @@ impl PeerRestClient {
|
||||
let pool_index = u32::try_from(pool_index)
|
||||
.map_err(|_| Error::other(format!("decommission pool index {pool_index} exceeds RPC range")))?;
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(CancelDecommissionRequest { pool_index });
|
||||
let mut request = Request::new(CancelDecommissionRequest { pool_index });
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.cancel_decommission(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1608,7 +1623,8 @@ impl PeerRestClient {
|
||||
let pool_index = u32::try_from(pool_index)
|
||||
.map_err(|_| Error::other(format!("decommission pool index {pool_index} exceeds RPC range")))?;
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(ClearDecommissionRequest { pool_index });
|
||||
let mut request = Request::new(ClearDecommissionRequest { pool_index });
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
|
||||
let response = client.clear_decommission(request).await?.into_inner();
|
||||
if !response.success {
|
||||
@@ -1661,6 +1677,9 @@ impl PeerRestClient {
|
||||
Err(err) => return tier_config_reload_connection_outcome(err),
|
||||
};
|
||||
let mut request = Request::new(LoadTransitionTierConfigRequest {});
|
||||
if let Err(err) = set_tonic_mutation_body_digest(&mut request) {
|
||||
return TierConfigReloadOutcome::Terminal(Error::other(err));
|
||||
}
|
||||
request.set_timeout(rustfs_protos::heal_control_execution_timeout());
|
||||
|
||||
let response = match client.load_transition_tier_config(request).await {
|
||||
|
||||
@@ -16,6 +16,7 @@ use crate::bucket::metadata_sys;
|
||||
use crate::cluster::rpc::client::{
|
||||
TonicInterceptor, gen_tonic_signature_interceptor, is_network_like_disk_error, node_service_time_out_client,
|
||||
};
|
||||
use crate::cluster::rpc::set_tonic_mutation_body_digest;
|
||||
use crate::disk::error::DiskError;
|
||||
use crate::disk::error::{Error, Result};
|
||||
use crate::disk::error_reduce::{BUCKET_OP_IGNORED_ERRS, is_all_buckets_not_found, reduce_write_quorum_errs};
|
||||
@@ -930,10 +931,11 @@ impl PeerS3Client for RemotePeerS3Client {
|
||||
|| async {
|
||||
let options: String = serde_json::to_string(opts)?;
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(HealBucketRequest {
|
||||
let mut request = Request::new(HealBucketRequest {
|
||||
bucket: bucket.to_string(),
|
||||
options,
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
let response = client.heal_bucket(request).await?.into_inner();
|
||||
if !response.success {
|
||||
return if let Some(err) = response.error {
|
||||
@@ -986,10 +988,11 @@ impl PeerS3Client for RemotePeerS3Client {
|
||||
|| async {
|
||||
let options = serde_json::to_string(opts)?;
|
||||
let mut client = self.get_client().await?;
|
||||
let request = Request::new(MakeBucketRequest {
|
||||
let mut request = Request::new(MakeBucketRequest {
|
||||
name: bucket.to_string(),
|
||||
options,
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
let response = client.make_bucket(request).await?.into_inner();
|
||||
|
||||
if !response.success {
|
||||
@@ -1040,10 +1043,11 @@ impl PeerS3Client for RemotePeerS3Client {
|
||||
let options = serde_json::to_string(opts)?;
|
||||
let mut client = self.get_client().await?;
|
||||
|
||||
let request = Request::new(DeleteBucketRequest {
|
||||
let mut request = Request::new(DeleteBucketRequest {
|
||||
bucket: bucket.to_string(),
|
||||
options,
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut request)?;
|
||||
let response = client.delete_bucket(request).await?.into_inner();
|
||||
if !response.success {
|
||||
return if let Some(err) = response.error {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::cluster::rpc::client::{TonicInterceptor, gen_tonic_signature_interceptor, node_service_time_out_client};
|
||||
use crate::cluster::rpc::set_tonic_mutation_body_digest;
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use rustfs_lock::{
|
||||
@@ -313,10 +314,11 @@ impl LockClient for RemoteClient {
|
||||
info!("remote acquire_exclusive for {}", request.resource);
|
||||
let mut client = self.get_client().await?;
|
||||
let resource_summary = request.resource.to_string();
|
||||
let req = Request::new(GenerallyLockRequest {
|
||||
let mut req = Request::new(GenerallyLockRequest {
|
||||
args: serde_json::to_string(&request)
|
||||
.map_err(|e| LockError::internal(format!("Failed to serialize request: {e}")))?,
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut req)?;
|
||||
|
||||
let resp = match self.execute_rpc("lock", &resource_summary, client.lock(req)).await {
|
||||
Ok(resp) => resp.into_inner(),
|
||||
@@ -347,7 +349,7 @@ impl LockClient for RemoteClient {
|
||||
|
||||
let mut client = self.get_client().await?;
|
||||
let resource_summary = Self::summarize_resources(requests);
|
||||
let req = Request::new(BatchGenerallyLockRequest {
|
||||
let mut req = Request::new(BatchGenerallyLockRequest {
|
||||
args: requests
|
||||
.iter()
|
||||
.map(|request| {
|
||||
@@ -355,6 +357,7 @@ impl LockClient for RemoteClient {
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?,
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut req)?;
|
||||
|
||||
let resp = match self
|
||||
.execute_rpc("lock_batch", &resource_summary, client.lock_batch(req))
|
||||
@@ -395,7 +398,8 @@ impl LockClient for RemoteClient {
|
||||
.map_err(|e| LockError::internal(format!("Failed to serialize request: {e}")))?;
|
||||
let mut client = self.get_client().await?;
|
||||
let resource_summary = unlock_request.resource.to_string();
|
||||
let req = Request::new(GenerallyLockRequest { args: request_string });
|
||||
let mut req = Request::new(GenerallyLockRequest { args: request_string });
|
||||
set_tonic_mutation_body_digest(&mut req)?;
|
||||
let resp = self
|
||||
.execute_rpc("release", &resource_summary, client.un_lock(req))
|
||||
.await?
|
||||
@@ -414,7 +418,7 @@ impl LockClient for RemoteClient {
|
||||
let unlock_requests = lock_ids.iter().map(Self::create_unlock_request).collect::<Vec<_>>();
|
||||
let mut client = self.get_client().await?;
|
||||
let resource_summary = Self::summarize_resources(&unlock_requests);
|
||||
let req = Request::new(BatchGenerallyLockRequest {
|
||||
let mut req = Request::new(BatchGenerallyLockRequest {
|
||||
args: unlock_requests
|
||||
.iter()
|
||||
.map(|request| {
|
||||
@@ -422,6 +426,7 @@ impl LockClient for RemoteClient {
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?,
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut req)?;
|
||||
|
||||
let resp = self
|
||||
.execute_rpc("release_batch", &resource_summary, client.un_lock_batch(req))
|
||||
@@ -440,10 +445,11 @@ impl LockClient for RemoteClient {
|
||||
let refresh_request = Self::create_unlock_request(lock_id);
|
||||
let mut client = self.get_client().await?;
|
||||
let resource_summary = refresh_request.resource.to_string();
|
||||
let req = Request::new(GenerallyLockRequest {
|
||||
let mut req = Request::new(GenerallyLockRequest {
|
||||
args: serde_json::to_string(&refresh_request)
|
||||
.map_err(|e| LockError::internal(format!("Failed to serialize request: {e}")))?,
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut req)?;
|
||||
let resp = self
|
||||
.execute_rpc("refresh", &resource_summary, client.refresh(req))
|
||||
.await?
|
||||
@@ -459,10 +465,11 @@ impl LockClient for RemoteClient {
|
||||
let force_request = Self::create_unlock_request(lock_id);
|
||||
let mut client = self.get_client().await?;
|
||||
let resource_summary = force_request.resource.to_string();
|
||||
let req = Request::new(GenerallyLockRequest {
|
||||
let mut req = Request::new(GenerallyLockRequest {
|
||||
args: serde_json::to_string(&force_request)
|
||||
.map_err(|e| LockError::internal(format!("Failed to serialize request: {e}")))?,
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut req)?;
|
||||
let resp = self
|
||||
.execute_rpc("force_release", &resource_summary, client.force_un_lock(req))
|
||||
.await?
|
||||
@@ -483,10 +490,11 @@ impl LockClient for RemoteClient {
|
||||
let mut client = self.get_client().await?;
|
||||
|
||||
// Try to acquire a very short-lived lock to test availability
|
||||
let req = Request::new(GenerallyLockRequest {
|
||||
let mut req = Request::new(GenerallyLockRequest {
|
||||
args: serde_json::to_string(&status_request)
|
||||
.map_err(|e| LockError::internal(format!("Failed to serialize request: {e}")))?,
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut req)?;
|
||||
|
||||
// Try exclusive lock first with very short timeout
|
||||
let resp = match self.execute_rpc("check_status", &resource_summary, client.lock(req)).await {
|
||||
@@ -497,10 +505,11 @@ impl LockClient for RemoteClient {
|
||||
if resp.success {
|
||||
// If we successfully acquired the lock, the resource was free.
|
||||
// Immediately release it on a best-effort basis.
|
||||
let release_req = Request::new(GenerallyLockRequest {
|
||||
let mut release_req = Request::new(GenerallyLockRequest {
|
||||
args: serde_json::to_string(&status_request)
|
||||
.map_err(|e| LockError::internal(format!("Failed to serialize request: {e}")))?,
|
||||
});
|
||||
set_tonic_mutation_body_digest(&mut release_req)?;
|
||||
let _ = self
|
||||
.execute_rpc("check_status_release", &resource_summary, client.un_lock(release_req))
|
||||
.await;
|
||||
|
||||
Reference in New Issue
Block a user