refactor: route admin config writes through app context (#3782)

This commit is contained in:
Zhengchao An
2026-06-23 13:46:13 +08:00
committed by GitHub
parent 4ef899dd5c
commit 825c01060c
9 changed files with 179 additions and 28 deletions
+6 -6
View File
@@ -24,7 +24,7 @@ use crate::admin::service::config::{
validate_server_config,
};
use crate::admin::utils::{encode_compatible_admin_payload, is_compat_admin_request, read_compatible_admin_body};
use crate::app::context::{resolve_object_store_handle, resolve_server_config};
use crate::app::context::{publish_server_config, resolve_object_store_handle, resolve_server_config};
use crate::auth::{check_key_valid, get_session_token};
use crate::error::ApiError;
use crate::server::{ADMIN_PREFIX, RemoteAddr};
@@ -54,7 +54,7 @@ use rustfs_config::oidc::{
OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_CONFIG_URL, OIDC_DISPLAY_NAME, OIDC_EMAIL_CLAIM, OIDC_GROUPS_CLAIM,
OIDC_REDIRECT_URI, OIDC_REDIRECT_URI_DYNAMIC, OIDC_ROLE_POLICY, OIDC_SCOPES, OIDC_USERNAME_CLAIM,
};
use rustfs_config::server_config::{Config as ServerConfig, DEFAULT_KVS, KV, KVS, set_global_server_config};
use rustfs_config::server_config::{Config as ServerConfig, DEFAULT_KVS, KV, KVS};
use rustfs_config::{
COMMENT_KEY, DEFAULT_DELIMITER, ENABLE_KEY, ENV_PREFIX, ENV_SCANNER_ALERT_EXCESS_FOLDERS,
ENV_SCANNER_ALERT_EXCESS_VERSION_SIZE, ENV_SCANNER_ALERT_EXCESS_VERSIONS, ENV_SCANNER_BITROT_CYCLE_SECS,
@@ -1592,7 +1592,7 @@ impl Operation for SetConfigKVHandler {
validate_server_config(&config, sub_system).await?;
save_server_config_history(&body).await?;
save_server_config_to_store(&config).await?;
set_global_server_config(config.clone());
publish_server_config(config.clone());
let mut config_applied = false;
if let Some(sub_system) = sub_system
&& is_dynamic_config_subsystem(sub_system)
@@ -1628,7 +1628,7 @@ impl Operation for DelConfigKVHandler {
validate_server_config(&config, sub_system).await?;
save_server_config_history(&body).await?;
save_server_config_to_store(&config).await?;
set_global_server_config(config.clone());
publish_server_config(config.clone());
let mut config_applied = false;
if let Some(sub_system) = sub_system
&& is_dynamic_config_subsystem(sub_system)
@@ -1726,7 +1726,7 @@ impl Operation for RestoreConfigHistoryKVHandler {
apply_set_directives(&mut config, &directives)?;
validate_server_config(&config, None).await?;
save_server_config_to_store(&config).await?;
set_global_server_config(config.clone());
publish_server_config(config.clone());
apply_and_signal_dynamic_subsystems(&config).await;
signal_config_snapshot_reload().await;
@@ -1765,7 +1765,7 @@ impl Operation for SetConfigHandler {
validate_server_config(&config, None).await?;
save_server_config_history(&body).await?;
save_server_config_to_store(&config).await?;
set_global_server_config(config.clone());
publish_server_config(config.clone());
apply_and_signal_dynamic_subsystems(&config).await;
signal_config_snapshot_reload().await;
+1 -5
View File
@@ -119,7 +119,7 @@ mod ecstore_client {
}
mod ecstore_config {
pub(crate) use crate::storage::ecstore_config::{com, init, set_global_storage_class, storageclass};
pub(crate) use crate::storage::ecstore_config::{com, init, storageclass};
}
mod ecstore_data_usage {
@@ -462,10 +462,6 @@ pub(crate) fn init_admin_config_defaults() {
ecstore_config::init();
}
pub(crate) fn set_global_storage_class(cfg: storageclass::Config) {
ecstore_config::set_global_storage_class(cfg);
}
pub(crate) async fn load_data_usage_from_backend(
store: Arc<ECStore>,
) -> std::result::Result<rustfs_data_usage::DataUsageInfo, Error> {
+6 -5
View File
@@ -12,15 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use super::super::set_global_storage_class;
use super::super::storageclass;
use super::super::{STORAGE_CLASS_SUB_SYS, read_admin_config_without_migrate};
use crate::app::context::{resolve_notification_system, resolve_object_store_handle};
use crate::app::context::{
publish_server_config, publish_storage_class_config, resolve_notification_system, resolve_object_store_handle,
};
use rustfs_audit::reload_audit_config;
use rustfs_config::audit::{AUDIT_MQTT_SUB_SYS, AUDIT_REDIS_DEFAULT_CHANNEL, AUDIT_WEBHOOK_SUB_SYS};
use rustfs_config::notify::{NOTIFY_MQTT_SUB_SYS, NOTIFY_REDIS_DEFAULT_CHANNEL, NOTIFY_WEBHOOK_SUB_SYS};
use rustfs_config::oidc::IDENTITY_OPENID_SUB_SYS;
use rustfs_config::server_config::{Config as ServerConfig, KVS, set_global_server_config};
use rustfs_config::server_config::{Config as ServerConfig, KVS};
use rustfs_config::{AUDIT_DEFAULT_DIR, EVENT_DEFAULT_DIR};
use rustfs_config::{DEFAULT_DELIMITER, ENABLE_KEY, EnableState};
use rustfs_config::{HEAL_SUB_SYS, SCANNER_SUB_SYS};
@@ -77,7 +78,7 @@ async fn apply_storage_class_runtime_config(config: &ServerConfig) -> S3Result<(
.unwrap_or(1);
let parsed = storageclass::lookup_config(&kvs, set_drive_count)
.map_err(|err| internal_error(format!("failed to apply storage class config: {err}")))?;
set_global_storage_class(parsed);
publish_storage_class_config(parsed);
Ok(())
}
@@ -335,7 +336,7 @@ pub async fn reload_runtime_config_snapshot() -> S3Result<()> {
}
}
set_global_server_config(config);
publish_server_config(config);
Ok(())
}
+80 -1
View File
@@ -29,6 +29,7 @@ use super::DailyAllTierStats;
use super::ECStore;
use super::EndpointServerPools;
use super::ScannerMetricsReport;
use super::StorageClassConfig;
use super::TierConfigMgr;
use super::metadata_sys::BucketMetadataSys;
use super::new_object_layer_fn;
@@ -179,6 +180,18 @@ pub fn resolve_server_config() -> Option<Config> {
resolve_server_config_with(get_global_app_context(), || default_server_config_interface().get())
}
/// Publish server config using AppContext-first precedence.
pub fn publish_server_config(config: Config) {
publish_server_config_with(get_global_app_context(), config, |config| default_server_config_interface().set(config));
}
/// Publish storage class config using AppContext-first precedence.
pub fn publish_storage_class_config(config: StorageClassConfig) {
publish_storage_class_config_with(get_global_app_context(), config, |config| {
default_storage_class_interface().set(config);
});
}
/// Resolve buffer profile config using AppContext-first precedence.
pub fn resolve_buffer_config() -> RustFSBufferConfig {
resolve_buffer_config_with(get_global_app_context(), || default_buffer_config_interface().get())
@@ -365,6 +378,26 @@ fn resolve_server_config_with(context: Option<Arc<AppContext>>, fallback: impl F
context.map_or_else(fallback, |context| context.server_config().get())
}
fn publish_server_config_with(context: Option<Arc<AppContext>>, config: Config, fallback: impl FnOnce(Config)) {
if let Some(context) = context {
context.server_config().set(config);
} else {
fallback(config);
}
}
fn publish_storage_class_config_with(
context: Option<Arc<AppContext>>,
config: StorageClassConfig,
fallback: impl FnOnce(StorageClassConfig),
) {
if let Some(context) = context {
context.storage_class().set(config);
} else {
fallback(config);
}
}
fn resolve_buffer_config_with(
context: Option<Arc<AppContext>>,
fallback: impl FnOnce() -> RustFSBufferConfig,
@@ -388,13 +421,14 @@ mod tests {
ActionCredentialInterface, BootTimeInterface, BucketMetadataInterface, BufferConfigInterface, DeploymentIdInterface,
EndpointsInterface, IamInterface, KmsInterface, KmsRuntimeInterface, LocalNodeNameInterface, LockClientInterface,
OutboundTlsRuntimeInterface, RegionInterface, ReplicationStatsInterface, RuntimePortInterface, ScannerMetricsInterface,
ServerConfigInterface, TierConfigInterface, TierStatsInterface,
ServerConfigInterface, StorageClassInterface, TierConfigInterface, TierStatsInterface,
};
use crate::config::{RustFSBufferConfig, WorkloadProfile};
use async_trait::async_trait;
use rustfs_iam::{store::object::ObjectStore, sys::IamSys};
use rustfs_lock::{LocalClient, LockClient};
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{Duration, SystemTime};
use tempfile::TempDir;
use tokio_util::sync::CancellationToken;
@@ -582,12 +616,27 @@ mod tests {
struct TestServerConfigInterface {
config: Option<Config>,
published: Arc<AtomicUsize>,
}
impl ServerConfigInterface for TestServerConfigInterface {
fn get(&self) -> Option<Config> {
self.config.clone()
}
fn set(&self, _config: Config) {
self.published.fetch_add(1, Ordering::SeqCst);
}
}
struct TestStorageClassInterface {
published: Arc<AtomicUsize>,
}
impl StorageClassInterface for TestStorageClassInterface {
fn set(&self, _config: StorageClassConfig) {
self.published.fetch_add(1, Ordering::SeqCst);
}
}
struct TestBufferConfigInterface {
@@ -674,6 +723,10 @@ mod tests {
};
let tier_config = TierConfigMgr::new();
let server_config = Config::new();
let context_server_config_published = Arc::new(AtomicUsize::new(0));
let fallback_server_config_published = Arc::new(AtomicUsize::new(0));
let context_storage_class_published = Arc::new(AtomicUsize::new(0));
let fallback_storage_class_published = Arc::new(AtomicUsize::new(0));
let buffer_config = RustFSBufferConfig::new(WorkloadProfile::AiTraining);
let context_lock_client: Arc<dyn LockClient> = Arc::new(LocalClient::new());
let fallback_lock_client: Arc<dyn LockClient> = Arc::new(LocalClient::new());
@@ -757,6 +810,10 @@ mod tests {
}),
server_config: Arc::new(TestServerConfigInterface {
config: Some(server_config.clone()),
published: context_server_config_published.clone(),
}),
storage_class: Arc::new(TestStorageClassInterface {
published: context_storage_class_published.clone(),
}),
buffer_config: Arc::new(TestBufferConfigInterface { config: buffer_config }),
},
@@ -847,6 +904,18 @@ mod tests {
resolve_server_config_with(Some(context.clone()), || None).expect("context server config"),
server_config
);
publish_server_config_with(Some(context.clone()), Config::new(), |config| {
drop(config);
fallback_server_config_published.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(context_server_config_published.load(Ordering::SeqCst), 1);
assert_eq!(fallback_server_config_published.load(Ordering::SeqCst), 0);
publish_storage_class_config_with(Some(context.clone()), StorageClassConfig::default(), |config| {
drop(config);
fallback_storage_class_published.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(context_storage_class_published.load(Ordering::SeqCst), 1);
assert_eq!(fallback_storage_class_published.load(Ordering::SeqCst), 0);
assert_eq!(
resolve_buffer_config_with(Some(context), || RustFSBufferConfig::new(WorkloadProfile::GeneralPurpose)).workload,
WorkloadProfile::AiTraining
@@ -922,6 +991,16 @@ mod tests {
resolve_server_config_with(None, || Some(server_config.clone())).expect("fallback server config"),
server_config
);
publish_server_config_with(None, Config::new(), |config| {
drop(config);
fallback_server_config_published.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(fallback_server_config_published.load(Ordering::SeqCst), 1);
publish_storage_class_config_with(None, StorageClassConfig::default(), |config| {
drop(config);
fallback_storage_class_published.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(fallback_storage_class_published.load(Ordering::SeqCst), 1);
assert_eq!(
resolve_buffer_config_with(None, || RustFSBufferConfig::new(WorkloadProfile::DataAnalytics)).workload,
WorkloadProfile::DataAnalytics
+10 -2
View File
@@ -20,14 +20,14 @@ use super::handles::{
default_notification_system_interface, default_notify_interface, default_outbound_tls_runtime_interface,
default_region_interface, default_replication_pool_interface, default_replication_stats_interface,
default_runtime_port_interface, default_scanner_metrics_interface, default_server_config_interface,
default_tier_config_interface, default_tier_stats_interface,
default_storage_class_interface, default_tier_config_interface, default_tier_stats_interface,
};
use super::interfaces::{
ActionCredentialInterface, BootTimeInterface, BucketMetadataInterface, BucketMonitorInterface, BufferConfigInterface,
DeploymentIdInterface, EndpointsInterface, IamInterface, KmsInterface, KmsRuntimeInterface, LocalNodeNameInterface,
LockClientInterface, NotificationSystemInterface, NotifyInterface, OutboundTlsRuntimeInterface, RegionInterface,
ReplicationPoolInterface, ReplicationStatsInterface, RuntimePortInterface, ScannerMetricsInterface, ServerConfigInterface,
TierConfigInterface, TierStatsInterface,
StorageClassInterface, TierConfigInterface, TierStatsInterface,
};
use rustfs_iam::{store::object::ObjectStore, sys::IamSys};
use rustfs_kms::KmsServiceManager;
@@ -60,6 +60,7 @@ pub struct AppContext {
region: Arc<dyn RegionInterface>,
tier_config: Arc<dyn TierConfigInterface>,
server_config: Arc<dyn ServerConfigInterface>,
storage_class: Arc<dyn StorageClassInterface>,
buffer_config: Arc<dyn BufferConfigInterface>,
}
@@ -89,6 +90,7 @@ impl AppContext {
region: default_region_interface(),
tier_config: default_tier_config_interface(),
server_config: default_server_config_interface(),
storage_class: default_storage_class_interface(),
buffer_config: default_buffer_config_interface(),
}
}
@@ -194,6 +196,10 @@ impl AppContext {
self.server_config.clone()
}
pub fn storage_class(&self) -> Arc<dyn StorageClassInterface> {
self.storage_class.clone()
}
pub fn buffer_config(&self) -> Arc<dyn BufferConfigInterface> {
self.buffer_config.clone()
}
@@ -223,6 +229,7 @@ pub(super) struct AppContextTestInterfaces {
pub(super) region: Arc<dyn RegionInterface>,
pub(super) tier_config: Arc<dyn TierConfigInterface>,
pub(super) server_config: Arc<dyn ServerConfigInterface>,
pub(super) storage_class: Arc<dyn StorageClassInterface>,
pub(super) buffer_config: Arc<dyn BufferConfigInterface>,
}
@@ -253,6 +260,7 @@ impl AppContext {
region: interfaces.region,
tier_config: interfaces.tier_config,
server_config: interfaces.server_config,
storage_class: interfaces.storage_class,
buffer_config: interfaces.buffer_config,
}
}
+22 -2
View File
@@ -13,25 +13,27 @@
// limitations under the License.
use super::super::EndpointServerPools;
use super::super::StorageClassConfig;
use super::super::TierConfigMgr;
use super::super::metadata_sys::{BucketMetadataSys, get_global_bucket_metadata_sys};
use super::super::{
collect_scanner_metrics_report, get_daily_all_tier_stats, get_global_boot_time, get_global_bucket_monitor,
get_global_deployment_id, get_global_endpoints_opt, get_global_lock_client, get_global_notification_sys, get_global_region,
get_global_replication_pool, get_global_replication_stats, get_global_tier_config_mgr, global_rustfs_port,
set_global_storage_class,
};
use super::interfaces::{
ActionCredentialInterface, BootTimeInterface, BucketMetadataInterface, BucketMonitorInterface, BufferConfigInterface,
DeploymentIdInterface, EndpointsInterface, IamInterface, KmsInterface, KmsRuntimeInterface, LocalNodeNameInterface,
LockClientInterface, NotificationSystemInterface, NotifyInterface, OutboundTlsRuntimeInterface, RegionInterface,
ReplicationPoolInterface, ReplicationStatsInterface, RuntimePortInterface, ScannerMetricsInterface, ServerConfigInterface,
TierConfigInterface, TierStatsInterface,
StorageClassInterface, TierConfigInterface, TierStatsInterface,
};
use crate::config::{RustFSBufferConfig, get_global_buffer_config};
use async_trait::async_trait;
use rustfs_common::get_global_local_node_name;
use rustfs_config::server_config::Config;
use rustfs_config::server_config::get_global_server_config;
use rustfs_config::server_config::{get_global_server_config, set_global_server_config};
use rustfs_credentials::{Credentials, get_global_action_cred};
use rustfs_iam::{store::object::ObjectStore, sys::IamSys};
use rustfs_kms::{KmsServiceManager, get_global_kms_service_manager};
@@ -303,6 +305,20 @@ impl ServerConfigInterface for ServerConfigHandle {
fn get(&self) -> Option<Config> {
get_global_server_config()
}
fn set(&self, config: Config) {
set_global_server_config(config);
}
}
/// Default storage class config interface adapter.
#[derive(Default)]
pub struct StorageClassHandle;
impl StorageClassInterface for StorageClassHandle {
fn set(&self, config: StorageClassConfig) {
set_global_storage_class(config);
}
}
/// Default buffer profile config interface adapter.
@@ -395,6 +411,10 @@ pub fn default_server_config_interface() -> Arc<dyn ServerConfigInterface> {
Arc::new(ServerConfigHandle)
}
pub fn default_storage_class_interface() -> Arc<dyn StorageClassInterface> {
Arc::new(StorageClassHandle)
}
pub fn default_buffer_config_interface() -> Arc<dyn BufferConfigInterface> {
Arc::new(BufferConfigHandle)
}
+7
View File
@@ -15,6 +15,7 @@
use super::super::DailyAllTierStats;
use super::super::EndpointServerPools;
use super::super::ScannerMetricsReport;
use super::super::StorageClassConfig;
use super::super::TierConfigMgr;
use super::super::metadata_sys::BucketMetadataSys;
use super::super::{BucketBandwidthMonitor, DynReplicationPool, NotificationSys, ReplicationStats};
@@ -156,6 +157,12 @@ pub trait TierConfigInterface: Send + Sync {
/// Server config interface for application-layer and server modules.
pub trait ServerConfigInterface: Send + Sync {
fn get(&self) -> Option<Config>;
fn set(&self, config: Config);
}
/// Storage class config interface for admin runtime config publication.
pub trait StorageClassInterface: Send + Sync {
fn set(&self, config: StorageClassConfig);
}
/// Buffer profile config interface for application-layer use-cases.
+6
View File
@@ -566,6 +566,12 @@ pub(crate) mod storageclass {
pub(crate) const STANDARD_IA: &str = super::ecstore_config::storageclass::STANDARD_IA;
}
pub(crate) type StorageClassConfig = crate::storage::ecstore_config::storageclass::Config;
pub(crate) fn set_global_storage_class(cfg: StorageClassConfig) {
crate::storage::ecstore_config::set_global_storage_class(cfg);
}
pub(crate) fn get_total_usable_capacity(disks: &[rustfs_madmin::Disk], info: &rustfs_madmin::StorageInfo) -> usize {
ecstore_capacity::get_total_usable_capacity(disks, info)
}