diff --git a/rustfs/src/admin/handlers/audit.rs b/rustfs/src/admin/handlers/audit.rs
index 900bbddbf..4b8192dc2 100644
--- a/rustfs/src/admin/handlers/audit.rs
+++ b/rustfs/src/admin/handlers/audit.rs
@@ -14,13 +14,14 @@
use crate::admin::{
auth::authorize_admin_request,
- handlers::audit_runtime_config::{load_server_config_from_store, update_audit_config_and_reload},
+ handlers::audit_runtime_config::update_audit_config_and_reload,
handlers::target_descriptor::{
AdminTargetSpec, EndpointKey, RuntimeHealthStatus, TargetEndpointSource, admin_target_spec_from_builtin,
- build_enabled_target_kvs, build_json_response, collect_runtime_statuses, extract_supported_target_params,
+ build_enabled_target_kvs, build_json_response, extract_supported_target_params,
merge_target_endpoints as shared_merge_target_endpoints, target_module_disabled_reason,
target_mutation_block_reason as shared_target_mutation_block_reason,
},
+ handlers::target_read::{collect_audit_runtime_statuses, load_server_config_from_store, load_target_read_snapshot},
router::{AdminOperation, Operation, S3Router},
};
use crate::server::{
@@ -29,7 +30,6 @@ use crate::server::{
use http::StatusCode;
use hyper::Method;
use matchit::Params;
-use rustfs_audit::audit_system;
use rustfs_config::audit::AUDIT_ROUTE_PREFIX;
use rustfs_config::server_config::Config;
use rustfs_config::{AUDIT_DEFAULT_DIR, MAX_ADMIN_REQUEST_BODY_SIZE};
@@ -344,12 +344,7 @@ impl Operation for ListAuditTargets {
async fn call(&self, req: S3Request
, _params: Params<'_, '_>) -> S3Result> {
authorize_audit_admin_request(&req, AdminAction::GetBucketTargetAction).await?;
- let mut runtime_statuses = HashMap::new();
- if let Some(system) = audit_system() {
- runtime_statuses = collect_runtime_statuses(system.get_target_values().await).await;
- }
-
- let config = load_server_config_from_store().await?;
+ let (config, runtime_statuses) = load_target_read_snapshot(collect_audit_runtime_statuses()).await?;
let audit_endpoints = merge_audit_endpoints(&config, runtime_statuses)?;
let data = serde_json::to_vec(&AuditEndpointsResponse { audit_endpoints }).map_err(|e| {
log_audit_target_request_failed!("list_audit_targets", "serialize_audit_targets_failed", None, None, e);
diff --git a/rustfs/src/admin/handlers/audit_runtime_config.rs b/rustfs/src/admin/handlers/audit_runtime_config.rs
index ad58b726a..3ebe8de83 100644
--- a/rustfs/src/admin/handlers/audit_runtime_config.rs
+++ b/rustfs/src/admin/handlers/audit_runtime_config.rs
@@ -17,8 +17,8 @@ use crate::admin::handlers::target_descriptor::AdminTargetSpec;
use crate::admin::runtime_sources::{AppContext, current_app_context, current_object_store_handle_for_context};
use crate::admin::service::config::with_runtime_config_reload_lock;
use crate::admin::storage_api::config::{
- read_admin_config_without_migrate, read_admin_server_config_snapshot, read_existing_admin_server_config_no_lock,
- save_admin_server_config_snapshot, with_admin_server_config_read_lock,
+ read_admin_server_config_snapshot, read_existing_admin_server_config_no_lock, save_admin_server_config_snapshot,
+ with_admin_server_config_read_lock,
};
use rustfs_audit::{audit_system, start_audit_system as start_global_audit_system, system::AuditSystemState};
use rustfs_config::DEFAULT_DELIMITER;
@@ -26,21 +26,6 @@ use rustfs_config::server_config::Config;
use s3s::{S3Error, S3Result, s3_error};
use tracing::warn;
-pub(crate) async fn load_server_config_from_store_for_context(context: Option<&AppContext>) -> S3Result {
- let Some(store) = current_object_store_handle_for_context(context) else {
- return Ok(Config::new());
- };
-
- read_admin_config_without_migrate(store)
- .await
- .map_err(|e| s3_error!(InternalError, "failed to read server config: {}", e))
-}
-
-pub(crate) async fn load_server_config_from_store() -> S3Result {
- let context = current_app_context();
- load_server_config_from_store_for_context(context.as_deref()).await
-}
-
fn has_any_audit_targets(specs: &[AdminTargetSpec], config: &Config) -> bool {
specs.iter().any(|spec| {
config
@@ -188,7 +173,7 @@ mod tests {
use super::*;
use crate::admin::handlers::target_descriptor::admin_target_spec_from_builtin;
use crate::admin::runtime_sources::{IamInterface, KmsInterface};
- use crate::admin::storage_api::config::save_admin_server_config;
+ use crate::admin::storage_api::config::{read_admin_config_without_migrate, save_admin_server_config};
use crate::admin::storage_api::error::StorageError;
use rustfs_config::audit::AUDIT_WEBHOOK_SUB_SYS;
use rustfs_config::server_config::KVS;
diff --git a/rustfs/src/admin/handlers/event.rs b/rustfs/src/admin/handlers/event.rs
index 7db37475a..7ac3c1c56 100644
--- a/rustfs/src/admin/handlers/event.rs
+++ b/rustfs/src/admin/handlers/event.rs
@@ -14,7 +14,7 @@
use crate::admin::{
auth::authorize_admin_request,
- handlers::notify_runtime_access::{get_notification_system, load_notification_config_snapshot},
+ handlers::notify_runtime_access::{get_notification_system, load_notification_mutation_snapshot},
handlers::supervise_admin_mutation,
handlers::target_descriptor::{
AdminTargetSpec, EndpointKey, RuntimeHealthStatus, TargetEndpointSource, admin_target_spec_from_builtin,
@@ -22,6 +22,7 @@ use crate::admin::{
merge_target_endpoints as shared_merge_target_endpoints, target_module_disabled_reason,
target_mutation_block_reason as shared_target_mutation_block_reason,
},
+ handlers::target_read::{collect_notification_runtime_statuses, load_target_read_snapshot},
router::{AdminOperation, Operation, S3Router},
runtime_sources::object_store_from_extensions,
runtime_sources::{AppContext, app_context_from_req},
@@ -464,7 +465,7 @@ impl Operation for NotificationTarget {
log_notification_target_operation_blocked!("set_target_config", Some(target_type), Some(target_name), &reason);
return Err(s3_error!(InvalidRequest, "{reason}"));
}
- let (ns, config_snapshot) = load_notification_config_snapshot().await?;
+ let (ns, config_snapshot) = load_notification_mutation_snapshot().await?;
if let Some(reason) = target_mutation_block_reason(&config_snapshot, target_type, target_name)? {
log_notification_target_operation_blocked!("set_target_config", Some(target_type), Some(target_name), &reason);
return Err(s3_error!(InvalidRequest, "{reason}"));
@@ -537,8 +538,7 @@ impl Operation for ListNotificationTargets {
s3_error!(InternalError, "failed to refresh notification module state")
})?;
let notify_enabled = refresh_notify_module_enabled();
- let (ns, config) = load_notification_config_snapshot().await?;
- let runtime_statuses = collect_runtime_statuses(ns.get_target_values().await).await;
+ let (config, runtime_statuses) = load_target_read_snapshot(collect_notification_runtime_statuses()).await?;
let notification_endpoints = merge_notification_endpoints(&config, runtime_statuses)?;
let data = serde_json::to_vec(&NotificationEndpointsResponse {
@@ -610,7 +610,7 @@ impl Operation for RemoveNotificationTarget {
log_notification_target_operation_blocked!("remove_target_config", Some(target_type), Some(target_name), &reason);
return Err(s3_error!(InvalidRequest, "{reason}"));
}
- let (ns, config_snapshot) = load_notification_config_snapshot().await?;
+ let (ns, config_snapshot) = load_notification_mutation_snapshot().await?;
if let Some(reason) = target_mutation_block_reason(&config_snapshot, target_type, target_name)? {
log_notification_target_operation_blocked!("remove_target_config", Some(target_type), Some(target_name), &reason);
return Err(s3_error!(InvalidRequest, "{reason}"));
@@ -676,6 +676,268 @@ mod tests {
}
}
+ #[tokio::test]
+ #[serial]
+ async fn target_list_reads_durable_config_without_runtime_reconciliation() {
+ use crate::admin::handlers::{
+ audit::ListAuditTargets,
+ extensions::ListExtensionInstancesHandler,
+ plugins_instances::{GetPluginInstanceHandler, ListPluginInstancesHandler},
+ target_read::load_target_read_snapshot,
+ };
+ use crate::admin::storage_api::config::{
+ read_admin_config, save_admin_config, save_admin_server_config, with_admin_server_config_write_lock,
+ };
+ use crate::server::{mark_event_notifier_unreconciled, with_notify_runtime_reconcile_lock};
+ use http_body_util::BodyExt as _;
+ use std::sync::Arc;
+ use std::time::Duration;
+
+ async fn response_json(
+ call: impl std::future::Future