mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-10 07:06:53 +00:00
add Error test, fix clippy
This commit is contained in:
@@ -11,7 +11,7 @@ use tokio::fs;
|
|||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio::sync::{mpsc, Mutex};
|
use tokio::sync::{Mutex, mpsc};
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
#[derive(RustEmbed)]
|
||||||
#[folder = "$CARGO_MANIFEST_DIR/embedded-rustfs/"]
|
#[folder = "$CARGO_MANIFEST_DIR/embedded-rustfs/"]
|
||||||
@@ -746,10 +746,10 @@ mod tests {
|
|||||||
assert_eq!(ServiceManager::extract_port("host:0"), Some(0));
|
assert_eq!(ServiceManager::extract_port("host:0"), Some(0));
|
||||||
assert_eq!(ServiceManager::extract_port("host:65535"), Some(65535));
|
assert_eq!(ServiceManager::extract_port("host:65535"), Some(65535));
|
||||||
assert_eq!(ServiceManager::extract_port("host:65536"), None); // Out of range
|
assert_eq!(ServiceManager::extract_port("host:65536"), None); // Out of range
|
||||||
// IPv6-like address - extract_port takes the second part after split(':')
|
// IPv6-like address - extract_port takes the second part after split(':')
|
||||||
// For "::1:8080", split(':') gives ["", "", "1", "8080"], nth(1) gives ""
|
// For "::1:8080", split(':') gives ["", "", "1", "8080"], nth(1) gives ""
|
||||||
assert_eq!(ServiceManager::extract_port("::1:8080"), None); // Second part is empty
|
assert_eq!(ServiceManager::extract_port("::1:8080"), None); // Second part is empty
|
||||||
// For "[::1]:8080", split(':') gives ["[", "", "1]", "8080"], nth(1) gives ""
|
// For "[::1]:8080", split(':') gives ["[", "", "1]", "8080"], nth(1) gives ""
|
||||||
assert_eq!(ServiceManager::extract_port("[::1]:8080"), None); // Second part is empty
|
assert_eq!(ServiceManager::extract_port("[::1]:8080"), None); // Second part is empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1091,9 +1091,9 @@ pub mod node_service_client {
|
|||||||
F: tonic::service::Interceptor,
|
F: tonic::service::Interceptor,
|
||||||
T::ResponseBody: Default,
|
T::ResponseBody: Default,
|
||||||
T: tonic::codegen::Service<
|
T: tonic::codegen::Service<
|
||||||
http::Request<tonic::body::Body>,
|
http::Request<tonic::body::Body>,
|
||||||
Response = http::Response<<T as tonic::client::GrpcService<tonic::body::Body>>::ResponseBody>,
|
Response = http::Response<<T as tonic::client::GrpcService<tonic::body::Body>>::ResponseBody>,
|
||||||
>,
|
>,
|
||||||
<T as tonic::codegen::Service<http::Request<tonic::body::Body>>>::Error:
|
<T as tonic::codegen::Service<http::Request<tonic::body::Body>>>::Error:
|
||||||
Into<StdError> + std::marker::Send + std::marker::Sync,
|
Into<StdError> + std::marker::Send + std::marker::Sync,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ use common::globals::GLOBAL_Conn_Map;
|
|||||||
pub use generated::*;
|
pub use generated::*;
|
||||||
use proto_gen::node_service::node_service_client::NodeServiceClient;
|
use proto_gen::node_service::node_service_client::NodeServiceClient;
|
||||||
use tonic::{
|
use tonic::{
|
||||||
|
Request, Status,
|
||||||
metadata::MetadataValue,
|
metadata::MetadataValue,
|
||||||
service::interceptor::InterceptedService,
|
service::interceptor::InterceptedService,
|
||||||
transport::{Channel, Endpoint},
|
transport::{Channel, Endpoint},
|
||||||
Request, Status,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Default 100 MB
|
// Default 100 MB
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::event::config::NotifierConfig;
|
|
||||||
use crate::ObservabilityConfig;
|
use crate::ObservabilityConfig;
|
||||||
|
use crate::event::config::NotifierConfig;
|
||||||
|
|
||||||
/// RustFs configuration
|
/// RustFs configuration
|
||||||
pub struct RustFsConfig {
|
pub struct RustFsConfig {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use rustfs_event_notifier::create_adapters;
|
|
||||||
use rustfs_event_notifier::NotifierSystem;
|
use rustfs_event_notifier::NotifierSystem;
|
||||||
|
use rustfs_event_notifier::create_adapters;
|
||||||
use rustfs_event_notifier::{AdapterConfig, NotifierConfig, WebhookConfig};
|
use rustfs_event_notifier::{AdapterConfig, NotifierConfig, WebhookConfig};
|
||||||
use rustfs_event_notifier::{Bucket, Event, Identity, Metadata, Name, Object, Source};
|
use rustfs_event_notifier::{Bucket, Event, Identity, Metadata, Name, Object, Source};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use axum::{extract::Json, http::StatusCode, routing::post, Router};
|
use axum::{Router, extract::Json, http::StatusCode, routing::post};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::Error;
|
use crate::Error;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{create_adapters, Error, Event, NotifierConfig, NotifierSystem};
|
use crate::{Error, Event, NotifierConfig, NotifierSystem, create_adapters};
|
||||||
use std::sync::{atomic, Arc};
|
use std::sync::{Arc, atomic};
|
||||||
use tokio::sync::{Mutex, OnceCell};
|
use tokio::sync::{Mutex, OnceCell};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ mod global;
|
|||||||
mod notifier;
|
mod notifier;
|
||||||
mod store;
|
mod store;
|
||||||
|
|
||||||
|
pub use adapter::ChannelAdapter;
|
||||||
pub use adapter::create_adapters;
|
pub use adapter::create_adapters;
|
||||||
#[cfg(all(feature = "kafka", target_os = "linux"))]
|
#[cfg(all(feature = "kafka", target_os = "linux"))]
|
||||||
pub use adapter::kafka::KafkaAdapter;
|
pub use adapter::kafka::KafkaAdapter;
|
||||||
@@ -14,7 +15,6 @@ pub use adapter::kafka::KafkaAdapter;
|
|||||||
pub use adapter::mqtt::MqttAdapter;
|
pub use adapter::mqtt::MqttAdapter;
|
||||||
#[cfg(feature = "webhook")]
|
#[cfg(feature = "webhook")]
|
||||||
pub use adapter::webhook::WebhookAdapter;
|
pub use adapter::webhook::WebhookAdapter;
|
||||||
pub use adapter::ChannelAdapter;
|
|
||||||
pub use bus::event_bus;
|
pub use bus::event_bus;
|
||||||
#[cfg(all(feature = "kafka", target_os = "linux"))]
|
#[cfg(all(feature = "kafka", target_os = "linux"))]
|
||||||
pub use config::KafkaConfig;
|
pub use config::KafkaConfig;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{event_bus, ChannelAdapter, Error, Event, EventStore, NotifierConfig};
|
use crate::{ChannelAdapter, Error, Event, EventStore, NotifierConfig, event_bus};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::Error;
|
|||||||
use crate::Log;
|
use crate::Log;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use tokio::fs::{create_dir_all, File, OpenOptions};
|
use tokio::fs::{File, OpenOptions, create_dir_all};
|
||||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter};
|
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
||||||
use rustfs_filemeta::{test_data::*, FileMeta};
|
use rustfs_filemeta::{FileMeta, test_data::*};
|
||||||
|
|
||||||
fn bench_create_real_xlmeta(c: &mut Criterion) {
|
fn bench_create_real_xlmeta(c: &mut Criterion) {
|
||||||
c.bench_function("create_real_xlmeta", |b| b.iter(|| black_box(create_real_xlmeta().unwrap())));
|
c.bench_function("create_real_xlmeta", |b| b.iter(|| black_box(create_real_xlmeta().unwrap())));
|
||||||
|
|||||||
@@ -176,3 +176,378 @@ pub fn is_io_eof(e: &Error) -> bool {
|
|||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::io::{Error as IoError, ErrorKind};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filemeta_error_from_io_error() {
|
||||||
|
let io_error = IoError::new(ErrorKind::PermissionDenied, "permission denied");
|
||||||
|
let filemeta_error: Error = io_error.into();
|
||||||
|
|
||||||
|
match filemeta_error {
|
||||||
|
Error::Io(inner_io) => {
|
||||||
|
assert_eq!(inner_io.kind(), ErrorKind::PermissionDenied);
|
||||||
|
assert!(inner_io.to_string().contains("permission denied"));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Io variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filemeta_error_other_function() {
|
||||||
|
let custom_error = "Custom filemeta error";
|
||||||
|
let filemeta_error = Error::other(custom_error);
|
||||||
|
|
||||||
|
match filemeta_error {
|
||||||
|
Error::Io(io_error) => {
|
||||||
|
assert!(io_error.to_string().contains(custom_error));
|
||||||
|
assert_eq!(io_error.kind(), ErrorKind::Other);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Io variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filemeta_error_conversions() {
|
||||||
|
// Test various error conversions
|
||||||
|
let serde_decode_err =
|
||||||
|
rmp_serde::decode::Error::InvalidMarkerRead(std::io::Error::new(ErrorKind::InvalidData, "invalid"));
|
||||||
|
let filemeta_error: Error = serde_decode_err.into();
|
||||||
|
assert!(matches!(filemeta_error, Error::RmpSerdeDecode(_)));
|
||||||
|
|
||||||
|
// Test with string-based error that we can actually create
|
||||||
|
let encode_error_string = "test encode error";
|
||||||
|
let filemeta_error = Error::RmpSerdeEncode(encode_error_string.to_string());
|
||||||
|
assert!(matches!(filemeta_error, Error::RmpSerdeEncode(_)));
|
||||||
|
|
||||||
|
let utf8_err = std::string::String::from_utf8(vec![0xFF]).unwrap_err();
|
||||||
|
let filemeta_error: Error = utf8_err.into();
|
||||||
|
assert!(matches!(filemeta_error, Error::FromUtf8(_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filemeta_error_clone() {
|
||||||
|
let test_cases = vec![
|
||||||
|
Error::FileNotFound,
|
||||||
|
Error::FileVersionNotFound,
|
||||||
|
Error::VolumeNotFound,
|
||||||
|
Error::FileCorrupt,
|
||||||
|
Error::DoneForNow,
|
||||||
|
Error::MethodNotAllowed,
|
||||||
|
Error::Unexpected,
|
||||||
|
Error::Io(IoError::new(ErrorKind::NotFound, "test")),
|
||||||
|
Error::RmpSerdeDecode("test decode error".to_string()),
|
||||||
|
Error::RmpSerdeEncode("test encode error".to_string()),
|
||||||
|
Error::FromUtf8("test utf8 error".to_string()),
|
||||||
|
Error::RmpDecodeValueRead("test value read error".to_string()),
|
||||||
|
Error::RmpEncodeValueWrite("test value write error".to_string()),
|
||||||
|
Error::RmpDecodeNumValueRead("test num read error".to_string()),
|
||||||
|
Error::RmpDecodeMarkerRead("test marker read error".to_string()),
|
||||||
|
Error::TimeComponentRange("test time error".to_string()),
|
||||||
|
Error::UuidParse("test uuid error".to_string()),
|
||||||
|
];
|
||||||
|
|
||||||
|
for original_error in test_cases {
|
||||||
|
let cloned_error = original_error.clone();
|
||||||
|
assert_eq!(original_error, cloned_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filemeta_error_partial_eq() {
|
||||||
|
// Test equality for simple variants
|
||||||
|
assert_eq!(Error::FileNotFound, Error::FileNotFound);
|
||||||
|
assert_ne!(Error::FileNotFound, Error::FileVersionNotFound);
|
||||||
|
|
||||||
|
// Test equality for Io variants
|
||||||
|
let io1 = Error::Io(IoError::new(ErrorKind::NotFound, "test"));
|
||||||
|
let io2 = Error::Io(IoError::new(ErrorKind::NotFound, "test"));
|
||||||
|
let io3 = Error::Io(IoError::new(ErrorKind::PermissionDenied, "test"));
|
||||||
|
assert_eq!(io1, io2);
|
||||||
|
assert_ne!(io1, io3);
|
||||||
|
|
||||||
|
// Test equality for string variants
|
||||||
|
let decode1 = Error::RmpSerdeDecode("error message".to_string());
|
||||||
|
let decode2 = Error::RmpSerdeDecode("error message".to_string());
|
||||||
|
let decode3 = Error::RmpSerdeDecode("different message".to_string());
|
||||||
|
assert_eq!(decode1, decode2);
|
||||||
|
assert_ne!(decode1, decode3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filemeta_error_display() {
|
||||||
|
let test_cases = vec![
|
||||||
|
(Error::FileNotFound, "File not found"),
|
||||||
|
(Error::FileVersionNotFound, "File version not found"),
|
||||||
|
(Error::VolumeNotFound, "Volume not found"),
|
||||||
|
(Error::FileCorrupt, "File corrupt"),
|
||||||
|
(Error::DoneForNow, "Done for now"),
|
||||||
|
(Error::MethodNotAllowed, "Method not allowed"),
|
||||||
|
(Error::Unexpected, "Unexpected error"),
|
||||||
|
(Error::RmpSerdeDecode("test".to_string()), "rmp serde decode error: test"),
|
||||||
|
(Error::RmpSerdeEncode("test".to_string()), "rmp serde encode error: test"),
|
||||||
|
(Error::FromUtf8("test".to_string()), "Invalid UTF-8: test"),
|
||||||
|
(Error::TimeComponentRange("test".to_string()), "time component range error: test"),
|
||||||
|
(Error::UuidParse("test".to_string()), "uuid parse error: test"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (error, expected_message) in test_cases {
|
||||||
|
assert_eq!(error.to_string(), expected_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rmp_conversions() {
|
||||||
|
// Test rmp value read error (this one works since it has the same signature)
|
||||||
|
let value_read_err = rmp::decode::ValueReadError::InvalidMarkerRead(std::io::Error::new(ErrorKind::InvalidData, "test"));
|
||||||
|
let filemeta_error: Error = value_read_err.into();
|
||||||
|
assert!(matches!(filemeta_error, Error::RmpDecodeValueRead(_)));
|
||||||
|
|
||||||
|
// Test rmp num value read error
|
||||||
|
let num_value_err =
|
||||||
|
rmp::decode::NumValueReadError::InvalidMarkerRead(std::io::Error::new(ErrorKind::InvalidData, "test"));
|
||||||
|
let filemeta_error: Error = num_value_err.into();
|
||||||
|
assert!(matches!(filemeta_error, Error::RmpDecodeNumValueRead(_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_time_and_uuid_conversions() {
|
||||||
|
// Test time component range error
|
||||||
|
use time::{Date, Month};
|
||||||
|
let time_result = Date::from_calendar_date(2023, Month::January, 32); // Invalid day
|
||||||
|
assert!(time_result.is_err());
|
||||||
|
let time_error = time_result.unwrap_err();
|
||||||
|
let filemeta_error: Error = time_error.into();
|
||||||
|
assert!(matches!(filemeta_error, Error::TimeComponentRange(_)));
|
||||||
|
|
||||||
|
// Test UUID parse error
|
||||||
|
let uuid_result = uuid::Uuid::parse_str("invalid-uuid");
|
||||||
|
assert!(uuid_result.is_err());
|
||||||
|
let uuid_error = uuid_result.unwrap_err();
|
||||||
|
let filemeta_error: Error = uuid_error.into();
|
||||||
|
assert!(matches!(filemeta_error, Error::UuidParse(_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_marker_read_error_conversion() {
|
||||||
|
// Test rmp marker read error conversion
|
||||||
|
let marker_err = rmp::decode::MarkerReadError(std::io::Error::new(ErrorKind::InvalidData, "marker test"));
|
||||||
|
let filemeta_error: Error = marker_err.into();
|
||||||
|
assert!(matches!(filemeta_error, Error::RmpDecodeMarkerRead(_)));
|
||||||
|
assert!(filemeta_error.to_string().contains("marker"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_io_eof_function() {
|
||||||
|
// Test is_io_eof helper function
|
||||||
|
let eof_error = Error::Io(IoError::new(ErrorKind::UnexpectedEof, "eof"));
|
||||||
|
assert!(is_io_eof(&eof_error));
|
||||||
|
|
||||||
|
let not_eof_error = Error::Io(IoError::new(ErrorKind::NotFound, "not found"));
|
||||||
|
assert!(!is_io_eof(¬_eof_error));
|
||||||
|
|
||||||
|
let non_io_error = Error::FileNotFound;
|
||||||
|
assert!(!is_io_eof(&non_io_error));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filemeta_error_to_io_error_conversion() {
|
||||||
|
// Test conversion from FileMeta Error to io::Error through other function
|
||||||
|
let original_io_error = IoError::new(ErrorKind::InvalidData, "test data");
|
||||||
|
let filemeta_error = Error::other(original_io_error);
|
||||||
|
|
||||||
|
match filemeta_error {
|
||||||
|
Error::Io(io_err) => {
|
||||||
|
assert_eq!(io_err.kind(), ErrorKind::Other);
|
||||||
|
assert!(io_err.to_string().contains("test data"));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Io variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filemeta_error_roundtrip_conversion() {
|
||||||
|
// Test roundtrip conversion: io::Error -> FileMeta Error -> io::Error
|
||||||
|
let original_io_error = IoError::new(ErrorKind::PermissionDenied, "permission test");
|
||||||
|
|
||||||
|
// Convert to FileMeta Error
|
||||||
|
let filemeta_error: Error = original_io_error.into();
|
||||||
|
|
||||||
|
// Extract the io::Error back
|
||||||
|
match filemeta_error {
|
||||||
|
Error::Io(extracted_io_error) => {
|
||||||
|
assert_eq!(extracted_io_error.kind(), ErrorKind::PermissionDenied);
|
||||||
|
assert!(extracted_io_error.to_string().contains("permission test"));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Io variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filemeta_error_io_error_kinds_preservation() {
|
||||||
|
let io_error_kinds = vec![
|
||||||
|
ErrorKind::NotFound,
|
||||||
|
ErrorKind::PermissionDenied,
|
||||||
|
ErrorKind::ConnectionRefused,
|
||||||
|
ErrorKind::ConnectionReset,
|
||||||
|
ErrorKind::ConnectionAborted,
|
||||||
|
ErrorKind::NotConnected,
|
||||||
|
ErrorKind::AddrInUse,
|
||||||
|
ErrorKind::AddrNotAvailable,
|
||||||
|
ErrorKind::BrokenPipe,
|
||||||
|
ErrorKind::AlreadyExists,
|
||||||
|
ErrorKind::WouldBlock,
|
||||||
|
ErrorKind::InvalidInput,
|
||||||
|
ErrorKind::InvalidData,
|
||||||
|
ErrorKind::TimedOut,
|
||||||
|
ErrorKind::WriteZero,
|
||||||
|
ErrorKind::Interrupted,
|
||||||
|
ErrorKind::UnexpectedEof,
|
||||||
|
ErrorKind::Other,
|
||||||
|
];
|
||||||
|
|
||||||
|
for kind in io_error_kinds {
|
||||||
|
let io_error = IoError::new(kind, format!("test error for {:?}", kind));
|
||||||
|
let filemeta_error: Error = io_error.into();
|
||||||
|
|
||||||
|
match filemeta_error {
|
||||||
|
Error::Io(extracted_io_error) => {
|
||||||
|
assert_eq!(extracted_io_error.kind(), kind);
|
||||||
|
assert!(extracted_io_error.to_string().contains("test error"));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Io variant for kind {:?}", kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filemeta_error_downcast_chain() {
|
||||||
|
// Test error downcast chain functionality
|
||||||
|
let original_io_error = IoError::new(ErrorKind::InvalidData, "original error");
|
||||||
|
let filemeta_error = Error::other(original_io_error);
|
||||||
|
|
||||||
|
// The error should be wrapped as an Io variant
|
||||||
|
if let Error::Io(io_err) = filemeta_error {
|
||||||
|
// The wrapped error should be Other kind (from std::io::Error::other)
|
||||||
|
assert_eq!(io_err.kind(), ErrorKind::Other);
|
||||||
|
// But the message should still contain the original error information
|
||||||
|
assert!(io_err.to_string().contains("original error"));
|
||||||
|
} else {
|
||||||
|
panic!("Expected Io variant");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filemeta_error_maintains_error_information() {
|
||||||
|
let test_cases = vec![
|
||||||
|
(ErrorKind::NotFound, "file not found"),
|
||||||
|
(ErrorKind::PermissionDenied, "access denied"),
|
||||||
|
(ErrorKind::InvalidData, "corrupt data"),
|
||||||
|
(ErrorKind::TimedOut, "operation timed out"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (kind, message) in test_cases {
|
||||||
|
let io_error = IoError::new(kind, message);
|
||||||
|
let error_message = io_error.to_string();
|
||||||
|
let filemeta_error: Error = io_error.into();
|
||||||
|
|
||||||
|
match filemeta_error {
|
||||||
|
Error::Io(extracted_io_error) => {
|
||||||
|
assert_eq!(extracted_io_error.kind(), kind);
|
||||||
|
assert_eq!(extracted_io_error.to_string(), error_message);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Io variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filemeta_error_complex_conversion_chain() {
|
||||||
|
// Test conversion from string error types that we can actually create
|
||||||
|
|
||||||
|
// Test with UUID error conversion
|
||||||
|
let uuid_result = uuid::Uuid::parse_str("invalid-uuid-format");
|
||||||
|
assert!(uuid_result.is_err());
|
||||||
|
let uuid_error = uuid_result.unwrap_err();
|
||||||
|
let filemeta_error: Error = uuid_error.into();
|
||||||
|
|
||||||
|
match filemeta_error {
|
||||||
|
Error::UuidParse(message) => {
|
||||||
|
assert!(message.contains("invalid"));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected UuidParse variant"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with time error conversion
|
||||||
|
use time::{Date, Month};
|
||||||
|
let time_result = Date::from_calendar_date(2023, Month::January, 32); // Invalid day
|
||||||
|
assert!(time_result.is_err());
|
||||||
|
let time_error = time_result.unwrap_err();
|
||||||
|
let filemeta_error2: Error = time_error.into();
|
||||||
|
|
||||||
|
match filemeta_error2 {
|
||||||
|
Error::TimeComponentRange(message) => {
|
||||||
|
assert!(message.contains("range"));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected TimeComponentRange variant"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with UTF8 error conversion
|
||||||
|
let utf8_result = std::string::String::from_utf8(vec![0xFF]);
|
||||||
|
assert!(utf8_result.is_err());
|
||||||
|
let utf8_error = utf8_result.unwrap_err();
|
||||||
|
let filemeta_error3: Error = utf8_error.into();
|
||||||
|
|
||||||
|
match filemeta_error3 {
|
||||||
|
Error::FromUtf8(message) => {
|
||||||
|
assert!(message.contains("utf"));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected FromUtf8 variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filemeta_error_equality_with_io_errors() {
|
||||||
|
// Test equality comparison for Io variants
|
||||||
|
let io_error1 = IoError::new(ErrorKind::NotFound, "test message");
|
||||||
|
let io_error2 = IoError::new(ErrorKind::NotFound, "test message");
|
||||||
|
let io_error3 = IoError::new(ErrorKind::PermissionDenied, "test message");
|
||||||
|
let io_error4 = IoError::new(ErrorKind::NotFound, "different message");
|
||||||
|
|
||||||
|
let filemeta_error1 = Error::Io(io_error1);
|
||||||
|
let filemeta_error2 = Error::Io(io_error2);
|
||||||
|
let filemeta_error3 = Error::Io(io_error3);
|
||||||
|
let filemeta_error4 = Error::Io(io_error4);
|
||||||
|
|
||||||
|
// Same kind and message should be equal
|
||||||
|
assert_eq!(filemeta_error1, filemeta_error2);
|
||||||
|
|
||||||
|
// Different kinds should not be equal
|
||||||
|
assert_ne!(filemeta_error1, filemeta_error3);
|
||||||
|
|
||||||
|
// Different messages should not be equal
|
||||||
|
assert_ne!(filemeta_error1, filemeta_error4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filemeta_error_clone_io_variants() {
|
||||||
|
let io_error = IoError::new(ErrorKind::ConnectionReset, "connection lost");
|
||||||
|
let original_error = Error::Io(io_error);
|
||||||
|
let cloned_error = original_error.clone();
|
||||||
|
|
||||||
|
// Cloned error should be equal to original
|
||||||
|
assert_eq!(original_error, cloned_error);
|
||||||
|
|
||||||
|
// Both should maintain the same properties
|
||||||
|
match (original_error, cloned_error) {
|
||||||
|
(Error::Io(orig_io), Error::Io(cloned_io)) => {
|
||||||
|
assert_eq!(orig_io.kind(), cloned_io.kind());
|
||||||
|
assert_eq!(orig_io.to_string(), cloned_io.to_string());
|
||||||
|
}
|
||||||
|
_ => panic!("Both should be Io variants"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,11 +27,7 @@ impl InlineData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn after_version(&self) -> &[u8] {
|
pub fn after_version(&self) -> &[u8] {
|
||||||
if self.0.is_empty() {
|
if self.0.is_empty() { &self.0 } else { &self.0[1..] }
|
||||||
&self.0
|
|
||||||
} else {
|
|
||||||
&self.0[1..]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find(&self, key: &str) -> Result<Option<Vec<u8>>> {
|
pub fn find(&self, key: &str) -> Result<Option<Vec<u8>>> {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::{merge_file_meta_versions, FileInfo, FileInfoVersions, FileMeta, FileMetaShallowVersion, VersionType};
|
use crate::{FileInfo, FileInfoVersions, FileMeta, FileMetaShallowVersion, VersionType, merge_file_meta_versions};
|
||||||
use rmp::Marker;
|
use rmp::Marker;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
@@ -10,8 +10,8 @@ use std::{
|
|||||||
pin::Pin,
|
pin::Pin,
|
||||||
ptr,
|
ptr,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicPtr, AtomicU64, Ordering as AtomicOrdering},
|
|
||||||
Arc,
|
Arc,
|
||||||
|
atomic::{AtomicPtr, AtomicU64, Ordering as AtomicOrdering},
|
||||||
},
|
},
|
||||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use opentelemetry::global;
|
use opentelemetry::global;
|
||||||
use rustfs_obs::{get_logger, init_obs, log_info, BaseLogEntry, ServerLogEntry, SystemObserver};
|
use rustfs_obs::{BaseLogEntry, ServerLogEntry, SystemObserver, get_logger, init_obs, log_info};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
use tracing::{error, info, instrument};
|
use tracing::{error, info, instrument};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::logger::InitLogStatus;
|
use crate::logger::InitLogStatus;
|
||||||
use crate::telemetry::{init_telemetry, OtelGuard};
|
use crate::telemetry::{OtelGuard, init_telemetry};
|
||||||
use crate::{get_global_logger, init_global_logger, AppConfig, Logger};
|
use crate::{AppConfig, Logger, get_global_logger, init_global_logger};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use tokio::sync::{OnceCell, SetError};
|
use tokio::sync::{OnceCell, SetError};
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::sinks::Sink;
|
use crate::sinks::Sink;
|
||||||
use crate::{
|
use crate::{
|
||||||
sinks, AppConfig, AuditLogEntry, BaseLogEntry, ConsoleLogEntry, GlobalError, OtelConfig, ServerLogEntry, UnifiedLogEntry,
|
AppConfig, AuditLogEntry, BaseLogEntry, ConsoleLogEntry, GlobalError, OtelConfig, ServerLogEntry, UnifiedLogEntry, sinks,
|
||||||
};
|
};
|
||||||
use rustfs_config::{APP_NAME, ENVIRONMENT, SERVICE_VERSION};
|
use rustfs_config::{APP_NAME, ENVIRONMENT, SERVICE_VERSION};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::sinks::Sink;
|
|
||||||
use crate::UnifiedLogEntry;
|
use crate::UnifiedLogEntry;
|
||||||
|
use crate::sinks::Sink;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
/// Webhook Sink Implementation
|
/// Webhook Sink Implementation
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
|
use crate::GlobalError;
|
||||||
use crate::system::attributes::ProcessAttributes;
|
use crate::system::attributes::ProcessAttributes;
|
||||||
use crate::system::gpu::GpuCollector;
|
use crate::system::gpu::GpuCollector;
|
||||||
use crate::system::metrics::{Metrics, DIRECTION, INTERFACE, STATUS};
|
use crate::system::metrics::{DIRECTION, INTERFACE, Metrics, STATUS};
|
||||||
use crate::GlobalError;
|
|
||||||
use opentelemetry::KeyValue;
|
use opentelemetry::KeyValue;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
use sysinfo::{Networks, Pid, ProcessStatus, System};
|
use sysinfo::{Networks, Pid, ProcessStatus, System};
|
||||||
use tokio::time::{sleep, Duration};
|
use tokio::time::{Duration, sleep};
|
||||||
|
|
||||||
/// Collector is responsible for collecting system metrics and attributes.
|
/// Collector is responsible for collecting system metrics and attributes.
|
||||||
/// It uses the sysinfo crate to gather information about the system and processes.
|
/// It uses the sysinfo crate to gather information about the system and processes.
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
|
use crate::GlobalError;
|
||||||
|
#[cfg(feature = "gpu")]
|
||||||
use crate::system::attributes::ProcessAttributes;
|
use crate::system::attributes::ProcessAttributes;
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
use crate::system::metrics::Metrics;
|
use crate::system::metrics::Metrics;
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
use crate::GlobalError;
|
use nvml_wrapper::Nvml;
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
use nvml_wrapper::enums::device::UsedGpuMemory;
|
use nvml_wrapper::enums::device::UsedGpuMemory;
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
use nvml_wrapper::Nvml;
|
|
||||||
#[cfg(feature = "gpu")]
|
|
||||||
use sysinfo::Pid;
|
use sysinfo::Pid;
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{sinks::Sink, UnifiedLogEntry};
|
use crate::{UnifiedLogEntry, sinks::Sink};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::mpsc::Receiver;
|
use tokio::sync::mpsc::Receiver;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{Reader, Writer};
|
use crate::{Reader, Writer};
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
use rustfs_utils::{read_full, write_all, HashAlgorithm};
|
use rustfs_utils::{HashAlgorithm, read_full, write_all};
|
||||||
use tokio::io::{AsyncRead, AsyncReadExt};
|
use tokio::io::{AsyncRead, AsyncReadExt};
|
||||||
|
|
||||||
pin_project! {
|
pin_project! {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::compress::{compress_block, decompress_block, CompressionAlgorithm};
|
use crate::compress::{CompressionAlgorithm, compress_block, decompress_block};
|
||||||
use crate::{EtagResolvable, HashReaderDetector};
|
use crate::{EtagResolvable, HashReaderDetector};
|
||||||
use crate::{HashReaderMut, Reader};
|
use crate::{HashReaderMut, Reader};
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ impl HashReaderDetector for HashReader {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{encrypt_reader, DecryptReader};
|
use crate::{DecryptReader, encrypt_reader};
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use tokio::io::{AsyncReadExt, BufReader};
|
use tokio::io::{AsyncReadExt, BufReader};
|
||||||
|
|
||||||
|
|||||||
@@ -396,10 +396,12 @@ mod tests {
|
|||||||
// Should fail because no certificates found
|
// Should fail because no certificates found
|
||||||
let result = load_all_certs_from_directory(temp_dir.path().to_str().unwrap());
|
let result = load_all_certs_from_directory(temp_dir.path().to_str().unwrap());
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(result
|
assert!(
|
||||||
.unwrap_err()
|
result
|
||||||
.to_string()
|
.unwrap_err()
|
||||||
.contains("No valid certificate/private key pair found"));
|
.to_string()
|
||||||
|
.contains("No valid certificate/private key pair found")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -412,10 +414,12 @@ mod tests {
|
|||||||
|
|
||||||
let result = load_all_certs_from_directory(unicode_dir.to_str().unwrap());
|
let result = load_all_certs_from_directory(unicode_dir.to_str().unwrap());
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert!(result
|
assert!(
|
||||||
.unwrap_err()
|
result
|
||||||
.to_string()
|
.unwrap_err()
|
||||||
.contains("No valid certificate/private key pair found"));
|
.to_string()
|
||||||
|
.contains("No valid certificate/private key pair found")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ mod tests {
|
|||||||
let data = b"test data";
|
let data = b"test data";
|
||||||
let hash = HashAlgorithm::BLAKE2b512.hash_encode(data);
|
let hash = HashAlgorithm::BLAKE2b512.hash_encode(data);
|
||||||
assert_eq!(hash.len(), 32); // blake3 outputs 32 bytes by default
|
assert_eq!(hash.len(), 32); // blake3 outputs 32 bytes by default
|
||||||
// BLAKE2b512 should be deterministic
|
// BLAKE2b512 should be deterministic
|
||||||
let hash2 = HashAlgorithm::BLAKE2b512.hash_encode(data);
|
let hash2 = HashAlgorithm::BLAKE2b512.hash_encode(data);
|
||||||
assert_eq!(hash, hash2);
|
assert_eq!(hash, hash2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use nix::sys::stat::{self, stat};
|
use nix::sys::stat::{self, stat};
|
||||||
use nix::sys::statfs::{self, statfs, FsType};
|
use nix::sys::statfs::{self, FsType, statfs};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{self, BufRead, Error, ErrorKind};
|
use std::io::{self, BufRead, Error, ErrorKind};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -26,7 +26,7 @@ pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
|
|||||||
bfree,
|
bfree,
|
||||||
p.as_ref().display()
|
p.as_ref().display()
|
||||||
),
|
),
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
|
|||||||
blocks,
|
blocks,
|
||||||
p.as_ref().display()
|
p.as_ref().display()
|
||||||
),
|
),
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
|
|||||||
total,
|
total,
|
||||||
p.as_ref().display()
|
p.as_ref().display()
|
||||||
),
|
),
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
|
|||||||
bavail,
|
bavail,
|
||||||
bfree,
|
bfree,
|
||||||
p.as_ref().display()
|
p.as_ref().display()
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
|
|||||||
reserved,
|
reserved,
|
||||||
blocks,
|
blocks,
|
||||||
p.as_ref().display()
|
p.as_ref().display()
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
|
|||||||
free,
|
free,
|
||||||
total,
|
total,
|
||||||
p.as_ref().display()
|
p.as_ref().display()
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -608,8 +608,8 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_decompress_with_invalid_format() {
|
async fn test_decompress_with_invalid_format() {
|
||||||
// Test decompression with invalid format
|
// Test decompression with invalid format
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
let sample_content = b"Hello, compression world!";
|
let sample_content = b"Hello, compression world!";
|
||||||
let cursor = Cursor::new(sample_content);
|
let cursor = Cursor::new(sample_content);
|
||||||
@@ -634,8 +634,8 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_decompress_with_zip_format() {
|
async fn test_decompress_with_zip_format() {
|
||||||
// Test decompression with Zip format (currently not supported)
|
// Test decompression with Zip format (currently not supported)
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
let sample_content = b"Hello, compression world!";
|
let sample_content = b"Hello, compression world!";
|
||||||
let cursor = Cursor::new(sample_content);
|
let cursor = Cursor::new(sample_content);
|
||||||
@@ -660,8 +660,8 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_decompress_error_propagation() {
|
async fn test_decompress_error_propagation() {
|
||||||
// Test error propagation during decompression process
|
// Test error propagation during decompression process
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
let sample_content = b"Hello, compression world!";
|
let sample_content = b"Hello, compression world!";
|
||||||
let cursor = Cursor::new(sample_content);
|
let cursor = Cursor::new(sample_content);
|
||||||
@@ -690,8 +690,8 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_decompress_callback_execution() {
|
async fn test_decompress_callback_execution() {
|
||||||
// Test callback function execution during decompression
|
// Test callback function execution during decompression
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
let sample_content = b"Hello, compression world!";
|
let sample_content = b"Hello, compression world!";
|
||||||
let cursor = Cursor::new(sample_content);
|
let cursor = Cursor::new(sample_content);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use jsonwebtoken::{Algorithm, DecodingKey, TokenData, Validation};
|
use jsonwebtoken::{Algorithm, DecodingKey, TokenData, Validation};
|
||||||
|
|
||||||
use crate::jwt::Claims;
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
use crate::jwt::Claims;
|
||||||
|
|
||||||
pub fn decode(token: &str, token_secret: &[u8]) -> Result<TokenData<Claims>, Error> {
|
pub fn decode(token: &str, token_secret: &[u8]) -> Result<TokenData<Claims>, Error> {
|
||||||
Ok(jsonwebtoken::decode(
|
Ok(jsonwebtoken::decode(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use jsonwebtoken::{Algorithm, EncodingKey, Header};
|
use jsonwebtoken::{Algorithm, EncodingKey, Header};
|
||||||
|
|
||||||
use crate::jwt::Claims;
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
use crate::jwt::Claims;
|
||||||
|
|
||||||
pub fn encode(token_secret: &[u8], claims: &Claims) -> Result<String, Error> {
|
pub fn encode(token_secret: &[u8], claims: &Claims) -> Result<String, Error> {
|
||||||
Ok(jsonwebtoken::encode(
|
Ok(jsonwebtoken::encode(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::{error::Error, sync::Arc, time::Duration};
|
|||||||
use lock::{
|
use lock::{
|
||||||
drwmutex::Options,
|
drwmutex::Options,
|
||||||
lock_args::LockArgs,
|
lock_args::LockArgs,
|
||||||
namespace_lock::{new_nslock, NsLockMap},
|
namespace_lock::{NsLockMap, new_nslock},
|
||||||
new_lock_api,
|
new_lock_api,
|
||||||
};
|
};
|
||||||
use protos::{node_service_time_out_client, proto_gen::node_service::GenerallyLockRequest};
|
use protos::{node_service_time_out_client, proto_gen::node_service::GenerallyLockRequest};
|
||||||
@@ -60,16 +60,16 @@ async fn test_lock_unlock_ns_lock() -> Result<(), Box<dyn Error>> {
|
|||||||
vec![locker],
|
vec![locker],
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert!(ns
|
assert!(
|
||||||
.0
|
ns.0.write()
|
||||||
.write()
|
.await
|
||||||
.await
|
.get_lock(&Options {
|
||||||
.get_lock(&Options {
|
timeout: Duration::from_secs(5),
|
||||||
timeout: Duration::from_secs(5),
|
retry_interval: Duration::from_secs(1),
|
||||||
retry_interval: Duration::from_secs(1),
|
})
|
||||||
})
|
.await
|
||||||
.await
|
.unwrap()
|
||||||
.unwrap());
|
);
|
||||||
|
|
||||||
ns.0.write().await.un_lock().await.unwrap();
|
ns.0.write().await.un_lock().await.unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ impl BucketMetadataError {
|
|||||||
0x06 => Some(BucketMetadataError::BucketQuotaConfigNotFound),
|
0x06 => Some(BucketMetadataError::BucketQuotaConfigNotFound),
|
||||||
0x07 => Some(BucketMetadataError::BucketReplicationConfigNotFound),
|
0x07 => Some(BucketMetadataError::BucketReplicationConfigNotFound),
|
||||||
0x08 => Some(BucketMetadataError::BucketRemoteTargetNotFound),
|
0x08 => Some(BucketMetadataError::BucketRemoteTargetNotFound),
|
||||||
0x09 => Some(BucketMetadataError::Io(std::io::Error::new(std::io::ErrorKind::Other, "Io error"))),
|
0x09 => Some(BucketMetadataError::Io(std::io::Error::other("Io error"))),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ use std::sync::OnceLock;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
|
use crate::StorageAPI;
|
||||||
use crate::bucket::error::BucketMetadataError;
|
use crate::bucket::error::BucketMetadataError;
|
||||||
use crate::bucket::metadata::{load_bucket_metadata_parse, BUCKET_LIFECYCLE_CONFIG};
|
use crate::bucket::metadata::{BUCKET_LIFECYCLE_CONFIG, load_bucket_metadata_parse};
|
||||||
use crate::bucket::utils::is_meta_bucketname;
|
use crate::bucket::utils::is_meta_bucketname;
|
||||||
use crate::error::{is_err_bucket_not_found, Error, Result};
|
use crate::error::{Error, Result, is_err_bucket_not_found};
|
||||||
use crate::global::{is_dist_erasure, is_erasure, new_object_layer_fn, GLOBAL_Endpoints};
|
use crate::global::{GLOBAL_Endpoints, is_dist_erasure, is_erasure, new_object_layer_fn};
|
||||||
use crate::heal::heal_commands::HealOpts;
|
use crate::heal::heal_commands::HealOpts;
|
||||||
use crate::store::ECStore;
|
use crate::store::ECStore;
|
||||||
use crate::utils::xml::deserialize;
|
use crate::utils::xml::deserialize;
|
||||||
use crate::StorageAPI;
|
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use policy::policy::BucketPolicy;
|
use policy::policy::BucketPolicy;
|
||||||
use s3s::dto::{
|
use s3s::dto::{
|
||||||
@@ -23,7 +23,7 @@ use tokio::sync::RwLock;
|
|||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
|
|
||||||
use super::metadata::{load_bucket_metadata, BucketMetadata};
|
use super::metadata::{BucketMetadata, load_bucket_metadata};
|
||||||
use super::quota::BucketQuota;
|
use super::quota::BucketQuota;
|
||||||
use super::target::BucketTargets;
|
use super::target::BucketTargets;
|
||||||
|
|
||||||
@@ -363,7 +363,7 @@ impl BucketMetadataSys {
|
|||||||
Err(Error::other("errBucketMetadataNotInitialized"))
|
Err(Error::other("errBucketMetadataNotInitialized"))
|
||||||
} else {
|
} else {
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::{storageclass, Config, GLOBAL_StorageClass};
|
use super::{Config, GLOBAL_StorageClass, storageclass};
|
||||||
use crate::disk::RUSTFS_META_BUCKET;
|
use crate::disk::RUSTFS_META_BUCKET;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::store_api::{ObjectInfo, ObjectOptions, PutObjReader, StorageAPI};
|
use crate::store_api::{ObjectInfo, ObjectOptions, PutObjReader, StorageAPI};
|
||||||
@@ -123,7 +123,7 @@ pub async fn read_config_without_migrate<S: StorageAPI>(api: Arc<S>) -> Result<C
|
|||||||
} else {
|
} else {
|
||||||
error!("read config err {:?}", &err);
|
error!("read config err {:?}", &err);
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ async fn read_server_config<S: StorageAPI>(api: Arc<S>, data: &[u8]) -> Result<C
|
|||||||
} else {
|
} else {
|
||||||
error!("read config err {:?}", &err);
|
error!("read config err {:?}", &err);
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// TODO: decrypt
|
// TODO: decrypt
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ pub mod storageclass;
|
|||||||
|
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::store::ECStore;
|
use crate::store::ECStore;
|
||||||
use com::{lookup_configs, read_config_without_migrate, STORAGE_CLASS_SUB_SYS};
|
use com::{STORAGE_CLASS_SUB_SYS, lookup_configs, read_config_without_migrate};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -69,11 +69,7 @@ impl KVS {
|
|||||||
KVS(Vec::new())
|
KVS(Vec::new())
|
||||||
}
|
}
|
||||||
pub fn get(&self, key: &str) -> String {
|
pub fn get(&self, key: &str) -> String {
|
||||||
if let Some(v) = self.lookup(key) {
|
if let Some(v) = self.lookup(key) { v } else { "".to_owned() }
|
||||||
v
|
|
||||||
} else {
|
|
||||||
"".to_owned()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn lookup(&self, key: &str) -> Option<String> {
|
pub fn lookup(&self, key: &str) -> Option<String> {
|
||||||
for kv in self.0.iter() {
|
for kv in self.0.iter() {
|
||||||
|
|||||||
@@ -174,13 +174,7 @@ pub fn lookup_config(kvs: &KVS, set_drive_count: usize) -> Result<Config> {
|
|||||||
parse_storage_class(&ssc_str)?
|
parse_storage_class(&ssc_str)?
|
||||||
} else {
|
} else {
|
||||||
StorageClass {
|
StorageClass {
|
||||||
parity: {
|
parity: { if set_drive_count == 1 { 0 } else { DEFAULT_RRS_PARITY } },
|
||||||
if set_drive_count == 1 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
DEFAULT_RRS_PARITY
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -193,7 +187,10 @@ pub fn lookup_config(kvs: &KVS, set_drive_count: usize) -> Result<Config> {
|
|||||||
if let Ok(ev) = env::var(INLINE_BLOCK_ENV) {
|
if let Ok(ev) = env::var(INLINE_BLOCK_ENV) {
|
||||||
if let Ok(block) = ev.parse::<bytesize::ByteSize>() {
|
if let Ok(block) = ev.parse::<bytesize::ByteSize>() {
|
||||||
if block.as_u64() as usize > DEFAULT_INLINE_BLOCK {
|
if block.as_u64() as usize > DEFAULT_INLINE_BLOCK {
|
||||||
warn!("inline block value bigger than recommended max of 128KiB -> {}, performance may degrade for PUT please benchmark the changes",block);
|
warn!(
|
||||||
|
"inline block value bigger than recommended max of 128KiB -> {}, performance may degrade for PUT please benchmark the changes",
|
||||||
|
block
|
||||||
|
);
|
||||||
}
|
}
|
||||||
block.as_u64() as usize
|
block.as_u64() as usize
|
||||||
} else {
|
} else {
|
||||||
@@ -295,7 +292,10 @@ pub fn validate_parity_inner(ss_parity: usize, rrs_parity: usize, set_drive_coun
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ss_parity > 0 && rrs_parity > 0 && ss_parity < rrs_parity {
|
if ss_parity > 0 && rrs_parity > 0 && ss_parity < rrs_parity {
|
||||||
return Err(Error::other(format!("Standard storage class parity drives {} should be greater than or equal to Reduced redundancy storage class parity drives {}", ss_parity, rrs_parity)));
|
return Err(Error::other(format!(
|
||||||
|
"Standard storage class parity drives {} should be greater than or equal to Reduced redundancy storage class parity drives {}",
|
||||||
|
ss_parity, rrs_parity
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -721,4 +721,144 @@ mod tests {
|
|||||||
assert!(inner.source().is_none()); // std::io::Error typically doesn't have a source
|
assert!(inner.source().is_none()); // std::io::Error typically doesn't have a source
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_io_error_roundtrip_conversion() {
|
||||||
|
// Test DiskError -> std::io::Error -> DiskError roundtrip
|
||||||
|
let original_disk_errors = vec![
|
||||||
|
DiskError::FileNotFound,
|
||||||
|
DiskError::VolumeNotFound,
|
||||||
|
DiskError::DiskFull,
|
||||||
|
DiskError::FileCorrupt,
|
||||||
|
DiskError::MethodNotAllowed,
|
||||||
|
];
|
||||||
|
|
||||||
|
for original_error in original_disk_errors {
|
||||||
|
// Convert to io::Error and back
|
||||||
|
let io_error: std::io::Error = original_error.clone().into();
|
||||||
|
let recovered_error: DiskError = io_error.into();
|
||||||
|
|
||||||
|
// For non-Io variants, they become Io(ErrorKind::Other) and then back to the original
|
||||||
|
match &original_error {
|
||||||
|
DiskError::Io(_) => {
|
||||||
|
// Io errors should maintain their kind
|
||||||
|
assert!(matches!(recovered_error, DiskError::Io(_)));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Other errors become Io(Other) and then are recovered via downcast
|
||||||
|
// The recovered error should be functionally equivalent
|
||||||
|
assert_eq!(original_error.to_u32(), recovered_error.to_u32());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_io_error_with_disk_error_inside() {
|
||||||
|
// Test that io::Error containing DiskError can be properly converted back
|
||||||
|
let original_disk_error = DiskError::FileNotFound;
|
||||||
|
let io_with_disk_error = std::io::Error::other(original_disk_error.clone());
|
||||||
|
|
||||||
|
// Convert io::Error back to DiskError
|
||||||
|
let recovered_disk_error: DiskError = io_with_disk_error.into();
|
||||||
|
assert_eq!(original_disk_error, recovered_disk_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_io_error_different_kinds() {
|
||||||
|
use std::io::ErrorKind;
|
||||||
|
|
||||||
|
let test_cases = vec![
|
||||||
|
(ErrorKind::NotFound, "file not found"),
|
||||||
|
(ErrorKind::PermissionDenied, "permission denied"),
|
||||||
|
(ErrorKind::ConnectionRefused, "connection refused"),
|
||||||
|
(ErrorKind::TimedOut, "timed out"),
|
||||||
|
(ErrorKind::InvalidInput, "invalid input"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (kind, message) in test_cases {
|
||||||
|
let io_error = std::io::Error::new(kind, message);
|
||||||
|
let disk_error: DiskError = io_error.into();
|
||||||
|
|
||||||
|
// Should become DiskError::Io with the same kind and message
|
||||||
|
match disk_error {
|
||||||
|
DiskError::Io(inner_io) => {
|
||||||
|
assert_eq!(inner_io.kind(), kind);
|
||||||
|
assert!(inner_io.to_string().contains(message));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected DiskError::Io variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_disk_error_to_io_error_preserves_information() {
|
||||||
|
let test_cases = vec![
|
||||||
|
DiskError::FileNotFound,
|
||||||
|
DiskError::VolumeNotFound,
|
||||||
|
DiskError::DiskFull,
|
||||||
|
DiskError::FileCorrupt,
|
||||||
|
DiskError::MethodNotAllowed,
|
||||||
|
DiskError::ErasureReadQuorum,
|
||||||
|
DiskError::ErasureWriteQuorum,
|
||||||
|
];
|
||||||
|
|
||||||
|
for disk_error in test_cases {
|
||||||
|
let io_error: std::io::Error = disk_error.clone().into();
|
||||||
|
|
||||||
|
// Error message should be preserved
|
||||||
|
assert!(io_error.to_string().contains(&disk_error.to_string()));
|
||||||
|
|
||||||
|
// Should be able to downcast back to DiskError
|
||||||
|
let recovered_error = io_error.downcast::<DiskError>();
|
||||||
|
assert!(recovered_error.is_ok());
|
||||||
|
assert_eq!(recovered_error.unwrap(), disk_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_io_error_downcast_chain() {
|
||||||
|
// Test nested error downcasting chain
|
||||||
|
let original_disk_error = DiskError::FileNotFound;
|
||||||
|
|
||||||
|
// Create a chain: DiskError -> io::Error -> DiskError -> io::Error
|
||||||
|
let io_error1: std::io::Error = original_disk_error.clone().into();
|
||||||
|
let disk_error2: DiskError = io_error1.into();
|
||||||
|
let io_error2: std::io::Error = disk_error2.into();
|
||||||
|
|
||||||
|
// Final io::Error should still contain the original DiskError
|
||||||
|
let final_disk_error = io_error2.downcast::<DiskError>();
|
||||||
|
assert!(final_disk_error.is_ok());
|
||||||
|
assert_eq!(final_disk_error.unwrap(), original_disk_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_io_error_with_original_io_content() {
|
||||||
|
// Test DiskError::Io variant preserves original io::Error
|
||||||
|
let original_io = std::io::Error::new(std::io::ErrorKind::BrokenPipe, "broken pipe");
|
||||||
|
let disk_error = DiskError::Io(original_io);
|
||||||
|
|
||||||
|
let converted_io: std::io::Error = disk_error.into();
|
||||||
|
assert_eq!(converted_io.kind(), std::io::ErrorKind::BrokenPipe);
|
||||||
|
assert!(converted_io.to_string().contains("broken pipe"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_display_preservation() {
|
||||||
|
let disk_errors = vec![
|
||||||
|
DiskError::MaxVersionsExceeded,
|
||||||
|
DiskError::CorruptedFormat,
|
||||||
|
DiskError::UnformattedDisk,
|
||||||
|
DiskError::DiskNotFound,
|
||||||
|
DiskError::FileAccessDenied,
|
||||||
|
];
|
||||||
|
|
||||||
|
for disk_error in disk_errors {
|
||||||
|
let original_message = disk_error.to_string();
|
||||||
|
let io_error: std::io::Error = disk_error.clone().into();
|
||||||
|
|
||||||
|
// The io::Error should contain the original error message
|
||||||
|
assert!(io_error.to_string().contains(&original_message));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use rustfs_rio::Reader;
|
|||||||
use super::Erasure;
|
use super::Erasure;
|
||||||
use crate::disk::error::Error;
|
use crate::disk::error::Error;
|
||||||
use crate::disk::error_reduce::count_errs;
|
use crate::disk::error_reduce::count_errs;
|
||||||
use crate::disk::error_reduce::{reduce_write_quorum_errs, OBJECT_OP_IGNORED_ERRS};
|
use crate::disk::error_reduce::{OBJECT_OP_IGNORED_ERRS, reduce_write_quorum_errs};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::vec;
|
use std::vec;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
@@ -62,15 +62,12 @@ impl<'a> MultiWriter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(write_err) = reduce_write_quorum_errs(&self.errs, OBJECT_OP_IGNORED_ERRS, self.write_quorum) {
|
if let Some(write_err) = reduce_write_quorum_errs(&self.errs, OBJECT_OP_IGNORED_ERRS, self.write_quorum) {
|
||||||
return Err(std::io::Error::new(
|
return Err(std::io::Error::other(format!(
|
||||||
std::io::ErrorKind::Other,
|
"Failed to write data: {} (offline-disks={}/{})",
|
||||||
format!(
|
write_err,
|
||||||
"Failed to write data: {} (offline-disks={}/{})",
|
count_errs(&self.errs, &Error::DiskNotFound),
|
||||||
write_err,
|
self.writers.len()
|
||||||
count_errs(&self.errs, &Error::DiskNotFound),
|
)));
|
||||||
self.writers.len()
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(std::io::Error::other(format!(
|
Err(std::io::Error::other(format!(
|
||||||
@@ -103,10 +100,7 @@ impl Erasure {
|
|||||||
total += n;
|
total += n;
|
||||||
let res = self.encode_data(&buf[..n])?;
|
let res = self.encode_data(&buf[..n])?;
|
||||||
if let Err(err) = tx.send(res).await {
|
if let Err(err) = tx.send(res).await {
|
||||||
return Err(std::io::Error::new(
|
return Err(std::io::Error::other(format!("Failed to send encoded data : {}", err)));
|
||||||
std::io::ErrorKind::Other,
|
|
||||||
format!("Failed to send encoded data : {}", err),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(_) => break,
|
Ok(_) => break,
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ use reed_solomon_erasure::galois_8::ReedSolomon;
|
|||||||
// use rustfs_rio::Reader;
|
// use rustfs_rio::Reader;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::ErrorKind;
|
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@@ -98,7 +97,7 @@ impl Erasure {
|
|||||||
if let Some(encoder) = self.encoder.as_ref() {
|
if let Some(encoder) = self.encoder.as_ref() {
|
||||||
encoder.encode(data_slices).map_err(|e| {
|
encoder.encode(data_slices).map_err(|e| {
|
||||||
error!("encode data error: {:?}", e);
|
error!("encode data error: {:?}", e);
|
||||||
io::Error::new(ErrorKind::Other, format!("encode data error {:?}", e))
|
io::Error::other(format!("encode data error {:?}", e))
|
||||||
})?;
|
})?;
|
||||||
} else {
|
} else {
|
||||||
warn!("parity_shards > 0, but encoder is None");
|
warn!("parity_shards > 0, but encoder is None");
|
||||||
@@ -129,7 +128,7 @@ impl Erasure {
|
|||||||
if let Some(encoder) = self.encoder.as_ref() {
|
if let Some(encoder) = self.encoder.as_ref() {
|
||||||
encoder.reconstruct(shards).map_err(|e| {
|
encoder.reconstruct(shards).map_err(|e| {
|
||||||
error!("decode data error: {:?}", e);
|
error!("decode data error: {:?}", e);
|
||||||
io::Error::new(ErrorKind::Other, format!("decode data error {:?}", e))
|
io::Error::other(format!("decode data error {:?}", e))
|
||||||
})?;
|
})?;
|
||||||
} else {
|
} else {
|
||||||
warn!("parity_shards > 0, but encoder is None");
|
warn!("parity_shards > 0, but encoder is None");
|
||||||
|
|||||||
+193
-2
@@ -732,7 +732,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_storage_error_to_u32() {
|
fn test_storage_error_to_u32() {
|
||||||
// Test Io error uses 0x01
|
// Test Io error uses 0x01
|
||||||
let io_error = StorageError::Io(IoError::new(ErrorKind::Other, "test"));
|
let io_error = StorageError::Io(IoError::other("test"));
|
||||||
assert_eq!(io_error.to_u32(), 0x01);
|
assert_eq!(io_error.to_u32(), 0x01);
|
||||||
|
|
||||||
// Test other errors have correct codes
|
// Test other errors have correct codes
|
||||||
@@ -781,7 +781,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_storage_error_from_disk_error() {
|
fn test_storage_error_from_disk_error() {
|
||||||
// Test conversion from DiskError
|
// Test conversion from DiskError
|
||||||
let disk_io = DiskError::Io(IoError::new(ErrorKind::Other, "disk io error"));
|
let disk_io = DiskError::Io(IoError::other("disk io error"));
|
||||||
let storage_error: StorageError = disk_io.into();
|
let storage_error: StorageError = disk_io.into();
|
||||||
assert!(matches!(storage_error, StorageError::Io(_)));
|
assert!(matches!(storage_error, StorageError::Io(_)));
|
||||||
|
|
||||||
@@ -877,4 +877,195 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_storage_error_io_roundtrip() {
|
||||||
|
// Test StorageError -> std::io::Error -> StorageError roundtrip conversion
|
||||||
|
let original_storage_errors = vec![
|
||||||
|
StorageError::FileNotFound,
|
||||||
|
StorageError::VolumeNotFound,
|
||||||
|
StorageError::DiskFull,
|
||||||
|
StorageError::FileCorrupt,
|
||||||
|
StorageError::MethodNotAllowed,
|
||||||
|
StorageError::BucketExists("test-bucket".to_string()),
|
||||||
|
StorageError::ObjectNotFound("bucket".to_string(), "object".to_string()),
|
||||||
|
];
|
||||||
|
|
||||||
|
for original_error in original_storage_errors {
|
||||||
|
// Convert to io::Error and back
|
||||||
|
let io_error: std::io::Error = original_error.clone().into();
|
||||||
|
let recovered_error: StorageError = io_error.into();
|
||||||
|
|
||||||
|
// Check that conversion preserves the essential error information
|
||||||
|
match &original_error {
|
||||||
|
StorageError::Io(_) => {
|
||||||
|
// Io errors should maintain their inner structure
|
||||||
|
assert!(matches!(recovered_error, StorageError::Io(_)));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Other errors should be recoverable via downcast or match to equivalent type
|
||||||
|
assert_eq!(original_error.to_u32(), recovered_error.to_u32());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_io_error_with_storage_error_inside() {
|
||||||
|
// Test that io::Error containing StorageError can be properly converted back
|
||||||
|
let original_storage_error = StorageError::FileNotFound;
|
||||||
|
let io_with_storage_error = std::io::Error::other(original_storage_error.clone());
|
||||||
|
|
||||||
|
// Convert io::Error back to StorageError
|
||||||
|
let recovered_storage_error: StorageError = io_with_storage_error.into();
|
||||||
|
assert_eq!(original_storage_error, recovered_storage_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_io_error_with_disk_error_inside() {
|
||||||
|
// Test io::Error containing DiskError -> StorageError conversion
|
||||||
|
let original_disk_error = DiskError::FileNotFound;
|
||||||
|
let io_with_disk_error = std::io::Error::other(original_disk_error.clone());
|
||||||
|
|
||||||
|
// Convert io::Error to StorageError
|
||||||
|
let storage_error: StorageError = io_with_disk_error.into();
|
||||||
|
assert_eq!(storage_error, StorageError::FileNotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nested_error_conversion_chain() {
|
||||||
|
// Test complex conversion chain: DiskError -> StorageError -> io::Error -> StorageError
|
||||||
|
let original_disk_error = DiskError::DiskFull;
|
||||||
|
let storage_error1: StorageError = original_disk_error.into();
|
||||||
|
let io_error: std::io::Error = storage_error1.into();
|
||||||
|
let storage_error2: StorageError = io_error.into();
|
||||||
|
|
||||||
|
assert_eq!(storage_error2, StorageError::DiskFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_storage_error_different_io_kinds() {
|
||||||
|
use std::io::ErrorKind;
|
||||||
|
|
||||||
|
let test_cases = vec![
|
||||||
|
(ErrorKind::NotFound, "not found"),
|
||||||
|
(ErrorKind::PermissionDenied, "permission denied"),
|
||||||
|
(ErrorKind::ConnectionRefused, "connection refused"),
|
||||||
|
(ErrorKind::TimedOut, "timed out"),
|
||||||
|
(ErrorKind::InvalidInput, "invalid input"),
|
||||||
|
(ErrorKind::BrokenPipe, "broken pipe"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (kind, message) in test_cases {
|
||||||
|
let io_error = std::io::Error::new(kind, message);
|
||||||
|
let storage_error: StorageError = io_error.into();
|
||||||
|
|
||||||
|
// Should become StorageError::Io with the same kind and message
|
||||||
|
match storage_error {
|
||||||
|
StorageError::Io(inner_io) => {
|
||||||
|
assert_eq!(inner_io.kind(), kind);
|
||||||
|
assert!(inner_io.to_string().contains(message));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected StorageError::Io variant for kind: {:?}", kind),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_storage_error_to_io_error_preserves_information() {
|
||||||
|
let test_cases = vec![
|
||||||
|
StorageError::FileNotFound,
|
||||||
|
StorageError::VolumeNotFound,
|
||||||
|
StorageError::DiskFull,
|
||||||
|
StorageError::FileCorrupt,
|
||||||
|
StorageError::MethodNotAllowed,
|
||||||
|
StorageError::StorageFull,
|
||||||
|
StorageError::SlowDown,
|
||||||
|
StorageError::BucketExists("test-bucket".to_string()),
|
||||||
|
];
|
||||||
|
|
||||||
|
for storage_error in test_cases {
|
||||||
|
let io_error: std::io::Error = storage_error.clone().into();
|
||||||
|
|
||||||
|
// Error message should be preserved
|
||||||
|
assert!(io_error.to_string().contains(&storage_error.to_string()));
|
||||||
|
|
||||||
|
// Should be able to downcast back to StorageError
|
||||||
|
let recovered_error = io_error.downcast::<StorageError>();
|
||||||
|
assert!(recovered_error.is_ok());
|
||||||
|
assert_eq!(recovered_error.unwrap(), storage_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_storage_error_io_variant_preservation() {
|
||||||
|
// Test StorageError::Io variant preserves original io::Error
|
||||||
|
let original_io = std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "unexpected eof");
|
||||||
|
let storage_error = StorageError::Io(original_io);
|
||||||
|
|
||||||
|
let converted_io: std::io::Error = storage_error.into();
|
||||||
|
assert_eq!(converted_io.kind(), std::io::ErrorKind::UnexpectedEof);
|
||||||
|
assert!(converted_io.to_string().contains("unexpected eof"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_filemeta_error_conversions() {
|
||||||
|
// Test conversions from rustfs_filemeta::Error
|
||||||
|
use rustfs_filemeta::Error as FilemetaError;
|
||||||
|
|
||||||
|
let filemeta_errors = vec![
|
||||||
|
(FilemetaError::FileNotFound, StorageError::FileNotFound),
|
||||||
|
(FilemetaError::FileVersionNotFound, StorageError::FileVersionNotFound),
|
||||||
|
(FilemetaError::FileCorrupt, StorageError::FileCorrupt),
|
||||||
|
(FilemetaError::MethodNotAllowed, StorageError::MethodNotAllowed),
|
||||||
|
(FilemetaError::VolumeNotFound, StorageError::VolumeNotFound),
|
||||||
|
(FilemetaError::DoneForNow, StorageError::DoneForNow),
|
||||||
|
(FilemetaError::Unexpected, StorageError::Unexpected),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (filemeta_error, expected_storage_error) in filemeta_errors {
|
||||||
|
let converted_storage_error: StorageError = filemeta_error.into();
|
||||||
|
assert_eq!(converted_storage_error, expected_storage_error);
|
||||||
|
|
||||||
|
// Test reverse conversion
|
||||||
|
let converted_back: rustfs_filemeta::Error = converted_storage_error.into();
|
||||||
|
assert_eq!(converted_back, expected_storage_error.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_message_consistency() {
|
||||||
|
let storage_errors = vec![
|
||||||
|
StorageError::BucketNotFound("test-bucket".to_string()),
|
||||||
|
StorageError::ObjectNotFound("bucket".to_string(), "object".to_string()),
|
||||||
|
StorageError::VersionNotFound("bucket".to_string(), "object".to_string(), "v1".to_string()),
|
||||||
|
StorageError::InvalidUploadID("bucket".to_string(), "object".to_string(), "upload123".to_string()),
|
||||||
|
];
|
||||||
|
|
||||||
|
for storage_error in storage_errors {
|
||||||
|
let original_message = storage_error.to_string();
|
||||||
|
let io_error: std::io::Error = storage_error.clone().into();
|
||||||
|
|
||||||
|
// The io::Error should contain the original error message or info
|
||||||
|
assert!(io_error.to_string().contains(&original_message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_equality_after_conversion() {
|
||||||
|
let storage_errors = vec![
|
||||||
|
StorageError::FileNotFound,
|
||||||
|
StorageError::VolumeNotFound,
|
||||||
|
StorageError::DiskFull,
|
||||||
|
StorageError::MethodNotAllowed,
|
||||||
|
];
|
||||||
|
|
||||||
|
for original_error in storage_errors {
|
||||||
|
// Test that equality is preserved through conversion
|
||||||
|
let io_error: std::io::Error = original_error.clone().into();
|
||||||
|
let recovered_error: StorageError = io_error.into();
|
||||||
|
|
||||||
|
assert_eq!(original_error, recovered_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ use std::{cmp::Ordering, env, path::PathBuf, sync::Arc, time::Duration};
|
|||||||
use tokio::{
|
use tokio::{
|
||||||
spawn,
|
spawn,
|
||||||
sync::{
|
sync::{
|
||||||
mpsc::{self, Receiver, Sender},
|
|
||||||
RwLock,
|
RwLock,
|
||||||
|
mpsc::{self, Receiver, Sender},
|
||||||
},
|
},
|
||||||
time::interval,
|
time::interval,
|
||||||
};
|
};
|
||||||
@@ -14,16 +14,16 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
heal_commands::HealOpts,
|
heal_commands::HealOpts,
|
||||||
heal_ops::{new_bg_heal_sequence, HealSequence},
|
heal_ops::{HealSequence, new_bg_heal_sequence},
|
||||||
};
|
};
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::global::GLOBAL_MRFState;
|
use crate::global::GLOBAL_MRFState;
|
||||||
use crate::heal::error::ERR_RETRY_HEALING;
|
use crate::heal::error::ERR_RETRY_HEALING;
|
||||||
use crate::heal::heal_commands::{HealScanMode, HEAL_ITEM_BUCKET};
|
use crate::heal::heal_commands::{HEAL_ITEM_BUCKET, HealScanMode};
|
||||||
use crate::heal::heal_ops::{HealSource, BG_HEALING_UUID};
|
use crate::heal::heal_ops::{BG_HEALING_UUID, HealSource};
|
||||||
use crate::{
|
use crate::{
|
||||||
config::RUSTFS_CONFIG_PREFIX,
|
config::RUSTFS_CONFIG_PREFIX,
|
||||||
disk::{endpoint::Endpoint, error::DiskError, DiskAPI, DiskInfoOptions, BUCKET_META_PREFIX, RUSTFS_META_BUCKET},
|
disk::{BUCKET_META_PREFIX, DiskAPI, DiskInfoOptions, RUSTFS_META_BUCKET, endpoint::Endpoint, error::DiskError},
|
||||||
global::{GLOBAL_BackgroundHealRoutine, GLOBAL_BackgroundHealState, GLOBAL_LOCAL_DISK_MAP},
|
global::{GLOBAL_BackgroundHealRoutine, GLOBAL_BackgroundHealState, GLOBAL_LOCAL_DISK_MAP},
|
||||||
heal::{
|
heal::{
|
||||||
data_usage::{DATA_USAGE_CACHE_NAME, DATA_USAGE_ROOT},
|
data_usage::{DATA_USAGE_CACHE_NAME, DATA_USAGE_ROOT},
|
||||||
@@ -34,7 +34,7 @@ use crate::{
|
|||||||
new_object_layer_fn,
|
new_object_layer_fn,
|
||||||
store::get_disk_via_endpoint,
|
store::get_disk_via_endpoint,
|
||||||
store_api::{BucketInfo, BucketOptions, StorageAPI},
|
store_api::{BucketInfo, BucketOptions, StorageAPI},
|
||||||
utils::path::{path_join, SLASH_SEPARATOR},
|
utils::path::{SLASH_SEPARATOR, path_join},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub static DEFAULT_MONITOR_NEW_DISK_INTERVAL: Duration = Duration::from_secs(10);
|
pub static DEFAULT_MONITOR_NEW_DISK_INTERVAL: Duration = Duration::from_secs(10);
|
||||||
@@ -149,7 +149,7 @@ async fn heal_fresh_disk(endpoint: &Endpoint) -> Result<()> {
|
|||||||
return Err(Error::other(format!(
|
return Err(Error::other(format!(
|
||||||
"Unexpected error disk must be initialized by now after formatting: {}",
|
"Unexpected error disk must be initialized by now after formatting: {}",
|
||||||
endpoint
|
endpoint
|
||||||
)))
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ use std::{
|
|||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering},
|
|
||||||
Arc,
|
Arc,
|
||||||
|
atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering},
|
||||||
},
|
},
|
||||||
time::{Duration, SystemTime},
|
time::{Duration, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
data_scanner_metric::{globalScannerMetrics, ScannerMetric, ScannerMetrics},
|
data_scanner_metric::{ScannerMetric, ScannerMetrics, globalScannerMetrics},
|
||||||
data_usage::{store_data_usage_in_backend, DATA_USAGE_BLOOM_NAME_PATH},
|
data_usage::{DATA_USAGE_BLOOM_NAME_PATH, store_data_usage_in_backend},
|
||||||
data_usage_cache::{DataUsageCache, DataUsageEntry, DataUsageHash},
|
data_usage_cache::{DataUsageCache, DataUsageEntry, DataUsageHash},
|
||||||
heal_commands::{HealScanMode, HEAL_DEEP_SCAN, HEAL_NORMAL_SCAN},
|
heal_commands::{HEAL_DEEP_SCAN, HEAL_NORMAL_SCAN, HealScanMode},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
bucket::{versioning::VersioningApi, versioning_sys::BucketVersioningSys},
|
bucket::{versioning::VersioningApi, versioning_sys::BucketVersioningSys},
|
||||||
@@ -24,7 +24,7 @@ use crate::{
|
|||||||
heal::data_usage::DATA_USAGE_ROOT,
|
heal::data_usage::DATA_USAGE_ROOT,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
cache_value::metacache_set::{list_path_raw, ListPathRawOptions},
|
cache_value::metacache_set::{ListPathRawOptions, list_path_raw},
|
||||||
config::{
|
config::{
|
||||||
com::{read_config, save_config},
|
com::{read_config, save_config},
|
||||||
heal::Config,
|
heal::Config,
|
||||||
@@ -33,22 +33,22 @@ use crate::{
|
|||||||
global::{GLOBAL_BackgroundHealState, GLOBAL_IsErasure, GLOBAL_IsErasureSD},
|
global::{GLOBAL_BackgroundHealState, GLOBAL_IsErasure, GLOBAL_IsErasureSD},
|
||||||
heal::{
|
heal::{
|
||||||
data_usage::BACKGROUND_HEAL_INFO_PATH,
|
data_usage::BACKGROUND_HEAL_INFO_PATH,
|
||||||
data_usage_cache::{hash_path, DataUsageHashMap},
|
data_usage_cache::{DataUsageHashMap, hash_path},
|
||||||
error::ERR_IGNORE_FILE_CONTRIB,
|
error::ERR_IGNORE_FILE_CONTRIB,
|
||||||
heal_commands::{HEAL_ITEM_BUCKET, HEAL_ITEM_OBJECT},
|
heal_commands::{HEAL_ITEM_BUCKET, HEAL_ITEM_OBJECT},
|
||||||
heal_ops::{HealSource, BG_HEALING_UUID},
|
heal_ops::{BG_HEALING_UUID, HealSource},
|
||||||
},
|
},
|
||||||
new_object_layer_fn,
|
new_object_layer_fn,
|
||||||
peer::is_reserved_or_invalid_bucket,
|
peer::is_reserved_or_invalid_bucket,
|
||||||
store::ECStore,
|
store::ECStore,
|
||||||
utils::path::{path_join, path_to_bucket_object, path_to_bucket_object_with_base_path, SLASH_SEPARATOR},
|
utils::path::{SLASH_SEPARATOR, path_join, path_to_bucket_object, path_to_bucket_object_with_base_path},
|
||||||
};
|
};
|
||||||
|
use crate::{disk::DiskAPI, store_api::ObjectInfo};
|
||||||
use crate::{
|
use crate::{
|
||||||
disk::error::DiskError,
|
disk::error::DiskError,
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
};
|
};
|
||||||
use crate::{disk::local::LocalDisk, heal::data_scanner_metric::current_path_updater};
|
use crate::{disk::local::LocalDisk, heal::data_scanner_metric::current_path_updater};
|
||||||
use crate::{disk::DiskAPI, store_api::ObjectInfo};
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@@ -58,9 +58,8 @@ use s3s::dto::{BucketLifecycleConfiguration, ExpirationStatus, LifecycleRule, Re
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::{
|
sync::{
|
||||||
broadcast,
|
RwLock, broadcast,
|
||||||
mpsc::{self, Sender},
|
mpsc::{self, Sender},
|
||||||
RwLock,
|
|
||||||
},
|
},
|
||||||
time::sleep,
|
time::sleep,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ use std::{
|
|||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicU64, Ordering},
|
|
||||||
Arc,
|
Arc,
|
||||||
|
atomic::{AtomicU64, Ordering},
|
||||||
},
|
},
|
||||||
time::{Duration, SystemTime},
|
time::{Duration, SystemTime},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use std::time::{Duration, SystemTime};
|
|||||||
use tokio::sync::mpsc::Sender;
|
use tokio::sync::mpsc::Sender;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
use super::data_scanner::{SizeSummary, DATA_SCANNER_FORCE_COMPACT_AT_FOLDERS};
|
use super::data_scanner::{DATA_SCANNER_FORCE_COMPACT_AT_FOLDERS, SizeSummary};
|
||||||
use super::data_usage::{BucketTargetUsageInfo, BucketUsageInfo, DataUsageInfo};
|
use super::data_usage::{BucketTargetUsageInfo, BucketUsageInfo, DataUsageInfo};
|
||||||
|
|
||||||
// DATA_USAGE_BUCKET_LEN must be length of ObjectsHistogramIntervals
|
// DATA_USAGE_BUCKET_LEN must be length of ObjectsHistogramIntervals
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use std::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::storageclass::{RRS, STANDARD},
|
config::storageclass::{RRS, STANDARD},
|
||||||
disk::{error::DiskError, DeleteOptions, DiskAPI, DiskStore, BUCKET_META_PREFIX, RUSTFS_META_BUCKET},
|
disk::{BUCKET_META_PREFIX, DeleteOptions, DiskAPI, DiskStore, RUSTFS_META_BUCKET, error::DiskError},
|
||||||
global::GLOBAL_BackgroundHealState,
|
global::GLOBAL_BackgroundHealState,
|
||||||
heal::heal_ops::HEALING_TRACKER_FILENAME,
|
heal::heal_ops::HEALING_TRACKER_FILENAME,
|
||||||
new_object_layer_fn,
|
new_object_layer_fn,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use super::{
|
|||||||
background_heal_ops::HealTask,
|
background_heal_ops::HealTask,
|
||||||
data_scanner::HEAL_DELETE_DANGLING,
|
data_scanner::HEAL_DELETE_DANGLING,
|
||||||
error::ERR_SKIP_FILE,
|
error::ERR_SKIP_FILE,
|
||||||
heal_commands::{HealOpts, HealScanMode, HealStopSuccess, HealingTracker, HEAL_ITEM_BUCKET_METADATA},
|
heal_commands::{HEAL_ITEM_BUCKET_METADATA, HealOpts, HealScanMode, HealStopSuccess, HealingTracker},
|
||||||
};
|
};
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::store_api::StorageAPI;
|
use crate::store_api::StorageAPI;
|
||||||
@@ -16,7 +16,7 @@ use crate::{
|
|||||||
disk::endpoint::Endpoint,
|
disk::endpoint::Endpoint,
|
||||||
endpoints::Endpoints,
|
endpoints::Endpoints,
|
||||||
global::GLOBAL_IsDistErasure,
|
global::GLOBAL_IsDistErasure,
|
||||||
heal::heal_commands::{HealStartSuccess, HEAL_UNKNOWN_SCAN},
|
heal::heal_commands::{HEAL_UNKNOWN_SCAN, HealStartSuccess},
|
||||||
new_object_layer_fn,
|
new_object_layer_fn,
|
||||||
utils::path::has_prefix,
|
utils::path::has_prefix,
|
||||||
};
|
};
|
||||||
@@ -41,10 +41,9 @@ use std::{
|
|||||||
use tokio::{
|
use tokio::{
|
||||||
select, spawn,
|
select, spawn,
|
||||||
sync::{
|
sync::{
|
||||||
broadcast,
|
RwLock, broadcast,
|
||||||
mpsc::{self, Receiver as M_Receiver, Sender as M_Sender},
|
mpsc::{self, Receiver as M_Receiver, Sender as M_Sender},
|
||||||
watch::{self, Receiver as W_Receiver, Sender as W_Sender},
|
watch::{self, Receiver as W_Receiver, Sender as W_Sender},
|
||||||
RwLock,
|
|
||||||
},
|
},
|
||||||
time::{interval, sleep},
|
time::{interval, sleep},
|
||||||
};
|
};
|
||||||
@@ -784,7 +783,10 @@ impl AllHealState {
|
|||||||
self.stop_heal_sequence(path_s).await?;
|
self.stop_heal_sequence(path_s).await?;
|
||||||
} else if let Some(hs) = self.get_heal_sequence(path_s).await {
|
} else if let Some(hs) = self.get_heal_sequence(path_s).await {
|
||||||
if !hs.has_ended().await {
|
if !hs.has_ended().await {
|
||||||
return Err(Error::other(format!("Heal is already running on the given path (use force-start option to stop and start afresh). The heal was started by IP {} at {:?}, token is {}", heal_sequence.client_address, heal_sequence.start_time, heal_sequence.client_token)));
|
return Err(Error::other(format!(
|
||||||
|
"Heal is already running on the given path (use force-start option to stop and start afresh). The heal was started by IP {} at {:?}, token is {}",
|
||||||
|
heal_sequence.client_address, heal_sequence.start_time, heal_sequence.client_token
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ use regex::Regex;
|
|||||||
use std::ops::Sub;
|
use std::ops::Sub;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::sync::mpsc::{Receiver, Sender};
|
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
use tokio::sync::mpsc::{Receiver, Sender};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
use crate::StorageAPI;
|
||||||
use crate::admin_server_info::get_commit_id;
|
use crate::admin_server_info::get_commit_id;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::global::{get_global_endpoints, GLOBAL_BOOT_TIME};
|
use crate::global::{GLOBAL_BOOT_TIME, get_global_endpoints};
|
||||||
use crate::peer_rest_client::PeerRestClient;
|
use crate::peer_rest_client::PeerRestClient;
|
||||||
use crate::StorageAPI;
|
|
||||||
use crate::{endpoints::EndpointServerPools, new_object_layer_fn};
|
use crate::{endpoints::EndpointServerPools, new_object_layer_fn};
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ use crate::{
|
|||||||
utils::net::XHost,
|
utils::net::XHost,
|
||||||
};
|
};
|
||||||
use madmin::{
|
use madmin::{
|
||||||
|
ServerProperties,
|
||||||
health::{Cpus, MemInfo, OsInfo, Partitions, ProcInfo, SysConfig, SysErrors, SysService},
|
health::{Cpus, MemInfo, OsInfo, Partitions, ProcInfo, SysConfig, SysErrors, SysService},
|
||||||
metrics::RealtimeMetrics,
|
metrics::RealtimeMetrics,
|
||||||
net::NetInfo,
|
net::NetInfo,
|
||||||
ServerProperties,
|
|
||||||
};
|
};
|
||||||
use protos::{
|
use protos::{
|
||||||
node_service_time_out_client,
|
node_service_time_out_client,
|
||||||
|
|||||||
@@ -3824,14 +3824,13 @@ impl ObjectIO for SetDisks {
|
|||||||
|
|
||||||
let sc_parity_drives = {
|
let sc_parity_drives = {
|
||||||
if let Some(sc) = GLOBAL_StorageClass.get() {
|
if let Some(sc) = GLOBAL_StorageClass.get() {
|
||||||
let a = sc.get_parity_for_sc(
|
sc.get_parity_for_sc(
|
||||||
user_defined
|
user_defined
|
||||||
.get(xhttp::AMZ_STORAGE_CLASS)
|
.get(xhttp::AMZ_STORAGE_CLASS)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.as_str(),
|
.as_str(),
|
||||||
);
|
)
|
||||||
a
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -4806,14 +4805,13 @@ impl StorageAPI for SetDisks {
|
|||||||
|
|
||||||
let sc_parity_drives = {
|
let sc_parity_drives = {
|
||||||
if let Some(sc) = GLOBAL_StorageClass.get() {
|
if let Some(sc) = GLOBAL_StorageClass.get() {
|
||||||
let a = sc.get_parity_for_sc(
|
sc.get_parity_for_sc(
|
||||||
user_defined
|
user_defined
|
||||||
.get(xhttp::AMZ_STORAGE_CLASS)
|
.get(xhttp::AMZ_STORAGE_CLASS)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.as_str(),
|
.as_str(),
|
||||||
);
|
)
|
||||||
a
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -5,15 +5,16 @@ use crate::disk::error_reduce::count_errs;
|
|||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::{
|
use crate::{
|
||||||
disk::{
|
disk::{
|
||||||
|
DiskAPI, DiskInfo, DiskOption, DiskStore,
|
||||||
error::DiskError,
|
error::DiskError,
|
||||||
format::{DistributionAlgoVersion, FormatV3},
|
format::{DistributionAlgoVersion, FormatV3},
|
||||||
new_disk, DiskAPI, DiskInfo, DiskOption, DiskStore,
|
new_disk,
|
||||||
},
|
},
|
||||||
endpoints::{Endpoints, PoolEndpoints},
|
endpoints::{Endpoints, PoolEndpoints},
|
||||||
error::StorageError,
|
error::StorageError,
|
||||||
global::{is_dist_erasure, GLOBAL_LOCAL_DISK_SET_DRIVES},
|
global::{GLOBAL_LOCAL_DISK_SET_DRIVES, is_dist_erasure},
|
||||||
heal::heal_commands::{
|
heal::heal_commands::{
|
||||||
HealOpts, DRIVE_STATE_CORRUPT, DRIVE_STATE_MISSING, DRIVE_STATE_OFFLINE, DRIVE_STATE_OK, HEAL_ITEM_METADATA,
|
DRIVE_STATE_CORRUPT, DRIVE_STATE_MISSING, DRIVE_STATE_OFFLINE, DRIVE_STATE_OK, HEAL_ITEM_METADATA, HealOpts,
|
||||||
},
|
},
|
||||||
set_disk::SetDisks,
|
set_disk::SetDisks,
|
||||||
store_api::{
|
store_api::{
|
||||||
@@ -27,7 +28,7 @@ use crate::{
|
|||||||
use common::globals::GLOBAL_Local_Node_Name;
|
use common::globals::GLOBAL_Local_Node_Name;
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use http::HeaderMap;
|
use http::HeaderMap;
|
||||||
use lock::{namespace_lock::NsLockMap, new_lock_api, LockApi};
|
use lock::{LockApi, namespace_lock::NsLockMap, new_lock_api};
|
||||||
use madmin::heal_commands::{HealDriveInfo, HealResultItem};
|
use madmin::heal_commands::{HealDriveInfo, HealResultItem};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|||||||
@@ -160,13 +160,7 @@ impl HTTPRangeSpec {
|
|||||||
Some(HTTPRangeSpec {
|
Some(HTTPRangeSpec {
|
||||||
is_suffix_length: false,
|
is_suffix_length: false,
|
||||||
start: start as usize,
|
start: start as usize,
|
||||||
end: {
|
end: { if end < 0 { None } else { Some(end as usize) } },
|
||||||
if end < 0 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(end as usize)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -827,7 +821,7 @@ pub trait StorageAPI: ObjectIO {
|
|||||||
opts: &HealOpts,
|
opts: &HealOpts,
|
||||||
) -> Result<(HealResultItem, Option<Error>)>;
|
) -> Result<(HealResultItem, Option<Error>)>;
|
||||||
async fn heal_objects(&self, bucket: &str, prefix: &str, opts: &HealOpts, hs: Arc<HealSequence>, is_meta: bool)
|
async fn heal_objects(&self, bucket: &str, prefix: &str, opts: &HealOpts, hs: Arc<HealSequence>, is_meta: bool)
|
||||||
-> Result<()>;
|
-> Result<()>;
|
||||||
async fn get_pool_and_set(&self, id: &str) -> Result<(Option<usize>, Option<usize>, Option<usize>)>;
|
async fn get_pool_and_set(&self, id: &str) -> Result<(Option<usize>, Option<usize>, Option<usize>)>;
|
||||||
async fn check_abandoned_parts(&self, bucket: &str, object: &str, opts: &HealOpts) -> Result<()>;
|
async fn check_abandoned_parts(&self, bucket: &str, object: &str, opts: &HealOpts) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
use crate::config::{storageclass, KVS};
|
use crate::config::{KVS, storageclass};
|
||||||
use crate::disk::error_reduce::{count_errs, reduce_write_quorum_errs};
|
use crate::disk::error_reduce::{count_errs, reduce_write_quorum_errs};
|
||||||
use crate::disk::{self, DiskAPI};
|
use crate::disk::{self, DiskAPI};
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::{
|
use crate::{
|
||||||
disk::{
|
disk::{
|
||||||
|
DiskInfoOptions, DiskOption, DiskStore, FORMAT_CONFIG_FILE, RUSTFS_META_BUCKET,
|
||||||
error::DiskError,
|
error::DiskError,
|
||||||
format::{FormatErasureVersion, FormatMetaVersion, FormatV3},
|
format::{FormatErasureVersion, FormatMetaVersion, FormatV3},
|
||||||
new_disk, DiskInfoOptions, DiskOption, DiskStore, FORMAT_CONFIG_FILE, RUSTFS_META_BUCKET,
|
new_disk,
|
||||||
},
|
},
|
||||||
endpoints::Endpoints,
|
endpoints::Endpoints,
|
||||||
heal::heal_commands::init_healing_tracker,
|
heal::heal_commands::init_healing_tracker,
|
||||||
};
|
};
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use std::collections::{hash_map::Entry, HashMap};
|
use std::collections::{HashMap, hash_map::Entry};
|
||||||
|
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ fn deep_match_rune(str_: &[u8], pattern: &[u8], simple: bool) -> bool {
|
|||||||
} else {
|
} else {
|
||||||
deep_match_rune(str_, &pattern[1..], simple)
|
deep_match_rune(str_, &pattern[1..], simple)
|
||||||
|| (!str_.is_empty() && deep_match_rune(&str_[1..], pattern, simple))
|
|| (!str_.is_empty() && deep_match_rune(&str_[1..], pattern, simple))
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
'?' => {
|
'?' => {
|
||||||
if str_.is_empty() {
|
if str_.is_empty() {
|
||||||
|
|||||||
@@ -97,6 +97,61 @@ pub enum Error {
|
|||||||
Io(std::io::Error),
|
Io(std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Error {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(Error::StringError(a), Error::StringError(b)) => a == b,
|
||||||
|
(Error::NoSuchUser(a), Error::NoSuchUser(b)) => a == b,
|
||||||
|
(Error::NoSuchAccount(a), Error::NoSuchAccount(b)) => a == b,
|
||||||
|
(Error::NoSuchServiceAccount(a), Error::NoSuchServiceAccount(b)) => a == b,
|
||||||
|
(Error::NoSuchTempAccount(a), Error::NoSuchTempAccount(b)) => a == b,
|
||||||
|
(Error::NoSuchGroup(a), Error::NoSuchGroup(b)) => a == b,
|
||||||
|
(Error::InvalidServiceType(a), Error::InvalidServiceType(b)) => a == b,
|
||||||
|
(Error::Io(a), Error::Io(b)) => a.kind() == b.kind() && a.to_string() == b.to_string(),
|
||||||
|
// For complex types like PolicyError, CryptoError, JWTError, compare string representations
|
||||||
|
(a, b) => std::mem::discriminant(a) == std::mem::discriminant(b) && a.to_string() == b.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Error {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
Error::PolicyError(e) => Error::StringError(e.to_string()), // Convert to string since PolicyError may not be cloneable
|
||||||
|
Error::StringError(s) => Error::StringError(s.clone()),
|
||||||
|
Error::CryptoError(e) => Error::StringError(format!("crypto: {}", e)), // Convert to string
|
||||||
|
Error::NoSuchUser(s) => Error::NoSuchUser(s.clone()),
|
||||||
|
Error::NoSuchAccount(s) => Error::NoSuchAccount(s.clone()),
|
||||||
|
Error::NoSuchServiceAccount(s) => Error::NoSuchServiceAccount(s.clone()),
|
||||||
|
Error::NoSuchTempAccount(s) => Error::NoSuchTempAccount(s.clone()),
|
||||||
|
Error::NoSuchGroup(s) => Error::NoSuchGroup(s.clone()),
|
||||||
|
Error::NoSuchPolicy => Error::NoSuchPolicy,
|
||||||
|
Error::PolicyInUse => Error::PolicyInUse,
|
||||||
|
Error::GroupNotEmpty => Error::GroupNotEmpty,
|
||||||
|
Error::InvalidArgument => Error::InvalidArgument,
|
||||||
|
Error::IamSysNotInitialized => Error::IamSysNotInitialized,
|
||||||
|
Error::InvalidServiceType(s) => Error::InvalidServiceType(s.clone()),
|
||||||
|
Error::ErrCredMalformed => Error::ErrCredMalformed,
|
||||||
|
Error::CredNotInitialized => Error::CredNotInitialized,
|
||||||
|
Error::InvalidAccessKeyLength => Error::InvalidAccessKeyLength,
|
||||||
|
Error::InvalidSecretKeyLength => Error::InvalidSecretKeyLength,
|
||||||
|
Error::ContainsReservedChars => Error::ContainsReservedChars,
|
||||||
|
Error::GroupNameContainsReservedChars => Error::GroupNameContainsReservedChars,
|
||||||
|
Error::JWTError(e) => Error::StringError(format!("jwt err {}", e)), // Convert to string
|
||||||
|
Error::NoAccessKey => Error::NoAccessKey,
|
||||||
|
Error::InvalidToken => Error::InvalidToken,
|
||||||
|
Error::InvalidAccessKey => Error::InvalidAccessKey,
|
||||||
|
Error::IAMActionNotAllowed => Error::IAMActionNotAllowed,
|
||||||
|
Error::InvalidExpiration => Error::InvalidExpiration,
|
||||||
|
Error::NoSecretKeyWithAccessKey => Error::NoSecretKeyWithAccessKey,
|
||||||
|
Error::NoAccessKeyWithSecretKey => Error::NoAccessKeyWithSecretKey,
|
||||||
|
Error::PolicyTooLarge => Error::PolicyTooLarge,
|
||||||
|
Error::ConfigNotFound => Error::ConfigNotFound,
|
||||||
|
Error::Io(e) => Error::Io(std::io::Error::new(e.kind(), e.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
pub fn other<E>(error: E) -> Self
|
pub fn other<E>(error: E) -> Self
|
||||||
where
|
where
|
||||||
@@ -225,3 +280,142 @@ pub fn is_err_no_such_service_account(err: &Error) -> bool {
|
|||||||
// Error::msg(e.to_string())
|
// Error::msg(e.to_string())
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::io::{Error as IoError, ErrorKind};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iam_error_to_io_error_conversion() {
|
||||||
|
let iam_errors = vec![
|
||||||
|
Error::NoSuchUser("testuser".to_string()),
|
||||||
|
Error::NoSuchAccount("testaccount".to_string()),
|
||||||
|
Error::InvalidArgument,
|
||||||
|
Error::IAMActionNotAllowed,
|
||||||
|
Error::PolicyTooLarge,
|
||||||
|
Error::ConfigNotFound,
|
||||||
|
];
|
||||||
|
|
||||||
|
for iam_error in iam_errors {
|
||||||
|
let io_error: std::io::Error = iam_error.clone().into();
|
||||||
|
|
||||||
|
// Check that conversion creates an io::Error
|
||||||
|
assert_eq!(io_error.kind(), ErrorKind::Other);
|
||||||
|
|
||||||
|
// Check that the error message is preserved
|
||||||
|
assert!(io_error.to_string().contains(&iam_error.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iam_error_from_storage_error() {
|
||||||
|
// Test conversion from StorageError
|
||||||
|
let storage_error = ecstore::error::StorageError::ConfigNotFound;
|
||||||
|
let iam_error: Error = storage_error.into();
|
||||||
|
assert_eq!(iam_error, Error::ConfigNotFound);
|
||||||
|
|
||||||
|
// Test reverse conversion
|
||||||
|
let back_to_storage: ecstore::error::StorageError = iam_error.into();
|
||||||
|
assert_eq!(back_to_storage, ecstore::error::StorageError::ConfigNotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iam_error_from_policy_error() {
|
||||||
|
use policy::error::Error as PolicyError;
|
||||||
|
|
||||||
|
let policy_errors = vec![
|
||||||
|
(PolicyError::NoSuchUser("user1".to_string()), Error::NoSuchUser("user1".to_string())),
|
||||||
|
(PolicyError::NoSuchPolicy, Error::NoSuchPolicy),
|
||||||
|
(PolicyError::InvalidArgument, Error::InvalidArgument),
|
||||||
|
(PolicyError::PolicyTooLarge, Error::PolicyTooLarge),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (policy_error, expected_iam_error) in policy_errors {
|
||||||
|
let converted_iam_error: Error = policy_error.into();
|
||||||
|
assert_eq!(converted_iam_error, expected_iam_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iam_error_other_function() {
|
||||||
|
let custom_error = "Custom IAM error";
|
||||||
|
let iam_error = Error::other(custom_error);
|
||||||
|
|
||||||
|
match iam_error {
|
||||||
|
Error::Io(io_error) => {
|
||||||
|
assert!(io_error.to_string().contains(custom_error));
|
||||||
|
assert_eq!(io_error.kind(), ErrorKind::Other);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Io variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iam_error_from_serde_json() {
|
||||||
|
// Test conversion from serde_json::Error
|
||||||
|
let invalid_json = r#"{"invalid": json}"#;
|
||||||
|
let json_error = serde_json::from_str::<serde_json::Value>(invalid_json).unwrap_err();
|
||||||
|
let iam_error: Error = json_error.into();
|
||||||
|
|
||||||
|
match iam_error {
|
||||||
|
Error::Io(io_error) => {
|
||||||
|
assert_eq!(io_error.kind(), ErrorKind::Other);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Io variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_helper_functions() {
|
||||||
|
// Test helper functions for error type checking
|
||||||
|
assert!(is_err_config_not_found(&Error::ConfigNotFound));
|
||||||
|
assert!(!is_err_config_not_found(&Error::NoSuchPolicy));
|
||||||
|
|
||||||
|
assert!(is_err_no_such_policy(&Error::NoSuchPolicy));
|
||||||
|
assert!(!is_err_no_such_policy(&Error::ConfigNotFound));
|
||||||
|
|
||||||
|
assert!(is_err_no_such_user(&Error::NoSuchUser("test".to_string())));
|
||||||
|
assert!(!is_err_no_such_user(&Error::NoSuchAccount("test".to_string())));
|
||||||
|
|
||||||
|
assert!(is_err_no_such_account(&Error::NoSuchAccount("test".to_string())));
|
||||||
|
assert!(!is_err_no_such_account(&Error::NoSuchUser("test".to_string())));
|
||||||
|
|
||||||
|
assert!(is_err_no_such_temp_account(&Error::NoSuchTempAccount("test".to_string())));
|
||||||
|
assert!(!is_err_no_such_temp_account(&Error::NoSuchAccount("test".to_string())));
|
||||||
|
|
||||||
|
assert!(is_err_no_such_group(&Error::NoSuchGroup("test".to_string())));
|
||||||
|
assert!(!is_err_no_such_group(&Error::NoSuchUser("test".to_string())));
|
||||||
|
|
||||||
|
assert!(is_err_no_such_service_account(&Error::NoSuchServiceAccount("test".to_string())));
|
||||||
|
assert!(!is_err_no_such_service_account(&Error::NoSuchAccount("test".to_string())));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iam_error_io_preservation() {
|
||||||
|
// Test that Io variant preserves original io::Error
|
||||||
|
let original_io = IoError::new(ErrorKind::PermissionDenied, "access denied");
|
||||||
|
let iam_error = Error::Io(original_io);
|
||||||
|
|
||||||
|
let converted_io: std::io::Error = iam_error.into();
|
||||||
|
// Note: Our clone implementation creates a new io::Error with the same kind and message
|
||||||
|
// but it becomes ErrorKind::Other when cloned
|
||||||
|
assert_eq!(converted_io.kind(), ErrorKind::Other);
|
||||||
|
assert!(converted_io.to_string().contains("access denied"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_display_format() {
|
||||||
|
let test_cases = vec![
|
||||||
|
(Error::NoSuchUser("testuser".to_string()), "user 'testuser' does not exist"),
|
||||||
|
(Error::NoSuchAccount("testaccount".to_string()), "account 'testaccount' does not exist"),
|
||||||
|
(Error::InvalidArgument, "invalid arguments specified"),
|
||||||
|
(Error::IAMActionNotAllowed, "action not allowed"),
|
||||||
|
(Error::ConfigNotFound, "config not found"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (error, expected_message) in test_cases {
|
||||||
|
assert_eq!(error.to_string(), expected_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+10
-11
@@ -1,22 +1,22 @@
|
|||||||
use crate::error::{is_err_config_not_found, Error, Result};
|
use crate::error::{Error, Result, is_err_config_not_found};
|
||||||
use crate::{
|
use crate::{
|
||||||
cache::{Cache, CacheEntity},
|
cache::{Cache, CacheEntity},
|
||||||
error::{is_err_no_such_group, is_err_no_such_policy, is_err_no_such_user, Error as IamError},
|
error::{Error as IamError, is_err_no_such_group, is_err_no_such_policy, is_err_no_such_user},
|
||||||
get_global_action_cred,
|
get_global_action_cred,
|
||||||
store::{object::IAM_CONFIG_PREFIX, GroupInfo, MappedPolicy, Store, UserType},
|
store::{GroupInfo, MappedPolicy, Store, UserType, object::IAM_CONFIG_PREFIX},
|
||||||
sys::{
|
sys::{
|
||||||
UpdateServiceAccountOpts, MAX_SVCSESSION_POLICY_SIZE, SESSION_POLICY_NAME, SESSION_POLICY_NAME_EXTRACTED,
|
MAX_SVCSESSION_POLICY_SIZE, SESSION_POLICY_NAME, SESSION_POLICY_NAME_EXTRACTED, STATUS_DISABLED, STATUS_ENABLED,
|
||||||
STATUS_DISABLED, STATUS_ENABLED,
|
UpdateServiceAccountOpts,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use ecstore::utils::{crypto::base64_encode, path::path_join_buf};
|
use ecstore::utils::{crypto::base64_encode, path::path_join_buf};
|
||||||
use madmin::{AccountStatus, AddOrUpdateUserReq, GroupDesc};
|
use madmin::{AccountStatus, AddOrUpdateUserReq, GroupDesc};
|
||||||
use policy::{
|
use policy::{
|
||||||
arn::ARN,
|
arn::ARN,
|
||||||
auth::{self, get_claims_from_token_with_secret, is_secret_key_valid, jwt_sign, Credentials, UserIdentity},
|
auth::{self, Credentials, UserIdentity, get_claims_from_token_with_secret, is_secret_key_valid, jwt_sign},
|
||||||
format::Format,
|
format::Format,
|
||||||
policy::{
|
policy::{
|
||||||
default::DEFAULT_POLICIES, iam_policy_claim_name_sa, Policy, PolicyDoc, EMBEDDED_POLICY_TYPE, INHERITED_POLICY_TYPE,
|
EMBEDDED_POLICY_TYPE, INHERITED_POLICY_TYPE, Policy, PolicyDoc, default::DEFAULT_POLICIES, iam_policy_claim_name_sa,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -24,8 +24,8 @@ use serde_json::Value;
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, AtomicI64, Ordering},
|
|
||||||
Arc,
|
Arc,
|
||||||
|
atomic::{AtomicBool, AtomicI64, Ordering},
|
||||||
},
|
},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
@@ -633,7 +633,7 @@ where
|
|||||||
Cache::add_or_update(&self.cache.user_policies, name, p, OffsetDateTime::now_utc());
|
Cache::add_or_update(&self.cache.user_policies, name, p, OffsetDateTime::now_utc());
|
||||||
p.clone()
|
p.clone()
|
||||||
} else {
|
} else {
|
||||||
let mp = match self.cache.sts_policies.load().get(name) {
|
match self.cache.sts_policies.load().get(name) {
|
||||||
Some(p) => p.clone(),
|
Some(p) => p.clone(),
|
||||||
None => {
|
None => {
|
||||||
let mut m = HashMap::new();
|
let mut m = HashMap::new();
|
||||||
@@ -645,8 +645,7 @@ where
|
|||||||
MappedPolicy::default()
|
MappedPolicy::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
mp
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-2
@@ -3,7 +3,7 @@ pub mod object;
|
|||||||
use crate::cache::Cache;
|
use crate::cache::Cache;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use policy::{auth::UserIdentity, policy::PolicyDoc};
|
use policy::{auth::UserIdentity, policy::PolicyDoc};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ pub trait Store: Clone + Send + Sync + 'static {
|
|||||||
m: &mut HashMap<String, MappedPolicy>,
|
m: &mut HashMap<String, MappedPolicy>,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
async fn load_mapped_policys(&self, user_type: UserType, is_group: bool, m: &mut HashMap<String, MappedPolicy>)
|
async fn load_mapped_policys(&self, user_type: UserType, is_group: bool, m: &mut HashMap<String, MappedPolicy>)
|
||||||
-> Result<()>;
|
-> Result<()>;
|
||||||
|
|
||||||
async fn load_all(&self, cache: &Cache) -> Result<()>;
|
async fn load_all(&self, cache: &Cache) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use super::{GroupInfo, MappedPolicy, Store, UserType};
|
use super::{GroupInfo, MappedPolicy, Store, UserType};
|
||||||
use crate::error::{is_err_config_not_found, Error, Result};
|
use crate::error::{Error, Result, is_err_config_not_found};
|
||||||
use crate::{
|
use crate::{
|
||||||
cache::{Cache, CacheEntity},
|
cache::{Cache, CacheEntity},
|
||||||
error::{is_err_no_such_policy, is_err_no_such_user},
|
error::{is_err_no_such_policy, is_err_no_such_user},
|
||||||
@@ -8,18 +8,18 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use ecstore::{
|
use ecstore::{
|
||||||
config::{
|
config::{
|
||||||
com::{delete_config, read_config, read_config_with_metadata, save_config},
|
|
||||||
RUSTFS_CONFIG_PREFIX,
|
RUSTFS_CONFIG_PREFIX,
|
||||||
|
com::{delete_config, read_config, read_config_with_metadata, save_config},
|
||||||
},
|
},
|
||||||
store::ECStore,
|
store::ECStore,
|
||||||
store_api::{ObjectInfo, ObjectOptions},
|
store_api::{ObjectInfo, ObjectOptions},
|
||||||
store_list_objects::{ObjectInfoOrErr, WalkOptions},
|
store_list_objects::{ObjectInfoOrErr, WalkOptions},
|
||||||
utils::path::{path_join_buf, SLASH_SEPARATOR},
|
utils::path::{SLASH_SEPARATOR, path_join_buf},
|
||||||
};
|
};
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use policy::{auth::UserIdentity, policy::PolicyDoc};
|
use policy::{auth::UserIdentity, policy::PolicyDoc};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{Serialize, de::DeserializeOwned};
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
use tokio::sync::broadcast::{self, Receiver as B_Receiver};
|
use tokio::sync::broadcast::{self, Receiver as B_Receiver};
|
||||||
use tokio::sync::mpsc::{self, Sender};
|
use tokio::sync::mpsc::{self, Sender};
|
||||||
|
|||||||
+9
-9
@@ -1,11 +1,11 @@
|
|||||||
|
use crate::error::Error as IamError;
|
||||||
use crate::error::is_err_no_such_account;
|
use crate::error::is_err_no_such_account;
|
||||||
use crate::error::is_err_no_such_temp_account;
|
use crate::error::is_err_no_such_temp_account;
|
||||||
use crate::error::Error as IamError;
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::get_global_action_cred;
|
use crate::get_global_action_cred;
|
||||||
|
use crate::manager::IamCache;
|
||||||
use crate::manager::extract_jwt_claims;
|
use crate::manager::extract_jwt_claims;
|
||||||
use crate::manager::get_default_policyes;
|
use crate::manager::get_default_policyes;
|
||||||
use crate::manager::IamCache;
|
|
||||||
use crate::store::MappedPolicy;
|
use crate::store::MappedPolicy;
|
||||||
use crate::store::Store;
|
use crate::store::Store;
|
||||||
use crate::store::UserType;
|
use crate::store::UserType;
|
||||||
@@ -14,22 +14,22 @@ use ecstore::utils::crypto::base64_encode;
|
|||||||
use madmin::AddOrUpdateUserReq;
|
use madmin::AddOrUpdateUserReq;
|
||||||
use madmin::GroupDesc;
|
use madmin::GroupDesc;
|
||||||
use policy::arn::ARN;
|
use policy::arn::ARN;
|
||||||
|
use policy::auth::ACCOUNT_ON;
|
||||||
|
use policy::auth::Credentials;
|
||||||
|
use policy::auth::UserIdentity;
|
||||||
use policy::auth::contains_reserved_chars;
|
use policy::auth::contains_reserved_chars;
|
||||||
use policy::auth::create_new_credentials_with_metadata;
|
use policy::auth::create_new_credentials_with_metadata;
|
||||||
use policy::auth::generate_credentials;
|
use policy::auth::generate_credentials;
|
||||||
use policy::auth::is_access_key_valid;
|
use policy::auth::is_access_key_valid;
|
||||||
use policy::auth::is_secret_key_valid;
|
use policy::auth::is_secret_key_valid;
|
||||||
use policy::auth::Credentials;
|
|
||||||
use policy::auth::UserIdentity;
|
|
||||||
use policy::auth::ACCOUNT_ON;
|
|
||||||
use policy::policy::iam_policy_claim_name_sa;
|
|
||||||
use policy::policy::Args;
|
use policy::policy::Args;
|
||||||
use policy::policy::Policy;
|
|
||||||
use policy::policy::PolicyDoc;
|
|
||||||
use policy::policy::EMBEDDED_POLICY_TYPE;
|
use policy::policy::EMBEDDED_POLICY_TYPE;
|
||||||
use policy::policy::INHERITED_POLICY_TYPE;
|
use policy::policy::INHERITED_POLICY_TYPE;
|
||||||
use serde_json::json;
|
use policy::policy::Policy;
|
||||||
|
use policy::policy::PolicyDoc;
|
||||||
|
use policy::policy::iam_policy_claim_name_sa;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
use serde_json::json;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header};
|
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header};
|
||||||
use rand::{Rng, RngCore};
|
use rand::{Rng, RngCore};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{Serialize, de::DeserializeOwned};
|
||||||
use std::io::{Error, Result};
|
use std::io::{Error, Result};
|
||||||
|
|
||||||
pub fn gen_access_key(length: usize) -> Result<String> {
|
pub fn gen_access_key(length: usize) -> Result<String> {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use crate::error::Error as IamError;
|
use crate::error::Error as IamError;
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::policy::{iam_policy_claim_name_sa, Policy, Validator, INHERITED_POLICY_TYPE};
|
use crate::policy::{INHERITED_POLICY_TYPE, Policy, Validator, iam_policy_claim_name_sa};
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
use crate::utils::extract_claims;
|
use crate::utils::extract_claims;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{Value, json};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use time::macros::offset;
|
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
use time::macros::offset;
|
||||||
|
|
||||||
const ACCESS_KEY_MIN_LEN: usize = 3;
|
const ACCESS_KEY_MIN_LEN: usize = 3;
|
||||||
const ACCESS_KEY_MAX_LEN: usize = 20;
|
const ACCESS_KEY_MAX_LEN: usize = 20;
|
||||||
|
|||||||
@@ -160,3 +160,204 @@ pub fn is_err_no_such_group(err: &Error) -> bool {
|
|||||||
pub fn is_err_no_such_service_account(err: &Error) -> bool {
|
pub fn is_err_no_such_service_account(err: &Error) -> bool {
|
||||||
matches!(err, Error::NoSuchServiceAccount(_))
|
matches!(err, Error::NoSuchServiceAccount(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::io::{Error as IoError, ErrorKind};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_policy_error_from_io_error() {
|
||||||
|
let io_error = IoError::new(ErrorKind::PermissionDenied, "permission denied");
|
||||||
|
let policy_error: Error = io_error.into();
|
||||||
|
|
||||||
|
match policy_error {
|
||||||
|
Error::Io(inner_io) => {
|
||||||
|
assert_eq!(inner_io.kind(), ErrorKind::PermissionDenied);
|
||||||
|
assert!(inner_io.to_string().contains("permission denied"));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Io variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_policy_error_other_function() {
|
||||||
|
let custom_error = "Custom policy error";
|
||||||
|
let policy_error = Error::other(custom_error);
|
||||||
|
|
||||||
|
match policy_error {
|
||||||
|
Error::Io(io_error) => {
|
||||||
|
assert!(io_error.to_string().contains(custom_error));
|
||||||
|
assert_eq!(io_error.kind(), ErrorKind::Other);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Io variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_policy_error_from_crypto_error() {
|
||||||
|
// Test conversion from crypto::Error - use an actual variant
|
||||||
|
let crypto_error = crypto::Error::ErrUnexpectedHeader;
|
||||||
|
let policy_error: Error = crypto_error.into();
|
||||||
|
|
||||||
|
match policy_error {
|
||||||
|
Error::CryptoError(_) => {
|
||||||
|
// Verify the conversion worked
|
||||||
|
assert!(policy_error.to_string().contains("crypto"));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected CryptoError variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_policy_error_from_jwt_error() {
|
||||||
|
use jsonwebtoken::{Algorithm, DecodingKey, Validation, decode};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct Claims {
|
||||||
|
sub: String,
|
||||||
|
exp: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an invalid JWT to generate a JWT error
|
||||||
|
let invalid_token = "invalid.jwt.token";
|
||||||
|
let key = DecodingKey::from_secret(b"secret");
|
||||||
|
let validation = Validation::new(Algorithm::HS256);
|
||||||
|
|
||||||
|
let jwt_result = decode::<Claims>(invalid_token, &key, &validation);
|
||||||
|
assert!(jwt_result.is_err());
|
||||||
|
|
||||||
|
let jwt_error = jwt_result.unwrap_err();
|
||||||
|
let policy_error: Error = jwt_error.into();
|
||||||
|
|
||||||
|
match policy_error {
|
||||||
|
Error::JWTError(_) => {
|
||||||
|
// Verify the conversion worked
|
||||||
|
assert!(policy_error.to_string().contains("jwt err"));
|
||||||
|
}
|
||||||
|
_ => panic!("Expected JWTError variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_policy_error_from_serde_json() {
|
||||||
|
// Test conversion from serde_json::Error
|
||||||
|
let invalid_json = r#"{"invalid": json}"#;
|
||||||
|
let json_error = serde_json::from_str::<serde_json::Value>(invalid_json).unwrap_err();
|
||||||
|
let policy_error: Error = json_error.into();
|
||||||
|
|
||||||
|
match policy_error {
|
||||||
|
Error::Io(io_error) => {
|
||||||
|
assert_eq!(io_error.kind(), ErrorKind::Other);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Io variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_policy_error_from_time_component_range() {
|
||||||
|
use time::{Date, Month};
|
||||||
|
|
||||||
|
// Create an invalid date to generate a ComponentRange error
|
||||||
|
let time_result = Date::from_calendar_date(2023, Month::January, 32); // Invalid day
|
||||||
|
assert!(time_result.is_err());
|
||||||
|
|
||||||
|
let time_error = time_result.unwrap_err();
|
||||||
|
let policy_error: Error = time_error.into();
|
||||||
|
|
||||||
|
match policy_error {
|
||||||
|
Error::Io(io_error) => {
|
||||||
|
assert_eq!(io_error.kind(), ErrorKind::Other);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Io variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[allow(clippy::invalid_regex)]
|
||||||
|
fn test_policy_error_from_regex_error() {
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
// Create an invalid regex to generate a regex error (unclosed bracket)
|
||||||
|
let regex_result = Regex::new("[");
|
||||||
|
assert!(regex_result.is_err());
|
||||||
|
|
||||||
|
let regex_error = regex_result.unwrap_err();
|
||||||
|
let policy_error: Error = regex_error.into();
|
||||||
|
|
||||||
|
match policy_error {
|
||||||
|
Error::Io(io_error) => {
|
||||||
|
assert_eq!(io_error.kind(), ErrorKind::Other);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected Io variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_helper_functions() {
|
||||||
|
// Test helper functions for error type checking
|
||||||
|
assert!(is_err_no_such_policy(&Error::NoSuchPolicy));
|
||||||
|
assert!(!is_err_no_such_policy(&Error::NoSuchUser("test".to_string())));
|
||||||
|
|
||||||
|
assert!(is_err_no_such_user(&Error::NoSuchUser("test".to_string())));
|
||||||
|
assert!(!is_err_no_such_user(&Error::NoSuchAccount("test".to_string())));
|
||||||
|
|
||||||
|
assert!(is_err_no_such_account(&Error::NoSuchAccount("test".to_string())));
|
||||||
|
assert!(!is_err_no_such_account(&Error::NoSuchUser("test".to_string())));
|
||||||
|
|
||||||
|
assert!(is_err_no_such_temp_account(&Error::NoSuchTempAccount("test".to_string())));
|
||||||
|
assert!(!is_err_no_such_temp_account(&Error::NoSuchAccount("test".to_string())));
|
||||||
|
|
||||||
|
assert!(is_err_no_such_group(&Error::NoSuchGroup("test".to_string())));
|
||||||
|
assert!(!is_err_no_such_group(&Error::NoSuchUser("test".to_string())));
|
||||||
|
|
||||||
|
assert!(is_err_no_such_service_account(&Error::NoSuchServiceAccount("test".to_string())));
|
||||||
|
assert!(!is_err_no_such_service_account(&Error::NoSuchAccount("test".to_string())));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_error_display_format() {
|
||||||
|
let test_cases = vec![
|
||||||
|
(Error::NoSuchUser("testuser".to_string()), "user 'testuser' does not exist"),
|
||||||
|
(Error::NoSuchAccount("testaccount".to_string()), "account 'testaccount' does not exist"),
|
||||||
|
(
|
||||||
|
Error::NoSuchServiceAccount("service1".to_string()),
|
||||||
|
"service account 'service1' does not exist",
|
||||||
|
),
|
||||||
|
(Error::NoSuchTempAccount("temp1".to_string()), "temp account 'temp1' does not exist"),
|
||||||
|
(Error::NoSuchGroup("group1".to_string()), "group 'group1' does not exist"),
|
||||||
|
(Error::NoSuchPolicy, "policy does not exist"),
|
||||||
|
(Error::PolicyInUse, "policy in use"),
|
||||||
|
(Error::GroupNotEmpty, "group not empty"),
|
||||||
|
(Error::InvalidArgument, "invalid arguments specified"),
|
||||||
|
(Error::IamSysNotInitialized, "not initialized"),
|
||||||
|
(Error::InvalidServiceType("invalid".to_string()), "invalid service type: invalid"),
|
||||||
|
(Error::ErrCredMalformed, "malformed credential"),
|
||||||
|
(Error::CredNotInitialized, "CredNotInitialized"),
|
||||||
|
(Error::InvalidAccessKeyLength, "invalid access key length"),
|
||||||
|
(Error::InvalidSecretKeyLength, "invalid secret key length"),
|
||||||
|
(Error::ContainsReservedChars, "access key contains reserved characters =,"),
|
||||||
|
(Error::GroupNameContainsReservedChars, "group name contains reserved characters =,"),
|
||||||
|
(Error::NoAccessKey, "no access key"),
|
||||||
|
(Error::InvalidToken, "invalid token"),
|
||||||
|
(Error::InvalidAccessKey, "invalid access_key"),
|
||||||
|
(Error::IAMActionNotAllowed, "action not allowed"),
|
||||||
|
(Error::InvalidExpiration, "invalid expiration"),
|
||||||
|
(Error::NoSecretKeyWithAccessKey, "no secret key with access key"),
|
||||||
|
(Error::NoAccessKeyWithSecretKey, "no access key with secret key"),
|
||||||
|
(Error::PolicyTooLarge, "policy too large"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (error, expected_message) in test_cases {
|
||||||
|
assert_eq!(error.to_string(), expected_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_string_error_variant() {
|
||||||
|
let custom_message = "Custom error message";
|
||||||
|
let error = Error::StringError(custom_message.to_string());
|
||||||
|
assert_eq!(error.to_string(), custom_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::{collections::HashSet, ops::Deref};
|
use std::{collections::HashSet, ops::Deref};
|
||||||
use strum::{EnumString, IntoStaticStr};
|
use strum::{EnumString, IntoStaticStr};
|
||||||
|
|
||||||
use super::{utils::wildcard, Error as IamError, Validator};
|
use super::{Error as IamError, Validator, utils::wildcard};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
||||||
pub struct ActionSet(pub HashSet<Action>);
|
pub struct ActionSet(pub HashSet<Action>);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::policy::function::condition::Condition;
|
use crate::policy::function::condition::Condition;
|
||||||
use serde::ser::SerializeMap;
|
use serde::ser::SerializeMap;
|
||||||
use serde::{de, Deserialize, Serialize, Serializer};
|
use serde::{Deserialize, Serialize, Serializer, de};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
@@ -163,12 +163,12 @@ pub struct Value;
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::policy::Functions;
|
||||||
use crate::policy::function::condition::Condition::*;
|
use crate::policy::function::condition::Condition::*;
|
||||||
use crate::policy::function::func::FuncKeyValue;
|
use crate::policy::function::func::FuncKeyValue;
|
||||||
use crate::policy::function::key::Key;
|
use crate::policy::function::key::Key;
|
||||||
use crate::policy::function::string::StringFunc;
|
use crate::policy::function::string::StringFunc;
|
||||||
use crate::policy::function::string::StringFuncValue;
|
use crate::policy::function::string::StringFuncValue;
|
||||||
use crate::policy::Functions;
|
|
||||||
use test_case::test_case;
|
use test_case::test_case;
|
||||||
|
|
||||||
#[test_case(
|
#[test_case(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::func::InnerFunc;
|
use super::func::InnerFunc;
|
||||||
use ipnetwork::IpNetwork;
|
use ipnetwork::IpNetwork;
|
||||||
use serde::{de::Visitor, Deserialize, Serialize};
|
use serde::{Deserialize, Serialize, de::Visitor};
|
||||||
use std::{borrow::Cow, collections::HashMap, net::IpAddr};
|
use std::{borrow::Cow, collections::HashMap, net::IpAddr};
|
||||||
|
|
||||||
pub type AddrFunc = InnerFunc<AddrFuncValue>;
|
pub type AddrFunc = InnerFunc<AddrFuncValue>;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::func::InnerFunc;
|
use super::func::InnerFunc;
|
||||||
use serde::de::{Error, IgnoredAny, SeqAccess};
|
use serde::de::{Error, IgnoredAny, SeqAccess};
|
||||||
use serde::{de, Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize, de};
|
||||||
use std::{collections::HashMap, fmt};
|
use std::{collections::HashMap, fmt};
|
||||||
|
|
||||||
pub type BoolFunc = InnerFunc<BoolFuncValue>;
|
pub type BoolFunc = InnerFunc<BoolFuncValue>;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
use serde::de::{Error, MapAccess};
|
use serde::de::{Error, MapAccess};
|
||||||
use serde::ser::SerializeMap;
|
use serde::ser::SerializeMap;
|
||||||
use serde::Deserialize;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
@@ -122,11 +122,7 @@ impl Condition {
|
|||||||
DateGreaterThanEquals(s) => s.evaluate(OffsetDateTime::ge, values),
|
DateGreaterThanEquals(s) => s.evaluate(OffsetDateTime::ge, values),
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.is_negate() {
|
if self.is_negate() { !r } else { r }
|
||||||
!r
|
|
||||||
} else {
|
|
||||||
r
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use super::func::InnerFunc;
|
use super::func::InnerFunc;
|
||||||
use serde::{de, Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize, de};
|
||||||
use std::{collections::HashMap, fmt};
|
use std::{collections::HashMap, fmt};
|
||||||
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
|
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
|
||||||
|
|
||||||
pub type DateFunc = InnerFunc<DateFuncValue>;
|
pub type DateFunc = InnerFunc<DateFuncValue>;
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ mod tests {
|
|||||||
key_name::S3KeyName::*,
|
key_name::S3KeyName::*,
|
||||||
};
|
};
|
||||||
use test_case::test_case;
|
use test_case::test_case;
|
||||||
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
|
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
|
||||||
|
|
||||||
fn new_func(name: KeyName, variable: Option<String>, value: &str) -> DateFunc {
|
fn new_func(name: KeyName, variable: Option<String>, value: &str) -> DateFunc {
|
||||||
DateFunc {
|
DateFunc {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use serde::{
|
use serde::{
|
||||||
de::{self, Visitor},
|
|
||||||
Deserialize, Deserializer, Serialize,
|
Deserialize, Deserializer, Serialize,
|
||||||
|
de::{self, Visitor},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::key::Key;
|
use super::key::Key;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use super::func::InnerFunc;
|
use super::func::InnerFunc;
|
||||||
use serde::{
|
use serde::{
|
||||||
de::{Error, Visitor},
|
|
||||||
Deserialize, Deserializer, Serialize,
|
Deserialize, Deserializer, Serialize,
|
||||||
|
de::{Error, Visitor},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type NumberFunc = InnerFunc<NumberFuncValue>;
|
pub type NumberFunc = InnerFunc<NumberFuncValue>;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use std::{borrow::Cow, collections::HashMap};
|
|||||||
|
|
||||||
use crate::policy::function::func::FuncKeyValue;
|
use crate::policy::function::func::FuncKeyValue;
|
||||||
use crate::policy::utils::wildcard;
|
use crate::policy::utils::wildcard;
|
||||||
use serde::{de, ser::SerializeSeq, Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize, de, ser::SerializeSeq};
|
||||||
|
|
||||||
use super::{func::InnerFunc, key_name::KeyName};
|
use super::{func::InnerFunc, key_name::KeyName};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::{action::Action, statement::BPStatement, Effect, Error as IamError, Statement, ID};
|
use super::{Effect, Error as IamError, ID, Statement, action::Action, statement::BPStatement};
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@@ -252,9 +252,9 @@ pub mod default {
|
|||||||
use std::{collections::HashSet, sync::LazyLock};
|
use std::{collections::HashSet, sync::LazyLock};
|
||||||
|
|
||||||
use crate::policy::{
|
use crate::policy::{
|
||||||
|
ActionSet, DEFAULT_VERSION, Effect, Functions, ResourceSet, Statement,
|
||||||
action::{Action, AdminAction, KmsAction, S3Action},
|
action::{Action, AdminAction, KmsAction, S3Action},
|
||||||
resource::Resource,
|
resource::Resource,
|
||||||
ActionSet, Effect, Functions, ResourceSet, Statement, DEFAULT_VERSION,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Policy;
|
use super::Policy;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use super::{utils::wildcard, Validator};
|
use super::{Validator, utils::wildcard};
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
Error as IamError, Validator,
|
||||||
function::key_name::KeyName,
|
function::key_name::KeyName,
|
||||||
utils::{path, wildcard},
|
utils::{path, wildcard},
|
||||||
Error as IamError, Validator,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::{
|
use super::{
|
||||||
action::Action, ActionSet, Args, BucketPolicyArgs, Effect, Error as IamError, Functions, Principal, ResourceSet, Validator,
|
ActionSet, Args, BucketPolicyArgs, Effect, Error as IamError, Functions, ID, Principal, ResourceSet, Validator,
|
||||||
ID,
|
action::Action,
|
||||||
};
|
};
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|||||||
@@ -85,11 +85,7 @@ pub fn clean(path: &str) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if out.w == 0 {
|
if out.w == 0 { ".".into() } else { out.string() }
|
||||||
".".into()
|
|
||||||
} else {
|
|
||||||
out.string()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header};
|
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header};
|
||||||
use rand::{Rng, RngCore};
|
use rand::{Rng, RngCore};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{Serialize, de::DeserializeOwned};
|
||||||
use std::io::{Error, Result};
|
use std::io::{Error, Result};
|
||||||
|
|
||||||
pub fn gen_access_key(length: usize) -> Result<String> {
|
pub fn gen_access_key(length: usize) -> Result<String> {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use policy::policy::action::Action;
|
|
||||||
use policy::policy::action::S3Action::*;
|
|
||||||
use policy::policy::ActionSet;
|
use policy::policy::ActionSet;
|
||||||
use policy::policy::Effect::*;
|
use policy::policy::Effect::*;
|
||||||
|
use policy::policy::action::Action;
|
||||||
|
use policy::policy::action::S3Action::*;
|
||||||
use policy::policy::*;
|
use policy::policy::*;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use iam::{
|
|||||||
};
|
};
|
||||||
use madmin::GroupAddRemove;
|
use madmin::GroupAddRemove;
|
||||||
use matchit::Params;
|
use matchit::Params;
|
||||||
use s3s::{header::CONTENT_TYPE, s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result};
|
use s3s::{Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result, header::CONTENT_TYPE, s3_error};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_urlencoded::from_bytes;
|
use serde_urlencoded::from_bytes;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use http::{HeaderMap, StatusCode};
|
|||||||
use iam::{error::is_err_no_such_user, get_global_action_cred, store::MappedPolicy};
|
use iam::{error::is_err_no_such_user, get_global_action_cred, store::MappedPolicy};
|
||||||
use matchit::Params;
|
use matchit::Params;
|
||||||
use policy::policy::Policy;
|
use policy::policy::Policy;
|
||||||
use s3s::{header::CONTENT_TYPE, s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result};
|
use s3s::{Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result, header::CONTENT_TYPE, s3_error};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_urlencoded::from_bytes;
|
use serde_urlencoded::from_bytes;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use matchit::Params;
|
|||||||
use policy::policy::action::{Action, AdminAction};
|
use policy::policy::action::{Action, AdminAction};
|
||||||
use policy::policy::{Args, Policy};
|
use policy::policy::{Args, Policy};
|
||||||
use s3s::S3ErrorCode::InvalidRequest;
|
use s3s::S3ErrorCode::InvalidRequest;
|
||||||
use s3s::{header::CONTENT_TYPE, s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result};
|
use s3s::{Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result, header::CONTENT_TYPE, s3_error};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_urlencoded::from_bytes;
|
use serde_urlencoded::from_bytes;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ use iam::{manager::get_token_signing_key, sys::SESSION_POLICY_NAME};
|
|||||||
use matchit::Params;
|
use matchit::Params;
|
||||||
use policy::{auth::get_new_credentials_with_metadata, policy::Policy};
|
use policy::{auth::get_new_credentials_with_metadata, policy::Policy};
|
||||||
use s3s::{
|
use s3s::{
|
||||||
|
Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result,
|
||||||
dto::{AssumeRoleOutput, Credentials, Timestamp},
|
dto::{AssumeRoleOutput, Credentials, Timestamp},
|
||||||
s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result,
|
s3_error,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use ecstore::{peer_rest_client::PeerRestClient, GLOBAL_Endpoints};
|
use ecstore::{GLOBAL_Endpoints, peer_rest_client::PeerRestClient};
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use hyper::Uri;
|
use hyper::Uri;
|
||||||
use madmin::service_commands::ServiceTraceOpts;
|
use madmin::service_commands::ServiceTraceOpts;
|
||||||
use matchit::Params;
|
use matchit::Params;
|
||||||
use s3s::{s3_error, Body, S3Request, S3Response, S3Result};
|
use s3s::{Body, S3Request, S3Response, S3Result, s3_error};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::admin::router::Operation;
|
use crate::admin::router::Operation;
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ use iam::get_global_action_cred;
|
|||||||
use madmin::{AccountStatus, AddOrUpdateUserReq};
|
use madmin::{AccountStatus, AddOrUpdateUserReq};
|
||||||
use matchit::Params;
|
use matchit::Params;
|
||||||
use policy::policy::{
|
use policy::policy::{
|
||||||
action::{Action, AdminAction},
|
|
||||||
Args,
|
Args,
|
||||||
|
action::{Action, AdminAction},
|
||||||
};
|
};
|
||||||
use s3s::{header::CONTENT_TYPE, s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result};
|
use s3s::{Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result, header::CONTENT_TYPE, s3_error};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_urlencoded::from_bytes;
|
use serde_urlencoded::from_bytes;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|||||||
+3
-3
@@ -7,13 +7,13 @@ use iam::get_global_action_cred;
|
|||||||
use iam::sys::SESSION_POLICY_NAME;
|
use iam::sys::SESSION_POLICY_NAME;
|
||||||
use policy::auth;
|
use policy::auth;
|
||||||
use policy::auth::get_claims_from_token_with_secret;
|
use policy::auth::get_claims_from_token_with_secret;
|
||||||
|
use s3s::S3Error;
|
||||||
|
use s3s::S3ErrorCode;
|
||||||
|
use s3s::S3Result;
|
||||||
use s3s::auth::S3Auth;
|
use s3s::auth::S3Auth;
|
||||||
use s3s::auth::SecretKey;
|
use s3s::auth::SecretKey;
|
||||||
use s3s::auth::SimpleAuth;
|
use s3s::auth::SimpleAuth;
|
||||||
use s3s::s3_error;
|
use s3s::s3_error;
|
||||||
use s3s::S3Error;
|
|
||||||
use s3s::S3ErrorCode;
|
|
||||||
use s3s::S3Result;
|
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
pub struct IAMAuth {
|
pub struct IAMAuth {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use crate::license::get_license;
|
use crate::license::get_license;
|
||||||
use axum::{
|
use axum::{
|
||||||
|
Router,
|
||||||
body::Body,
|
body::Body,
|
||||||
http::{Response, StatusCode},
|
http::{Response, StatusCode},
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
routing::get,
|
routing::get,
|
||||||
Router,
|
|
||||||
};
|
};
|
||||||
use axum_extra::extract::Host;
|
use axum_extra::extract::Host;
|
||||||
use rustfs_config::{RUSTFS_TLS_CERT, RUSTFS_TLS_KEY};
|
use rustfs_config::{RUSTFS_TLS_CERT, RUSTFS_TLS_KEY};
|
||||||
@@ -12,7 +12,7 @@ use std::io;
|
|||||||
|
|
||||||
use axum::response::Redirect;
|
use axum::response::Redirect;
|
||||||
use axum_server::tls_rustls::RustlsConfig;
|
use axum_server::tls_rustls::RustlsConfig;
|
||||||
use http::{header, Uri};
|
use http::{Uri, header};
|
||||||
use mime_guess::from_path;
|
use mime_guess::from_path;
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|||||||
@@ -94,3 +94,238 @@ impl From<iam::error::Error> for ApiError {
|
|||||||
serr.into()
|
serr.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use s3s::{S3Error, S3ErrorCode};
|
||||||
|
use std::io::{Error as IoError, ErrorKind};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_api_error_from_io_error() {
|
||||||
|
let io_error = IoError::new(ErrorKind::PermissionDenied, "permission denied");
|
||||||
|
let api_error: ApiError = io_error.into();
|
||||||
|
|
||||||
|
assert_eq!(api_error.code, S3ErrorCode::InternalError);
|
||||||
|
assert!(api_error.message.contains("permission denied"));
|
||||||
|
assert!(api_error.source.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_api_error_from_io_error_different_kinds() {
|
||||||
|
let test_cases = vec![
|
||||||
|
(ErrorKind::NotFound, "not found"),
|
||||||
|
(ErrorKind::InvalidInput, "invalid input"),
|
||||||
|
(ErrorKind::TimedOut, "timed out"),
|
||||||
|
(ErrorKind::WriteZero, "write zero"),
|
||||||
|
(ErrorKind::Other, "other error"),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (kind, message) in test_cases {
|
||||||
|
let io_error = IoError::new(kind, message);
|
||||||
|
let api_error: ApiError = io_error.into();
|
||||||
|
|
||||||
|
assert_eq!(api_error.code, S3ErrorCode::InternalError);
|
||||||
|
assert!(api_error.message.contains(message));
|
||||||
|
assert!(api_error.source.is_some());
|
||||||
|
|
||||||
|
// Test that source can be downcast back to io::Error
|
||||||
|
let source = api_error.source.as_ref().unwrap();
|
||||||
|
let downcast_io_error = source.downcast_ref::<IoError>();
|
||||||
|
assert!(downcast_io_error.is_some());
|
||||||
|
assert_eq!(downcast_io_error.unwrap().kind(), kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_api_error_other_function() {
|
||||||
|
let custom_error = "Custom API error";
|
||||||
|
let api_error = ApiError::other(custom_error);
|
||||||
|
|
||||||
|
assert_eq!(api_error.code, S3ErrorCode::InternalError);
|
||||||
|
assert_eq!(api_error.message, custom_error);
|
||||||
|
assert!(api_error.source.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_api_error_other_function_with_complex_error() {
|
||||||
|
let io_error = IoError::new(ErrorKind::InvalidData, "complex error");
|
||||||
|
let api_error = ApiError::other(io_error);
|
||||||
|
|
||||||
|
assert_eq!(api_error.code, S3ErrorCode::InternalError);
|
||||||
|
assert!(api_error.message.contains("complex error"));
|
||||||
|
assert!(api_error.source.is_some());
|
||||||
|
|
||||||
|
// Test that source can be downcast back to io::Error
|
||||||
|
let source = api_error.source.as_ref().unwrap();
|
||||||
|
let downcast_io_error = source.downcast_ref::<IoError>();
|
||||||
|
assert!(downcast_io_error.is_some());
|
||||||
|
assert_eq!(downcast_io_error.unwrap().kind(), ErrorKind::InvalidData);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_api_error_from_storage_error() {
|
||||||
|
let storage_error = StorageError::BucketNotFound("test-bucket".to_string());
|
||||||
|
let api_error: ApiError = storage_error.into();
|
||||||
|
|
||||||
|
assert_eq!(api_error.code, S3ErrorCode::NoSuchBucket);
|
||||||
|
assert!(api_error.message.contains("test-bucket"));
|
||||||
|
assert!(api_error.source.is_some());
|
||||||
|
|
||||||
|
// Test that source can be downcast back to StorageError
|
||||||
|
let source = api_error.source.as_ref().unwrap();
|
||||||
|
let downcast_storage_error = source.downcast_ref::<StorageError>();
|
||||||
|
assert!(downcast_storage_error.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_api_error_from_storage_error_mappings() {
|
||||||
|
let test_cases = vec![
|
||||||
|
(StorageError::NotImplemented, S3ErrorCode::NotImplemented),
|
||||||
|
(
|
||||||
|
StorageError::InvalidArgument("test".into(), "test".into(), "test".into()),
|
||||||
|
S3ErrorCode::InvalidArgument,
|
||||||
|
),
|
||||||
|
(StorageError::MethodNotAllowed, S3ErrorCode::MethodNotAllowed),
|
||||||
|
(StorageError::BucketNotFound("test".into()), S3ErrorCode::NoSuchBucket),
|
||||||
|
(StorageError::BucketNotEmpty("test".into()), S3ErrorCode::BucketNotEmpty),
|
||||||
|
(StorageError::BucketNameInvalid("test".into()), S3ErrorCode::InvalidBucketName),
|
||||||
|
(
|
||||||
|
StorageError::ObjectNameInvalid("test".into(), "test".into()),
|
||||||
|
S3ErrorCode::InvalidArgument,
|
||||||
|
),
|
||||||
|
(StorageError::BucketExists("test".into()), S3ErrorCode::BucketAlreadyExists),
|
||||||
|
(StorageError::StorageFull, S3ErrorCode::ServiceUnavailable),
|
||||||
|
(StorageError::SlowDown, S3ErrorCode::SlowDown),
|
||||||
|
(StorageError::PrefixAccessDenied("test".into(), "test".into()), S3ErrorCode::AccessDenied),
|
||||||
|
(StorageError::ObjectNotFound("test".into(), "test".into()), S3ErrorCode::NoSuchKey),
|
||||||
|
(StorageError::ConfigNotFound, S3ErrorCode::NoSuchKey),
|
||||||
|
(StorageError::VolumeNotFound, S3ErrorCode::NoSuchBucket),
|
||||||
|
(StorageError::FileNotFound, S3ErrorCode::NoSuchKey),
|
||||||
|
(StorageError::FileVersionNotFound, S3ErrorCode::NoSuchVersion),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (storage_error, expected_code) in test_cases {
|
||||||
|
let api_error: ApiError = storage_error.into();
|
||||||
|
assert_eq!(api_error.code, expected_code);
|
||||||
|
assert!(api_error.source.is_some());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_api_error_from_iam_error() {
|
||||||
|
let iam_error = iam::error::Error::other("IAM test error");
|
||||||
|
let api_error: ApiError = iam_error.into();
|
||||||
|
|
||||||
|
// IAM error is first converted to StorageError, then to ApiError
|
||||||
|
assert!(api_error.source.is_some());
|
||||||
|
assert!(api_error.message.contains("test error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_api_error_to_s3_error() {
|
||||||
|
let api_error = ApiError {
|
||||||
|
code: S3ErrorCode::NoSuchBucket,
|
||||||
|
message: "Bucket not found".to_string(),
|
||||||
|
source: Some(Box::new(IoError::new(ErrorKind::NotFound, "not found"))),
|
||||||
|
};
|
||||||
|
|
||||||
|
let s3_error: S3Error = api_error.into();
|
||||||
|
assert_eq!(*s3_error.code(), S3ErrorCode::NoSuchBucket);
|
||||||
|
assert!(s3_error.message().unwrap_or("").contains("Bucket not found"));
|
||||||
|
assert!(s3_error.source().is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_api_error_to_s3_error_without_source() {
|
||||||
|
let api_error = ApiError {
|
||||||
|
code: S3ErrorCode::InvalidArgument,
|
||||||
|
message: "Invalid argument".to_string(),
|
||||||
|
source: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let s3_error: S3Error = api_error.into();
|
||||||
|
assert_eq!(*s3_error.code(), S3ErrorCode::InvalidArgument);
|
||||||
|
assert!(s3_error.message().unwrap_or("").contains("Invalid argument"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_api_error_display() {
|
||||||
|
let api_error = ApiError {
|
||||||
|
code: S3ErrorCode::InternalError,
|
||||||
|
message: "Test error message".to_string(),
|
||||||
|
source: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(api_error.to_string(), "Test error message");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_api_error_debug() {
|
||||||
|
let api_error = ApiError {
|
||||||
|
code: S3ErrorCode::NoSuchKey,
|
||||||
|
message: "Object not found".to_string(),
|
||||||
|
source: Some(Box::new(IoError::new(ErrorKind::NotFound, "file not found"))),
|
||||||
|
};
|
||||||
|
|
||||||
|
let debug_str = format!("{:?}", api_error);
|
||||||
|
assert!(debug_str.contains("NoSuchKey"));
|
||||||
|
assert!(debug_str.contains("Object not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_api_error_roundtrip_through_io_error() {
|
||||||
|
let original_io_error = IoError::new(ErrorKind::PermissionDenied, "original permission error");
|
||||||
|
|
||||||
|
// Convert to ApiError
|
||||||
|
let api_error: ApiError = original_io_error.into();
|
||||||
|
|
||||||
|
// Verify the conversion preserved the information
|
||||||
|
assert_eq!(api_error.code, S3ErrorCode::InternalError);
|
||||||
|
assert!(api_error.message.contains("original permission error"));
|
||||||
|
assert!(api_error.source.is_some());
|
||||||
|
|
||||||
|
// Test that we can downcast back to the original io::Error
|
||||||
|
let source = api_error.source.as_ref().unwrap();
|
||||||
|
let downcast_io_error = source.downcast_ref::<IoError>();
|
||||||
|
assert!(downcast_io_error.is_some());
|
||||||
|
assert_eq!(downcast_io_error.unwrap().kind(), ErrorKind::PermissionDenied);
|
||||||
|
assert!(downcast_io_error.unwrap().to_string().contains("original permission error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_api_error_chain_conversion() {
|
||||||
|
// Start with an io::Error
|
||||||
|
let io_error = IoError::new(ErrorKind::InvalidData, "invalid data");
|
||||||
|
|
||||||
|
// Convert to StorageError (simulating what happens in the codebase)
|
||||||
|
let storage_error = StorageError::other(io_error);
|
||||||
|
|
||||||
|
// Convert to ApiError
|
||||||
|
let api_error: ApiError = storage_error.into();
|
||||||
|
|
||||||
|
// Verify the chain is preserved
|
||||||
|
assert!(api_error.source.is_some());
|
||||||
|
|
||||||
|
// Check that we can still access the original error information
|
||||||
|
let source = api_error.source.as_ref().unwrap();
|
||||||
|
let downcast_storage_error = source.downcast_ref::<StorageError>();
|
||||||
|
assert!(downcast_storage_error.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_api_error_error_trait_implementation() {
|
||||||
|
let api_error = ApiError {
|
||||||
|
code: S3ErrorCode::InternalError,
|
||||||
|
message: "Test error".to_string(),
|
||||||
|
source: Some(Box::new(IoError::other("source error"))),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test that it implements std::error::Error
|
||||||
|
let error: &dyn std::error::Error = &api_error;
|
||||||
|
assert_eq!(error.to_string(), "Test error");
|
||||||
|
// ApiError doesn't implement Error::source() properly, so this would be None
|
||||||
|
// This is expected because ApiError is not a typical Error implementation
|
||||||
|
assert!(error.source().is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
mod service_state;
|
mod service_state;
|
||||||
pub(crate) use service_state::wait_for_shutdown;
|
pub(crate) use service_state::SHUTDOWN_TIMEOUT;
|
||||||
pub(crate) use service_state::ServiceState;
|
pub(crate) use service_state::ServiceState;
|
||||||
pub(crate) use service_state::ServiceStateManager;
|
pub(crate) use service_state::ServiceStateManager;
|
||||||
pub(crate) use service_state::ShutdownSignal;
|
pub(crate) use service_state::ShutdownSignal;
|
||||||
pub(crate) use service_state::SHUTDOWN_TIMEOUT;
|
pub(crate) use service_state::wait_for_shutdown;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use atomic_enum::atomic_enum;
|
use atomic_enum::atomic_enum;
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::signal::unix::{signal, SignalKind};
|
use tokio::signal::unix::{SignalKind, signal};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
// a configurable shutdown timeout
|
// a configurable shutdown timeout
|
||||||
@@ -10,7 +10,7 @@ pub(crate) const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(1);
|
|||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn notify_systemd(state: &str) {
|
fn notify_systemd(state: &str) {
|
||||||
use libsystemd::daemon::{notify, NotifyState};
|
use libsystemd::daemon::{NotifyState, notify};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
let notify_state = match state {
|
let notify_state = match state {
|
||||||
"ready" => NotifyState::Ready,
|
"ready" => NotifyState::Ready,
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ use async_trait::async_trait;
|
|||||||
use crate::QueryResult;
|
use crate::QueryResult;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
Query,
|
||||||
execution::{Output, QueryStateMachine},
|
execution::{Output, QueryStateMachine},
|
||||||
logical_planner::Plan,
|
logical_planner::Plan,
|
||||||
Query,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
@@ -13,9 +13,9 @@ use futures::{Stream, StreamExt, TryStreamExt};
|
|||||||
|
|
||||||
use crate::{QueryError, QueryResult};
|
use crate::{QueryError, QueryResult};
|
||||||
|
|
||||||
|
use super::Query;
|
||||||
use super::logical_planner::Plan;
|
use super::logical_planner::Plan;
|
||||||
use super::session::SessionCtx;
|
use super::session::SessionCtx;
|
||||||
use super::Query;
|
|
||||||
|
|
||||||
pub type QueryExecutionRef = Arc<dyn QueryExecution>;
|
pub type QueryExecutionRef = Arc<dyn QueryExecution>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use datafusion::{
|
use datafusion::{
|
||||||
execution::{context::SessionState, runtime_env::RuntimeEnvBuilder, SessionStateBuilder},
|
execution::{SessionStateBuilder, context::SessionState, runtime_env::RuntimeEnvBuilder},
|
||||||
parquet::data_type::AsBytes,
|
parquet::data_type::AsBytes,
|
||||||
prelude::SessionContext,
|
prelude::SessionContext,
|
||||||
};
|
};
|
||||||
use object_store::{memory::InMemory, path::Path, ObjectStore};
|
use object_store::{ObjectStore, memory::InMemory, path::Path};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use crate::{object_store::EcObjectStore, QueryError, QueryResult};
|
use crate::{QueryError, QueryResult, object_store::EcObjectStore};
|
||||||
|
|
||||||
use super::Context;
|
use super::Context;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
QueryResult,
|
||||||
query::{
|
query::{
|
||||||
|
Query,
|
||||||
execution::{Output, QueryStateMachineRef},
|
execution::{Output, QueryStateMachineRef},
|
||||||
logical_planner::Plan,
|
logical_planner::Plan,
|
||||||
Query,
|
|
||||||
},
|
},
|
||||||
QueryResult,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct QueryHandle {
|
pub struct QueryHandle {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use async_trait::async_trait;
|
|||||||
use datafusion::arrow::datatypes::SchemaRef;
|
use datafusion::arrow::datatypes::SchemaRef;
|
||||||
use datafusion::common::Result as DFResult;
|
use datafusion::common::Result as DFResult;
|
||||||
use datafusion::datasource::listing::ListingTable;
|
use datafusion::datasource::listing::ListingTable;
|
||||||
use datafusion::datasource::{provider_as_source, TableProvider};
|
use datafusion::datasource::{TableProvider, provider_as_source};
|
||||||
use datafusion::error::DataFusionError;
|
use datafusion::error::DataFusionError;
|
||||||
use datafusion::logical_expr::{LogicalPlan, LogicalPlanBuilder, TableProviderFilterPushDown, TableSource};
|
use datafusion::logical_expr::{LogicalPlan, LogicalPlanBuilder, TableProviderFilterPushDown, TableSource};
|
||||||
use datafusion::prelude::Expr;
|
use datafusion::prelude::Expr;
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use api::{
|
use api::{
|
||||||
|
QueryError, QueryResult,
|
||||||
query::{
|
query::{
|
||||||
|
Query,
|
||||||
ast::ExtStatement,
|
ast::ExtStatement,
|
||||||
dispatcher::QueryDispatcher,
|
dispatcher::QueryDispatcher,
|
||||||
execution::{Output, QueryStateMachine},
|
execution::{Output, QueryStateMachine},
|
||||||
@@ -14,9 +16,7 @@ use api::{
|
|||||||
logical_planner::{LogicalPlanner, Plan},
|
logical_planner::{LogicalPlanner, Plan},
|
||||||
parser::Parser,
|
parser::Parser,
|
||||||
session::{SessionCtx, SessionCtxFactory},
|
session::{SessionCtx, SessionCtxFactory},
|
||||||
Query,
|
|
||||||
},
|
},
|
||||||
QueryError, QueryResult,
|
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use datafusion::{
|
use datafusion::{
|
||||||
@@ -37,7 +37,7 @@ use s3s::dto::{FileHeaderInfo, SelectObjectContentInput};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
execution::factory::QueryExecutionFactoryRef,
|
execution::factory::QueryExecutionFactoryRef,
|
||||||
metadata::{base_table::BaseTableProvider, ContextProviderExtension, MetadataProvider, TableHandleProviderRef},
|
metadata::{ContextProviderExtension, MetadataProvider, TableHandleProviderRef, base_table::BaseTableProvider},
|
||||||
sql::logical::planner::DefaultLogicalPlanner,
|
sql::logical::planner::DefaultLogicalPlanner,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use api::{
|
use api::{
|
||||||
|
QueryError,
|
||||||
query::{
|
query::{
|
||||||
execution::{QueryExecutionFactory, QueryExecutionRef, QueryStateMachineRef},
|
execution::{QueryExecutionFactory, QueryExecutionRef, QueryStateMachineRef},
|
||||||
logical_planner::Plan,
|
logical_planner::Plan,
|
||||||
optimizer::Optimizer,
|
optimizer::Optimizer,
|
||||||
scheduler::SchedulerRef,
|
scheduler::SchedulerRef,
|
||||||
},
|
},
|
||||||
QueryError,
|
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use api::query::scheduler::{ExecutionResults, Scheduler};
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use datafusion::error::DataFusionError;
|
use datafusion::error::DataFusionError;
|
||||||
use datafusion::execution::context::TaskContext;
|
use datafusion::execution::context::TaskContext;
|
||||||
use datafusion::physical_plan::{execute_stream, ExecutionPlan};
|
use datafusion::physical_plan::{ExecutionPlan, execute_stream};
|
||||||
|
|
||||||
pub struct LocalScheduler {}
|
pub struct LocalScheduler {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use api::{
|
use api::{
|
||||||
|
QueryResult,
|
||||||
query::{
|
query::{
|
||||||
dispatcher::QueryDispatcher, execution::QueryStateMachineRef, logical_planner::Plan, session::SessionCtxFactory, Query,
|
Query, dispatcher::QueryDispatcher, execution::QueryStateMachineRef, logical_planner::Plan, session::SessionCtxFactory,
|
||||||
},
|
},
|
||||||
server::dbms::{DatabaseManagerSystem, QueryHandle},
|
server::dbms::{DatabaseManagerSystem, QueryHandle},
|
||||||
QueryResult,
|
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use derive_builder::Builder;
|
use derive_builder::Builder;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user