mirror of
https://github.com/rustfs/rustfs.git
synced 2026-01-17 01:30:33 +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::io::AsyncWriteExt;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
use tokio::sync::{Mutex, mpsc};
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[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:65535"), Some(65535));
|
||||
assert_eq!(ServiceManager::extract_port("host:65536"), None); // Out of range
|
||||
// IPv6-like address - extract_port takes the second part after split(':')
|
||||
// For "::1:8080", split(':') gives ["", "", "1", "8080"], nth(1) gives ""
|
||||
// IPv6-like address - extract_port takes the second part after split(':')
|
||||
// For "::1:8080", split(':') gives ["", "", "1", "8080"], nth(1) gives ""
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -1091,9 +1091,9 @@ pub mod node_service_client {
|
||||
F: tonic::service::Interceptor,
|
||||
T::ResponseBody: Default,
|
||||
T: tonic::codegen::Service<
|
||||
http::Request<tonic::body::Body>,
|
||||
Response = http::Response<<T as tonic::client::GrpcService<tonic::body::Body>>::ResponseBody>,
|
||||
>,
|
||||
http::Request<tonic::body::Body>,
|
||||
Response = http::Response<<T as tonic::client::GrpcService<tonic::body::Body>>::ResponseBody>,
|
||||
>,
|
||||
<T as tonic::codegen::Service<http::Request<tonic::body::Body>>>::Error:
|
||||
Into<StdError> + std::marker::Send + std::marker::Sync,
|
||||
{
|
||||
|
||||
@@ -7,10 +7,10 @@ use common::globals::GLOBAL_Conn_Map;
|
||||
pub use generated::*;
|
||||
use proto_gen::node_service::node_service_client::NodeServiceClient;
|
||||
use tonic::{
|
||||
Request, Status,
|
||||
metadata::MetadataValue,
|
||||
service::interceptor::InterceptedService,
|
||||
transport::{Channel, Endpoint},
|
||||
Request, Status,
|
||||
};
|
||||
|
||||
// Default 100 MB
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::event::config::NotifierConfig;
|
||||
use crate::ObservabilityConfig;
|
||||
use crate::event::config::NotifierConfig;
|
||||
|
||||
/// RustFs configuration
|
||||
pub struct RustFsConfig {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use rustfs_event_notifier::create_adapters;
|
||||
use rustfs_event_notifier::NotifierSystem;
|
||||
use rustfs_event_notifier::create_adapters;
|
||||
use rustfs_event_notifier::{AdapterConfig, NotifierConfig, WebhookConfig};
|
||||
use rustfs_event_notifier::{Bucket, Event, Identity, Metadata, Name, Object, Source};
|
||||
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 std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::Error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{create_adapters, Error, Event, NotifierConfig, NotifierSystem};
|
||||
use std::sync::{atomic, Arc};
|
||||
use crate::{Error, Event, NotifierConfig, NotifierSystem, create_adapters};
|
||||
use std::sync::{Arc, atomic};
|
||||
use tokio::sync::{Mutex, OnceCell};
|
||||
use tracing::instrument;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ mod global;
|
||||
mod notifier;
|
||||
mod store;
|
||||
|
||||
pub use adapter::ChannelAdapter;
|
||||
pub use adapter::create_adapters;
|
||||
#[cfg(all(feature = "kafka", target_os = "linux"))]
|
||||
pub use adapter::kafka::KafkaAdapter;
|
||||
@@ -14,7 +15,6 @@ pub use adapter::kafka::KafkaAdapter;
|
||||
pub use adapter::mqtt::MqttAdapter;
|
||||
#[cfg(feature = "webhook")]
|
||||
pub use adapter::webhook::WebhookAdapter;
|
||||
pub use adapter::ChannelAdapter;
|
||||
pub use bus::event_bus;
|
||||
#[cfg(all(feature = "kafka", target_os = "linux"))]
|
||||
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 tokio::sync::mpsc;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::Error;
|
||||
use crate::Log;
|
||||
use std::sync::Arc;
|
||||
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::sync::RwLock;
|
||||
use tracing::instrument;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use rustfs_filemeta::{test_data::*, FileMeta};
|
||||
use criterion::{Criterion, black_box, criterion_group, criterion_main};
|
||||
use rustfs_filemeta::{FileMeta, test_data::*};
|
||||
|
||||
fn bench_create_real_xlmeta(c: &mut Criterion) {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
#[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] {
|
||||
if self.0.is_empty() {
|
||||
&self.0
|
||||
} else {
|
||||
&self.0[1..]
|
||||
}
|
||||
if self.0.is_empty() { &self.0 } else { &self.0[1..] }
|
||||
}
|
||||
|
||||
pub fn find(&self, key: &str) -> Result<Option<Vec<u8>>> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
@@ -10,8 +10,8 @@ use std::{
|
||||
pin::Pin,
|
||||
ptr,
|
||||
sync::{
|
||||
atomic::{AtomicPtr, AtomicU64, Ordering as AtomicOrdering},
|
||||
Arc,
|
||||
atomic::{AtomicPtr, AtomicU64, Ordering as AtomicOrdering},
|
||||
},
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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::time::{Duration, SystemTime};
|
||||
use tracing::{error, info, instrument};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::logger::InitLogStatus;
|
||||
use crate::telemetry::{init_telemetry, OtelGuard};
|
||||
use crate::{get_global_logger, init_global_logger, AppConfig, Logger};
|
||||
use crate::telemetry::{OtelGuard, init_telemetry};
|
||||
use crate::{AppConfig, Logger, get_global_logger, init_global_logger};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::sync::{OnceCell, SetError};
|
||||
use tracing::{error, info};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::sinks::Sink;
|
||||
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 std::sync::Arc;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::sinks::Sink;
|
||||
use crate::UnifiedLogEntry;
|
||||
use crate::sinks::Sink;
|
||||
use async_trait::async_trait;
|
||||
|
||||
/// Webhook Sink Implementation
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::GlobalError;
|
||||
use crate::system::attributes::ProcessAttributes;
|
||||
use crate::system::gpu::GpuCollector;
|
||||
use crate::system::metrics::{Metrics, DIRECTION, INTERFACE, STATUS};
|
||||
use crate::GlobalError;
|
||||
use crate::system::metrics::{DIRECTION, INTERFACE, Metrics, STATUS};
|
||||
use opentelemetry::KeyValue;
|
||||
use std::time::SystemTime;
|
||||
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.
|
||||
/// It uses the sysinfo crate to gather information about the system and processes.
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#[cfg(feature = "gpu")]
|
||||
use crate::GlobalError;
|
||||
#[cfg(feature = "gpu")]
|
||||
use crate::system::attributes::ProcessAttributes;
|
||||
#[cfg(feature = "gpu")]
|
||||
use crate::system::metrics::Metrics;
|
||||
#[cfg(feature = "gpu")]
|
||||
use crate::GlobalError;
|
||||
use nvml_wrapper::Nvml;
|
||||
#[cfg(feature = "gpu")]
|
||||
use nvml_wrapper::enums::device::UsedGpuMemory;
|
||||
#[cfg(feature = "gpu")]
|
||||
use nvml_wrapper::Nvml;
|
||||
#[cfg(feature = "gpu")]
|
||||
use sysinfo::Pid;
|
||||
#[cfg(feature = "gpu")]
|
||||
use tracing::warn;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{sinks::Sink, UnifiedLogEntry};
|
||||
use crate::{UnifiedLogEntry, sinks::Sink};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{Reader, Writer};
|
||||
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};
|
||||
|
||||
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::{HashReaderMut, Reader};
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
@@ -284,7 +284,7 @@ impl HashReaderDetector for HashReader {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{encrypt_reader, DecryptReader};
|
||||
use crate::{DecryptReader, encrypt_reader};
|
||||
use std::io::Cursor;
|
||||
use tokio::io::{AsyncReadExt, BufReader};
|
||||
|
||||
|
||||
@@ -396,10 +396,12 @@ mod tests {
|
||||
// Should fail because no certificates found
|
||||
let result = load_all_certs_from_directory(temp_dir.path().to_str().unwrap());
|
||||
assert!(result.is_err());
|
||||
assert!(result
|
||||
.unwrap_err()
|
||||
.to_string()
|
||||
.contains("No valid certificate/private key pair found"));
|
||||
assert!(
|
||||
result
|
||||
.unwrap_err()
|
||||
.to_string()
|
||||
.contains("No valid certificate/private key pair found")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -412,10 +414,12 @@ mod tests {
|
||||
|
||||
let result = load_all_certs_from_directory(unicode_dir.to_str().unwrap());
|
||||
assert!(result.is_err());
|
||||
assert!(result
|
||||
.unwrap_err()
|
||||
.to_string()
|
||||
.contains("No valid certificate/private key pair found"));
|
||||
assert!(
|
||||
result
|
||||
.unwrap_err()
|
||||
.to_string()
|
||||
.contains("No valid certificate/private key pair found")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -114,7 +114,7 @@ mod tests {
|
||||
let data = b"test data";
|
||||
let hash = HashAlgorithm::BLAKE2b512.hash_encode(data);
|
||||
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);
|
||||
assert_eq!(hash, hash2);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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::io::{self, BufRead, Error, ErrorKind};
|
||||
use std::path::Path;
|
||||
@@ -26,7 +26,7 @@ pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
|
||||
bfree,
|
||||
p.as_ref().display()
|
||||
),
|
||||
))
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
|
||||
blocks,
|
||||
p.as_ref().display()
|
||||
),
|
||||
))
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -57,7 +57,7 @@ pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
|
||||
total,
|
||||
p.as_ref().display()
|
||||
),
|
||||
))
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
|
||||
bavail,
|
||||
bfree,
|
||||
p.as_ref().display()
|
||||
)))
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
|
||||
reserved,
|
||||
blocks,
|
||||
p.as_ref().display()
|
||||
)))
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -45,7 +45,7 @@ pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
|
||||
free,
|
||||
total,
|
||||
p.as_ref().display()
|
||||
)))
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -608,8 +608,8 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_decompress_with_invalid_format() {
|
||||
// Test decompression with invalid format
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
let sample_content = b"Hello, compression world!";
|
||||
let cursor = Cursor::new(sample_content);
|
||||
@@ -634,8 +634,8 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_decompress_with_zip_format() {
|
||||
// Test decompression with Zip format (currently not supported)
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
let sample_content = b"Hello, compression world!";
|
||||
let cursor = Cursor::new(sample_content);
|
||||
@@ -660,8 +660,8 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_decompress_error_propagation() {
|
||||
// Test error propagation during decompression process
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
let sample_content = b"Hello, compression world!";
|
||||
let cursor = Cursor::new(sample_content);
|
||||
@@ -690,8 +690,8 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_decompress_callback_execution() {
|
||||
// Test callback function execution during decompression
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
let sample_content = b"Hello, compression world!";
|
||||
let cursor = Cursor::new(sample_content);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use jsonwebtoken::{Algorithm, DecodingKey, TokenData, Validation};
|
||||
|
||||
use crate::jwt::Claims;
|
||||
use crate::Error;
|
||||
use crate::jwt::Claims;
|
||||
|
||||
pub fn decode(token: &str, token_secret: &[u8]) -> Result<TokenData<Claims>, Error> {
|
||||
Ok(jsonwebtoken::decode(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use jsonwebtoken::{Algorithm, EncodingKey, Header};
|
||||
|
||||
use crate::jwt::Claims;
|
||||
use crate::Error;
|
||||
use crate::jwt::Claims;
|
||||
|
||||
pub fn encode(token_secret: &[u8], claims: &Claims) -> Result<String, Error> {
|
||||
Ok(jsonwebtoken::encode(
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::{error::Error, sync::Arc, time::Duration};
|
||||
use lock::{
|
||||
drwmutex::Options,
|
||||
lock_args::LockArgs,
|
||||
namespace_lock::{new_nslock, NsLockMap},
|
||||
namespace_lock::{NsLockMap, new_nslock},
|
||||
new_lock_api,
|
||||
};
|
||||
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],
|
||||
)
|
||||
.await;
|
||||
assert!(ns
|
||||
.0
|
||||
.write()
|
||||
.await
|
||||
.get_lock(&Options {
|
||||
timeout: Duration::from_secs(5),
|
||||
retry_interval: Duration::from_secs(1),
|
||||
})
|
||||
.await
|
||||
.unwrap());
|
||||
assert!(
|
||||
ns.0.write()
|
||||
.await
|
||||
.get_lock(&Options {
|
||||
timeout: Duration::from_secs(5),
|
||||
retry_interval: Duration::from_secs(1),
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
ns.0.write().await.un_lock().await.unwrap();
|
||||
Ok(())
|
||||
|
||||
@@ -95,7 +95,7 @@ impl BucketMetadataError {
|
||||
0x06 => Some(BucketMetadataError::BucketQuotaConfigNotFound),
|
||||
0x07 => Some(BucketMetadataError::BucketReplicationConfigNotFound),
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,15 @@ use std::sync::OnceLock;
|
||||
use std::time::Duration;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use crate::StorageAPI;
|
||||
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::error::{is_err_bucket_not_found, Error, Result};
|
||||
use crate::global::{is_dist_erasure, is_erasure, new_object_layer_fn, GLOBAL_Endpoints};
|
||||
use crate::error::{Error, Result, is_err_bucket_not_found};
|
||||
use crate::global::{GLOBAL_Endpoints, is_dist_erasure, is_erasure, new_object_layer_fn};
|
||||
use crate::heal::heal_commands::HealOpts;
|
||||
use crate::store::ECStore;
|
||||
use crate::utils::xml::deserialize;
|
||||
use crate::StorageAPI;
|
||||
use futures::future::join_all;
|
||||
use policy::policy::BucketPolicy;
|
||||
use s3s::dto::{
|
||||
@@ -23,7 +23,7 @@ use tokio::sync::RwLock;
|
||||
use tokio::time::sleep;
|
||||
use tracing::{error, warn};
|
||||
|
||||
use super::metadata::{load_bucket_metadata, BucketMetadata};
|
||||
use super::metadata::{BucketMetadata, load_bucket_metadata};
|
||||
use super::quota::BucketQuota;
|
||||
use super::target::BucketTargets;
|
||||
|
||||
@@ -363,7 +363,7 @@ impl BucketMetadataSys {
|
||||
Err(Error::other("errBucketMetadataNotInitialized"))
|
||||
} else {
|
||||
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::error::{Error, Result};
|
||||
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 {
|
||||
error!("read config err {:?}", &err);
|
||||
Err(err)
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -145,7 +145,7 @@ async fn read_server_config<S: StorageAPI>(api: Arc<S>, data: &[u8]) -> Result<C
|
||||
} else {
|
||||
error!("read config err {:?}", &err);
|
||||
Err(err)
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
// TODO: decrypt
|
||||
|
||||
@@ -5,7 +5,7 @@ pub mod storageclass;
|
||||
|
||||
use crate::error::Result;
|
||||
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 serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
@@ -69,11 +69,7 @@ impl KVS {
|
||||
KVS(Vec::new())
|
||||
}
|
||||
pub fn get(&self, key: &str) -> String {
|
||||
if let Some(v) = self.lookup(key) {
|
||||
v
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
if let Some(v) = self.lookup(key) { v } else { "".to_owned() }
|
||||
}
|
||||
pub fn lookup(&self, key: &str) -> Option<String> {
|
||||
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)?
|
||||
} else {
|
||||
StorageClass {
|
||||
parity: {
|
||||
if set_drive_count == 1 {
|
||||
0
|
||||
} else {
|
||||
DEFAULT_RRS_PARITY
|
||||
}
|
||||
},
|
||||
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(block) = ev.parse::<bytesize::ByteSize>() {
|
||||
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
|
||||
} 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 {
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -721,4 +721,144 @@ mod tests {
|
||||
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 crate::disk::error::Error;
|
||||
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::vec;
|
||||
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) {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!(
|
||||
"Failed to write data: {} (offline-disks={}/{})",
|
||||
write_err,
|
||||
count_errs(&self.errs, &Error::DiskNotFound),
|
||||
self.writers.len()
|
||||
),
|
||||
));
|
||||
return Err(std::io::Error::other(format!(
|
||||
"Failed to write data: {} (offline-disks={}/{})",
|
||||
write_err,
|
||||
count_errs(&self.errs, &Error::DiskNotFound),
|
||||
self.writers.len()
|
||||
)));
|
||||
}
|
||||
|
||||
Err(std::io::Error::other(format!(
|
||||
@@ -103,10 +100,7 @@ impl Erasure {
|
||||
total += n;
|
||||
let res = self.encode_data(&buf[..n])?;
|
||||
if let Err(err) = tx.send(res).await {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
format!("Failed to send encoded data : {}", err),
|
||||
));
|
||||
return Err(std::io::Error::other(format!("Failed to send encoded data : {}", err)));
|
||||
}
|
||||
}
|
||||
Ok(_) => break,
|
||||
|
||||
@@ -3,7 +3,6 @@ use reed_solomon_erasure::galois_8::ReedSolomon;
|
||||
// use rustfs_rio::Reader;
|
||||
use smallvec::SmallVec;
|
||||
use std::io;
|
||||
use std::io::ErrorKind;
|
||||
use tracing::error;
|
||||
use tracing::warn;
|
||||
use uuid::Uuid;
|
||||
@@ -98,7 +97,7 @@ impl Erasure {
|
||||
if let Some(encoder) = self.encoder.as_ref() {
|
||||
encoder.encode(data_slices).map_err(|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 {
|
||||
warn!("parity_shards > 0, but encoder is None");
|
||||
@@ -129,7 +128,7 @@ impl Erasure {
|
||||
if let Some(encoder) = self.encoder.as_ref() {
|
||||
encoder.reconstruct(shards).map_err(|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 {
|
||||
warn!("parity_shards > 0, but encoder is None");
|
||||
|
||||
@@ -732,7 +732,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_storage_error_to_u32() {
|
||||
// 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);
|
||||
|
||||
// Test other errors have correct codes
|
||||
@@ -781,7 +781,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_storage_error_from_disk_error() {
|
||||
// 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();
|
||||
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::{
|
||||
spawn,
|
||||
sync::{
|
||||
mpsc::{self, Receiver, Sender},
|
||||
RwLock,
|
||||
mpsc::{self, Receiver, Sender},
|
||||
},
|
||||
time::interval,
|
||||
};
|
||||
@@ -14,16 +14,16 @@ use uuid::Uuid;
|
||||
|
||||
use super::{
|
||||
heal_commands::HealOpts,
|
||||
heal_ops::{new_bg_heal_sequence, HealSequence},
|
||||
heal_ops::{HealSequence, new_bg_heal_sequence},
|
||||
};
|
||||
use crate::error::{Error, Result};
|
||||
use crate::global::GLOBAL_MRFState;
|
||||
use crate::heal::error::ERR_RETRY_HEALING;
|
||||
use crate::heal::heal_commands::{HealScanMode, HEAL_ITEM_BUCKET};
|
||||
use crate::heal::heal_ops::{HealSource, BG_HEALING_UUID};
|
||||
use crate::heal::heal_commands::{HEAL_ITEM_BUCKET, HealScanMode};
|
||||
use crate::heal::heal_ops::{BG_HEALING_UUID, HealSource};
|
||||
use crate::{
|
||||
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},
|
||||
heal::{
|
||||
data_usage::{DATA_USAGE_CACHE_NAME, DATA_USAGE_ROOT},
|
||||
@@ -34,7 +34,7 @@ use crate::{
|
||||
new_object_layer_fn,
|
||||
store::get_disk_via_endpoint,
|
||||
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);
|
||||
@@ -149,7 +149,7 @@ async fn heal_fresh_disk(endpoint: &Endpoint) -> Result<()> {
|
||||
return Err(Error::other(format!(
|
||||
"Unexpected error disk must be initialized by now after formatting: {}",
|
||||
endpoint
|
||||
)))
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -6,17 +6,17 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
pin::Pin,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering},
|
||||
Arc,
|
||||
atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering},
|
||||
},
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use super::{
|
||||
data_scanner_metric::{globalScannerMetrics, ScannerMetric, ScannerMetrics},
|
||||
data_usage::{store_data_usage_in_backend, DATA_USAGE_BLOOM_NAME_PATH},
|
||||
data_scanner_metric::{ScannerMetric, ScannerMetrics, globalScannerMetrics},
|
||||
data_usage::{DATA_USAGE_BLOOM_NAME_PATH, store_data_usage_in_backend},
|
||||
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::{
|
||||
bucket::{versioning::VersioningApi, versioning_sys::BucketVersioningSys},
|
||||
@@ -24,7 +24,7 @@ use crate::{
|
||||
heal::data_usage::DATA_USAGE_ROOT,
|
||||
};
|
||||
use crate::{
|
||||
cache_value::metacache_set::{list_path_raw, ListPathRawOptions},
|
||||
cache_value::metacache_set::{ListPathRawOptions, list_path_raw},
|
||||
config::{
|
||||
com::{read_config, save_config},
|
||||
heal::Config,
|
||||
@@ -33,22 +33,22 @@ use crate::{
|
||||
global::{GLOBAL_BackgroundHealState, GLOBAL_IsErasure, GLOBAL_IsErasureSD},
|
||||
heal::{
|
||||
data_usage::BACKGROUND_HEAL_INFO_PATH,
|
||||
data_usage_cache::{hash_path, DataUsageHashMap},
|
||||
data_usage_cache::{DataUsageHashMap, hash_path},
|
||||
error::ERR_IGNORE_FILE_CONTRIB,
|
||||
heal_commands::{HEAL_ITEM_BUCKET, HEAL_ITEM_OBJECT},
|
||||
heal_ops::{HealSource, BG_HEALING_UUID},
|
||||
heal_ops::{BG_HEALING_UUID, HealSource},
|
||||
},
|
||||
new_object_layer_fn,
|
||||
peer::is_reserved_or_invalid_bucket,
|
||||
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::{
|
||||
disk::error::DiskError,
|
||||
error::{Error, Result},
|
||||
};
|
||||
use crate::{disk::local::LocalDisk, heal::data_scanner_metric::current_path_updater};
|
||||
use crate::{disk::DiskAPI, store_api::ObjectInfo};
|
||||
use chrono::{DateTime, Utc};
|
||||
use lazy_static::lazy_static;
|
||||
use rand::Rng;
|
||||
@@ -58,9 +58,8 @@ use s3s::dto::{BucketLifecycleConfiguration, ExpirationStatus, LifecycleRule, Re
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::{
|
||||
sync::{
|
||||
broadcast,
|
||||
RwLock, broadcast,
|
||||
mpsc::{self, Sender},
|
||||
RwLock,
|
||||
},
|
||||
time::sleep,
|
||||
};
|
||||
|
||||
@@ -6,8 +6,8 @@ use std::{
|
||||
collections::HashMap,
|
||||
pin::Pin,
|
||||
sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc,
|
||||
atomic::{AtomicU64, Ordering},
|
||||
},
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ use std::time::{Duration, SystemTime};
|
||||
use tokio::sync::mpsc::Sender;
|
||||
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};
|
||||
|
||||
// DATA_USAGE_BUCKET_LEN must be length of ObjectsHistogramIntervals
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::{
|
||||
|
||||
use crate::{
|
||||
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,
|
||||
heal::heal_ops::HEALING_TRACKER_FILENAME,
|
||||
new_object_layer_fn,
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::{
|
||||
background_heal_ops::HealTask,
|
||||
data_scanner::HEAL_DELETE_DANGLING,
|
||||
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::store_api::StorageAPI;
|
||||
@@ -16,7 +16,7 @@ use crate::{
|
||||
disk::endpoint::Endpoint,
|
||||
endpoints::Endpoints,
|
||||
global::GLOBAL_IsDistErasure,
|
||||
heal::heal_commands::{HealStartSuccess, HEAL_UNKNOWN_SCAN},
|
||||
heal::heal_commands::{HEAL_UNKNOWN_SCAN, HealStartSuccess},
|
||||
new_object_layer_fn,
|
||||
utils::path::has_prefix,
|
||||
};
|
||||
@@ -41,10 +41,9 @@ use std::{
|
||||
use tokio::{
|
||||
select, spawn,
|
||||
sync::{
|
||||
broadcast,
|
||||
RwLock, broadcast,
|
||||
mpsc::{self, Receiver as M_Receiver, Sender as M_Sender},
|
||||
watch::{self, Receiver as W_Receiver, Sender as W_Sender},
|
||||
RwLock,
|
||||
},
|
||||
time::{interval, sleep},
|
||||
};
|
||||
@@ -784,7 +783,10 @@ impl AllHealState {
|
||||
self.stop_heal_sequence(path_s).await?;
|
||||
} else if let Some(hs) = self.get_heal_sequence(path_s).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::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::Duration;
|
||||
use tokio::sync::mpsc::{Receiver, Sender};
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::sync::mpsc::{Receiver, Sender};
|
||||
use tokio::time::sleep;
|
||||
use tracing::error;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::StorageAPI;
|
||||
use crate::admin_server_info::get_commit_id;
|
||||
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::StorageAPI;
|
||||
use crate::{endpoints::EndpointServerPools, new_object_layer_fn};
|
||||
use futures::future::join_all;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
@@ -7,10 +7,10 @@ use crate::{
|
||||
utils::net::XHost,
|
||||
};
|
||||
use madmin::{
|
||||
ServerProperties,
|
||||
health::{Cpus, MemInfo, OsInfo, Partitions, ProcInfo, SysConfig, SysErrors, SysService},
|
||||
metrics::RealtimeMetrics,
|
||||
net::NetInfo,
|
||||
ServerProperties,
|
||||
};
|
||||
use protos::{
|
||||
node_service_time_out_client,
|
||||
|
||||
@@ -3824,14 +3824,13 @@ impl ObjectIO for SetDisks {
|
||||
|
||||
let sc_parity_drives = {
|
||||
if let Some(sc) = GLOBAL_StorageClass.get() {
|
||||
let a = sc.get_parity_for_sc(
|
||||
sc.get_parity_for_sc(
|
||||
user_defined
|
||||
.get(xhttp::AMZ_STORAGE_CLASS)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.as_str(),
|
||||
);
|
||||
a
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -4806,14 +4805,13 @@ impl StorageAPI for SetDisks {
|
||||
|
||||
let sc_parity_drives = {
|
||||
if let Some(sc) = GLOBAL_StorageClass.get() {
|
||||
let a = sc.get_parity_for_sc(
|
||||
sc.get_parity_for_sc(
|
||||
user_defined
|
||||
.get(xhttp::AMZ_STORAGE_CLASS)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.as_str(),
|
||||
);
|
||||
a
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -5,15 +5,16 @@ use crate::disk::error_reduce::count_errs;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::{
|
||||
disk::{
|
||||
DiskAPI, DiskInfo, DiskOption, DiskStore,
|
||||
error::DiskError,
|
||||
format::{DistributionAlgoVersion, FormatV3},
|
||||
new_disk, DiskAPI, DiskInfo, DiskOption, DiskStore,
|
||||
new_disk,
|
||||
},
|
||||
endpoints::{Endpoints, PoolEndpoints},
|
||||
error::StorageError,
|
||||
global::{is_dist_erasure, GLOBAL_LOCAL_DISK_SET_DRIVES},
|
||||
global::{GLOBAL_LOCAL_DISK_SET_DRIVES, is_dist_erasure},
|
||||
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,
|
||||
store_api::{
|
||||
@@ -27,7 +28,7 @@ use crate::{
|
||||
use common::globals::GLOBAL_Local_Node_Name;
|
||||
use futures::future::join_all;
|
||||
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 tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -160,13 +160,7 @@ impl HTTPRangeSpec {
|
||||
Some(HTTPRangeSpec {
|
||||
is_suffix_length: false,
|
||||
start: start as usize,
|
||||
end: {
|
||||
if end < 0 {
|
||||
None
|
||||
} else {
|
||||
Some(end as usize)
|
||||
}
|
||||
},
|
||||
end: { if end < 0 { None } else { Some(end as usize) } },
|
||||
})
|
||||
}
|
||||
|
||||
@@ -827,7 +821,7 @@ pub trait StorageAPI: ObjectIO {
|
||||
opts: &HealOpts,
|
||||
) -> Result<(HealResultItem, Option<Error>)>;
|
||||
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 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::{self, DiskAPI};
|
||||
use crate::error::{Error, Result};
|
||||
use crate::{
|
||||
disk::{
|
||||
DiskInfoOptions, DiskOption, DiskStore, FORMAT_CONFIG_FILE, RUSTFS_META_BUCKET,
|
||||
error::DiskError,
|
||||
format::{FormatErasureVersion, FormatMetaVersion, FormatV3},
|
||||
new_disk, DiskInfoOptions, DiskOption, DiskStore, FORMAT_CONFIG_FILE, RUSTFS_META_BUCKET,
|
||||
new_disk,
|
||||
},
|
||||
endpoints::Endpoints,
|
||||
heal::heal_commands::init_healing_tracker,
|
||||
};
|
||||
use futures::future::join_all;
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
use std::collections::{HashMap, hash_map::Entry};
|
||||
|
||||
use tracing::{debug, warn};
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -32,7 +32,7 @@ fn deep_match_rune(str_: &[u8], pattern: &[u8], simple: bool) -> bool {
|
||||
} else {
|
||||
deep_match_rune(str_, &pattern[1..], simple)
|
||||
|| (!str_.is_empty() && deep_match_rune(&str_[1..], pattern, simple))
|
||||
}
|
||||
};
|
||||
}
|
||||
'?' => {
|
||||
if str_.is_empty() {
|
||||
|
||||
194
iam/src/error.rs
194
iam/src/error.rs
@@ -97,6 +97,61 @@ pub enum 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 {
|
||||
pub fn other<E>(error: E) -> Self
|
||||
where
|
||||
@@ -225,3 +280,142 @@ pub fn is_err_no_such_service_account(err: &Error) -> bool {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::{
|
||||
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,
|
||||
store::{object::IAM_CONFIG_PREFIX, GroupInfo, MappedPolicy, Store, UserType},
|
||||
store::{GroupInfo, MappedPolicy, Store, UserType, object::IAM_CONFIG_PREFIX},
|
||||
sys::{
|
||||
UpdateServiceAccountOpts, MAX_SVCSESSION_POLICY_SIZE, SESSION_POLICY_NAME, SESSION_POLICY_NAME_EXTRACTED,
|
||||
STATUS_DISABLED, STATUS_ENABLED,
|
||||
MAX_SVCSESSION_POLICY_SIZE, SESSION_POLICY_NAME, SESSION_POLICY_NAME_EXTRACTED, STATUS_DISABLED, STATUS_ENABLED,
|
||||
UpdateServiceAccountOpts,
|
||||
},
|
||||
};
|
||||
use ecstore::utils::{crypto::base64_encode, path::path_join_buf};
|
||||
use madmin::{AccountStatus, AddOrUpdateUserReq, GroupDesc};
|
||||
use policy::{
|
||||
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,
|
||||
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};
|
||||
@@ -24,8 +24,8 @@ use serde_json::Value;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicI64, Ordering},
|
||||
Arc,
|
||||
atomic::{AtomicBool, AtomicI64, Ordering},
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
@@ -633,7 +633,7 @@ where
|
||||
Cache::add_or_update(&self.cache.user_policies, name, p, OffsetDateTime::now_utc());
|
||||
p.clone()
|
||||
} else {
|
||||
let mp = match self.cache.sts_policies.load().get(name) {
|
||||
match self.cache.sts_policies.load().get(name) {
|
||||
Some(p) => p.clone(),
|
||||
None => {
|
||||
let mut m = HashMap::new();
|
||||
@@ -645,8 +645,7 @@ where
|
||||
MappedPolicy::default()
|
||||
}
|
||||
}
|
||||
};
|
||||
mp
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ pub mod object;
|
||||
use crate::cache::Cache;
|
||||
use crate::error::Result;
|
||||
use policy::{auth::UserIdentity, policy::PolicyDoc};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
@@ -49,7 +49,7 @@ pub trait Store: Clone + Send + Sync + 'static {
|
||||
m: &mut HashMap<String, MappedPolicy>,
|
||||
) -> Result<()>;
|
||||
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<()>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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::{
|
||||
cache::{Cache, CacheEntity},
|
||||
error::{is_err_no_such_policy, is_err_no_such_user},
|
||||
@@ -8,18 +8,18 @@ use crate::{
|
||||
};
|
||||
use ecstore::{
|
||||
config::{
|
||||
com::{delete_config, read_config, read_config_with_metadata, save_config},
|
||||
RUSTFS_CONFIG_PREFIX,
|
||||
com::{delete_config, read_config, read_config_with_metadata, save_config},
|
||||
},
|
||||
store::ECStore,
|
||||
store_api::{ObjectInfo, ObjectOptions},
|
||||
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 lazy_static::lazy_static;
|
||||
use policy::{auth::UserIdentity, policy::PolicyDoc};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use tokio::sync::broadcast::{self, Receiver as B_Receiver};
|
||||
use tokio::sync::mpsc::{self, Sender};
|
||||
|
||||
@@ -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_temp_account;
|
||||
use crate::error::Error as IamError;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::get_global_action_cred;
|
||||
use crate::manager::IamCache;
|
||||
use crate::manager::extract_jwt_claims;
|
||||
use crate::manager::get_default_policyes;
|
||||
use crate::manager::IamCache;
|
||||
use crate::store::MappedPolicy;
|
||||
use crate::store::Store;
|
||||
use crate::store::UserType;
|
||||
@@ -14,22 +14,22 @@ use ecstore::utils::crypto::base64_encode;
|
||||
use madmin::AddOrUpdateUserReq;
|
||||
use madmin::GroupDesc;
|
||||
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::create_new_credentials_with_metadata;
|
||||
use policy::auth::generate_credentials;
|
||||
use policy::auth::is_access_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::Policy;
|
||||
use policy::policy::PolicyDoc;
|
||||
use policy::policy::EMBEDDED_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::json;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header};
|
||||
use rand::{Rng, RngCore};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use std::io::{Error, Result};
|
||||
|
||||
pub fn gen_access_key(length: usize) -> Result<String> {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use crate::error::Error as IamError;
|
||||
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::extract_claims;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
use std::collections::HashMap;
|
||||
use time::macros::offset;
|
||||
use time::OffsetDateTime;
|
||||
use time::macros::offset;
|
||||
|
||||
const ACCESS_KEY_MIN_LEN: usize = 3;
|
||||
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 {
|
||||
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 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)]
|
||||
pub struct ActionSet(pub HashSet<Action>);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::policy::function::condition::Condition;
|
||||
use serde::ser::SerializeMap;
|
||||
use serde::{de, Deserialize, Serialize, Serializer};
|
||||
use serde::{Deserialize, Serialize, Serializer, de};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
|
||||
@@ -163,12 +163,12 @@ pub struct Value;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::policy::Functions;
|
||||
use crate::policy::function::condition::Condition::*;
|
||||
use crate::policy::function::func::FuncKeyValue;
|
||||
use crate::policy::function::key::Key;
|
||||
use crate::policy::function::string::StringFunc;
|
||||
use crate::policy::function::string::StringFuncValue;
|
||||
use crate::policy::Functions;
|
||||
use test_case::test_case;
|
||||
|
||||
#[test_case(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::func::InnerFunc;
|
||||
use ipnetwork::IpNetwork;
|
||||
use serde::{de::Visitor, Deserialize, Serialize};
|
||||
use serde::{Deserialize, Serialize, de::Visitor};
|
||||
use std::{borrow::Cow, collections::HashMap, net::IpAddr};
|
||||
|
||||
pub type AddrFunc = InnerFunc<AddrFuncValue>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::func::InnerFunc;
|
||||
use serde::de::{Error, IgnoredAny, SeqAccess};
|
||||
use serde::{de, Deserialize, Deserializer, Serialize};
|
||||
use serde::{Deserialize, Deserializer, Serialize, de};
|
||||
use std::{collections::HashMap, fmt};
|
||||
|
||||
pub type BoolFunc = InnerFunc<BoolFuncValue>;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use serde::Deserialize;
|
||||
use serde::de::{Error, MapAccess};
|
||||
use serde::ser::SerializeMap;
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
@@ -122,11 +122,7 @@ impl Condition {
|
||||
DateGreaterThanEquals(s) => s.evaluate(OffsetDateTime::ge, values),
|
||||
};
|
||||
|
||||
if self.is_negate() {
|
||||
!r
|
||||
} else {
|
||||
r
|
||||
}
|
||||
if self.is_negate() { !r } else { r }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::func::InnerFunc;
|
||||
use serde::{de, Deserialize, Deserializer, Serialize};
|
||||
use serde::{Deserialize, Deserializer, Serialize, de};
|
||||
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>;
|
||||
|
||||
@@ -82,7 +82,7 @@ mod tests {
|
||||
key_name::S3KeyName::*,
|
||||
};
|
||||
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 {
|
||||
DateFunc {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use serde::{
|
||||
de::{self, Visitor},
|
||||
Deserialize, Deserializer, Serialize,
|
||||
de::{self, Visitor},
|
||||
};
|
||||
|
||||
use super::key::Key;
|
||||
|
||||
@@ -2,8 +2,8 @@ use std::collections::HashMap;
|
||||
|
||||
use super::func::InnerFunc;
|
||||
use serde::{
|
||||
de::{Error, Visitor},
|
||||
Deserialize, Deserializer, Serialize,
|
||||
de::{Error, Visitor},
|
||||
};
|
||||
|
||||
pub type NumberFunc = InnerFunc<NumberFuncValue>;
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
use crate::policy::function::func::FuncKeyValue;
|
||||
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};
|
||||
|
||||
|
||||
@@ -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 serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
@@ -252,9 +252,9 @@ pub mod default {
|
||||
use std::{collections::HashSet, sync::LazyLock};
|
||||
|
||||
use crate::policy::{
|
||||
ActionSet, DEFAULT_VERSION, Effect, Functions, ResourceSet, Statement,
|
||||
action::{Action, AdminAction, KmsAction, S3Action},
|
||||
resource::Resource,
|
||||
ActionSet, Effect, Functions, ResourceSet, Statement, DEFAULT_VERSION,
|
||||
};
|
||||
|
||||
use super::Policy;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{utils::wildcard, Validator};
|
||||
use super::{Validator, utils::wildcard};
|
||||
use crate::error::{Error, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
|
||||
@@ -7,9 +7,9 @@ use std::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
Error as IamError, Validator,
|
||||
function::key_name::KeyName,
|
||||
utils::{path, wildcard},
|
||||
Error as IamError, Validator,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Default, Debug)]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
action::Action, ActionSet, Args, BucketPolicyArgs, Effect, Error as IamError, Functions, Principal, ResourceSet, Validator,
|
||||
ID,
|
||||
ActionSet, Args, BucketPolicyArgs, Effect, Error as IamError, Functions, ID, Principal, ResourceSet, Validator,
|
||||
action::Action,
|
||||
};
|
||||
use crate::error::{Error, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -85,11 +85,7 @@ pub fn clean(path: &str) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
if out.w == 0 {
|
||||
".".into()
|
||||
} else {
|
||||
out.string()
|
||||
}
|
||||
if out.w == 0 { ".".into() } else { out.string() }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header};
|
||||
use rand::{Rng, RngCore};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use std::io::{Error, Result};
|
||||
|
||||
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::Effect::*;
|
||||
use policy::policy::action::Action;
|
||||
use policy::policy::action::S3Action::*;
|
||||
use policy::policy::*;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -5,7 +5,7 @@ use iam::{
|
||||
};
|
||||
use madmin::GroupAddRemove;
|
||||
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_urlencoded::from_bytes;
|
||||
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 matchit::Params;
|
||||
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_urlencoded::from_bytes;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -16,7 +16,7 @@ use matchit::Params;
|
||||
use policy::policy::action::{Action, AdminAction};
|
||||
use policy::policy::{Args, Policy};
|
||||
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_urlencoded::from_bytes;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -10,8 +10,9 @@ use iam::{manager::get_token_signing_key, sys::SESSION_POLICY_NAME};
|
||||
use matchit::Params;
|
||||
use policy::{auth::get_new_credentials_with_metadata, policy::Policy};
|
||||
use s3s::{
|
||||
Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result,
|
||||
dto::{AssumeRoleOutput, Credentials, Timestamp},
|
||||
s3_error, Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result,
|
||||
s3_error,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
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 hyper::Uri;
|
||||
use madmin::service_commands::ServiceTraceOpts;
|
||||
use matchit::Params;
|
||||
use s3s::{s3_error, Body, S3Request, S3Response, S3Result};
|
||||
use s3s::{Body, S3Request, S3Response, S3Result, s3_error};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::admin::router::Operation;
|
||||
|
||||
@@ -5,10 +5,10 @@ use iam::get_global_action_cred;
|
||||
use madmin::{AccountStatus, AddOrUpdateUserReq};
|
||||
use matchit::Params;
|
||||
use policy::policy::{
|
||||
action::{Action, AdminAction},
|
||||
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_urlencoded::from_bytes;
|
||||
use tracing::warn;
|
||||
|
||||
@@ -7,13 +7,13 @@ use iam::get_global_action_cred;
|
||||
use iam::sys::SESSION_POLICY_NAME;
|
||||
use policy::auth;
|
||||
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::SecretKey;
|
||||
use s3s::auth::SimpleAuth;
|
||||
use s3s::s3_error;
|
||||
use s3s::S3Error;
|
||||
use s3s::S3ErrorCode;
|
||||
use s3s::S3Result;
|
||||
use serde_json::Value;
|
||||
|
||||
pub struct IAMAuth {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::license::get_license;
|
||||
use axum::{
|
||||
Router,
|
||||
body::Body,
|
||||
http::{Response, StatusCode},
|
||||
response::IntoResponse,
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use axum_extra::extract::Host;
|
||||
use rustfs_config::{RUSTFS_TLS_CERT, RUSTFS_TLS_KEY};
|
||||
@@ -12,7 +12,7 @@ use std::io;
|
||||
|
||||
use axum::response::Redirect;
|
||||
use axum_server::tls_rustls::RustlsConfig;
|
||||
use http::{header, Uri};
|
||||
use http::{Uri, header};
|
||||
use mime_guess::from_path;
|
||||
use rust_embed::RustEmbed;
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -94,3 +94,238 @@ impl From<iam::error::Error> for ApiError {
|
||||
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;
|
||||
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::ServiceStateManager;
|
||||
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 std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
use tokio::signal::unix::{SignalKind, signal};
|
||||
use tracing::info;
|
||||
|
||||
// a configurable shutdown timeout
|
||||
@@ -10,7 +10,7 @@ pub(crate) const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(1);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn notify_systemd(state: &str) {
|
||||
use libsystemd::daemon::{notify, NotifyState};
|
||||
use libsystemd::daemon::{NotifyState, notify};
|
||||
use tracing::{debug, error};
|
||||
let notify_state = match state {
|
||||
"ready" => NotifyState::Ready,
|
||||
|
||||
@@ -5,9 +5,9 @@ use async_trait::async_trait;
|
||||
use crate::QueryResult;
|
||||
|
||||
use super::{
|
||||
Query,
|
||||
execution::{Output, QueryStateMachine},
|
||||
logical_planner::Plan,
|
||||
Query,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::fmt::Display;
|
||||
use std::pin::Pin;
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
@@ -13,9 +13,9 @@ use futures::{Stream, StreamExt, TryStreamExt};
|
||||
|
||||
use crate::{QueryError, QueryResult};
|
||||
|
||||
use super::Query;
|
||||
use super::logical_planner::Plan;
|
||||
use super::session::SessionCtx;
|
||||
use super::Query;
|
||||
|
||||
pub type QueryExecutionRef = Arc<dyn QueryExecution>;
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use datafusion::{
|
||||
execution::{context::SessionState, runtime_env::RuntimeEnvBuilder, SessionStateBuilder},
|
||||
execution::{SessionStateBuilder, context::SessionState, runtime_env::RuntimeEnvBuilder},
|
||||
parquet::data_type::AsBytes,
|
||||
prelude::SessionContext,
|
||||
};
|
||||
use object_store::{memory::InMemory, path::Path, ObjectStore};
|
||||
use object_store::{ObjectStore, memory::InMemory, path::Path};
|
||||
use tracing::error;
|
||||
|
||||
use crate::{object_store::EcObjectStore, QueryError, QueryResult};
|
||||
use crate::{QueryError, QueryResult, object_store::EcObjectStore};
|
||||
|
||||
use super::Context;
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
QueryResult,
|
||||
query::{
|
||||
Query,
|
||||
execution::{Output, QueryStateMachineRef},
|
||||
logical_planner::Plan,
|
||||
Query,
|
||||
},
|
||||
QueryResult,
|
||||
};
|
||||
|
||||
pub struct QueryHandle {
|
||||
|
||||
@@ -8,7 +8,7 @@ use async_trait::async_trait;
|
||||
use datafusion::arrow::datatypes::SchemaRef;
|
||||
use datafusion::common::Result as DFResult;
|
||||
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::logical_expr::{LogicalPlan, LogicalPlanBuilder, TableProviderFilterPushDown, TableSource};
|
||||
use datafusion::prelude::Expr;
|
||||
|
||||
@@ -6,7 +6,9 @@ use std::{
|
||||
};
|
||||
|
||||
use api::{
|
||||
QueryError, QueryResult,
|
||||
query::{
|
||||
Query,
|
||||
ast::ExtStatement,
|
||||
dispatcher::QueryDispatcher,
|
||||
execution::{Output, QueryStateMachine},
|
||||
@@ -14,9 +16,7 @@ use api::{
|
||||
logical_planner::{LogicalPlanner, Plan},
|
||||
parser::Parser,
|
||||
session::{SessionCtx, SessionCtxFactory},
|
||||
Query,
|
||||
},
|
||||
QueryError, QueryResult,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use datafusion::{
|
||||
@@ -37,7 +37,7 @@ use s3s::dto::{FileHeaderInfo, SelectObjectContentInput};
|
||||
|
||||
use crate::{
|
||||
execution::factory::QueryExecutionFactoryRef,
|
||||
metadata::{base_table::BaseTableProvider, ContextProviderExtension, MetadataProvider, TableHandleProviderRef},
|
||||
metadata::{ContextProviderExtension, MetadataProvider, TableHandleProviderRef, base_table::BaseTableProvider},
|
||||
sql::logical::planner::DefaultLogicalPlanner,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use api::{
|
||||
QueryError,
|
||||
query::{
|
||||
execution::{QueryExecutionFactory, QueryExecutionRef, QueryStateMachineRef},
|
||||
logical_planner::Plan,
|
||||
optimizer::Optimizer,
|
||||
scheduler::SchedulerRef,
|
||||
},
|
||||
QueryError,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use api::query::scheduler::{ExecutionResults, Scheduler};
|
||||
use async_trait::async_trait;
|
||||
use datafusion::error::DataFusionError;
|
||||
use datafusion::execution::context::TaskContext;
|
||||
use datafusion::physical_plan::{execute_stream, ExecutionPlan};
|
||||
use datafusion::physical_plan::{ExecutionPlan, execute_stream};
|
||||
|
||||
pub struct LocalScheduler {}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use api::{
|
||||
QueryResult,
|
||||
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},
|
||||
QueryResult,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
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