mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-06 21:33:14 +00:00
refactor(s3): consolidate semantic boundaries and remove s3-common (#3012)
* refactor(common): introduce rustfs-data-usage core crate * refactor(concurrency): migrate workers crate into concurrency * refactor(crypto): migrate appauth token APIs into crypto * fix docs urls * remove unused crate * refactor(data-usage): switch consumers to rustfs-data-usage * chore(fmt): apply cargo fmt and lockfile sync * refactor(common): remove data_usage compatibility re-export * refactor(capacity): move capacity_scope to object-capacity * refactor(io-metrics): relocate internode metrics from common * refactor(common): decouple scanner report from madmin * chore(fmt): normalize import ordering after pre-commit * refactor(s3): split s3 types and ops crates * refactor(s3): centralize event version and safe parsing * refactor(s3): add op-event compatibility guardrails * refactor(s3): add runtime op-event mismatch observability * refactor(s3): extract delete event mapping helper * refactor(s3): extract put event mapping helper * refactor(s3): consolidate remaining event semantic helpers * refactor(s3): add op-event coverage checks and observability alerts * refactor(s3-ops): consolidate op-event semantic mapping * refactor(scanner): remove last_minute wrapper module * refactor(scanner): consolidate duplicated data usage models
This commit is contained in:
+3
-2
@@ -61,7 +61,6 @@ workspace = true
|
||||
[dependencies]
|
||||
# RustFS Internal Crates
|
||||
rustfs-heal = { workspace = true }
|
||||
rustfs-appauth = { workspace = true }
|
||||
rustfs-audit = { workspace = true }
|
||||
rustfs-common = { workspace = true }
|
||||
rustfs-config = { workspace = true, features = ["constants", "notify"] }
|
||||
@@ -80,7 +79,9 @@ rustfs-policy = { workspace = true }
|
||||
rustfs-protocols = { workspace = true }
|
||||
rustfs-protos = { workspace = true }
|
||||
rustfs-rio.workspace = true
|
||||
rustfs-s3-common = { workspace = true }
|
||||
rustfs-s3-types = { workspace = true }
|
||||
rustfs-s3-ops = { workspace = true }
|
||||
rustfs-data-usage = { workspace = true }
|
||||
rustfs-s3select-api = { workspace = true }
|
||||
rustfs-s3select-query = { workspace = true }
|
||||
rustfs-targets = { workspace = true }
|
||||
|
||||
@@ -27,8 +27,8 @@ use http::{HeaderMap, StatusCode};
|
||||
use hyper::Method;
|
||||
use matchit::Params;
|
||||
use percent_encoding::percent_decode_str;
|
||||
use rustfs_common::data_usage::TierStats;
|
||||
use rustfs_config::MAX_ADMIN_REQUEST_BODY_SIZE;
|
||||
use rustfs_data_usage::TierStats;
|
||||
use rustfs_ecstore::bucket::lifecycle::bucket_lifecycle_ops::GLOBAL_TransitionState;
|
||||
use rustfs_ecstore::{
|
||||
bucket::lifecycle::tier_last_day_stats::DailyAllTierStats,
|
||||
|
||||
@@ -65,7 +65,7 @@ use rustfs_filemeta::{ReplicationStatusType, ReplicationType};
|
||||
use rustfs_madmin::utils::parse_duration;
|
||||
use rustfs_notify::{Event as NotificationEvent, notification_system};
|
||||
use rustfs_policy::policy::action::{Action, S3Action};
|
||||
use rustfs_s3_common::EventName;
|
||||
use rustfs_s3_types::EventName;
|
||||
use rustfs_signer::pre_sign_v4;
|
||||
use rustfs_utils::http::{
|
||||
SUFFIX_SOURCE_DELETEMARKER, SUFFIX_SOURCE_MTIME, SUFFIX_SOURCE_REPLICATION_CHECK, SUFFIX_SOURCE_REPLICATION_REQUEST,
|
||||
@@ -3803,7 +3803,7 @@ mod tests {
|
||||
fn event_matches_listen_notification_respects_bucket_event_and_object_filters() {
|
||||
let filter = ListenNotificationFilter {
|
||||
bucket: Some("demo-bucket".to_string()),
|
||||
event_mask: EventName::ObjectCreatedPut.mask() | EventName::ObjectCreatedPost.mask(),
|
||||
event_mask: rustfs_s3_ops::put_object_created_event_mask(),
|
||||
prefix: Some("logs/".to_string()),
|
||||
suffix: Some(".json".to_string()),
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
use crate::app::context::{AppContext, get_global_app_context};
|
||||
use crate::capacity::resolve_admin_used_capacity;
|
||||
use crate::error::ApiError;
|
||||
use rustfs_common::data_usage::DataUsageInfo;
|
||||
use rustfs_data_usage::DataUsageInfo;
|
||||
use rustfs_ecstore::admin_server_info::get_server_info;
|
||||
use rustfs_ecstore::data_usage::load_data_usage_from_backend;
|
||||
use rustfs_ecstore::endpoints::EndpointServerPools;
|
||||
|
||||
@@ -64,7 +64,7 @@ use rustfs_policy::policy::{
|
||||
action::{Action, S3Action},
|
||||
{BucketPolicy, BucketPolicyArgs, Effect, Validator},
|
||||
};
|
||||
use rustfs_s3_common::S3Operation;
|
||||
use rustfs_s3_ops::S3Operation;
|
||||
use rustfs_targets::{
|
||||
EventName,
|
||||
arn::{ARN, TargetIDError},
|
||||
|
||||
@@ -52,7 +52,7 @@ use rustfs_filemeta::{ReplicationStatusType, ReplicationType};
|
||||
use rustfs_rio::{CompressReader, EncryptReader, HashReader};
|
||||
#[cfg(test)]
|
||||
use rustfs_rio::{DecryptReader, HardLimitReader, boxed_reader, wrap_reader};
|
||||
use rustfs_s3_common::S3Operation;
|
||||
use rustfs_s3_ops::S3Operation;
|
||||
use rustfs_targets::EventName;
|
||||
use rustfs_utils::CompressionAlgorithm;
|
||||
use rustfs_utils::http::{
|
||||
|
||||
@@ -86,7 +86,7 @@ use rustfs_io_metrics;
|
||||
use rustfs_notify::EventArgsBuilder;
|
||||
use rustfs_policy::policy::action::{Action, S3Action};
|
||||
use rustfs_rio::{CompressReader, DynReader, EncryptReader, HashReader, wrap_reader};
|
||||
use rustfs_s3_common::S3Operation;
|
||||
use rustfs_s3_ops::{S3Operation, delete_event_name_for_marker, put_event_name_for_post_object};
|
||||
use rustfs_s3select_api::{
|
||||
object_store::bytes_stream,
|
||||
query::{Context, Query},
|
||||
@@ -1625,9 +1625,9 @@ impl DefaultObjectUsecase {
|
||||
|
||||
fn put_object_execution_context(req: &S3Request<PutObjectInput>) -> (EventName, QuotaOperation, &'static str) {
|
||||
if req.extensions.get::<PostObjectRequestMarker>().is_some() {
|
||||
(EventName::ObjectCreatedPost, QuotaOperation::PostObject, "POST")
|
||||
(put_event_name_for_post_object(true), QuotaOperation::PostObject, "POST")
|
||||
} else {
|
||||
(EventName::ObjectCreatedPut, QuotaOperation::PutObject, "PUT")
|
||||
(put_event_name_for_post_object(false), QuotaOperation::PutObject, "PUT")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3141,11 +3141,7 @@ impl DefaultObjectUsecase {
|
||||
let _activity_guard = DeleteTailActivityGuard::new(DeleteTailStage::Notify);
|
||||
for res in delete_results {
|
||||
if let Some(dobj) = res.delete_object {
|
||||
let event_name = if dobj.delete_marker {
|
||||
EventName::ObjectRemovedDeleteMarkerCreated
|
||||
} else {
|
||||
EventName::ObjectRemovedDelete
|
||||
};
|
||||
let event_name = delete_event_name_for_marker(dobj.delete_marker);
|
||||
let event_args = EventArgsBuilder::new(
|
||||
event_name,
|
||||
bucket.clone(),
|
||||
@@ -3310,7 +3306,7 @@ impl DefaultObjectUsecase {
|
||||
}
|
||||
// Prefix/force-delete returns empty ObjectInfo; still emit bucket notification so webhooks match S3 DELETE.
|
||||
helper = helper
|
||||
.event_name(EventName::ObjectRemovedDelete)
|
||||
.event_name(delete_event_name_for_marker(false))
|
||||
.object(ObjectInfo {
|
||||
name: key.clone(),
|
||||
bucket: bucket.clone(),
|
||||
@@ -3378,11 +3374,7 @@ impl DefaultObjectUsecase {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let event_name = if delete_marker {
|
||||
EventName::ObjectRemovedDeleteMarkerCreated
|
||||
} else {
|
||||
EventName::ObjectRemovedDelete
|
||||
};
|
||||
let event_name = delete_event_name_for_marker(delete_marker);
|
||||
|
||||
helper = helper.event_name(event_name);
|
||||
helper = helper
|
||||
@@ -4341,7 +4333,7 @@ impl DefaultObjectUsecase {
|
||||
};
|
||||
|
||||
let event_args = rustfs_notify::EventArgs {
|
||||
event_name: EventName::ObjectCreatedPut,
|
||||
event_name: put_event_name_for_post_object(false),
|
||||
bucket_name: bucket.clone(),
|
||||
object: obj_info.clone(),
|
||||
req_params: req_params.clone(),
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use rustfs_appauth::token::{Token, parse_license_with_public_key};
|
||||
use rustfs_crypto::{Token, parse_license_with_public_key};
|
||||
use std::fmt;
|
||||
use std::io::{Error, ErrorKind, Result};
|
||||
use std::sync::Arc;
|
||||
@@ -328,7 +328,7 @@ mod tests {
|
||||
RsaPrivateKey, RsaPublicKey,
|
||||
pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding},
|
||||
};
|
||||
use rustfs_appauth::token::sign_license_token;
|
||||
use rustfs_crypto::sign_license_token;
|
||||
use serial_test::serial;
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -16,7 +16,7 @@ use super::{module_switch::resolve_notify_module_state, refresh_persisted_module
|
||||
use crate::app::context::resolve_server_config;
|
||||
use rustfs_ecstore::event_notification::{EventArgs as EcstoreEventArgs, register_event_dispatch_hook};
|
||||
use rustfs_notify::EventArgs as NotifyEventArgs;
|
||||
use rustfs_s3_common::EventName;
|
||||
use rustfs_s3_types::EventName;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use tokio::spawn;
|
||||
@@ -38,14 +38,26 @@ pub fn is_notify_module_enabled() -> bool {
|
||||
NOTIFY_MODULE_ENABLED.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn convert_ecstore_event_args(args: EcstoreEventArgs) -> NotifyEventArgs {
|
||||
fn convert_ecstore_event_args(args: EcstoreEventArgs) -> Option<NotifyEventArgs> {
|
||||
let version_id = args.object.version_id.map(|v| v.to_string()).unwrap_or_default();
|
||||
let (host, port) = parse_host_and_port(args.host);
|
||||
let req_params = args.req_params.into_iter().collect();
|
||||
let resp_elements = args.resp_elements.into_iter().collect();
|
||||
let event_name = match EventName::try_from_event_str(args.event_name.as_str()) {
|
||||
Ok(event_name) => event_name,
|
||||
Err(err) => {
|
||||
warn!(
|
||||
event_name = args.event_name,
|
||||
bucket = args.bucket_name,
|
||||
error = %err,
|
||||
"dropping ecstore event with invalid event name"
|
||||
);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
NotifyEventArgs {
|
||||
event_name: EventName::from(args.event_name.as_str()),
|
||||
Some(NotifyEventArgs {
|
||||
event_name,
|
||||
bucket_name: args.bucket_name,
|
||||
object: args.object,
|
||||
req_params,
|
||||
@@ -54,7 +66,7 @@ fn convert_ecstore_event_args(args: EcstoreEventArgs) -> NotifyEventArgs {
|
||||
host,
|
||||
port,
|
||||
user_agent: args.user_agent,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_host_and_port(host: String) -> (String, u16) {
|
||||
@@ -77,7 +89,9 @@ fn parse_host_and_port(host: String) -> (String, u16) {
|
||||
|
||||
fn install_ecstore_event_dispatch_hook() {
|
||||
let installed = register_event_dispatch_hook(|args| {
|
||||
let notify_args = convert_ecstore_event_args(args);
|
||||
let Some(notify_args) = convert_ecstore_event_args(args) else {
|
||||
return;
|
||||
};
|
||||
spawn(async move {
|
||||
rustfs_notify::notifier_global::notify(notify_args).await;
|
||||
});
|
||||
|
||||
@@ -41,7 +41,8 @@ use rustfs_ecstore::{
|
||||
new_object_layer_fn,
|
||||
store_api::{BucketOperations, BucketOptions, ObjectLockRetentionOptions, ObjectOperations, ObjectOptions},
|
||||
};
|
||||
use rustfs_s3_common::{S3Operation, record_s3_op};
|
||||
use rustfs_io_metrics::record_s3_op;
|
||||
use rustfs_s3_ops::S3Operation;
|
||||
use rustfs_targets::EventName;
|
||||
use rustfs_utils::http::headers::{
|
||||
AMZ_OBJECT_LOCK_LEGAL_HOLD_LOWER, AMZ_OBJECT_LOCK_MODE_LOWER, AMZ_OBJECT_LOCK_RETAIN_UNTIL_DATE_LOWER,
|
||||
@@ -71,7 +72,7 @@ impl Default for FS {
|
||||
|
||||
impl FS {
|
||||
pub fn new() -> Self {
|
||||
rustfs_s3_common::init_s3_metrics();
|
||||
rustfs_io_metrics::init_s3_metrics();
|
||||
Self {}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,10 @@ use rustfs_audit::{
|
||||
global::AuditLogger,
|
||||
};
|
||||
use rustfs_ecstore::store_api::ObjectInfo;
|
||||
use rustfs_io_metrics::record_s3_op;
|
||||
use rustfs_notify::{EventArgsBuilder, notifier_global};
|
||||
use rustfs_s3_common::record_s3_op;
|
||||
use rustfs_s3_common::{EventName, S3Operation};
|
||||
use rustfs_s3_ops::{S3Operation, operation_matches_event_name};
|
||||
use rustfs_s3_types::EventName;
|
||||
use rustfs_utils::{
|
||||
extract_params_header, extract_req_params, extract_resp_elements, get_request_host, get_request_port, get_request_user_agent,
|
||||
http::headers::AMZ_REQUEST_ID,
|
||||
@@ -34,7 +35,7 @@ use s3s::{S3Request, S3Response, S3Result};
|
||||
use serde_json::Value;
|
||||
use std::future::Future;
|
||||
use tokio::runtime::{Builder, Handle};
|
||||
use tracing::{Instrument, info_span};
|
||||
use tracing::{Instrument, info_span, warn};
|
||||
|
||||
/// Schedules an asynchronous task on the current runtime;
|
||||
/// if there is no runtime, creates a minimal runtime execution on a new thread.
|
||||
@@ -89,6 +90,22 @@ pub struct EnabledOperationHelper {
|
||||
impl OperationHelper {
|
||||
/// Create a new OperationHelper for S3 requests.
|
||||
pub fn new(req: &S3Request<impl Send + Sync>, event: EventName, op: S3Operation) -> Self {
|
||||
let op_event_matches = operation_matches_event_name(op, event);
|
||||
debug_assert!(op_event_matches, "operation/event mismatch: op={} event={}", op.as_str(), event.as_str());
|
||||
if !op_event_matches {
|
||||
counter!(
|
||||
"rustfs_log_chain_op_event_mismatch_total",
|
||||
"op" => op.as_str(),
|
||||
"event" => event.as_str().to_string()
|
||||
)
|
||||
.increment(1);
|
||||
warn!(
|
||||
op = op.as_str(),
|
||||
event = event.as_str(),
|
||||
"operation/event mismatch detected; check S3 semantic mapping"
|
||||
);
|
||||
}
|
||||
|
||||
let audit_enabled = is_audit_module_enabled();
|
||||
let notify_enabled = should_build_notification_event(is_notify_module_enabled());
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
use super::*;
|
||||
use rustfs_common::internode_metrics::{
|
||||
use rustfs_io_metrics::internode_metrics::{
|
||||
INTERNODE_OPERATION_GRPC_READ_ALL, INTERNODE_OPERATION_GRPC_WRITE_ALL, global_internode_metrics,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
@@ -18,15 +18,15 @@ use futures_util::TryStreamExt;
|
||||
use http::{HeaderMap, Method, Request, Response, StatusCode, Uri};
|
||||
use http_body_util::{BodyExt, Limited};
|
||||
use hyper::body::Incoming;
|
||||
use rustfs_common::internode_metrics::{
|
||||
INTERNODE_OPERATION_PUT_FILE_STREAM, INTERNODE_OPERATION_READ_FILE_STREAM, INTERNODE_OPERATION_WALK_DIR,
|
||||
global_internode_metrics,
|
||||
};
|
||||
use rustfs_config::MAX_ADMIN_REQUEST_BODY_SIZE;
|
||||
use rustfs_ecstore::disk::{DiskAPI, WalkDirOptions};
|
||||
use rustfs_ecstore::rpc::verify_rpc_signature;
|
||||
use rustfs_ecstore::set_disk::DEFAULT_READ_BUFFER_SIZE;
|
||||
use rustfs_ecstore::store::find_local_disk_by_ref;
|
||||
use rustfs_io_metrics::internode_metrics::{
|
||||
INTERNODE_OPERATION_PUT_FILE_STREAM, INTERNODE_OPERATION_READ_FILE_STREAM, INTERNODE_OPERATION_WALK_DIR,
|
||||
global_internode_metrics,
|
||||
};
|
||||
use rustfs_utils::net::bytes_stream;
|
||||
use s3s::Body;
|
||||
use s3s::dto::StreamingBlob;
|
||||
|
||||
Reference in New Issue
Block a user