mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 08:18:18 +00:00
refactor: remove standalone compat bridge modules (#3740)
This commit is contained in:
@@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
mod reliant;
|
||||
mod storage_compat;
|
||||
|
||||
// Common utilities for all E2E tests
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
// Used by test_distributed_lock_4_nodes_grpc in lock.rs
|
||||
#![allow(dead_code)]
|
||||
|
||||
use super::super::storage_compat::node_service_time_out_client_no_auth;
|
||||
use async_trait::async_trait;
|
||||
use rustfs_ecstore::api::rpc::{self as ecstore_rpc, TonicInterceptor};
|
||||
use rustfs_lock::{
|
||||
LockClient, LockError, LockId, LockInfo, LockRequest, LockResponse, LockStats, LockStatus, LockType, Result,
|
||||
types::{LockMetadata, LockPriority},
|
||||
@@ -41,13 +41,10 @@ impl GrpcLockClient {
|
||||
&self,
|
||||
) -> Result<
|
||||
rustfs_protos::proto_gen::node_service::node_service_client::NodeServiceClient<
|
||||
tonic::service::interceptor::InterceptedService<
|
||||
tonic::transport::Channel,
|
||||
super::super::storage_compat::TonicInterceptor,
|
||||
>,
|
||||
tonic::service::interceptor::InterceptedService<tonic::transport::Channel, TonicInterceptor>,
|
||||
>,
|
||||
> {
|
||||
node_service_time_out_client_no_auth(&self.addr)
|
||||
ecstore_rpc::node_service_time_out_client_no_auth(&self.addr)
|
||||
.await
|
||||
.map_err(|err| LockError::internal(format!("can not get client, err: {err}")))
|
||||
}
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::super::storage_compat::{VolumeInfo, WalkDirOptions};
|
||||
use super::super::storage_compat::{gen_tonic_signature_interceptor, node_service_time_out_client};
|
||||
use crate::common::workspace_root;
|
||||
use futures::future::join_all;
|
||||
use rmp_serde::{Deserializer, Serializer};
|
||||
use rustfs_ecstore::api::{
|
||||
disk::{VolumeInfo, WalkDirOptions},
|
||||
rpc::{self as ecstore_rpc, TonicInterceptor},
|
||||
};
|
||||
use rustfs_filemeta::{MetaCacheEntry, MetacacheReader, MetacacheWriter};
|
||||
use rustfs_protos::proto_gen::node_service::WalkDirRequest;
|
||||
use rustfs_protos::{
|
||||
@@ -36,6 +38,10 @@ use tonic::codegen::tokio_stream::StreamExt;
|
||||
|
||||
const CLUSTER_ADDR: &str = "http://localhost:9000";
|
||||
|
||||
fn signature_interceptor() -> TonicInterceptor {
|
||||
TonicInterceptor::Signature(ecstore_rpc::gen_tonic_signature_interceptor())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore = "requires running RustFS server at localhost:9000"]
|
||||
async fn ping() -> Result<(), Box<dyn Error>> {
|
||||
@@ -53,7 +59,7 @@ async fn ping() -> Result<(), Box<dyn Error>> {
|
||||
assert!(decoded_payload.is_ok());
|
||||
|
||||
// Create client
|
||||
let mut client = node_service_time_out_client(&CLUSTER_ADDR.to_string(), gen_tonic_signature_interceptor()).await?;
|
||||
let mut client = ecstore_rpc::node_service_time_out_client(&CLUSTER_ADDR.to_string(), signature_interceptor()).await?;
|
||||
|
||||
// Construct PingRequest
|
||||
let request = Request::new(PingRequest {
|
||||
@@ -78,7 +84,7 @@ async fn ping() -> Result<(), Box<dyn Error>> {
|
||||
#[tokio::test]
|
||||
#[ignore = "requires running RustFS server at localhost:9000"]
|
||||
async fn make_volume() -> Result<(), Box<dyn Error>> {
|
||||
let mut client = node_service_time_out_client(&CLUSTER_ADDR.to_string(), gen_tonic_signature_interceptor()).await?;
|
||||
let mut client = ecstore_rpc::node_service_time_out_client(&CLUSTER_ADDR.to_string(), signature_interceptor()).await?;
|
||||
let request = Request::new(MakeVolumeRequest {
|
||||
disk: "data".to_string(),
|
||||
volume: "dandan".to_string(),
|
||||
@@ -96,7 +102,7 @@ async fn make_volume() -> Result<(), Box<dyn Error>> {
|
||||
#[tokio::test]
|
||||
#[ignore = "requires running RustFS server at localhost:9000"]
|
||||
async fn list_volumes() -> Result<(), Box<dyn Error>> {
|
||||
let mut client = node_service_time_out_client(&CLUSTER_ADDR.to_string(), gen_tonic_signature_interceptor()).await?;
|
||||
let mut client = ecstore_rpc::node_service_time_out_client(&CLUSTER_ADDR.to_string(), signature_interceptor()).await?;
|
||||
let request = Request::new(ListVolumesRequest {
|
||||
disk: "data".to_string(),
|
||||
});
|
||||
@@ -126,7 +132,7 @@ async fn walk_dir() -> Result<(), Box<dyn Error>> {
|
||||
let (rd, mut wr) = tokio::io::duplex(1024);
|
||||
let mut buf = Vec::new();
|
||||
opts.serialize(&mut Serializer::new(&mut buf))?;
|
||||
let mut client = node_service_time_out_client(&CLUSTER_ADDR.to_string(), gen_tonic_signature_interceptor()).await?;
|
||||
let mut client = ecstore_rpc::node_service_time_out_client(&CLUSTER_ADDR.to_string(), signature_interceptor()).await?;
|
||||
let disk_path = std::env::var_os("RUSTFS_DISK_PATH").map(PathBuf::from).unwrap_or_else(|| {
|
||||
let mut path = workspace_root();
|
||||
path.push("target");
|
||||
@@ -179,7 +185,7 @@ async fn walk_dir() -> Result<(), Box<dyn Error>> {
|
||||
#[tokio::test]
|
||||
#[ignore = "requires running RustFS server at localhost:9000"]
|
||||
async fn read_all() -> Result<(), Box<dyn Error>> {
|
||||
let mut client = node_service_time_out_client(&CLUSTER_ADDR.to_string(), gen_tonic_signature_interceptor()).await?;
|
||||
let mut client = ecstore_rpc::node_service_time_out_client(&CLUSTER_ADDR.to_string(), signature_interceptor()).await?;
|
||||
let request = Request::new(ReadAllRequest {
|
||||
disk: "data".to_string(),
|
||||
volume: "ff".to_string(),
|
||||
@@ -197,7 +203,7 @@ async fn read_all() -> Result<(), Box<dyn Error>> {
|
||||
#[tokio::test]
|
||||
#[ignore = "requires running RustFS server at localhost:9000"]
|
||||
async fn storage_info() -> Result<(), Box<dyn Error>> {
|
||||
let mut client = node_service_time_out_client(&CLUSTER_ADDR.to_string(), gen_tonic_signature_interceptor()).await?;
|
||||
let mut client = ecstore_rpc::node_service_time_out_client(&CLUSTER_ADDR.to_string(), signature_interceptor()).await?;
|
||||
let request = Request::new(LocalStorageInfoRequest { metrics: true });
|
||||
|
||||
let response = client.local_storage_info(request).await?.into_inner();
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::storage_compat::BucketTargetSys;
|
||||
use crate::common::{
|
||||
RustFSTestEnvironment, awscurl_available, awscurl_post_sts_form_urlencoded, init_logging, local_http_client,
|
||||
};
|
||||
@@ -22,6 +21,7 @@ use aws_sdk_s3::types::{BucketVersioningStatus, VersioningConfiguration};
|
||||
use aws_sdk_s3::{Client, Config};
|
||||
use http::header::{CONTENT_TYPE, HOST};
|
||||
use reqwest::StatusCode;
|
||||
use rustfs_ecstore::api::bucket::bucket_target_sys::BucketTargetSys;
|
||||
use rustfs_madmin::{
|
||||
AddServiceAccountReq, ListServiceAccountsResp, PeerInfo, PeerSite, ReplicateAddStatus, ReplicateEditStatus,
|
||||
ReplicateRemoveStatus, SRRemoveReq, SRResyncOpStatus, SRStatusInfo, SiteReplicationInfo, SyncStatus,
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright 2024 RustFS Team
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![allow(dead_code, unused_imports)]
|
||||
|
||||
use rustfs_ecstore::api::bucket as ecstore_bucket;
|
||||
use rustfs_ecstore::api::disk as ecstore_disk;
|
||||
use rustfs_ecstore::api::rpc as ecstore_rpc;
|
||||
|
||||
pub(crate) type BucketTargetSys = ecstore_bucket::bucket_target_sys::BucketTargetSys;
|
||||
pub(crate) type TonicInterceptor = ecstore_rpc::TonicInterceptor;
|
||||
pub(crate) type VolumeInfo = ecstore_disk::VolumeInfo;
|
||||
pub(crate) type WalkDirOptions = ecstore_disk::WalkDirOptions;
|
||||
|
||||
pub(crate) fn gen_tonic_signature_interceptor() -> TonicInterceptor {
|
||||
TonicInterceptor::Signature(ecstore_rpc::gen_tonic_signature_interceptor())
|
||||
}
|
||||
|
||||
pub(crate) async fn node_service_time_out_client(
|
||||
addr: &String,
|
||||
interceptor: TonicInterceptor,
|
||||
) -> Result<
|
||||
rustfs_protos::proto_gen::node_service::node_service_client::NodeServiceClient<
|
||||
tonic::service::interceptor::InterceptedService<tonic::transport::Channel, TonicInterceptor>,
|
||||
>,
|
||||
Box<dyn std::error::Error>,
|
||||
> {
|
||||
ecstore_rpc::node_service_time_out_client(addr, interceptor).await
|
||||
}
|
||||
|
||||
pub(crate) async fn node_service_time_out_client_no_auth(
|
||||
addr: &String,
|
||||
) -> Result<
|
||||
rustfs_protos::proto_gen::node_service::node_service_client::NodeServiceClient<
|
||||
tonic::service::interceptor::InterceptedService<tonic::transport::Channel, TonicInterceptor>,
|
||||
>,
|
||||
Box<dyn std::error::Error>,
|
||||
> {
|
||||
ecstore_rpc::node_service_time_out_client_no_auth(addr).await
|
||||
}
|
||||
@@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
pub mod object;
|
||||
mod storage_compat;
|
||||
|
||||
use crate::cache::Cache;
|
||||
use crate::error::Result;
|
||||
|
||||
@@ -28,7 +28,7 @@ use futures::future::join_all;
|
||||
use rustfs_credentials::get_global_action_cred;
|
||||
use rustfs_io_metrics::record_system_path_failure;
|
||||
use rustfs_policy::{auth::UserIdentity, policy::PolicyDoc};
|
||||
use rustfs_storage_api::{HTTPPreconditions, ListOperations as _, ObjectInfoOrErr as StorageObjectInfoOrErr};
|
||||
use rustfs_storage_api::{HTTPPreconditions, ListOperations as _, ObjectInfoOrErr as StorageObjectInfoOrErr, ObjectOperations};
|
||||
use rustfs_utils::path::{SLASH_SEPARATOR, path_join_buf};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
@@ -38,8 +38,6 @@ use tokio::sync::mpsc::{self, Sender};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
use super::storage_compat::{IamObjectInfo, IamObjectOptions};
|
||||
|
||||
pub static IAM_CONFIG_PREFIX: LazyLock<String> = LazyLock::new(|| format!("{IAM_CONFIG_ROOT_PREFIX}/iam"));
|
||||
pub static IAM_CONFIG_USERS_PREFIX: LazyLock<String> = LazyLock::new(|| format!("{IAM_CONFIG_ROOT_PREFIX}/iam/users/"));
|
||||
pub static IAM_CONFIG_SERVICE_ACCOUNTS_PREFIX: LazyLock<String> =
|
||||
@@ -58,6 +56,8 @@ pub static IAM_CONFIG_POLICY_DB_GROUPS_PREFIX: LazyLock<String> =
|
||||
LazyLock::new(|| format!("{IAM_CONFIG_ROOT_PREFIX}/iam/policydb/groups/"));
|
||||
|
||||
type ObjectInfoOrErr = StorageObjectInfoOrErr<IamObjectInfo, IamStorageError>;
|
||||
type IamObjectInfo = <IamStore as ObjectOperations>::ObjectInfo;
|
||||
type IamObjectOptions = <IamStore as ObjectOperations>::ObjectOptions;
|
||||
|
||||
const IAM_IDENTITY_FILE: &str = "identity.json";
|
||||
const IAM_POLICY_FILE: &str = "policy.json";
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright 2024 RustFS Team
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::super::storage_compat::IamStore;
|
||||
|
||||
pub(super) type IamObjectInfo = <IamStore as rustfs_storage_api::ObjectOperations>::ObjectInfo;
|
||||
pub(super) type IamObjectOptions = <IamStore as rustfs_storage_api::ObjectOperations>::ObjectOptions;
|
||||
@@ -13,17 +13,14 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
Event, NotificationError,
|
||||
registry::TargetRegistry,
|
||||
rule_engine::NotifyRuleEngine,
|
||||
runtime_facade::NotifyRuntimeFacade,
|
||||
storage_compat::{self, NotifyConfigStoreError},
|
||||
Event, NotificationError, registry::TargetRegistry, rule_engine::NotifyRuleEngine, runtime_facade::NotifyRuntimeFacade,
|
||||
};
|
||||
use rustfs_config::notify::{
|
||||
NOTIFY_AMQP_SUB_SYS, NOTIFY_KAFKA_SUB_SYS, NOTIFY_MQTT_SUB_SYS, NOTIFY_MYSQL_SUB_SYS, NOTIFY_NATS_SUB_SYS,
|
||||
NOTIFY_POSTGRES_SUB_SYS, NOTIFY_PULSAR_SUB_SYS, NOTIFY_REDIS_SUB_SYS, NOTIFY_WEBHOOK_SUB_SYS,
|
||||
};
|
||||
use rustfs_config::server_config::{Config, KVS};
|
||||
use rustfs_ecstore::api::{config as ecstore_config, global as ecstore_global, storage as ecstore_storage};
|
||||
use rustfs_targets::{Target, arn::TargetID};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
@@ -34,6 +31,50 @@ const LOG_SUBSYSTEM_CONFIG: &str = "config";
|
||||
const EVENT_NOTIFY_RUNTIME_LIFECYCLE: &str = "notify_runtime_lifecycle";
|
||||
const EVENT_NOTIFY_CONFIG_UPDATE: &str = "notify_config_update";
|
||||
|
||||
type NotifyStore = ecstore_storage::ECStore;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum NotifyConfigStoreError {
|
||||
StorageNotAvailable,
|
||||
Read(String),
|
||||
Save(String),
|
||||
}
|
||||
|
||||
async fn update_server_config<F>(mut modifier: F) -> Result<Option<Config>, NotifyConfigStoreError>
|
||||
where
|
||||
F: FnMut(&mut Config) -> bool,
|
||||
{
|
||||
let Some(store) = resolve_notify_object_store_handle() else {
|
||||
return Err(NotifyConfigStoreError::StorageNotAvailable);
|
||||
};
|
||||
|
||||
let mut new_config = read_notify_server_config_without_migrate(store.clone()).await?;
|
||||
|
||||
if !modifier(&mut new_config) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
save_notify_server_config(store, &new_config).await?;
|
||||
|
||||
Ok(Some(new_config))
|
||||
}
|
||||
|
||||
fn resolve_notify_object_store_handle() -> Option<Arc<NotifyStore>> {
|
||||
ecstore_global::resolve_object_store_handle()
|
||||
}
|
||||
|
||||
async fn read_notify_server_config_without_migrate(store: Arc<NotifyStore>) -> Result<Config, NotifyConfigStoreError> {
|
||||
ecstore_config::com::read_config_without_migrate(store)
|
||||
.await
|
||||
.map_err(|err| NotifyConfigStoreError::Read(err.to_string()))
|
||||
}
|
||||
|
||||
async fn save_notify_server_config(store: Arc<NotifyStore>, config: &Config) -> Result<(), NotifyConfigStoreError> {
|
||||
ecstore_config::com::save_server_config(store, config)
|
||||
.await
|
||||
.map_err(|err| NotifyConfigStoreError::Save(err.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn notify_configuration_hint() -> String {
|
||||
let webhook_enable_primary = format!("{}_PRIMARY", rustfs_config::notify::ENV_NOTIFY_WEBHOOK_ENABLE);
|
||||
let webhook_endpoint_primary = format!("{}_PRIMARY", rustfs_config::notify::ENV_NOTIFY_WEBHOOK_ENDPOINT);
|
||||
@@ -320,15 +361,13 @@ impl NotifyConfigManager {
|
||||
where
|
||||
F: FnMut(&mut Config) -> bool,
|
||||
{
|
||||
let Some(new_config) = storage_compat::update_server_config(&mut modifier)
|
||||
.await
|
||||
.map_err(|err| match err {
|
||||
NotifyConfigStoreError::StorageNotAvailable => NotificationError::StorageNotAvailable(
|
||||
"Failed to save target configuration: server storage not initialized".to_string(),
|
||||
),
|
||||
NotifyConfigStoreError::Read(err) => NotificationError::ReadConfig(err),
|
||||
NotifyConfigStoreError::Save(err) => NotificationError::SaveConfig(err),
|
||||
})?
|
||||
let Some(new_config) = update_server_config(&mut modifier).await.map_err(|err| match err {
|
||||
NotifyConfigStoreError::StorageNotAvailable => NotificationError::StorageNotAvailable(
|
||||
"Failed to save target configuration: server storage not initialized".to_string(),
|
||||
),
|
||||
NotifyConfigStoreError::Read(err) => NotificationError::ReadConfig(err),
|
||||
NotifyConfigStoreError::Save(err) => NotificationError::SaveConfig(err),
|
||||
})?
|
||||
else {
|
||||
debug!(
|
||||
event = EVENT_NOTIFY_CONFIG_UPDATE,
|
||||
|
||||
@@ -36,7 +36,6 @@ mod runtime_facade;
|
||||
mod runtime_view;
|
||||
mod services;
|
||||
mod status_view;
|
||||
mod storage_compat;
|
||||
|
||||
pub use bucket_config_manager::NotifyBucketConfigManager;
|
||||
pub use config_manager::{NotifyConfigManager, runtime_target_id_for_subsystem};
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright 2024 RustFS Team
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use rustfs_config::server_config::Config;
|
||||
use rustfs_ecstore::api::config as ecstore_config;
|
||||
use rustfs_ecstore::api::global as ecstore_global;
|
||||
use rustfs_ecstore::api::storage as ecstore_storage;
|
||||
use std::sync::Arc;
|
||||
|
||||
type NotifyStore = ecstore_storage::ECStore;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum NotifyConfigStoreError {
|
||||
StorageNotAvailable,
|
||||
Read(String),
|
||||
Save(String),
|
||||
}
|
||||
|
||||
pub(crate) async fn update_server_config<F>(mut modifier: F) -> Result<Option<Config>, NotifyConfigStoreError>
|
||||
where
|
||||
F: FnMut(&mut Config) -> bool,
|
||||
{
|
||||
let Some(store) = resolve_notify_object_store_handle() else {
|
||||
return Err(NotifyConfigStoreError::StorageNotAvailable);
|
||||
};
|
||||
|
||||
let mut new_config = read_notify_server_config_without_migrate(store.clone()).await?;
|
||||
|
||||
if !modifier(&mut new_config) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
save_notify_server_config(store, &new_config).await?;
|
||||
|
||||
Ok(Some(new_config))
|
||||
}
|
||||
|
||||
fn resolve_notify_object_store_handle() -> Option<Arc<NotifyStore>> {
|
||||
ecstore_global::resolve_object_store_handle()
|
||||
}
|
||||
|
||||
async fn read_notify_server_config_without_migrate(store: Arc<NotifyStore>) -> Result<Config, NotifyConfigStoreError> {
|
||||
ecstore_config::com::read_config_without_migrate(store)
|
||||
.await
|
||||
.map_err(|err| NotifyConfigStoreError::Read(err.to_string()))
|
||||
}
|
||||
|
||||
async fn save_notify_server_config(store: Arc<NotifyStore>, config: &Config) -> Result<(), NotifyConfigStoreError> {
|
||||
ecstore_config::com::save_server_config(store, config)
|
||||
.await
|
||||
.map_err(|err| NotifyConfigStoreError::Save(err.to_string()))
|
||||
}
|
||||
@@ -64,7 +64,6 @@ mod error;
|
||||
mod global;
|
||||
mod logging;
|
||||
pub mod metrics;
|
||||
mod storage_compat;
|
||||
mod telemetry;
|
||||
|
||||
pub use cleaner::*;
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
//! - Process disk I/O metrics
|
||||
//! - Host network I/O metrics
|
||||
|
||||
use super::super::storage_compat::obs_bucket_monitor_available;
|
||||
use crate::metrics::collectors::{
|
||||
AuditTargetStats,
|
||||
BucketReplicationBandwidthStats,
|
||||
@@ -83,6 +82,7 @@ use crate::metrics::stats_collector::{
|
||||
collect_scanner_metric_stats, collect_system_cpu_and_memory_stats_with,
|
||||
};
|
||||
use rustfs_audit::audit_target_metrics;
|
||||
use rustfs_ecstore::api::global as ecstore_global;
|
||||
use rustfs_notify::{notification_metrics_snapshot, notification_target_metrics};
|
||||
use rustfs_utils::get_env_opt_u64;
|
||||
use serde::Serialize;
|
||||
@@ -599,7 +599,7 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = interval.tick() => {
|
||||
let monitor_available = obs_bucket_monitor_available();
|
||||
let monitor_available = ecstore_global::get_global_bucket_monitor().is_some();
|
||||
let stats = collect_bucket_replication_bandwidth_stats();
|
||||
|
||||
let current_live_keys = repl_bw_live_keys(&stats);
|
||||
|
||||
@@ -20,25 +20,24 @@
|
||||
//! RustFS internal sources (storage layer, bucket monitor, system info)
|
||||
//! and convert them to the Stats structs used by collectors.
|
||||
|
||||
use super::super::storage_compat::{
|
||||
load_obs_data_usage_from_backend, obs_bucket_quota_limit_bytes, obs_bucket_replication_bandwidth_stats,
|
||||
obs_bucket_replication_detail_stats, obs_ilm_runtime_snapshot, obs_site_replication_stats, obs_total_usable_capacity_bytes,
|
||||
obs_total_usable_capacity_free_bytes, resolve_obs_object_store_handle,
|
||||
};
|
||||
use crate::metrics::collectors::{
|
||||
BucketReplicationBandwidthStats, BucketReplicationStats, BucketStats, BucketUsageStats, ClusterConfigStats,
|
||||
ClusterHealthStats, ClusterStats, ClusterUsageStats, CpuStats, DiskStats, DriveCountStats, DriveDetailedStats,
|
||||
ErasureSetStats, HostNetworkStats, IamStats, IlmStats, MemoryStats, NetworkStats, ProcessStats, ProcessStatusType,
|
||||
ReplicationStats, ResourceStats, ScannerStats,
|
||||
BucketReplicationBandwidthStats, BucketReplicationStats, BucketReplicationTargetStats, BucketStats, BucketUsageStats,
|
||||
ClusterConfigStats, ClusterHealthStats, ClusterStats, ClusterUsageStats, CpuStats, DiskStats, DriveCountStats,
|
||||
DriveDetailedStats, ErasureSetStats, HostNetworkStats, IamStats, IlmStats, MemoryStats, NetworkStats, ProcessStats,
|
||||
ProcessStatusType, ReplicationStats, ResourceStats, ScannerStats,
|
||||
};
|
||||
use chrono::Utc;
|
||||
use rustfs_common::heal_channel::HealScanMode;
|
||||
use rustfs_common::metrics::global_metrics;
|
||||
use rustfs_ecstore::api::{
|
||||
bucket as ecstore_bucket, capacity as ecstore_capacity, data_usage as ecstore_data_usage, error as ecstore_error,
|
||||
global as ecstore_global, storage as ecstore_storage,
|
||||
};
|
||||
use rustfs_iam::{get_global_iam_sys, oidc::oidc_plugin_authn_metrics_snapshot};
|
||||
use rustfs_io_metrics::internode_metrics::global_internode_metrics;
|
||||
use rustfs_io_metrics::{ProcessStatusSnapshot, snapshot_process_resource_and_system};
|
||||
use rustfs_storage_api::{BucketOperations, BucketOptions, StorageAdminApi};
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||
use sysinfo::{Networks, System};
|
||||
use tracing::{instrument, warn};
|
||||
|
||||
@@ -46,6 +45,260 @@ const LOG_COMPONENT_OBS: &str = "obs";
|
||||
const LOG_SUBSYSTEM_METRICS_COLLECTOR: &str = "metrics_collector";
|
||||
const EVENT_METRICS_COLLECTOR_STATE: &str = "metrics_collector_state";
|
||||
|
||||
type ObsStore = ecstore_storage::ECStore;
|
||||
type ObsStorageInfo = <ObsStore as StorageAdminApi>::StorageInfo;
|
||||
|
||||
struct ObsDataUsageInfo {
|
||||
buckets_count: u64,
|
||||
objects_total_count: u64,
|
||||
versions_total_count: u64,
|
||||
delete_markers_total_count: u64,
|
||||
objects_total_size: u64,
|
||||
buckets_usage: HashMap<String, ObsBucketUsageInfo>,
|
||||
}
|
||||
|
||||
struct ObsBucketUsageInfo {
|
||||
size: u64,
|
||||
objects_count: u64,
|
||||
object_size_histogram: HashMap<String, u64>,
|
||||
object_versions_histogram: HashMap<String, u64>,
|
||||
versions_count: u64,
|
||||
delete_markers_count: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
struct ObsBucketReplicationBandwidthStats {
|
||||
bucket: String,
|
||||
target_arn: String,
|
||||
limit_bytes_per_sec: i64,
|
||||
current_bandwidth_bytes_per_sec: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
struct ObsIlmRuntimeSnapshot {
|
||||
expiry_pending_tasks: u64,
|
||||
transition_active_tasks: u64,
|
||||
transition_pending_tasks: u64,
|
||||
transition_missed_immediate_tasks: u64,
|
||||
transition_queue_full_tasks: u64,
|
||||
transition_queue_send_timeout_tasks: u64,
|
||||
transition_compensation_scheduled_tasks: u64,
|
||||
transition_compensation_running_tasks: u64,
|
||||
}
|
||||
|
||||
fn usize_to_u64_saturating(value: usize) -> u64 {
|
||||
u64::try_from(value).unwrap_or(u64::MAX)
|
||||
}
|
||||
|
||||
fn i64_to_u64_floor_zero(value: i64) -> u64 {
|
||||
u64::try_from(value.max(0)).unwrap_or(0)
|
||||
}
|
||||
|
||||
fn i32_to_u64_floor_zero(value: i32) -> u64 {
|
||||
u64::try_from(value.max(0)).unwrap_or(0)
|
||||
}
|
||||
|
||||
async fn load_obs_data_usage_from_backend(store: Arc<ObsStore>) -> ecstore_error::Result<ObsDataUsageInfo> {
|
||||
let data_usage = ecstore_data_usage::load_data_usage_from_backend(store).await?;
|
||||
|
||||
Ok(ObsDataUsageInfo {
|
||||
buckets_count: data_usage.buckets_count,
|
||||
objects_total_count: data_usage.objects_total_count,
|
||||
versions_total_count: data_usage.versions_total_count,
|
||||
delete_markers_total_count: data_usage.delete_markers_total_count,
|
||||
objects_total_size: data_usage.objects_total_size,
|
||||
buckets_usage: data_usage
|
||||
.buckets_usage
|
||||
.into_iter()
|
||||
.map(|(bucket, usage)| {
|
||||
(
|
||||
bucket,
|
||||
ObsBucketUsageInfo {
|
||||
size: usage.size,
|
||||
objects_count: usage.objects_count,
|
||||
object_size_histogram: usage.object_size_histogram,
|
||||
object_versions_histogram: usage.object_versions_histogram,
|
||||
versions_count: usage.versions_count,
|
||||
delete_markers_count: usage.delete_markers_count,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_obs_object_store_handle() -> Option<Arc<ObsStore>> {
|
||||
ecstore_global::resolve_object_store_handle()
|
||||
}
|
||||
|
||||
fn obs_total_usable_capacity_bytes(storage_info: &ObsStorageInfo) -> u64 {
|
||||
usize_to_u64_saturating(ecstore_capacity::get_total_usable_capacity(&storage_info.disks, storage_info))
|
||||
}
|
||||
|
||||
fn obs_total_usable_capacity_free_bytes(storage_info: &ObsStorageInfo) -> u64 {
|
||||
usize_to_u64_saturating(ecstore_capacity::get_total_usable_capacity_free(&storage_info.disks, storage_info))
|
||||
}
|
||||
|
||||
async fn obs_bucket_quota_limit_bytes(bucket: &str) -> u64 {
|
||||
ecstore_bucket::metadata_sys::get_quota_config(bucket)
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|(quota, _)| quota.get_quota_limit())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn obs_bucket_replication_bandwidth_stats() -> Option<Vec<ObsBucketReplicationBandwidthStats>> {
|
||||
let monitor = ecstore_global::get_global_bucket_monitor()?;
|
||||
Some(
|
||||
monitor
|
||||
.get_report(|_| true)
|
||||
.bucket_stats
|
||||
.into_iter()
|
||||
.map(|(opts, details)| ObsBucketReplicationBandwidthStats {
|
||||
bucket: opts.name,
|
||||
target_arn: opts.replication_arn,
|
||||
limit_bytes_per_sec: details.limit_bytes_per_sec,
|
||||
current_bandwidth_bytes_per_sec: details.current_bandwidth_bytes_per_sec,
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn obs_ilm_runtime_snapshot() -> ObsIlmRuntimeSnapshot {
|
||||
let expiry_pending_tasks = {
|
||||
let expiry = ecstore_bucket::lifecycle::bucket_lifecycle_ops::GLOBAL_ExpiryState
|
||||
.read()
|
||||
.await;
|
||||
usize_to_u64_saturating(expiry.pending_tasks())
|
||||
};
|
||||
let transition = &ecstore_bucket::lifecycle::bucket_lifecycle_ops::GLOBAL_TransitionState;
|
||||
|
||||
ObsIlmRuntimeSnapshot {
|
||||
expiry_pending_tasks,
|
||||
transition_active_tasks: i64_to_u64_floor_zero(transition.active_tasks()),
|
||||
transition_pending_tasks: usize_to_u64_saturating(transition.pending_tasks()),
|
||||
transition_missed_immediate_tasks: i64_to_u64_floor_zero(transition.missed_immediate_tasks()),
|
||||
transition_queue_full_tasks: i64_to_u64_floor_zero(transition.queue_full_tasks()),
|
||||
transition_queue_send_timeout_tasks: i64_to_u64_floor_zero(transition.queue_send_timeout_tasks()),
|
||||
transition_compensation_scheduled_tasks: i64_to_u64_floor_zero(transition.compensation_scheduled_tasks()),
|
||||
transition_compensation_running_tasks: i64_to_u64_floor_zero(transition.compensation_running_tasks()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn obs_bucket_replication_detail_stats() -> Vec<BucketReplicationStats> {
|
||||
let Some(stats) = ecstore_bucket::replication::GLOBAL_REPLICATION_STATS.get() else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
let all_bucket_stats = stats.get_all().await;
|
||||
let mut buckets = Vec::with_capacity(all_bucket_stats.len());
|
||||
|
||||
for (bucket, bucket_stats) in all_bucket_stats {
|
||||
let proxy = stats.get_proxy_stats(&bucket).await;
|
||||
let mut total_failed_bytes = 0u64;
|
||||
let mut total_failed_count = 0u64;
|
||||
let mut last_min_failed_bytes = 0u64;
|
||||
let mut last_min_failed_count = 0u64;
|
||||
let mut last_hour_failed_bytes = 0u64;
|
||||
let mut last_hour_failed_count = 0u64;
|
||||
let mut sent_bytes = 0u64;
|
||||
let mut sent_count = 0u64;
|
||||
let mut targets = Vec::with_capacity(bucket_stats.stats.len());
|
||||
|
||||
for (target_arn, target_stats) in bucket_stats.stats {
|
||||
total_failed_bytes += i64_to_u64_floor_zero(target_stats.fail_stats.size);
|
||||
total_failed_count += i64_to_u64_floor_zero(target_stats.fail_stats.count);
|
||||
|
||||
let last_min = target_stats.fail_stats.recent_since(Duration::from_secs(60));
|
||||
last_min_failed_bytes += i64_to_u64_floor_zero(last_min.size);
|
||||
last_min_failed_count += i64_to_u64_floor_zero(last_min.count);
|
||||
|
||||
let last_hour = target_stats.fail_stats.recent_since(Duration::from_secs(60 * 60));
|
||||
last_hour_failed_bytes += i64_to_u64_floor_zero(last_hour.size);
|
||||
last_hour_failed_count += i64_to_u64_floor_zero(last_hour.count);
|
||||
|
||||
sent_bytes += i64_to_u64_floor_zero(target_stats.replicated_size);
|
||||
sent_count += i64_to_u64_floor_zero(target_stats.replicated_count);
|
||||
|
||||
targets.push(BucketReplicationTargetStats {
|
||||
target_arn,
|
||||
bandwidth_limit_bytes_per_sec: i64_to_u64_floor_zero(target_stats.bandwidth_limit_bytes_per_sec),
|
||||
current_bandwidth_bytes_per_sec: target_stats.current_bandwidth_bytes_per_sec,
|
||||
latency_ms: target_stats.latency.curr,
|
||||
});
|
||||
}
|
||||
|
||||
buckets.push(BucketReplicationStats {
|
||||
bucket,
|
||||
total_failed_bytes,
|
||||
total_failed_count,
|
||||
last_min_failed_bytes,
|
||||
last_min_failed_count,
|
||||
last_hour_failed_bytes,
|
||||
last_hour_failed_count,
|
||||
sent_bytes,
|
||||
sent_count,
|
||||
proxied_get_requests_total: i64_to_u64_floor_zero(proxy.get_total),
|
||||
proxied_get_requests_failures: i64_to_u64_floor_zero(proxy.get_failed),
|
||||
proxied_head_requests_total: i64_to_u64_floor_zero(proxy.head_total),
|
||||
proxied_head_requests_failures: i64_to_u64_floor_zero(proxy.head_failed),
|
||||
proxied_put_requests_total: i64_to_u64_floor_zero(proxy.put_total),
|
||||
proxied_put_requests_failures: i64_to_u64_floor_zero(proxy.put_failed),
|
||||
proxied_put_tagging_requests_total: i64_to_u64_floor_zero(proxy.put_tag_total),
|
||||
proxied_put_tagging_requests_failures: i64_to_u64_floor_zero(proxy.put_tag_failed),
|
||||
proxied_get_tagging_requests_total: i64_to_u64_floor_zero(proxy.get_tag_total),
|
||||
proxied_get_tagging_requests_failures: i64_to_u64_floor_zero(proxy.get_tag_failed),
|
||||
proxied_delete_tagging_requests_total: i64_to_u64_floor_zero(proxy.delete_tag_total),
|
||||
proxied_delete_tagging_requests_failures: i64_to_u64_floor_zero(proxy.delete_tag_failed),
|
||||
targets,
|
||||
});
|
||||
}
|
||||
|
||||
buckets
|
||||
}
|
||||
|
||||
async fn obs_site_replication_stats() -> ReplicationStats {
|
||||
let Some(stats) = ecstore_bucket::replication::GLOBAL_REPLICATION_STATS.get() else {
|
||||
return ReplicationStats::default();
|
||||
};
|
||||
|
||||
let site_metrics = stats.get_sr_metrics_for_node().await;
|
||||
let current_data_transfer_rate = obs_bucket_replication_bandwidth_stats()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|stat| stat.current_bandwidth_bytes_per_sec)
|
||||
.sum::<f64>();
|
||||
|
||||
let all_bucket_stats = stats.get_all().await;
|
||||
let average_data_transfer_rate = all_bucket_stats
|
||||
.values()
|
||||
.flat_map(|bucket| bucket.stats.values())
|
||||
.map(|stat| stat.xfer_rate_lrg.avg + stat.xfer_rate_sml.avg)
|
||||
.sum::<f64>();
|
||||
let max_data_transfer_rate = all_bucket_stats
|
||||
.values()
|
||||
.flat_map(|bucket| bucket.stats.values())
|
||||
.map(|stat| stat.xfer_rate_lrg.peak + stat.xfer_rate_sml.peak)
|
||||
.sum::<f64>();
|
||||
let recent_backlog_count = stats.mrf_stats.values().copied().filter(|value| *value > 0).sum::<i64>();
|
||||
|
||||
ReplicationStats {
|
||||
average_active_workers: site_metrics.active_workers.avg,
|
||||
average_queued_bytes: site_metrics.queued.avg.bytes,
|
||||
average_queued_count: site_metrics.queued.avg.count,
|
||||
average_data_transfer_rate,
|
||||
active_workers: i32_to_u64_floor_zero(site_metrics.active_workers.curr),
|
||||
current_data_transfer_rate,
|
||||
last_minute_queued_bytes: i64_to_u64_floor_zero(site_metrics.queued.last_minute.bytes),
|
||||
last_minute_queued_count: i64_to_u64_floor_zero(site_metrics.queued.last_minute.count),
|
||||
max_active_workers: i32_to_u64_floor_zero(site_metrics.active_workers.max),
|
||||
max_queued_bytes: i64_to_u64_floor_zero(site_metrics.queued.max.bytes),
|
||||
max_queued_count: i64_to_u64_floor_zero(site_metrics.queued.max.count),
|
||||
max_data_transfer_rate,
|
||||
recent_backlog_count: i64_to_u64_floor_zero(recent_backlog_count),
|
||||
}
|
||||
}
|
||||
|
||||
fn current_scanner_cycle_age_seconds(
|
||||
current_cycle: u64,
|
||||
current_started: chrono::DateTime<Utc>,
|
||||
|
||||
@@ -1,286 +0,0 @@
|
||||
// Copyright 2024 RustFS Team
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::metrics::collectors::{
|
||||
BucketReplicationStats as MetricBucketReplicationStats, BucketReplicationTargetStats,
|
||||
ReplicationStats as MetricReplicationStats,
|
||||
};
|
||||
use rustfs_ecstore::api::bucket as ecstore_bucket;
|
||||
use rustfs_ecstore::api::capacity as ecstore_capacity;
|
||||
use rustfs_ecstore::api::data_usage as ecstore_data_usage;
|
||||
use rustfs_ecstore::api::error as ecstore_error;
|
||||
use rustfs_ecstore::api::global as ecstore_global;
|
||||
use rustfs_ecstore::api::storage as ecstore_storage;
|
||||
use rustfs_storage_api::StorageAdminApi;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
pub(crate) type ObsStore = ecstore_storage::ECStore;
|
||||
type ObsStorageInfo = <ObsStore as StorageAdminApi>::StorageInfo;
|
||||
|
||||
pub(crate) struct ObsDataUsageInfo {
|
||||
pub(crate) buckets_count: u64,
|
||||
pub(crate) objects_total_count: u64,
|
||||
pub(crate) versions_total_count: u64,
|
||||
pub(crate) delete_markers_total_count: u64,
|
||||
pub(crate) objects_total_size: u64,
|
||||
pub(crate) buckets_usage: HashMap<String, ObsBucketUsageInfo>,
|
||||
}
|
||||
|
||||
pub(crate) struct ObsBucketUsageInfo {
|
||||
pub(crate) size: u64,
|
||||
pub(crate) objects_count: u64,
|
||||
pub(crate) object_size_histogram: HashMap<String, u64>,
|
||||
pub(crate) object_versions_histogram: HashMap<String, u64>,
|
||||
pub(crate) versions_count: u64,
|
||||
pub(crate) delete_markers_count: u64,
|
||||
}
|
||||
|
||||
pub(crate) async fn load_obs_data_usage_from_backend(store: Arc<ObsStore>) -> ecstore_error::Result<ObsDataUsageInfo> {
|
||||
let data_usage = ecstore_data_usage::load_data_usage_from_backend(store).await?;
|
||||
|
||||
Ok(ObsDataUsageInfo {
|
||||
buckets_count: data_usage.buckets_count,
|
||||
objects_total_count: data_usage.objects_total_count,
|
||||
versions_total_count: data_usage.versions_total_count,
|
||||
delete_markers_total_count: data_usage.delete_markers_total_count,
|
||||
objects_total_size: data_usage.objects_total_size,
|
||||
buckets_usage: data_usage
|
||||
.buckets_usage
|
||||
.into_iter()
|
||||
.map(|(bucket, usage)| {
|
||||
(
|
||||
bucket,
|
||||
ObsBucketUsageInfo {
|
||||
size: usage.size,
|
||||
objects_count: usage.objects_count,
|
||||
object_size_histogram: usage.object_size_histogram,
|
||||
object_versions_histogram: usage.object_versions_histogram,
|
||||
versions_count: usage.versions_count,
|
||||
delete_markers_count: usage.delete_markers_count,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct ObsBucketReplicationBandwidthStats {
|
||||
pub(crate) bucket: String,
|
||||
pub(crate) target_arn: String,
|
||||
pub(crate) limit_bytes_per_sec: i64,
|
||||
pub(crate) current_bandwidth_bytes_per_sec: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) struct ObsIlmRuntimeSnapshot {
|
||||
pub(crate) expiry_pending_tasks: u64,
|
||||
pub(crate) transition_active_tasks: u64,
|
||||
pub(crate) transition_pending_tasks: u64,
|
||||
pub(crate) transition_missed_immediate_tasks: u64,
|
||||
pub(crate) transition_queue_full_tasks: u64,
|
||||
pub(crate) transition_queue_send_timeout_tasks: u64,
|
||||
pub(crate) transition_compensation_scheduled_tasks: u64,
|
||||
pub(crate) transition_compensation_running_tasks: u64,
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_obs_object_store_handle() -> Option<Arc<ObsStore>> {
|
||||
ecstore_global::resolve_object_store_handle()
|
||||
}
|
||||
|
||||
pub(crate) fn obs_total_usable_capacity_bytes(storage_info: &ObsStorageInfo) -> u64 {
|
||||
usize_to_u64_saturating(ecstore_capacity::get_total_usable_capacity(&storage_info.disks, storage_info))
|
||||
}
|
||||
|
||||
pub(crate) fn obs_total_usable_capacity_free_bytes(storage_info: &ObsStorageInfo) -> u64 {
|
||||
usize_to_u64_saturating(ecstore_capacity::get_total_usable_capacity_free(&storage_info.disks, storage_info))
|
||||
}
|
||||
|
||||
pub(crate) async fn obs_bucket_quota_limit_bytes(bucket: &str) -> u64 {
|
||||
ecstore_bucket::metadata_sys::get_quota_config(bucket)
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|(quota, _)| quota.get_quota_limit())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
pub(crate) fn obs_bucket_monitor_available() -> bool {
|
||||
ecstore_global::get_global_bucket_monitor().is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn obs_bucket_replication_bandwidth_stats() -> Option<Vec<ObsBucketReplicationBandwidthStats>> {
|
||||
let monitor = ecstore_global::get_global_bucket_monitor()?;
|
||||
Some(
|
||||
monitor
|
||||
.get_report(|_| true)
|
||||
.bucket_stats
|
||||
.into_iter()
|
||||
.map(|(opts, details)| ObsBucketReplicationBandwidthStats {
|
||||
bucket: opts.name,
|
||||
target_arn: opts.replication_arn,
|
||||
limit_bytes_per_sec: details.limit_bytes_per_sec,
|
||||
current_bandwidth_bytes_per_sec: details.current_bandwidth_bytes_per_sec,
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) async fn obs_ilm_runtime_snapshot() -> ObsIlmRuntimeSnapshot {
|
||||
let expiry_pending_tasks = {
|
||||
let expiry = ecstore_bucket::lifecycle::bucket_lifecycle_ops::GLOBAL_ExpiryState
|
||||
.read()
|
||||
.await;
|
||||
usize_to_u64_saturating(expiry.pending_tasks())
|
||||
};
|
||||
let transition = &ecstore_bucket::lifecycle::bucket_lifecycle_ops::GLOBAL_TransitionState;
|
||||
|
||||
ObsIlmRuntimeSnapshot {
|
||||
expiry_pending_tasks,
|
||||
transition_active_tasks: i64_to_u64_floor_zero(transition.active_tasks()),
|
||||
transition_pending_tasks: usize_to_u64_saturating(transition.pending_tasks()),
|
||||
transition_missed_immediate_tasks: i64_to_u64_floor_zero(transition.missed_immediate_tasks()),
|
||||
transition_queue_full_tasks: i64_to_u64_floor_zero(transition.queue_full_tasks()),
|
||||
transition_queue_send_timeout_tasks: i64_to_u64_floor_zero(transition.queue_send_timeout_tasks()),
|
||||
transition_compensation_scheduled_tasks: i64_to_u64_floor_zero(transition.compensation_scheduled_tasks()),
|
||||
transition_compensation_running_tasks: i64_to_u64_floor_zero(transition.compensation_running_tasks()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn obs_bucket_replication_detail_stats() -> Vec<MetricBucketReplicationStats> {
|
||||
let Some(stats) = ecstore_bucket::replication::GLOBAL_REPLICATION_STATS.get() else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
let all_bucket_stats = stats.get_all().await;
|
||||
let mut buckets = Vec::with_capacity(all_bucket_stats.len());
|
||||
|
||||
for (bucket, bucket_stats) in all_bucket_stats {
|
||||
let proxy = stats.get_proxy_stats(&bucket).await;
|
||||
let mut total_failed_bytes = 0u64;
|
||||
let mut total_failed_count = 0u64;
|
||||
let mut last_min_failed_bytes = 0u64;
|
||||
let mut last_min_failed_count = 0u64;
|
||||
let mut last_hour_failed_bytes = 0u64;
|
||||
let mut last_hour_failed_count = 0u64;
|
||||
let mut sent_bytes = 0u64;
|
||||
let mut sent_count = 0u64;
|
||||
let mut targets = Vec::with_capacity(bucket_stats.stats.len());
|
||||
|
||||
for (target_arn, target_stats) in bucket_stats.stats {
|
||||
total_failed_bytes += i64_to_u64_floor_zero(target_stats.fail_stats.size);
|
||||
total_failed_count += i64_to_u64_floor_zero(target_stats.fail_stats.count);
|
||||
|
||||
let last_min = target_stats.fail_stats.recent_since(Duration::from_secs(60));
|
||||
last_min_failed_bytes += i64_to_u64_floor_zero(last_min.size);
|
||||
last_min_failed_count += i64_to_u64_floor_zero(last_min.count);
|
||||
|
||||
let last_hour = target_stats.fail_stats.recent_since(Duration::from_secs(60 * 60));
|
||||
last_hour_failed_bytes += i64_to_u64_floor_zero(last_hour.size);
|
||||
last_hour_failed_count += i64_to_u64_floor_zero(last_hour.count);
|
||||
|
||||
sent_bytes += i64_to_u64_floor_zero(target_stats.replicated_size);
|
||||
sent_count += i64_to_u64_floor_zero(target_stats.replicated_count);
|
||||
|
||||
targets.push(BucketReplicationTargetStats {
|
||||
target_arn,
|
||||
bandwidth_limit_bytes_per_sec: i64_to_u64_floor_zero(target_stats.bandwidth_limit_bytes_per_sec),
|
||||
current_bandwidth_bytes_per_sec: target_stats.current_bandwidth_bytes_per_sec,
|
||||
latency_ms: target_stats.latency.curr,
|
||||
});
|
||||
}
|
||||
|
||||
buckets.push(MetricBucketReplicationStats {
|
||||
bucket,
|
||||
total_failed_bytes,
|
||||
total_failed_count,
|
||||
last_min_failed_bytes,
|
||||
last_min_failed_count,
|
||||
last_hour_failed_bytes,
|
||||
last_hour_failed_count,
|
||||
sent_bytes,
|
||||
sent_count,
|
||||
proxied_get_requests_total: i64_to_u64_floor_zero(proxy.get_total),
|
||||
proxied_get_requests_failures: i64_to_u64_floor_zero(proxy.get_failed),
|
||||
proxied_head_requests_total: i64_to_u64_floor_zero(proxy.head_total),
|
||||
proxied_head_requests_failures: i64_to_u64_floor_zero(proxy.head_failed),
|
||||
proxied_put_requests_total: i64_to_u64_floor_zero(proxy.put_total),
|
||||
proxied_put_requests_failures: i64_to_u64_floor_zero(proxy.put_failed),
|
||||
proxied_put_tagging_requests_total: i64_to_u64_floor_zero(proxy.put_tag_total),
|
||||
proxied_put_tagging_requests_failures: i64_to_u64_floor_zero(proxy.put_tag_failed),
|
||||
proxied_get_tagging_requests_total: i64_to_u64_floor_zero(proxy.get_tag_total),
|
||||
proxied_get_tagging_requests_failures: i64_to_u64_floor_zero(proxy.get_tag_failed),
|
||||
proxied_delete_tagging_requests_total: i64_to_u64_floor_zero(proxy.delete_tag_total),
|
||||
proxied_delete_tagging_requests_failures: i64_to_u64_floor_zero(proxy.delete_tag_failed),
|
||||
targets,
|
||||
});
|
||||
}
|
||||
|
||||
buckets
|
||||
}
|
||||
|
||||
pub(crate) async fn obs_site_replication_stats() -> MetricReplicationStats {
|
||||
let Some(stats) = ecstore_bucket::replication::GLOBAL_REPLICATION_STATS.get() else {
|
||||
return MetricReplicationStats::default();
|
||||
};
|
||||
|
||||
let site_metrics = stats.get_sr_metrics_for_node().await;
|
||||
let current_data_transfer_rate = obs_bucket_replication_bandwidth_stats()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|stat| stat.current_bandwidth_bytes_per_sec)
|
||||
.sum::<f64>();
|
||||
|
||||
let all_bucket_stats = stats.get_all().await;
|
||||
let average_data_transfer_rate = all_bucket_stats
|
||||
.values()
|
||||
.flat_map(|bucket| bucket.stats.values())
|
||||
.map(|stat| stat.xfer_rate_lrg.avg + stat.xfer_rate_sml.avg)
|
||||
.sum::<f64>();
|
||||
let max_data_transfer_rate = all_bucket_stats
|
||||
.values()
|
||||
.flat_map(|bucket| bucket.stats.values())
|
||||
.map(|stat| stat.xfer_rate_lrg.peak + stat.xfer_rate_sml.peak)
|
||||
.sum::<f64>();
|
||||
let recent_backlog_count = stats.mrf_stats.values().copied().filter(|value| *value > 0).sum::<i64>();
|
||||
|
||||
MetricReplicationStats {
|
||||
average_active_workers: site_metrics.active_workers.avg,
|
||||
average_queued_bytes: site_metrics.queued.avg.bytes,
|
||||
average_queued_count: site_metrics.queued.avg.count,
|
||||
average_data_transfer_rate,
|
||||
active_workers: i32_to_u64_floor_zero(site_metrics.active_workers.curr),
|
||||
current_data_transfer_rate,
|
||||
last_minute_queued_bytes: i64_to_u64_floor_zero(site_metrics.queued.last_minute.bytes),
|
||||
last_minute_queued_count: i64_to_u64_floor_zero(site_metrics.queued.last_minute.count),
|
||||
max_active_workers: i32_to_u64_floor_zero(site_metrics.active_workers.max),
|
||||
max_queued_bytes: i64_to_u64_floor_zero(site_metrics.queued.max.bytes),
|
||||
max_queued_count: i64_to_u64_floor_zero(site_metrics.queued.max.count),
|
||||
max_data_transfer_rate,
|
||||
recent_backlog_count: i64_to_u64_floor_zero(recent_backlog_count),
|
||||
}
|
||||
}
|
||||
|
||||
fn usize_to_u64_saturating(value: usize) -> u64 {
|
||||
u64::try_from(value).unwrap_or(u64::MAX)
|
||||
}
|
||||
|
||||
fn i64_to_u64_floor_zero(value: i64) -> u64 {
|
||||
u64::try_from(value.max(0)).unwrap_or(0)
|
||||
}
|
||||
|
||||
fn i32_to_u64_floor_zero(value: i32) -> u64 {
|
||||
u64::try_from(value.max(0)).unwrap_or(0)
|
||||
}
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
//! Swift account operations and validation
|
||||
|
||||
use super::storage_compat::{get_swift_bucket_metadata, resolve_swift_object_store_handle, set_swift_bucket_metadata};
|
||||
use super::{SwiftError, SwiftResult};
|
||||
use super::{get_swift_bucket_metadata, resolve_swift_object_store_handle, set_swift_bucket_metadata};
|
||||
use rustfs_credentials::Credentials;
|
||||
use rustfs_storage_api::{BucketOperations, MakeBucketOptions};
|
||||
use s3s::dto::{Tag, Tagging};
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
//! This module implements Swift container CRUD operations and container-bucket translation.
|
||||
|
||||
use super::account::validate_account_access;
|
||||
use super::storage_compat::{get_swift_bucket_metadata, resolve_swift_object_store_handle, set_swift_bucket_metadata};
|
||||
use super::types::Container;
|
||||
use super::{SwiftError, SwiftResult};
|
||||
use super::{get_swift_bucket_metadata, resolve_swift_object_store_handle, set_swift_bucket_metadata};
|
||||
use rustfs_credentials::Credentials;
|
||||
use rustfs_storage_api::{
|
||||
BucketInfo, BucketOperations, BucketOptions, DeleteBucketOptions, ListOperations as _, MakeBucketOptions,
|
||||
|
||||
@@ -33,6 +33,12 @@
|
||||
//! and stores credentials in task-local storage, which Swift handlers access
|
||||
//! to enforce tenant isolation.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustfs_ecstore::api::{
|
||||
bucket as ecstore_bucket, error as ecstore_error, global as ecstore_global, storage as ecstore_storage,
|
||||
};
|
||||
|
||||
pub mod account;
|
||||
pub mod acl;
|
||||
pub mod bulk;
|
||||
@@ -51,7 +57,6 @@ pub mod ratelimit;
|
||||
pub mod router;
|
||||
pub mod slo;
|
||||
pub mod staticweb;
|
||||
pub mod storage_compat;
|
||||
pub mod symlink;
|
||||
pub mod sync;
|
||||
pub mod tempurl;
|
||||
@@ -63,3 +68,23 @@ pub use router::{SwiftRoute, SwiftRouter};
|
||||
// Note: Container, Object, and SwiftMetadata types used by Swift implementation
|
||||
#[allow(unused_imports)]
|
||||
pub use types::{Container, Object, SwiftMetadata};
|
||||
|
||||
type SwiftBucketMetadata = ecstore_bucket::metadata::BucketMetadata;
|
||||
type SwiftStorageResult<T> = ecstore_error::Result<T>;
|
||||
type SwiftStore = ecstore_storage::ECStore;
|
||||
pub type SwiftGetObjectReader = <SwiftStore as rustfs_storage_api::ObjectIO>::GetObjectReader;
|
||||
pub type SwiftObjectInfo = <SwiftStore as rustfs_storage_api::ObjectOperations>::ObjectInfo;
|
||||
pub type SwiftObjectOptions = <SwiftStore as rustfs_storage_api::ObjectOperations>::ObjectOptions;
|
||||
pub type SwiftPutObjReader = <SwiftStore as rustfs_storage_api::ObjectIO>::PutObjectReader;
|
||||
|
||||
fn resolve_swift_object_store_handle() -> Option<Arc<SwiftStore>> {
|
||||
ecstore_global::resolve_object_store_handle()
|
||||
}
|
||||
|
||||
async fn get_swift_bucket_metadata(bucket: &str) -> SwiftStorageResult<Arc<SwiftBucketMetadata>> {
|
||||
ecstore_bucket::metadata_sys::get(bucket).await
|
||||
}
|
||||
|
||||
async fn set_swift_bucket_metadata(bucket: String, metadata: SwiftBucketMetadata) -> SwiftStorageResult<()> {
|
||||
ecstore_bucket::metadata_sys::set_bucket_metadata(bucket, metadata).await
|
||||
}
|
||||
|
||||
@@ -51,8 +51,7 @@
|
||||
|
||||
use super::account::validate_account_access;
|
||||
use super::container::ContainerMapper;
|
||||
use super::storage_compat::resolve_swift_object_store_handle;
|
||||
use super::{SwiftError, SwiftResult};
|
||||
use super::{SwiftError, SwiftResult, resolve_swift_object_store_handle};
|
||||
use axum::http::HeaderMap;
|
||||
use rustfs_credentials::Credentials;
|
||||
use rustfs_rio::HashReader;
|
||||
@@ -61,7 +60,7 @@ use std::collections::HashMap;
|
||||
use tracing::debug;
|
||||
use tracing::error;
|
||||
|
||||
pub use super::storage_compat::{SwiftGetObjectReader, SwiftObjectInfo, SwiftObjectOptions, SwiftPutObjReader};
|
||||
pub use super::{SwiftGetObjectReader, SwiftObjectInfo, SwiftObjectOptions, SwiftPutObjReader};
|
||||
|
||||
const LOG_COMPONENT_PROTOCOLS: &str = "protocols";
|
||||
const LOG_SUBSYSTEM_SWIFT_OBJECT: &str = "swift_object";
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright 2024 RustFS Team
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustfs_ecstore::api::bucket as ecstore_bucket;
|
||||
use rustfs_ecstore::api::error as ecstore_error;
|
||||
use rustfs_ecstore::api::global as ecstore_global;
|
||||
use rustfs_ecstore::api::storage as ecstore_storage;
|
||||
|
||||
pub(super) type SwiftBucketMetadata = ecstore_bucket::metadata::BucketMetadata;
|
||||
pub(super) type SwiftStorageResult<T> = ecstore_error::Result<T>;
|
||||
pub(super) type SwiftStore = ecstore_storage::ECStore;
|
||||
pub type SwiftGetObjectReader = <SwiftStore as rustfs_storage_api::ObjectIO>::GetObjectReader;
|
||||
pub type SwiftObjectInfo = <SwiftStore as rustfs_storage_api::ObjectOperations>::ObjectInfo;
|
||||
pub type SwiftObjectOptions = <SwiftStore as rustfs_storage_api::ObjectOperations>::ObjectOptions;
|
||||
pub type SwiftPutObjReader = <SwiftStore as rustfs_storage_api::ObjectIO>::PutObjectReader;
|
||||
|
||||
pub fn resolve_swift_object_store_handle() -> Option<Arc<SwiftStore>> {
|
||||
ecstore_global::resolve_object_store_handle()
|
||||
}
|
||||
|
||||
pub async fn get_swift_bucket_metadata(bucket: &str) -> SwiftStorageResult<Arc<SwiftBucketMetadata>> {
|
||||
ecstore_bucket::metadata_sys::get(bucket).await
|
||||
}
|
||||
|
||||
pub async fn set_swift_bucket_metadata(bucket: String, metadata: SwiftBucketMetadata) -> SwiftStorageResult<()> {
|
||||
ecstore_bucket::metadata_sys::set_bucket_metadata(bucket, metadata).await
|
||||
}
|
||||
@@ -54,7 +54,7 @@
|
||||
use super::account::validate_account_access;
|
||||
use super::container::ContainerMapper;
|
||||
use super::object::{ObjectKeyMapper, SwiftObjectOptions as ObjectOptions, head_object};
|
||||
use super::storage_compat::resolve_swift_object_store_handle;
|
||||
use super::resolve_swift_object_store_handle;
|
||||
use super::{SwiftError, SwiftResult};
|
||||
use rustfs_credentials::Credentials;
|
||||
use rustfs_storage_api::{ListOperations as _, ObjectOperations as _};
|
||||
|
||||
@@ -19,7 +19,6 @@ use std::fmt::Display;
|
||||
pub mod object_store;
|
||||
pub mod query;
|
||||
pub mod server;
|
||||
mod storage_compat;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::storage_compat::{
|
||||
SELECT_DEFAULT_READ_BUFFER_SIZE, SelectGetObjectReader, SelectObjectInfo, SelectObjectOptions, SelectStorageError,
|
||||
SelectStore, resolve_select_object_store_handle, select_default_read_buffer_size_u64, select_is_err_bucket_not_found,
|
||||
select_is_err_object_not_found, select_is_err_version_not_found,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use bytes::Bytes;
|
||||
use chrono::Utc;
|
||||
@@ -30,6 +25,9 @@ use object_store::{
|
||||
};
|
||||
use pin_project_lite::pin_project;
|
||||
use rustfs_common::DEFAULT_DELIMITER;
|
||||
use rustfs_ecstore::api::{
|
||||
error as ecstore_error, global as ecstore_global, set_disk as ecstore_set_disk, storage as ecstore_storage,
|
||||
};
|
||||
use rustfs_storage_api::{HTTPRangeSpec, ObjectIO as _, ObjectOperations as _};
|
||||
use s3s::S3Result;
|
||||
use s3s::dto::SelectObjectContentInput;
|
||||
@@ -49,6 +47,34 @@ use tokio::io::{AsyncRead, ReadBuf};
|
||||
use tokio_util::io::ReaderStream;
|
||||
use transform_stream::AsyncTryStream;
|
||||
|
||||
type SelectStorageError = ecstore_error::StorageError;
|
||||
type SelectStore = ecstore_storage::ECStore;
|
||||
type SelectGetObjectReader = <SelectStore as rustfs_storage_api::ObjectIO>::GetObjectReader;
|
||||
type SelectObjectInfo = <SelectStore as rustfs_storage_api::ObjectOperations>::ObjectInfo;
|
||||
type SelectObjectOptions = <SelectStore as rustfs_storage_api::ObjectOperations>::ObjectOptions;
|
||||
|
||||
const SELECT_DEFAULT_READ_BUFFER_SIZE: usize = ecstore_set_disk::DEFAULT_READ_BUFFER_SIZE;
|
||||
|
||||
fn select_default_read_buffer_size_u64() -> u64 {
|
||||
u64::try_from(SELECT_DEFAULT_READ_BUFFER_SIZE).unwrap_or(u64::MAX)
|
||||
}
|
||||
|
||||
fn resolve_select_object_store_handle() -> Option<Arc<SelectStore>> {
|
||||
ecstore_global::resolve_object_store_handle()
|
||||
}
|
||||
|
||||
fn select_is_err_bucket_not_found(err: &SelectStorageError) -> bool {
|
||||
ecstore_error::is_err_bucket_not_found(err)
|
||||
}
|
||||
|
||||
fn select_is_err_object_not_found(err: &SelectStorageError) -> bool {
|
||||
ecstore_error::is_err_object_not_found(err)
|
||||
}
|
||||
|
||||
fn select_is_err_version_not_found(err: &SelectStorageError) -> bool {
|
||||
ecstore_error::is_err_version_not_found(err)
|
||||
}
|
||||
|
||||
/// Maximum allowed object size for JSON DOCUMENT mode.
|
||||
///
|
||||
/// JSON DOCUMENT format requires loading the entire file into memory for DOM
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
// Copyright 2024 RustFS Team
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use rustfs_ecstore::api::error as ecstore_error;
|
||||
use rustfs_ecstore::api::global as ecstore_global;
|
||||
use rustfs_ecstore::api::set_disk as ecstore_set_disk;
|
||||
use rustfs_ecstore::api::storage as ecstore_storage;
|
||||
|
||||
pub(crate) type SelectStorageError = ecstore_error::StorageError;
|
||||
pub(crate) type SelectStore = ecstore_storage::ECStore;
|
||||
pub(crate) type SelectGetObjectReader = <SelectStore as rustfs_storage_api::ObjectIO>::GetObjectReader;
|
||||
pub(crate) type SelectObjectInfo = <SelectStore as rustfs_storage_api::ObjectOperations>::ObjectInfo;
|
||||
pub(crate) type SelectObjectOptions = <SelectStore as rustfs_storage_api::ObjectOperations>::ObjectOptions;
|
||||
|
||||
pub(crate) const SELECT_DEFAULT_READ_BUFFER_SIZE: usize = ecstore_set_disk::DEFAULT_READ_BUFFER_SIZE;
|
||||
|
||||
pub(crate) fn select_default_read_buffer_size_u64() -> u64 {
|
||||
u64::try_from(SELECT_DEFAULT_READ_BUFFER_SIZE).unwrap_or(u64::MAX)
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_select_object_store_handle() -> Option<std::sync::Arc<SelectStore>> {
|
||||
ecstore_global::resolve_object_store_handle()
|
||||
}
|
||||
|
||||
pub(crate) fn select_is_err_bucket_not_found(err: &SelectStorageError) -> bool {
|
||||
ecstore_error::is_err_bucket_not_found(err)
|
||||
}
|
||||
|
||||
pub(crate) fn select_is_err_object_not_found(err: &SelectStorageError) -> bool {
|
||||
ecstore_error::is_err_object_not_found(err)
|
||||
}
|
||||
|
||||
pub(crate) fn select_is_err_version_not_found(err: &SelectStorageError) -> bool {
|
||||
ecstore_error::is_err_version_not_found(err)
|
||||
}
|
||||
@@ -5,16 +5,17 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
||||
## Current Context
|
||||
|
||||
- Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660)
|
||||
- Branch: `overtrue/arch-test-fuzz-compat-bridge-cleanup`
|
||||
- Baseline: completed `C-011/C-012/C-013/API-055/API-059/API-079/API-080/API-081/API-082/API-083/API-084/API-085/API-086/API-087/API-088/API-089/API-090/API-091/API-092/API-093/API-094/API-095/API-096/API-097/API-098/API-099/API-100/API-101/API-102/API-103/API-104/API-105/API-106/API-107/API-108/API-109/API-110/API-111/API-112/API-113/API-114/API-115/API-116/API-117/API-118/API-119/API-120/API-121/API-122/API-123`.
|
||||
- Based on: API-123 slice.
|
||||
- Branch: `overtrue/arch-standalone-thin-compat-cleanup`
|
||||
- Baseline: completed `C-011/C-012/C-013/API-055/API-059/API-079/API-080/API-081/API-082/API-083/API-084/API-085/API-086/API-087/API-088/API-089/API-090/API-091/API-092/API-093/API-094/API-095/API-096/API-097/API-098/API-099/API-100/API-101/API-102/API-103/API-104/API-105/API-106/API-107/API-108/API-109/API-110/API-111/API-112/API-113/API-114/API-115/API-116/API-117/API-118/API-119/API-120/API-121/API-122/API-123/API-124`.
|
||||
- Based on: API-124 slice.
|
||||
- PR type for this branch: `pure-move`
|
||||
- Runtime behavior changes: none.
|
||||
- Rust code changes: remove heal/scanner test and fuzz storage compatibility
|
||||
bridges, then route their consumers directly to ECStore API owner modules.
|
||||
- CI/script changes: allow the migrated test/fuzz targets to import owner APIs
|
||||
directly and reject reintroduced test/fuzz bridge modules.
|
||||
- Docs changes: record the API-124 test/fuzz bridge cleanup.
|
||||
- Rust code changes: remove standalone thin storage compatibility bridges from
|
||||
e2e, IAM store, notify, OBS, Swift, and S3 Select, then route their consumers
|
||||
directly to ECStore API owner modules.
|
||||
- CI/script changes: allow migrated standalone consumers to import owner APIs
|
||||
directly and reject reintroduced bridge modules.
|
||||
- Docs changes: record the API-125/API-126 standalone bridge cleanup.
|
||||
|
||||
## Phase 0 Tasks
|
||||
|
||||
@@ -751,6 +752,32 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
||||
- Verification: heal/scanner test compile coverage, fuzz target compile
|
||||
coverage, test/fuzz bridge residual scan, migration and layer guards,
|
||||
formatting, diff hygiene, Rust risk scan, and three-expert review.
|
||||
- [x] `API-125` Remove standalone thin compatibility bridges.
|
||||
- Completed slice: replace e2e tests, IAM store object access, and notify
|
||||
config persistence consumers with direct owner APIs, then delete their local
|
||||
`storage_compat.rs` bridge modules.
|
||||
- Acceptance: e2e, IAM store, and notify no longer route through local thin
|
||||
storage compatibility bridges; migration rules reject deleted files, module
|
||||
declarations, or bridge consumers.
|
||||
- Must preserve: e2e RPC client behavior, site-replication target contracts,
|
||||
IAM object associated types, notify server-config read/modify/save behavior,
|
||||
and reload-if-changed semantics.
|
||||
- Verification: affected crate compile coverage, standalone thin bridge
|
||||
residual scan, migration and layer guards, formatting, diff hygiene, Rust
|
||||
risk scan, and three-expert review.
|
||||
- [x] `API-126` Remove remaining standalone owner compatibility bridges.
|
||||
- Completed slice: replace OBS metrics, Swift object/container/account, and
|
||||
S3 Select object-store consumers with direct owner APIs, then delete their
|
||||
local `storage_compat.rs` bridge modules.
|
||||
- Acceptance: OBS, Swift, and S3 Select no longer route through local thin
|
||||
storage compatibility bridges; migration rules reject deleted files, module
|
||||
declarations, or bridge consumers.
|
||||
- Must preserve: OBS capacity, bucket usage, replication, and ILM metrics;
|
||||
Swift bucket metadata and object IO contracts; S3 Select object reader,
|
||||
error mapping, and default read-buffer behavior.
|
||||
- Verification: affected crate compile coverage, remaining standalone bridge
|
||||
residual scan, migration and layer guards, formatting, diff hygiene, Rust
|
||||
risk scan, and three-expert review.
|
||||
- [x] `G-012` Inventory placement and repair invariants.
|
||||
- Acceptance:
|
||||
[`placement-repair-invariants.md`](placement-repair-invariants.md) records
|
||||
@@ -3784,14 +3811,40 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
||||
|
||||
| Expert | Status | Notes |
|
||||
|---|---|---|
|
||||
| Quality/architecture | pass | API-124 removes only test/fuzz bridge modules and keeps call sites on explicit ECStore owner APIs. |
|
||||
| Migration preservation | pass | The migration guard now rejects the deleted test/fuzz bridge files, module declarations, and bridge consumers. |
|
||||
| Testing/verification | pass | Heal/scanner test compile, fuzz target compile, residual scan, migration guard, layer guard, formatting, and diff hygiene passed. |
|
||||
| Quality/architecture | pass | API-125/API-126 remove only standalone thin bridge modules and keep call sites on explicit owner APIs. |
|
||||
| Migration preservation | pass | The migration guard now rejects the deleted e2e, IAM-store, notify, OBS, Swift, and S3 Select bridge files, module declarations, and bridge consumers. |
|
||||
| Testing/verification | pass | Affected crate compile, residual scan, migration guard, layer guard, formatting, diff hygiene, and diff-only Rust risk scan passed. |
|
||||
|
||||
## Verification Notes
|
||||
|
||||
Passed before push:
|
||||
|
||||
- Issue #660 API-126 current slice:
|
||||
- `cargo check --tests -p e2e_test -p rustfs-iam -p rustfs-notify -p rustfs-obs -p rustfs-protocols -p rustfs-s3select-api`: passed.
|
||||
- `cargo fmt --all`: passed.
|
||||
- `cargo fmt --all --check`: passed.
|
||||
- `git diff --check`: passed.
|
||||
- `bash -n scripts/check_architecture_migration_rules.sh`: passed.
|
||||
- `./scripts/check_architecture_migration_rules.sh`: passed.
|
||||
- `./scripts/check_layer_dependencies.sh`: passed.
|
||||
- Remaining standalone compatibility bridge residual scan: passed.
|
||||
- Rust risk scan: diff-only scan found no new unwrap/expect, numeric casts,
|
||||
string-error public APIs, boxed public errors, println/eprintln, or relaxed
|
||||
ordering.
|
||||
|
||||
- Issue #660 API-125 current slice:
|
||||
- `cargo check --tests -p e2e_test -p rustfs-iam -p rustfs-notify`: passed.
|
||||
- `cargo fmt --all`: passed.
|
||||
- `cargo fmt --all --check`: passed.
|
||||
- `git diff --check`: passed.
|
||||
- `bash -n scripts/check_architecture_migration_rules.sh`: passed.
|
||||
- `./scripts/check_architecture_migration_rules.sh`: passed.
|
||||
- `./scripts/check_layer_dependencies.sh`: passed.
|
||||
- Standalone thin compatibility bridge residual scan: passed.
|
||||
- Rust risk scan: diff-only scan found no new unwrap/expect, numeric casts,
|
||||
string-error public APIs, boxed public errors, println/eprintln, or relaxed
|
||||
ordering.
|
||||
|
||||
- Issue #660 API-124 current slice:
|
||||
- `cargo check --tests -p rustfs-heal -p rustfs-scanner`: passed.
|
||||
- `cargo check --manifest-path fuzz/Cargo.toml --bins`: passed; transient `fuzz/Cargo.lock` refresh was restored to avoid dependency churn.
|
||||
|
||||
@@ -119,6 +119,7 @@ OBS_STORAGE_COMPAT_PASSTHROUGH_HITS_FILE="${TMP_DIR}/obs_storage_compat_passthro
|
||||
E2E_STORAGE_COMPAT_RPC_PASSTHROUGH_HITS_FILE="${TMP_DIR}/e2e_storage_compat_rpc_passthrough_hits.txt"
|
||||
TEST_STORAGE_COMPAT_PASSTHROUGH_HITS_FILE="${TMP_DIR}/test_storage_compat_passthrough_hits.txt"
|
||||
TEST_FUZZ_COMPAT_BRIDGE_HITS_FILE="${TMP_DIR}/test_fuzz_compat_bridge_hits.txt"
|
||||
STANDALONE_THIN_COMPAT_BRIDGE_HITS_FILE="${TMP_DIR}/standalone_thin_compat_bridge_hits.txt"
|
||||
PRODUCTION_UNUSED_COMPAT_ALLOW_HITS_FILE="${TMP_DIR}/production_unused_compat_allow_hits.txt"
|
||||
BROAD_STORE_API_COMPAT_REEXPORT_HITS_FILE="${TMP_DIR}/broad_store_api_compat_reexport_hits.txt"
|
||||
NESTED_STORE_API_COMPAT_MODULE_HITS_FILE="${TMP_DIR}/nested_store_api_compat_module_hits.txt"
|
||||
@@ -713,7 +714,7 @@ fi
|
||||
--glob '!**/storage_compat.rs' \
|
||||
--glob '!**/*storage_compat.rs' \
|
||||
--glob '!target/**' \
|
||||
| rg -v '^(rustfs/src/(capacity/service|config/config_test|error|init|runtime_capabilities|startup_(background|bucket_metadata|fs_guard|iam|lifecycle|notification|server|services|shutdown|storage)|table_catalog|workload_admission)\.rs|rustfs/src/server/(event|http|module_switch|readiness)\.rs|rustfs/src/storage/s3_api/(bucket|multipart)\.rs|crates/heal/tests/(endpoint_index_test|heal_bug_fixes_test|heal_integration_test)\.rs|crates/scanner/tests/lifecycle_integration_test\.rs|fuzz/fuzz_targets/(bucket_validation|path_containment)\.rs):' || true
|
||||
| rg -v '^(rustfs/src/(capacity/service|config/config_test|error|init|runtime_capabilities|startup_(background|bucket_metadata|fs_guard|iam|lifecycle|notification|server|services|shutdown|storage)|table_catalog|workload_admission)\.rs|rustfs/src/server/(event|http|module_switch|readiness)\.rs|rustfs/src/storage/s3_api/(bucket|multipart)\.rs|crates/e2e_test/src/(replication_extension_test|reliant/(grpc_lock_client|node_interact_test))\.rs|crates/heal/tests/(endpoint_index_test|heal_bug_fixes_test|heal_integration_test)\.rs|crates/notify/src/config_manager\.rs|crates/obs/src/metrics/(scheduler|stats_collector)\.rs|crates/protocols/src/swift/mod\.rs|crates/s3select-api/src/object_store\.rs|crates/scanner/tests/lifecycle_integration_test\.rs|fuzz/fuzz_targets/(bucket_validation|path_containment)\.rs):' || true
|
||||
) |
|
||||
cat >"$DIRECT_ECSTORE_IMPORT_HITS_FILE"
|
||||
|
||||
@@ -984,14 +985,18 @@ fi
|
||||
|
||||
(
|
||||
cd "$ROOT_DIR"
|
||||
rg -n --no-heading 'rustfs_ecstore::api::' \
|
||||
crates/iam/src/storage_compat.rs \
|
||||
crates/heal/src/heal/storage_compat.rs \
|
||||
crates/obs/src/storage_compat.rs \
|
||||
crates/notify/src/storage_compat.rs \
|
||||
crates/protocols/src/swift/storage_compat.rs \
|
||||
crates/s3select-api/src/storage_compat.rs \
|
||||
crates/scanner/src/storage_compat.rs \
|
||||
{
|
||||
for file in \
|
||||
crates/iam/src/storage_compat.rs \
|
||||
crates/heal/src/heal/storage_compat.rs \
|
||||
crates/obs/src/storage_compat.rs \
|
||||
crates/notify/src/storage_compat.rs \
|
||||
crates/protocols/src/swift/storage_compat.rs \
|
||||
crates/s3select-api/src/storage_compat.rs \
|
||||
crates/scanner/src/storage_compat.rs; do
|
||||
[[ -f "$file" ]] && rg -n --with-filename --no-heading 'rustfs_ecstore::api::' "$file"
|
||||
done
|
||||
} \
|
||||
| rg -v '^[^:]+:[0-9]+:use rustfs_ecstore::api::[a-z_]+ as ecstore_[a-z_]+;' || true
|
||||
) >"$OUTER_CONSUMER_COMPAT_RAW_FACADE_PATH_HITS_FILE"
|
||||
|
||||
@@ -1001,8 +1006,10 @@ fi
|
||||
|
||||
(
|
||||
cd "$ROOT_DIR"
|
||||
rg -n --with-filename --no-heading 'rustfs_ecstore::api::' crates/e2e_test/src/storage_compat.rs \
|
||||
| rg -v '^[^:]+:[0-9]+:use rustfs_ecstore::api::[a-z_]+ as ecstore_[a-z_]+;' || true
|
||||
if [[ -f crates/e2e_test/src/storage_compat.rs ]]; then
|
||||
rg -n --with-filename --no-heading 'rustfs_ecstore::api::' crates/e2e_test/src/storage_compat.rs \
|
||||
| rg -v '^[^:]+:[0-9]+:use rustfs_ecstore::api::[a-z_]+ as ecstore_[a-z_]+;' || true
|
||||
fi
|
||||
) >"$RUSTFS_ROOT_E2E_COMPAT_RAW_FACADE_PATH_HITS_FILE"
|
||||
|
||||
if [[ -s "$RUSTFS_ROOT_E2E_COMPAT_RAW_FACADE_PATH_HITS_FILE" ]]; then
|
||||
@@ -1303,6 +1310,35 @@ if [[ -s "$TEST_FUZZ_COMPAT_BRIDGE_HITS_FILE" ]]; then
|
||||
report_failure "heal/scanner test and fuzz targets must import ECStore owner APIs directly instead of local storage compatibility bridges: $(paste -sd '; ' "$TEST_FUZZ_COMPAT_BRIDGE_HITS_FILE")"
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$ROOT_DIR"
|
||||
{
|
||||
for file in \
|
||||
crates/e2e_test/src/storage_compat.rs \
|
||||
crates/iam/src/store/storage_compat.rs \
|
||||
crates/notify/src/storage_compat.rs \
|
||||
crates/obs/src/storage_compat.rs \
|
||||
crates/protocols/src/swift/storage_compat.rs \
|
||||
crates/s3select-api/src/storage_compat.rs; do
|
||||
[[ -e "$file" ]] && printf '%s:1:standalone thin bridge file exists\n' "$file"
|
||||
done
|
||||
rg -n --with-filename 'storage_compat' \
|
||||
crates/e2e_test/src \
|
||||
crates/notify/src \
|
||||
crates/obs/src \
|
||||
crates/protocols/src/swift \
|
||||
crates/s3select-api/src \
|
||||
-g '*.rs' || true
|
||||
rg -n --with-filename '^\s*use\s+super::storage_compat|store::storage_compat|\bmod\s+storage_compat' \
|
||||
crates/iam/src/store.rs \
|
||||
crates/iam/src/store/object.rs || true
|
||||
}
|
||||
) >"$STANDALONE_THIN_COMPAT_BRIDGE_HITS_FILE"
|
||||
|
||||
if [[ -s "$STANDALONE_THIN_COMPAT_BRIDGE_HITS_FILE" ]]; then
|
||||
report_failure "standalone e2e/IAM-store/notify consumers must import owner APIs directly instead of local storage compatibility bridges: $(paste -sd '; ' "$STANDALONE_THIN_COMPAT_BRIDGE_HITS_FILE")"
|
||||
fi
|
||||
|
||||
(
|
||||
cd "$ROOT_DIR"
|
||||
rg -n --with-filename 'crate::storage_compat' \
|
||||
@@ -1331,8 +1367,10 @@ fi
|
||||
|
||||
(
|
||||
cd "$ROOT_DIR"
|
||||
rg -n --no-heading 'use rustfs_ecstore::api::\{[^}]*\b(?:config|global)\b[^}]*\}\s*;' \
|
||||
crates/notify/src/storage_compat.rs || true
|
||||
if [[ -f crates/notify/src/storage_compat.rs ]]; then
|
||||
rg -n --no-heading 'use rustfs_ecstore::api::\{[^}]*\b(?:config|global)\b[^}]*\}\s*;' \
|
||||
crates/notify/src/storage_compat.rs || true
|
||||
fi
|
||||
) >"$NOTIFY_STORAGE_COMPAT_MODULE_HITS_FILE"
|
||||
|
||||
if [[ -s "$NOTIFY_STORAGE_COMPAT_MODULE_HITS_FILE" ]]; then
|
||||
@@ -1341,8 +1379,10 @@ fi
|
||||
|
||||
(
|
||||
cd "$ROOT_DIR"
|
||||
rg -n --no-heading 'pub\(crate\)\s+use rustfs_ecstore::api::data_usage::load_data_usage_from_backend' \
|
||||
crates/obs/src/storage_compat.rs || true
|
||||
if [[ -f crates/obs/src/storage_compat.rs ]]; then
|
||||
rg -n --no-heading 'pub\(crate\)\s+use rustfs_ecstore::api::data_usage::load_data_usage_from_backend' \
|
||||
crates/obs/src/storage_compat.rs || true
|
||||
fi
|
||||
) >"$OBS_STORAGE_COMPAT_PASSTHROUGH_HITS_FILE"
|
||||
|
||||
if [[ -s "$OBS_STORAGE_COMPAT_PASSTHROUGH_HITS_FILE" ]]; then
|
||||
@@ -1351,8 +1391,10 @@ fi
|
||||
|
||||
(
|
||||
cd "$ROOT_DIR"
|
||||
rg -n --no-heading 'pub\(crate\)\s+use rustfs_ecstore::api::rpc::\{[^}]*\b(?:gen_tonic_signature_interceptor|node_service_time_out_client|node_service_time_out_client_no_auth)\b[^}]*\}\s*;' \
|
||||
crates/e2e_test/src/storage_compat.rs || true
|
||||
if [[ -f crates/e2e_test/src/storage_compat.rs ]]; then
|
||||
rg -n --no-heading 'pub\(crate\)\s+use rustfs_ecstore::api::rpc::\{[^}]*\b(?:gen_tonic_signature_interceptor|node_service_time_out_client|node_service_time_out_client_no_auth)\b[^}]*\}\s*;' \
|
||||
crates/e2e_test/src/storage_compat.rs || true
|
||||
fi
|
||||
) >"$E2E_STORAGE_COMPAT_RPC_PASSTHROUGH_HITS_FILE"
|
||||
|
||||
if [[ -s "$E2E_STORAGE_COMPAT_RPC_PASSTHROUGH_HITS_FILE" ]]; then
|
||||
|
||||
Reference in New Issue
Block a user