refactor: Reimplement bucket replication system with enhanced architecture (#590)

* feat:refactor replication

* use aws sdk for replication client

* refactor/replication

* merge main

* fix lifecycle test
This commit is contained in:
weisd
2025-09-26 14:27:53 +08:00
committed by GitHub
parent 9b029d18b2
commit 90f21a9102
91 changed files with 10532 additions and 4917 deletions
+9 -13
View File
@@ -111,7 +111,7 @@ impl LocalKmsClient {
/// Get the file path for a master key
fn master_key_path(&self, key_id: &str) -> PathBuf {
self.config.key_dir.join(format!("{}.key", key_id))
self.config.key_dir.join(format!("{key_id}.key"))
}
/// Load a master key from disk
@@ -334,12 +334,11 @@ impl KmsClient for LocalKmsClient {
if let Some(actual_value) = envelope.encryption_context.get(key) {
if actual_value != expected_value {
return Err(KmsError::context_mismatch(format!(
"Context mismatch for key '{}': expected '{}', got '{}'",
key, expected_value, actual_value
"Context mismatch for key '{key}': expected '{expected_value}', got '{actual_value}'"
)));
}
} else {
return Err(KmsError::context_mismatch(format!("Missing context key '{}'", key)));
return Err(KmsError::context_mismatch(format!("Missing context key '{key}'")));
}
}
}
@@ -720,14 +719,14 @@ impl KmsBackend for LocalKmsBackend {
.client
.load_master_key(key_id)
.await
.map_err(|_| crate::error::KmsError::key_not_found(format!("Key {} not found", key_id)))?;
.map_err(|_| crate::error::KmsError::key_not_found(format!("Key {key_id} not found")))?;
let (deletion_date_str, deletion_date_dt) = if request.force_immediate.unwrap_or(false) {
// For immediate deletion, actually delete the key from filesystem
let key_path = self.client.master_key_path(key_id);
tokio::fs::remove_file(&key_path)
.await
.map_err(|e| crate::error::KmsError::internal_error(format!("Failed to delete key file: {}", e)))?;
.map_err(|e| crate::error::KmsError::internal_error(format!("Failed to delete key file: {e}")))?;
// Remove from cache
let mut cache = self.client.key_cache.write().await;
@@ -773,9 +772,9 @@ impl KmsBackend for LocalKmsBackend {
let key_path = self.client.master_key_path(key_id);
let content = tokio::fs::read(&key_path)
.await
.map_err(|e| crate::error::KmsError::internal_error(format!("Failed to read key file: {}", e)))?;
.map_err(|e| crate::error::KmsError::internal_error(format!("Failed to read key file: {e}")))?;
let stored_key: crate::backends::local::StoredMasterKey = serde_json::from_slice(&content)
.map_err(|e| crate::error::KmsError::internal_error(format!("Failed to parse stored key: {}", e)))?;
.map_err(|e| crate::error::KmsError::internal_error(format!("Failed to parse stored key: {e}")))?;
// Decrypt the existing key material to preserve it
let existing_key_material = if let Some(ref cipher) = self.client.master_cipher {
@@ -821,13 +820,10 @@ impl KmsBackend for LocalKmsBackend {
.client
.load_master_key(key_id)
.await
.map_err(|_| crate::error::KmsError::key_not_found(format!("Key {} not found", key_id)))?;
.map_err(|_| crate::error::KmsError::key_not_found(format!("Key {key_id} not found")))?;
if master_key.status != KeyStatus::PendingDeletion {
return Err(crate::error::KmsError::invalid_key_state(format!(
"Key {} is not pending deletion",
key_id
)));
return Err(crate::error::KmsError::invalid_key_state(format!("Key {key_id} is not pending deletion")));
}
// Cancel the deletion by resetting the state
+9 -12
View File
@@ -89,10 +89,10 @@ impl VaultKmsClient {
let settings = settings_builder
.build()
.map_err(|e| KmsError::backend_error(format!("Failed to build Vault client settings: {}", e)))?;
.map_err(|e| KmsError::backend_error(format!("Failed to build Vault client settings: {e}")))?;
let client =
VaultClient::new(settings).map_err(|e| KmsError::backend_error(format!("Failed to create Vault client: {}", e)))?;
VaultClient::new(settings).map_err(|e| KmsError::backend_error(format!("Failed to create Vault client: {e}")))?;
info!("Successfully connected to Vault at {}", config.address);
@@ -144,7 +144,7 @@ impl VaultKmsClient {
kv2::set(&self.client, &self.kv_mount, &path, key_data)
.await
.map_err(|e| KmsError::backend_error(format!("Failed to store key in Vault: {}", e)))?;
.map_err(|e| KmsError::backend_error(format!("Failed to store key in Vault: {e}")))?;
debug!("Stored key {} in Vault at path {}", key_id, path);
Ok(())
@@ -176,7 +176,7 @@ impl VaultKmsClient {
let secret: VaultKeyData = kv2::read(&self.client, &self.kv_mount, &path).await.map_err(|e| match e {
vaultrs::error::ClientError::ResponseWrapError => KmsError::key_not_found(key_id),
vaultrs::error::ClientError::APIError { code: 404, .. } => KmsError::key_not_found(key_id),
_ => KmsError::backend_error(format!("Failed to read key from Vault: {}", e)),
_ => KmsError::backend_error(format!("Failed to read key from Vault: {e}")),
})?;
debug!("Retrieved key {} from Vault, tags: {:?}", key_id, secret.tags);
@@ -200,7 +200,7 @@ impl VaultKmsClient {
debug!("Key path doesn't exist in Vault (404), returning empty list");
Ok(Vec::new())
}
Err(e) => Err(KmsError::backend_error(format!("Failed to list keys in Vault: {}", e))),
Err(e) => Err(KmsError::backend_error(format!("Failed to list keys in Vault: {e}"))),
}
}
@@ -214,7 +214,7 @@ impl VaultKmsClient {
.await
.map_err(|e| match e {
vaultrs::error::ClientError::APIError { code: 404, .. } => KmsError::key_not_found(key_id),
_ => KmsError::backend_error(format!("Failed to delete key metadata from Vault: {}", e)),
_ => KmsError::backend_error(format!("Failed to delete key metadata from Vault: {e}")),
})?;
debug!("Permanently deleted key {} metadata from Vault at path {}", key_id, path);
@@ -649,7 +649,7 @@ impl KmsBackend for VaultKmsBackend {
let mut key_metadata = match self.describe_key(describe_request).await {
Ok(response) => response.key_metadata,
Err(_) => {
return Err(crate::error::KmsError::key_not_found(format!("Key {} not found", key_id)));
return Err(crate::error::KmsError::key_not_found(format!("Key {key_id} not found")));
}
};
@@ -705,15 +705,12 @@ impl KmsBackend for VaultKmsBackend {
let mut key_metadata = match self.describe_key(describe_request).await {
Ok(response) => response.key_metadata,
Err(_) => {
return Err(crate::error::KmsError::key_not_found(format!("Key {} not found", key_id)));
return Err(crate::error::KmsError::key_not_found(format!("Key {key_id} not found")));
}
};
if key_metadata.key_state != KeyState::PendingDeletion {
return Err(crate::error::KmsError::invalid_key_state(format!(
"Key {} is not pending deletion",
key_id
)));
return Err(crate::error::KmsError::invalid_key_state(format!("Key {key_id} is not pending deletion")));
}
// Cancel the deletion by resetting the state
+1 -1
View File
@@ -315,7 +315,7 @@ impl KmsConfig {
config.backend = match backend_type.to_lowercase().as_str() {
"local" => KmsBackend::Local,
"vault" => KmsBackend::Vault,
_ => return Err(KmsError::configuration_error(format!("Unknown KMS backend: {}", backend_type))),
_ => return Err(KmsError::configuration_error(format!("Unknown KMS backend: {backend_type}"))),
};
}
+11 -12
View File
@@ -227,7 +227,7 @@ impl ObjectEncryptionService {
self.kms_manager
.create_key(create_req)
.await
.map_err(|e| KmsError::backend_error(format!("Failed to auto-create SSE-S3 key {}: {}", actual_key_id, e)))?;
.map_err(|e| KmsError::backend_error(format!("Failed to auto-create SSE-S3 key {actual_key_id}: {e}")))?;
}
} else {
// For SSE-KMS, key must exist
@@ -235,7 +235,7 @@ impl ObjectEncryptionService {
key_id: actual_key_id.to_string(),
};
self.kms_manager.describe_key(describe_req).await.map_err(|_| {
KmsError::invalid_operation(format!("SSE-KMS key '{}' not found. Please create it first.", actual_key_id))
KmsError::invalid_operation(format!("SSE-KMS key '{actual_key_id}' not found. Please create it first."))
})?;
}
@@ -250,7 +250,7 @@ impl ObjectEncryptionService {
.kms_manager
.generate_data_key(request)
.await
.map_err(|e| KmsError::backend_error(format!("Failed to generate data key: {}", e)))?;
.map_err(|e| KmsError::backend_error(format!("Failed to generate data key: {e}")))?;
let plaintext_key = data_key.plaintext_key;
@@ -325,7 +325,7 @@ impl ObjectEncryptionService {
.kms_manager
.decrypt(decrypt_request)
.await
.map_err(|e| KmsError::backend_error(format!("Failed to decrypt data key: {}", e)))?;
.map_err(|e| KmsError::backend_error(format!("Failed to decrypt data key: {e}")))?;
// Create cipher
let cipher = create_cipher(&algorithm, &decrypt_response.plaintext)?;
@@ -379,7 +379,7 @@ impl ObjectEncryptionService {
// Validate key MD5 if provided
if let Some(expected_md5) = customer_key_md5 {
let actual_md5 = md5::compute(customer_key);
let actual_md5_hex = format!("{:x}", actual_md5);
let actual_md5_hex = format!("{actual_md5:x}");
if actual_md5_hex != expected_md5.to_lowercase() {
return Err(KmsError::validation_error("Customer key MD5 mismatch"));
}
@@ -487,12 +487,11 @@ impl ObjectEncryptionService {
Some(actual_value) if actual_value == expected_value => continue,
Some(actual_value) => {
return Err(KmsError::context_mismatch(format!(
"Context mismatch for '{}': expected '{}', got '{}'",
key, expected_value, actual_value
"Context mismatch for '{key}': expected '{expected_value}', got '{actual_value}'"
)));
}
None => {
return Err(KmsError::context_mismatch(format!("Missing context key '{}'", key)));
return Err(KmsError::context_mismatch(format!("Missing context key '{key}'")));
}
}
}
@@ -562,13 +561,13 @@ impl ObjectEncryptionService {
.ok_or_else(|| KmsError::validation_error("Missing IV header"))?;
let iv = base64::engine::general_purpose::STANDARD
.decode(iv)
.map_err(|e| KmsError::validation_error(format!("Invalid IV: {}", e)))?;
.map_err(|e| KmsError::validation_error(format!("Invalid IV: {e}")))?;
let tag = if let Some(tag_str) = headers.get("x-rustfs-encryption-tag") {
Some(
base64::engine::general_purpose::STANDARD
.decode(tag_str)
.map_err(|e| KmsError::validation_error(format!("Invalid tag: {}", e)))?,
.map_err(|e| KmsError::validation_error(format!("Invalid tag: {e}")))?,
)
} else {
None
@@ -577,14 +576,14 @@ impl ObjectEncryptionService {
let encrypted_data_key = if let Some(key_str) = headers.get("x-rustfs-encryption-key") {
base64::engine::general_purpose::STANDARD
.decode(key_str)
.map_err(|e| KmsError::validation_error(format!("Invalid encrypted key: {}", e)))?
.map_err(|e| KmsError::validation_error(format!("Invalid encrypted key: {e}")))?
} else {
Vec::new() // Empty for SSE-C
};
let encryption_context = if let Some(context_str) = headers.get("x-rustfs-encryption-context") {
serde_json::from_str(context_str)
.map_err(|e| KmsError::validation_error(format!("Invalid encryption context: {}", e)))?
.map_err(|e| KmsError::validation_error(format!("Invalid encryption context: {e}")))?
} else {
HashMap::new()
};
+2 -2
View File
@@ -225,7 +225,7 @@ impl KmsError {
impl From<url::ParseError> for KmsError {
fn from(error: url::ParseError) -> Self {
Self::ConfigurationError {
message: format!("Invalid URL: {}", error),
message: format!("Invalid URL: {error}"),
}
}
}
@@ -233,7 +233,7 @@ impl From<url::ParseError> for KmsError {
impl From<reqwest::Error> for KmsError {
fn from(error: reqwest::Error) -> Self {
Self::BackendError {
message: format!("HTTP request failed: {}", error),
message: format!("HTTP request failed: {error}"),
}
}
}
+2 -2
View File
@@ -137,7 +137,7 @@ impl KmsServiceManager {
Ok(())
}
Err(e) => {
let err_msg = format!("Failed to create KMS backend: {}", e);
let err_msg = format!("Failed to create KMS backend: {e}");
error!("{}", err_msg);
let mut status = self.status.write().await;
*status = KmsServiceStatus::Error(err_msg.clone());
@@ -218,7 +218,7 @@ impl KmsServiceManager {
error!("KMS health check error: {}", e);
// Update status to error
let mut status = self.status.write().await;
*status = KmsServiceStatus::Error(format!("Health check failed: {}", e));
*status = KmsServiceStatus::Error(format!("Health check failed: {e}"));
Err(e)
}
}