refactor: route admin runtime config through app context (#3942)

This commit is contained in:
Zhengchao An
2026-06-27 13:10:00 +08:00
committed by GitHub
parent 1b3dea012e
commit 996e58fd9a
10 changed files with 166 additions and 66 deletions
@@ -44,6 +44,9 @@ update this plan and the guard in the same reviewed migration PR.
cover both paths. cover both paths.
2. Move one remaining consumer group at a time to explicit context or 2. Move one remaining consumer group at a time to explicit context or
owner-local runtime-source handles. owner-local runtime-source handles.
- Current handoff: admin config, OIDC config, audit runtime config, and
dynamic KMS config storage reads/writes resolve through current
AppContext-aware boundaries before using the legacy object-store fallback.
3. Remove one fallback family per PR only after scans prove no production caller 3. Remove one fallback family per PR only after scans prove no production caller
depends on it. depends on it.
4. Keep embedded startup and tests working before deleting any fallback. 4. Keep embedded startup and tests working before deleting any fallback.
+43 -17
View File
@@ -5,24 +5,24 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
## Current Context ## Current Context
- Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660) - Issue: [`rustfs/backlog#660`](https://github.com/rustfs/backlog/issues/660)
- Branch: `overtrue/arch-global-runtime-boundary-batch` - Branch: `overtrue/arch-global-fallback-removal-batch`
- 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/API-125/API-126/API-127/API-128/API-129/API-130/API-131/API-132/API-133/API-134/API-135/API-136/API-137/API-138/API-139/API-140/API-141/API-142/API-143/API-144/API-145/API-146/API-147/API-148/API-149/API-150/API-151/API-152/API-153/API-154/API-155/API-156/API-157/API-158/API-159/API-160/API-161/API-162/API-163/API-164/API-165/API-166/API-167/API-168/API-169/API-170/API-171/API-172/API-173/API-174/API-175/API-176/API-177/API-178/API-179/API-180/API-181/API-182/API-183/API-184/API-185/API-186/API-187/API-188/API-189/API-190/API-191/API-192/API-193/API-194/API-195/API-196/API-197/API-198/API-199/API-200/API-201/API-202/API-203/API-204/API-205/API-206/API-207/API-208/API-209/API-210/API-211/API-212/API-213/API-214/API-215/API-216/API-217/API-218/API-219/API-220/API-221/API-222/API-223/API-224/API-225/API-226/API-227/API-228/API-229/API-230/API-231/API-232/API-233/API-234/API-235/API-236/API-237/API-238/API-239/API-240/API-241/API-242/API-243/API-244/API-245/API-246/API-247/API-248/API-249/API-250/API-251/API-252/API-253/API-254/CTX-002`. - 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/API-125/API-126/API-127/API-128/API-129/API-130/API-131/API-132/API-133/API-134/API-135/API-136/API-137/API-138/API-139/API-140/API-141/API-142/API-143/API-144/API-145/API-146/API-147/API-148/API-149/API-150/API-151/API-152/API-153/API-154/API-155/API-156/API-157/API-158/API-159/API-160/API-161/API-162/API-163/API-164/API-165/API-166/API-167/API-168/API-169/API-170/API-171/API-172/API-173/API-174/API-175/API-176/API-177/API-178/API-179/API-180/API-181/API-182/API-183/API-184/API-185/API-186/API-187/API-188/API-189/API-190/API-191/API-192/API-193/API-194/API-195/API-196/API-197/API-198/API-199/API-200/API-201/API-202/API-203/API-204/API-205/API-206/API-207/API-208/API-209/API-210/API-211/API-212/API-213/API-214/API-215/API-216/API-217/API-218/API-219/API-220/API-221/API-222/API-223/API-224/API-225/API-226/API-227/API-228/API-229/API-230/API-231/API-232/API-233/API-234/API-235/API-236/API-237/API-238/API-239/API-240/API-241/API-242/API-243/API-244/API-245/API-246/API-247/API-248/API-249/API-250/API-251/API-252/API-253/API-254/CTX-002`.
- Current baseline also includes API-255 from PR #3923, API-256 from PR - Current baseline also includes API-255 from PR #3923, API-256 from PR
#3925, CFG-009 from PR #3927, C-007/C-009 from PR #3935, C-008/C-010 #3925, CFG-009 from PR #3927, C-007/C-009 from PR #3935, C-008/C-010
from PR #3936, and DOC-001/DOC-002/DOC-003/DOC-004/DOC-005/ from PR #3936, and DOC-001/DOC-002/DOC-003/DOC-004/DOC-005/
TEST-DOC-001 from PR #3938, plus GLOB-001/GLOB-002/GLOB-003/GLOB-004/ TEST-DOC-001 from PR #3938, GLOB-001/GLOB-002/GLOB-003/GLOB-004/
GLOB-005/GLOB-006/CRATE-001/CRATE-002 from PR #3939. GLOB-005/CRATE-001/CRATE-002 from PR #3939, and GLOB-006 from PR #3941.
- Current phase PR: GLOB-006 ECStore runtime facade boundary batch. - Current phase PR: GLOB-007 admin runtime context handoff batch.
- Based on: `origin/main` after PR #3939 merged. - Based on: `origin/main` after PR #3941 merged.
- PR type for this branch: `ci-gate`. - PR type for this branch: `ci-gate`.
- Runtime behavior changes: none intended. - Runtime behavior changes: none intended.
- Rust code changes: add the selected `rustfs_ecstore::api::runtime` facade and - Rust code changes: add context-aware notification-system and server-config
route external crate-local `storage_api` boundaries away from direct resolution, then route admin config, OIDC config, audit runtime config, and
`rustfs_ecstore::api::global` imports. dynamic KMS config storage access through current AppContext-aware resolver
- CI/script changes: shrink the direct ECStore global facade allowlist to the boundaries before falling back to legacy globals.
root storage owner boundary. - CI/script changes: none intended.
- Docs changes: update the global-state plan and this progress ledger for the - Docs changes: update the global-state plan and this progress ledger for the
runtime facade batch. admin runtime context handoff batch.
## Phase 0 Tasks ## Phase 0 Tasks
@@ -2764,20 +2764,27 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
[`global-state-crate-split-plan.md`](global-state-crate-split-plan.md) to [`global-state-crate-split-plan.md`](global-state-crate-split-plan.md) to
record the remaining owner boundaries and fallback removal sequence. record the remaining owner boundaries and fallback removal sequence.
- Verification: architecture migration guard and diff hygiene. - Verification: architecture migration guard and diff hygiene.
- [~] `GLOB-006` Shrink `ecstore::global`. - [x] `GLOB-006` Shrink `ecstore::global`.
- Current slice: expose selected ECStore runtime-source reads through - Completed slice: expose selected ECStore runtime-source reads through
`rustfs_ecstore::api::runtime`, migrate heal, IAM, notify, observability, `rustfs_ecstore::api::runtime`, migrate heal, IAM, notify, observability,
Swift, S3 Select, and scanner storage API boundaries away from direct Swift, S3 Select, and scanner storage API boundaries away from direct
`rustfs_ecstore::api::global` imports, and keep the direct global facade `rustfs_ecstore::api::global` imports, and keep the direct global facade
confined to the root storage owner boundary. confined to the root storage owner boundary.
- Remaining work: move additional ECStore bootstrap/runtime globals behind - Remaining work: move additional ECStore bootstrap/runtime globals behind
explicit owner handles where safe; do not remove bootstrap state in this explicit owner handles where safe; do not remove bootstrap state until all
runtime facade PR. dependent runtime owners have explicit handles.
- Verification: focused compile coverage, architecture migration guard, diff - Verification: focused compile coverage, architecture migration guard, diff
hygiene, Rust risk scan, and full PR gate. hygiene, Rust risk scan, and full PR gate.
- [ ] `GLOB-007` Remove fallbacks. - [~] `GLOB-007` Remove fallbacks.
- Current slice: move admin config, OIDC config, audit runtime config, and
dynamic KMS config storage access paths to current AppContext-aware
resolver boundaries, and expose notification-system/server-config
resolution for explicit AppContext handoff.
- Remaining work: remove one fallback family per PR only after scans prove no - Remaining work: remove one fallback family per PR only after scans prove no
production caller depends on it. production caller depends on it; this slice keeps the fallback behavior
because broader admin/server consumers still use the root resolvers.
- Verification: focused RustFS compile, resolver residual scan, formatting,
diff hygiene, architecture guard, Rust risk scan, and full PR gate.
- [~] `CRATE-001/CRATE-002` Evaluate future crate splits. - [~] `CRATE-001/CRATE-002` Evaluate future crate splits.
- Current slice: record evidence required before `ecstore-erasure` or - Current slice: record evidence required before `ecstore-erasure` or
`storage-cluster` split proposals. `storage-cluster` split proposals.
@@ -6041,6 +6048,9 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
| Expert | Status | Notes | | Expert | Status | Notes |
|---|---|---| |---|---|---|
| Quality/architecture | pass | GLOB-007 adds AppContext-aware notification-system and server-config resolver boundaries, then routes admin config, OIDC config, audit runtime config, and dynamic KMS config storage access through current AppContext-aware owners before legacy fallbacks. |
| Migration preservation | pass | Existing admin config reload, OIDC restart detection, audit config writes, and dynamic KMS config load/save behavior stay unchanged because context lookup falls back to the same root object-store, notification, and server-config owners. |
| Testing/verification | pass | Focused RustFS compile/tests, resolver residual scan, formatting, architecture guard, diff hygiene, Rust risk scan, and full `make pre-pr` passed before PR. |
| Quality/architecture | pass | GLOB-006 exposes selected ECStore runtime-source reads through `rustfs_ecstore::api::runtime` and removes external crate direct global facade imports while leaving ECStore owner state intact. | | Quality/architecture | pass | GLOB-006 exposes selected ECStore runtime-source reads through `rustfs_ecstore::api::runtime` and removes external crate direct global facade imports while leaving ECStore owner state intact. |
| Migration preservation | pass | Object-store, bucket-monitor, tier-config, erasure flags, IAM first-node, and local-disk-map reads delegate to the same ECStore runtime/global owners; startup, readiness, IAM/KMS, lock quorum, scanner, heal, Swift, S3 Select, and notification behavior stay unchanged. | | Migration preservation | pass | Object-store, bucket-monitor, tier-config, erasure flags, IAM first-node, and local-disk-map reads delegate to the same ECStore runtime/global owners; startup, readiness, IAM/KMS, lock quorum, scanner, heal, Swift, S3 Select, and notification behavior stay unchanged. |
| Testing/verification | pass | Focused compile, formatting, shell syntax, architecture guard, global facade scan, diff hygiene, Rust risk scan, full `make pre-pr`, and post-rebase focused checks passed before PR. | | Testing/verification | pass | Focused compile, formatting, shell syntax, architecture guard, global facade scan, diff hygiene, Rust risk scan, full `make pre-pr`, and post-rebase focused checks passed before PR. |
@@ -6398,6 +6408,22 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
Passed before push: Passed before push:
- Issue #660 GLOB-007 current slice:
- Branch freshness check: rebased onto `origin/main` after PR #3941 merged.
- `cargo fmt --all --check`: passed.
- `git diff --check`: passed.
- Resolver residual scan over migrated admin files: passed.
- Diff-added Rust risk scan: passed; no new production unwrap/expect,
unchecked narrow casts, stringly errors, or ad-hoc stdout/stderr.
- `cargo check -p rustfs --lib`: passed.
- `./scripts/check_architecture_migration_rules.sh`: passed.
- `cargo test -p rustfs --lib app::context::tests::resolver_helpers_are_context_first_and_fallback_when_context_is_absent`:
passed.
- `cargo test -p rustfs --lib admin::service::config`: passed.
- Three-expert review: passed.
- `make pre-pr`: passed, including 6911 nextest tests passed, 112 skipped,
and doctests.
- Issue #660 GLOB-001/GLOB-002/GLOB-003/GLOB-004/GLOB-005/GLOB-006/ - Issue #660 GLOB-001/GLOB-002/GLOB-003/GLOB-004/GLOB-005/GLOB-006/
CRATE-001/CRATE-002 current slice: CRATE-001/CRATE-002 current slice:
- Branch freshness check: rebased onto `origin/main` after PR #3938 merged. - Branch freshness check: rebased onto `origin/main` after PR #3938 merged.
@@ -13,15 +13,15 @@
// limitations under the License. // limitations under the License.
use crate::admin::handlers::target_descriptor::AdminTargetSpec; use crate::admin::handlers::target_descriptor::AdminTargetSpec;
use crate::admin::runtime_sources::resolve_object_store_handle; use crate::admin::runtime_sources::{AppContext, current_app_context, resolve_object_store_handle_for_context};
use crate::admin::storage_api::config::{read_admin_config_without_migrate, save_admin_server_config}; use crate::admin::storage_api::config::{read_admin_config_without_migrate, save_admin_server_config};
use rustfs_audit::{audit_system, start_audit_system as start_global_audit_system, system::AuditSystemState}; use rustfs_audit::{audit_system, start_audit_system as start_global_audit_system, system::AuditSystemState};
use rustfs_config::DEFAULT_DELIMITER; use rustfs_config::DEFAULT_DELIMITER;
use rustfs_config::server_config::Config; use rustfs_config::server_config::Config;
use s3s::{S3Result, s3_error}; use s3s::{S3Result, s3_error};
pub(crate) async fn load_server_config_from_store() -> S3Result<Config> { pub(crate) async fn load_server_config_from_store_for_context(context: Option<&AppContext>) -> S3Result<Config> {
let Some(store) = resolve_object_store_handle() else { let Some(store) = resolve_object_store_handle_for_context(context) else {
return Ok(Config::new()); return Ok(Config::new());
}; };
@@ -30,6 +30,11 @@ pub(crate) async fn load_server_config_from_store() -> S3Result<Config> {
.map_err(|e| s3_error!(InternalError, "failed to read server config: {}", e)) .map_err(|e| s3_error!(InternalError, "failed to read server config: {}", e))
} }
pub(crate) async fn load_server_config_from_store() -> S3Result<Config> {
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 { fn has_any_audit_targets(specs: &[AdminTargetSpec], config: &Config) -> bool {
specs.iter().any(|spec| { specs.iter().any(|spec| {
config config
@@ -75,11 +80,15 @@ pub(crate) async fn apply_audit_runtime_config(specs: &[AdminTargetSpec], config
Ok(()) Ok(())
} }
pub(crate) async fn update_audit_config_and_reload<F>(specs: &[AdminTargetSpec], mut modifier: F) -> S3Result<()> async fn update_audit_config_and_reload_for_context<F>(
context: Option<&AppContext>,
specs: &[AdminTargetSpec],
mut modifier: F,
) -> S3Result<()>
where where
F: FnMut(&mut Config) -> bool, F: FnMut(&mut Config) -> bool,
{ {
let Some(store) = resolve_object_store_handle() else { let Some(store) = resolve_object_store_handle_for_context(context) else {
return Err(s3_error!(InternalError, "server storage not initialized")); return Err(s3_error!(InternalError, "server storage not initialized"));
}; };
@@ -98,6 +107,14 @@ where
apply_audit_runtime_config(specs, config).await apply_audit_runtime_config(specs, config).await
} }
pub(crate) async fn update_audit_config_and_reload<F>(specs: &[AdminTargetSpec], modifier: F) -> S3Result<()>
where
F: FnMut(&mut Config) -> bool,
{
let context = current_app_context();
update_audit_config_and_reload_for_context(context.as_deref(), specs, modifier).await
}
pub(crate) async fn set_audit_target_config( pub(crate) async fn set_audit_target_config(
specs: &[AdminTargetSpec], specs: &[AdminTargetSpec],
subsystem: &str, subsystem: &str,
+9 -3
View File
@@ -14,7 +14,9 @@
use crate::admin::auth::validate_admin_request; use crate::admin::auth::validate_admin_request;
use crate::admin::router::{AdminOperation, Operation, S3Router}; use crate::admin::router::{AdminOperation, Operation, S3Router};
use crate::admin::runtime_sources::{publish_server_config, resolve_object_store_handle, resolve_server_config}; use crate::admin::runtime_sources::{
current_app_context, publish_server_config, resolve_object_store_handle_for_context, resolve_server_config_for_context,
};
use crate::admin::service::config::{ use crate::admin::service::config::{
apply_dynamic_config_for_subsystem, is_dynamic_config_subsystem, signal_config_snapshot_reload, signal_dynamic_config_reload, apply_dynamic_config_for_subsystem, is_dynamic_config_subsystem, signal_config_snapshot_reload, signal_dynamic_config_reload,
validate_server_config, validate_server_config,
@@ -709,7 +711,9 @@ fn success_response(config_applied: bool) -> S3Result<S3Response<(StatusCode, Bo
} }
fn object_store() -> S3Result<std::sync::Arc<crate::admin::storage_api::runtime::ECStore>> { fn object_store() -> S3Result<std::sync::Arc<crate::admin::storage_api::runtime::ECStore>> {
resolve_object_store_handle().ok_or_else(|| s3_error!(InternalError, "server storage not initialized")) let context = current_app_context();
resolve_object_store_handle_for_context(context.as_deref())
.ok_or_else(|| s3_error!(InternalError, "server storage not initialized"))
} }
async fn load_server_config_from_store() -> S3Result<ServerConfig> { async fn load_server_config_from_store() -> S3Result<ServerConfig> {
@@ -725,7 +729,9 @@ async fn load_active_server_config() -> S3Result<ServerConfig> {
return Ok(config); return Ok(config);
} }
resolve_server_config().ok_or_else(|| s3_error!(InternalError, "server config is not initialized")) let context = current_app_context();
resolve_server_config_for_context(context.as_deref())
.ok_or_else(|| s3_error!(InternalError, "server config is not initialized"))
} }
async fn save_server_config_to_store(config: &ServerConfig) -> S3Result<()> { async fn save_server_config_to_store(config: &ServerConfig) -> S3Result<()> {
+6 -3
View File
@@ -17,7 +17,8 @@
use crate::admin::auth::validate_admin_request; use crate::admin::auth::validate_admin_request;
use crate::admin::router::{AdminOperation, Operation, S3Router}; use crate::admin::router::{AdminOperation, Operation, S3Router};
use crate::admin::runtime_sources::{ use crate::admin::runtime_sources::{
resolve_kms_runtime_service_manager, resolve_object_store_handle, resolve_or_init_kms_runtime_service_manager, current_app_context, resolve_kms_runtime_service_manager, resolve_object_store_handle_for_context,
resolve_or_init_kms_runtime_service_manager,
}; };
use crate::admin::storage_api::config::{read_admin_config, save_admin_config}; use crate::admin::storage_api::config::{read_admin_config, save_admin_config};
use crate::auth::{check_key_valid, get_session_token}; use crate::auth::{check_key_valid, get_session_token};
@@ -105,7 +106,8 @@ fn normalize_configure_request_auth(
/// Save KMS configuration to cluster storage /// Save KMS configuration to cluster storage
#[instrument(skip(config))] #[instrument(skip(config))]
async fn save_kms_config(config: &KmsConfig) -> Result<(), String> { async fn save_kms_config(config: &KmsConfig) -> Result<(), String> {
let Some(store) = resolve_object_store_handle() else { let context = current_app_context();
let Some(store) = resolve_object_store_handle_for_context(context.as_deref()) else {
return Err("Storage layer not initialized".to_string()); return Err("Storage layer not initialized".to_string());
}; };
@@ -129,7 +131,8 @@ async fn save_kms_config(config: &KmsConfig) -> Result<(), String> {
/// Load KMS configuration from cluster storage /// Load KMS configuration from cluster storage
#[instrument] #[instrument]
pub async fn load_kms_config() -> Option<KmsConfig> { pub async fn load_kms_config() -> Option<KmsConfig> {
let Some(store) = resolve_object_store_handle() else { let context = current_app_context();
let Some(store) = resolve_object_store_handle_for_context(context.as_deref()) else {
warn!( warn!(
component = LOG_COMPONENT_ADMIN, component = LOG_COMPONENT_ADMIN,
subsystem = LOG_SUBSYSTEM_KMS, subsystem = LOG_SUBSYSTEM_KMS,
+9 -4
View File
@@ -15,7 +15,9 @@
use super::sts::create_oidc_sts_credentials; use super::sts::create_oidc_sts_credentials;
use crate::admin::auth::validate_admin_request; use crate::admin::auth::validate_admin_request;
use crate::admin::router::{AdminOperation, Operation, S3Router}; use crate::admin::router::{AdminOperation, Operation, S3Router};
use crate::admin::runtime_sources::{resolve_object_store_handle, resolve_oidc_handle, resolve_server_config}; use crate::admin::runtime_sources::{
current_app_context, resolve_object_store_handle_for_context, resolve_oidc_handle, resolve_server_config_for_context,
};
use crate::admin::storage_api::config::{read_admin_config_without_migrate, save_admin_server_config}; use crate::admin::storage_api::config::{read_admin_config_without_migrate, save_admin_server_config};
use crate::auth::{check_key_valid, get_session_token}; use crate::auth::{check_key_valid, get_session_token};
use crate::server::{ADMIN_PREFIX, MINIO_ADMIN_PREFIX, RemoteAddr}; use crate::server::{ADMIN_PREFIX, MINIO_ADMIN_PREFIX, RemoteAddr};
@@ -792,7 +794,8 @@ fn json_response<T: Serialize>(status: StatusCode, payload: &T) -> S3Result<S3Re
} }
async fn load_server_config_from_store() -> S3Result<ServerConfig> { async fn load_server_config_from_store() -> S3Result<ServerConfig> {
let Some(store) = resolve_object_store_handle() else { let context = current_app_context();
let Some(store) = resolve_object_store_handle_for_context(context.as_deref()) else {
return Err(s3_error!(InternalError, "storage layer not initialized")); return Err(s3_error!(InternalError, "storage layer not initialized"));
}; };
@@ -802,7 +805,8 @@ async fn load_server_config_from_store() -> S3Result<ServerConfig> {
} }
async fn save_server_config_to_store(config: &ServerConfig) -> S3Result<()> { async fn save_server_config_to_store(config: &ServerConfig) -> S3Result<()> {
let Some(store) = resolve_object_store_handle() else { let context = current_app_context();
let Some(store) = resolve_object_store_handle_for_context(context.as_deref()) else {
return Err(s3_error!(InternalError, "storage layer not initialized")); return Err(s3_error!(InternalError, "storage layer not initialized"));
}; };
@@ -826,7 +830,8 @@ fn provider_instance_key(provider_id: &str) -> String {
} }
fn oidc_restart_required(config: &ServerConfig) -> bool { fn oidc_restart_required(config: &ServerConfig) -> bool {
let active_config = resolve_server_config(); let context = current_app_context();
let active_config = resolve_server_config_for_context(context.as_deref());
oidc_restart_required_from_active_config(config, active_config.as_ref()) oidc_restart_required_from_active_config(config, active_config.as_ref())
} }
+5 -5
View File
@@ -20,11 +20,11 @@ pub(crate) use crate::runtime_sources::{
AppContext, publish_server_config, publish_storage_class_config, resolve_action_credentials, resolve_boot_time, AppContext, publish_server_config, publish_storage_class_config, resolve_action_credentials, resolve_boot_time,
resolve_bucket_metadata_handle, resolve_bucket_monitor_handle, resolve_daily_tier_stats, resolve_deployment_id, resolve_bucket_metadata_handle, resolve_bucket_monitor_handle, resolve_daily_tier_stats, resolve_deployment_id,
resolve_endpoints_handle, resolve_iam_handle, resolve_kms_runtime_service_manager, resolve_notification_system, resolve_endpoints_handle, resolve_iam_handle, resolve_kms_runtime_service_manager, resolve_notification_system,
resolve_object_store_handle, resolve_object_store_handle_for_context, resolve_oidc_handle, resolve_notification_system_for_context, resolve_object_store_handle, resolve_object_store_handle_for_context,
resolve_or_init_kms_runtime_service_manager, resolve_outbound_tls_generation, resolve_outbound_tls_state, resolve_oidc_handle, resolve_or_init_kms_runtime_service_manager, resolve_outbound_tls_generation,
resolve_ready_iam_handle, resolve_region, resolve_replication_pool_handle, resolve_replication_stats_handle, resolve_outbound_tls_state, resolve_ready_iam_handle, resolve_region, resolve_replication_pool_handle,
resolve_runtime_port, resolve_scanner_metrics_report, resolve_server_config, resolve_tier_config_handle, resolve_replication_stats_handle, resolve_runtime_port, resolve_scanner_metrics_report, resolve_server_config,
resolve_token_signing_key, resolve_server_config_for_context, resolve_tier_config_handle, resolve_token_signing_key,
}; };
use std::sync::Arc; use std::sync::Arc;
+50 -24
View File
@@ -13,8 +13,8 @@
// limitations under the License. // limitations under the License.
use crate::admin::runtime_sources::{ use crate::admin::runtime_sources::{
AppContext, current_app_context, publish_server_config, publish_storage_class_config, resolve_notification_system, AppContext, current_app_context, publish_server_config, publish_storage_class_config,
resolve_object_store_handle, resolve_object_store_handle_for_context, resolve_notification_system_for_context, resolve_object_store_handle_for_context,
}; };
use crate::admin::storage_api::config::{STORAGE_CLASS_SUB_SYS, read_admin_config_without_migrate, storageclass}; use crate::admin::storage_api::config::{STORAGE_CLASS_SUB_SYS, read_admin_config_without_migrate, storageclass};
use crate::admin::storage_api::contract::admin::StorageAdminApi; use crate::admin::storage_api::contract::admin::StorageAdminApi;
@@ -71,10 +71,8 @@ fn resolve_runtime_config_store_for_context(context: Option<&AppContext>) -> S3R
resolve_object_store_handle_for_context(context).ok_or_else(|| internal_error("storage layer not initialized")) resolve_object_store_handle_for_context(context).ok_or_else(|| internal_error("storage layer not initialized"))
} }
async fn apply_storage_class_runtime_config(config: &ServerConfig) -> S3Result<()> { async fn apply_storage_class_runtime_config_for_context(context: Option<&AppContext>, config: &ServerConfig) -> S3Result<()> {
let Some(store) = resolve_object_store_handle() else { let store = resolve_runtime_config_store_for_context(context)?;
return Err(internal_error("storage layer not initialized"));
};
let kvs = config.get_value(STORAGE_CLASS_SUB_SYS, DEFAULT_DELIMITER).unwrap_or_default(); let kvs = config.get_value(STORAGE_CLASS_SUB_SYS, DEFAULT_DELIMITER).unwrap_or_default();
let set_drive_count = StorageAdminApi::set_drive_counts(store.as_ref()) let set_drive_count = StorageAdminApi::set_drive_counts(store.as_ref())
@@ -96,10 +94,8 @@ fn validate_storage_class_kvs(kvs: &KVS, set_drive_counts: &[usize]) -> S3Result
Ok(()) Ok(())
} }
async fn validate_storage_class_config(config: &ServerConfig) -> S3Result<()> { async fn validate_storage_class_config_for_context(context: Option<&AppContext>, config: &ServerConfig) -> S3Result<()> {
let Some(store) = resolve_object_store_handle() else { let store = resolve_runtime_config_store_for_context(context)?;
return Err(internal_error("storage layer not initialized"));
};
let kvs = config.get_value(STORAGE_CLASS_SUB_SYS, DEFAULT_DELIMITER).unwrap_or_default(); let kvs = config.get_value(STORAGE_CLASS_SUB_SYS, DEFAULT_DELIMITER).unwrap_or_default();
let set_drive_counts = StorageAdminApi::set_drive_counts(store.as_ref()); let set_drive_counts = StorageAdminApi::set_drive_counts(store.as_ref());
@@ -252,9 +248,13 @@ fn validate_identity_openid_config(config: &ServerConfig) -> S3Result<()> {
Ok(()) Ok(())
} }
pub async fn validate_server_config(config: &ServerConfig, sub_system: Option<&str>) -> S3Result<()> { pub async fn validate_server_config_for_context(
context: Option<&AppContext>,
config: &ServerConfig,
sub_system: Option<&str>,
) -> S3Result<()> {
match sub_system { match sub_system {
Some(STORAGE_CLASS_SUB_SYS) => validate_storage_class_config(config).await, Some(STORAGE_CLASS_SUB_SYS) => validate_storage_class_config_for_context(context, config).await,
Some(NOTIFY_WEBHOOK_SUB_SYS) => validate_notify_subsystem_config(config, NOTIFY_WEBHOOK_SUB_SYS), Some(NOTIFY_WEBHOOK_SUB_SYS) => validate_notify_subsystem_config(config, NOTIFY_WEBHOOK_SUB_SYS),
Some(NOTIFY_MQTT_SUB_SYS) => validate_notify_subsystem_config(config, NOTIFY_MQTT_SUB_SYS), Some(NOTIFY_MQTT_SUB_SYS) => validate_notify_subsystem_config(config, NOTIFY_MQTT_SUB_SYS),
Some(AUDIT_WEBHOOK_SUB_SYS) => validate_audit_subsystem_config(config, AUDIT_WEBHOOK_SUB_SYS), Some(AUDIT_WEBHOOK_SUB_SYS) => validate_audit_subsystem_config(config, AUDIT_WEBHOOK_SUB_SYS),
@@ -264,7 +264,7 @@ pub async fn validate_server_config(config: &ServerConfig, sub_system: Option<&s
.map_err(|err| invalid_request(format!("invalid scanner config: {err}"))), .map_err(|err| invalid_request(format!("invalid scanner config: {err}"))),
Some(_) => Ok(()), Some(_) => Ok(()),
None => { None => {
validate_storage_class_config(config).await?; validate_storage_class_config_for_context(context, config).await?;
validate_notify_subsystem_config(config, NOTIFY_WEBHOOK_SUB_SYS)?; validate_notify_subsystem_config(config, NOTIFY_WEBHOOK_SUB_SYS)?;
validate_notify_subsystem_config(config, NOTIFY_MQTT_SUB_SYS)?; validate_notify_subsystem_config(config, NOTIFY_MQTT_SUB_SYS)?;
validate_audit_subsystem_config(config, AUDIT_WEBHOOK_SUB_SYS)?; validate_audit_subsystem_config(config, AUDIT_WEBHOOK_SUB_SYS)?;
@@ -277,13 +277,22 @@ pub async fn validate_server_config(config: &ServerConfig, sub_system: Option<&s
} }
} }
pub async fn apply_dynamic_config_for_subsystem(config: &ServerConfig, sub_system: &str) -> S3Result<bool> { pub async fn validate_server_config(config: &ServerConfig, sub_system: Option<&str>) -> S3Result<()> {
let context = current_app_context();
validate_server_config_for_context(context.as_deref(), config, sub_system).await
}
pub async fn apply_dynamic_config_for_subsystem_for_context(
context: Option<&AppContext>,
config: &ServerConfig,
sub_system: &str,
) -> S3Result<bool> {
if dynamic_config_reload_plan(sub_system).is_none() { if dynamic_config_reload_plan(sub_system).is_none() {
return Ok(false); return Ok(false);
} }
match sub_system { match sub_system {
STORAGE_CLASS_SUB_SYS => apply_storage_class_runtime_config(config).await?, STORAGE_CLASS_SUB_SYS => apply_storage_class_runtime_config_for_context(context, config).await?,
AUDIT_WEBHOOK_SUB_SYS | AUDIT_MQTT_SUB_SYS => reload_audit_config(config.clone()) AUDIT_WEBHOOK_SUB_SYS | AUDIT_MQTT_SUB_SYS => reload_audit_config(config.clone())
.await .await
.map_err(|err| internal_error(format!("failed to reload audit config: {err}")))?, .map_err(|err| internal_error(format!("failed to reload audit config: {err}")))?,
@@ -297,6 +306,11 @@ pub async fn apply_dynamic_config_for_subsystem(config: &ServerConfig, sub_syste
Ok(true) Ok(true)
} }
pub async fn apply_dynamic_config_for_subsystem(config: &ServerConfig, sub_system: &str) -> S3Result<bool> {
let context = current_app_context();
apply_dynamic_config_for_subsystem_for_context(context.as_deref(), config, sub_system).await
}
pub async fn reload_dynamic_config_runtime_state_for_context(context: Option<&AppContext>, sub_system: &str) -> S3Result<()> { pub async fn reload_dynamic_config_runtime_state_for_context(context: Option<&AppContext>, sub_system: &str) -> S3Result<()> {
if !is_dynamic_config_subsystem(sub_system) { if !is_dynamic_config_subsystem(sub_system) {
return Err(internal_error(format!("unsupported dynamic config subsystem: {sub_system}"))); return Err(internal_error(format!("unsupported dynamic config subsystem: {sub_system}")));
@@ -308,10 +322,12 @@ pub async fn reload_dynamic_config_runtime_state_for_context(context: Option<&Ap
warn!("peer reload_dynamic_config: failed to load server config for {sub_system}: {err}"); warn!("peer reload_dynamic_config: failed to load server config for {sub_system}: {err}");
internal_error(format!("failed to load server config: {err}")) internal_error(format!("failed to load server config: {err}"))
})?; })?;
apply_dynamic_config_for_subsystem(&config, sub_system).await.map_err(|err| { apply_dynamic_config_for_subsystem_for_context(context, &config, sub_system)
warn!("peer reload_dynamic_config: failed to apply {sub_system}: {err}"); .await
err .map_err(|err| {
})?; warn!("peer reload_dynamic_config: failed to apply {sub_system}: {err}");
err
})?;
Ok(()) Ok(())
} }
@@ -337,7 +353,7 @@ pub async fn reload_runtime_config_snapshot_for_context(context: Option<&AppCont
SCANNER_SUB_SYS, SCANNER_SUB_SYS,
HEAL_SUB_SYS, HEAL_SUB_SYS,
] { ] {
if let Err(err) = apply_dynamic_config_for_subsystem(&config, sub_system).await { if let Err(err) = apply_dynamic_config_for_subsystem_for_context(context, &config, sub_system).await {
warn!("peer reload_runtime_config_snapshot: failed to apply {sub_system}: {err}"); warn!("peer reload_runtime_config_snapshot: failed to apply {sub_system}: {err}");
} }
} }
@@ -351,12 +367,12 @@ pub async fn reload_runtime_config_snapshot() -> S3Result<()> {
reload_runtime_config_snapshot_for_context(context.as_deref()).await reload_runtime_config_snapshot_for_context(context.as_deref()).await
} }
pub async fn signal_dynamic_config_reload(sub_system: &str) { pub async fn signal_dynamic_config_reload_for_context(context: Option<&AppContext>, sub_system: &str) {
if !is_dynamic_config_subsystem(sub_system) { if !is_dynamic_config_subsystem(sub_system) {
return; return;
} }
let Some(notification_sys) = resolve_notification_system() else { let Some(notification_sys) = resolve_notification_system_for_context(context) else {
return; return;
}; };
@@ -367,8 +383,13 @@ pub async fn signal_dynamic_config_reload(sub_system: &str) {
} }
} }
pub async fn signal_config_snapshot_reload() { pub async fn signal_dynamic_config_reload(sub_system: &str) {
let Some(notification_sys) = resolve_notification_system() else { let context = current_app_context();
signal_dynamic_config_reload_for_context(context.as_deref(), sub_system).await;
}
pub async fn signal_config_snapshot_reload_for_context(context: Option<&AppContext>) {
let Some(notification_sys) = resolve_notification_system_for_context(context) else {
return; return;
}; };
@@ -379,6 +400,11 @@ pub async fn signal_config_snapshot_reload() {
} }
} }
pub async fn signal_config_snapshot_reload() {
let context = current_app_context();
signal_config_snapshot_reload_for_context(context.as_deref()).await;
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
+12
View File
@@ -141,6 +141,13 @@ pub fn resolve_notification_system() -> Option<&'static NotificationSys> {
resolve_notification_system_with(get_global_app_context(), || default_notification_system_interface().handle()) resolve_notification_system_with(get_global_app_context(), || default_notification_system_interface().handle())
} }
/// Resolve notification system handle using an explicit AppContext, falling back to the legacy global notification system.
pub fn resolve_notification_system_for_context(context: Option<&AppContext>) -> Option<&'static NotificationSys> {
context
.and_then(|context| context.notification_system().handle())
.or_else(|| default_notification_system_interface().handle())
}
/// Resolve endpoints using AppContext-first precedence. /// Resolve endpoints using AppContext-first precedence.
pub fn resolve_endpoints_handle() -> Option<EndpointServerPools> { pub fn resolve_endpoints_handle() -> Option<EndpointServerPools> {
resolve_endpoints_handle_with(get_global_app_context(), || default_endpoints_interface().handle()) resolve_endpoints_handle_with(get_global_app_context(), || default_endpoints_interface().handle())
@@ -248,6 +255,11 @@ pub fn resolve_server_config() -> Option<Config> {
resolve_server_config_with(get_global_app_context(), || default_server_config_interface().get()) resolve_server_config_with(get_global_app_context(), || default_server_config_interface().get())
} }
/// Resolve server config using an explicit AppContext, falling back to the legacy global server config.
pub fn resolve_server_config_for_context(context: Option<&AppContext>) -> Option<Config> {
context.map_or_else(|| default_server_config_interface().get(), |context| context.server_config().get())
}
/// Publish server config using AppContext-first precedence. /// Publish server config using AppContext-first precedence.
pub fn publish_server_config(config: Config) { pub fn publish_server_config(config: Config) {
publish_server_config_with(get_global_app_context(), config, |config| default_server_config_interface().set(config)); publish_server_config_with(get_global_app_context(), config, |config| default_server_config_interface().set(config));
+7 -5
View File
@@ -21,11 +21,13 @@ pub(crate) use context::{
resolve_buffer_config, resolve_daily_tier_stats, resolve_deployment_id, resolve_encryption_service, resolve_endpoints_handle, resolve_buffer_config, resolve_daily_tier_stats, resolve_deployment_id, resolve_encryption_service, resolve_endpoints_handle,
resolve_expiry_state_handle, resolve_iam_handle, resolve_iam_ready, resolve_internode_metrics, resolve_expiry_state_handle, resolve_iam_handle, resolve_iam_ready, resolve_internode_metrics,
resolve_kms_runtime_service_manager, resolve_local_node_name, resolve_lock_client, resolve_lock_clients_handle, resolve_kms_runtime_service_manager, resolve_local_node_name, resolve_lock_client, resolve_lock_clients_handle,
resolve_notification_system, resolve_notify_interface, resolve_notify_interface_for_context, resolve_object_store_handle, resolve_notification_system, resolve_notification_system_for_context, resolve_notify_interface,
resolve_object_store_handle_for_context, resolve_oidc_handle, resolve_or_init_kms_runtime_service_manager, resolve_notify_interface_for_context, resolve_object_store_handle, resolve_object_store_handle_for_context,
resolve_outbound_tls_generation, resolve_outbound_tls_state, resolve_performance_metrics, resolve_ready_iam_handle, resolve_oidc_handle, resolve_or_init_kms_runtime_service_manager, resolve_outbound_tls_generation,
resolve_region, resolve_replication_pool_handle, resolve_replication_stats_handle, resolve_runtime_port, resolve_s3select_db, resolve_outbound_tls_state, resolve_performance_metrics, resolve_ready_iam_handle, resolve_region,
resolve_scanner_metrics_report, resolve_server_config, resolve_tier_config_handle, resolve_token_signing_key, resolve_replication_pool_handle, resolve_replication_stats_handle, resolve_runtime_port, resolve_s3select_db,
resolve_scanner_metrics_report, resolve_server_config, resolve_server_config_for_context, resolve_tier_config_handle,
resolve_token_signing_key,
}; };
#[cfg(test)] #[cfg(test)]