feat: enhance storage stats and cluster health reporting
- Introduced new data structures for bucket and storage statistics, including BucketSummary, StorageStats, and ClusterHealth. - Implemented runtime statistics tracking for buckets, including object count and total size. - Added methods to retrieve storage stats and bucket summaries in the FileStore. - Enhanced the SmartStorage interface to expose storage stats and cluster health. - Implemented tests for runtime stats, cluster health, and credential management. - Added support for runtime-managed credentials with atomic replacement. - Improved filesystem usage reporting for storage locations.
This commit is contained in:
+71
-8
@@ -2,9 +2,10 @@ use hmac::{Hmac, Mac};
|
||||
use hyper::body::Incoming;
|
||||
use hyper::Request;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::config::{Credential, SmartStorageConfig};
|
||||
use crate::config::{AuthConfig, Credential};
|
||||
use crate::error::StorageError;
|
||||
|
||||
type HmacSha256 = Hmac<Sha256>;
|
||||
@@ -27,7 +28,7 @@ struct SigV4Header {
|
||||
/// Verify the request's SigV4 signature. Returns the caller identity on success.
|
||||
pub fn verify_request(
|
||||
req: &Request<Incoming>,
|
||||
config: &SmartStorageConfig,
|
||||
credentials: &[Credential],
|
||||
) -> Result<AuthenticatedIdentity, StorageError> {
|
||||
let auth_header = req
|
||||
.headers()
|
||||
@@ -47,7 +48,7 @@ pub fn verify_request(
|
||||
let parsed = parse_auth_header(auth_header)?;
|
||||
|
||||
// Look up credential
|
||||
let credential = find_credential(&parsed.access_key_id, config)
|
||||
let credential = find_credential(&parsed.access_key_id, credentials)
|
||||
.ok_or_else(StorageError::invalid_access_key_id)?;
|
||||
|
||||
// Get x-amz-date
|
||||
@@ -163,14 +164,76 @@ fn parse_auth_header(header: &str) -> Result<SigV4Header, StorageError> {
|
||||
}
|
||||
|
||||
/// Find a credential by access key ID.
|
||||
fn find_credential<'a>(access_key_id: &str, config: &'a SmartStorageConfig) -> Option<&'a Credential> {
|
||||
config
|
||||
.auth
|
||||
.credentials
|
||||
fn find_credential<'a>(access_key_id: &str, credentials: &'a [Credential]) -> Option<&'a Credential> {
|
||||
credentials
|
||||
.iter()
|
||||
.find(|c| c.access_key_id == access_key_id)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RuntimeCredentialStore {
|
||||
enabled: bool,
|
||||
credentials: RwLock<Vec<Credential>>,
|
||||
}
|
||||
|
||||
impl RuntimeCredentialStore {
|
||||
pub fn new(config: &AuthConfig) -> Self {
|
||||
Self {
|
||||
enabled: config.enabled,
|
||||
credentials: RwLock::new(config.credentials.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enabled(&self) -> bool {
|
||||
self.enabled
|
||||
}
|
||||
|
||||
pub async fn list_credentials(&self) -> Vec<Credential> {
|
||||
self.credentials.read().await.clone()
|
||||
}
|
||||
|
||||
pub async fn snapshot_credentials(&self) -> Vec<Credential> {
|
||||
self.credentials.read().await.clone()
|
||||
}
|
||||
|
||||
pub async fn replace_credentials(&self, credentials: Vec<Credential>) -> Result<(), StorageError> {
|
||||
validate_credentials(&credentials)?;
|
||||
*self.credentials.write().await = credentials;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_credentials(credentials: &[Credential]) -> Result<(), StorageError> {
|
||||
if credentials.is_empty() {
|
||||
return Err(StorageError::invalid_request(
|
||||
"Credential replacement requires at least one credential.",
|
||||
));
|
||||
}
|
||||
|
||||
let mut seen_access_keys = HashSet::new();
|
||||
for credential in credentials {
|
||||
if credential.access_key_id.trim().is_empty() {
|
||||
return Err(StorageError::invalid_request(
|
||||
"Credential accessKeyId must not be empty.",
|
||||
));
|
||||
}
|
||||
|
||||
if credential.secret_access_key.trim().is_empty() {
|
||||
return Err(StorageError::invalid_request(
|
||||
"Credential secretAccessKey must not be empty.",
|
||||
));
|
||||
}
|
||||
|
||||
if !seen_access_keys.insert(credential.access_key_id.as_str()) {
|
||||
return Err(StorageError::invalid_request(
|
||||
"Credential accessKeyId values must be unique.",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check clock skew (15 minutes max).
|
||||
fn check_clock_skew(amz_date: &str) -> Result<(), StorageError> {
|
||||
// Parse ISO 8601 basic format: YYYYMMDDTHHMMSSZ
|
||||
|
||||
Reference in New Issue
Block a user