mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-27 15:37:02 +00:00
refactor: centralize startup listen bootstrap (#3460)
This commit is contained in:
@@ -5,20 +5,19 @@ 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-startup-server-preflight`
|
- Branch: `overtrue/arch-startup-listen-and-server-bootstrap`
|
||||||
- Baseline: `origin/main` at `2288b1bb634bda8a06bf7c19780b0318473dbe33`
|
- Baseline: `origin/main` at `c2e0792f6ffa6d0c30c4d516204fcc252c529592`
|
||||||
- PR type for this branch: `pure-move`
|
- PR type for this branch: `pure-move`
|
||||||
- Runtime behavior changes: no external behavior change expected; external
|
- Runtime behavior changes: no external behavior change expected; server config
|
||||||
prefix compatibility reporting, config snapshot initialization, license
|
logging, readiness creation, region/global address/global port setup, action
|
||||||
initialization, observability guard initialization/storage, and startup
|
credentials, capacity/state setup, and S3/console HTTP server startup still
|
||||||
runtime foundation bootstrap still run in the same order and with the same
|
run in the same relative order.
|
||||||
fatal boundaries.
|
- Rust code changes: add
|
||||||
- Rust code changes: centralize server startup preflight bootstrap in
|
`startup_server::{init_startup_listen_context, init_startup_http_servers}`
|
||||||
`startup_preflight::init_startup_server_preflight` and use it from binary
|
and use them from binary startup.
|
||||||
startup.
|
|
||||||
- CI/script changes: none.
|
- CI/script changes: none.
|
||||||
- Docs changes: record `R-013` startup server preflight bootstrap progress and
|
- Docs changes: record `R-014` startup listen and HTTP server bootstrap progress
|
||||||
verification.
|
and verification.
|
||||||
|
|
||||||
## Phase 0 Tasks
|
## Phase 0 Tasks
|
||||||
|
|
||||||
@@ -627,6 +626,24 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
|||||||
formatting, migration guards, Rust risk scan, branch freshness check, and
|
formatting, migration guards, Rust risk scan, branch freshness check, and
|
||||||
pre-commit quality gate.
|
pre-commit quality gate.
|
||||||
|
|
||||||
|
- [x] `R-014` Centralize startup listen and HTTP server bootstrap.
|
||||||
|
- Do: move server config logging, readiness creation, region/address setup,
|
||||||
|
default credential warning, global action credentials, global port/address
|
||||||
|
publication, capacity management, service state manager setup, and
|
||||||
|
S3/console HTTP server startup behind `startup_server` helpers.
|
||||||
|
- Acceptance: endpoint/storage initialization still happens after listen
|
||||||
|
context setup and before HTTP server startup; S3 still disables console
|
||||||
|
mode; console server still starts only when enabled with a non-empty console
|
||||||
|
address; global action credential and address error mappings remain
|
||||||
|
unchanged.
|
||||||
|
- Must preserve: sanitized config/start/default credential/action credential
|
||||||
|
log events, region validation, server address/port derivation, global
|
||||||
|
port/address publication, capacity init timing, service `Starting` update,
|
||||||
|
S3/console server config shape, and shutdown handle ownership.
|
||||||
|
- Verification: focused startup server tests, binary/lib compile checks,
|
||||||
|
formatting, migration guards, Rust risk scan, branch freshness check, and
|
||||||
|
pre-commit quality gate.
|
||||||
|
|
||||||
## Next PRs
|
## Next PRs
|
||||||
|
|
||||||
1. `pure-move`: continue extracting startup boot wrappers in larger slices while
|
1. `pure-move`: continue extracting startup boot wrappers in larger slices while
|
||||||
@@ -638,39 +655,41 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
|
|||||||
|
|
||||||
| Expert | Status | Notes |
|
| Expert | Status | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Quality/architecture | passed | Pure-move slice removes server preflight details from binary startup behind the existing startup module pattern; the logging governance test was updated only to match the new fatal-stderr call shape. |
|
| Quality/architecture | passed | Pure-move slice removes listen context and HTTP server bootstrap details from binary startup behind the existing startup module pattern. |
|
||||||
| Migration preservation | passed | Env compatibility apply/report order, config snapshot, license init, observability init/store, fatal stderr sentinel, guard-store error return, and runtime foundation order are preserved. |
|
| Migration preservation | passed | Server config logging, readiness creation, region/address publication, action credentials, capacity/state setup, endpoint/storage interleaving, and shutdown handle ownership are preserved. |
|
||||||
| Testing/verification | passed | Focused startup preflight and logging-governance tests, compile checks, formatting, migration/layer guards, Rust risk scan, branch freshness check, and full `make pre-commit` passed. |
|
| Testing/verification | passed | Focused startup server tests, compile checks, formatting, migration/layer guards, Rust risk scan, branch freshness check, and full `make pre-commit` passed. |
|
||||||
|
|
||||||
## Verification Notes
|
## Verification Notes
|
||||||
|
|
||||||
Passed on `2288b1bb634bda8a06bf7c19780b0318473dbe33`:
|
Passed on `c2e0792f6ffa6d0c30c4d516204fcc252c529592`:
|
||||||
|
|
||||||
- `cargo test -p rustfs startup_preflight --no-fail-fast`: passed.
|
- `cargo test -p rustfs startup_server --no-fail-fast`: passed.
|
||||||
- `cargo test -p rustfs-obs startup_fatal_stderr_uses_single_formatter_for_pre_observability_failures --no-fail-fast`: passed.
|
|
||||||
- `cargo check -p rustfs --lib`: passed.
|
- `cargo check -p rustfs --lib`: passed.
|
||||||
- `cargo check -p rustfs --bin rustfs`: passed.
|
- `cargo check -p rustfs --bin rustfs`: passed.
|
||||||
- `cargo fmt --all --check`: passed.
|
- `cargo fmt --all --check`: passed.
|
||||||
- `git diff --check`: passed.
|
- `git diff --check`: passed.
|
||||||
- `./scripts/check_architecture_migration_rules.sh`: passed.
|
- `./scripts/check_architecture_migration_rules.sh`: passed.
|
||||||
- `./scripts/check_layer_dependencies.sh`: passed.
|
- `./scripts/check_layer_dependencies.sh`: passed.
|
||||||
- `git rev-list --left-right --count HEAD...origin/main` returned `0 0`.
|
- `git rev-list --left-right --count HEAD...origin/main` returned `1 0`
|
||||||
|
after the final rebase onto `origin/main`.
|
||||||
- Added-line Rust risk scan for changed Rust files: no matches.
|
- Added-line Rust risk scan for changed Rust files: no matches.
|
||||||
- Full-file risk scan for changed Rust files: matches are existing docs example
|
- Full-file risk scan for changed Rust files: matches are existing docs example
|
||||||
output and existing binary startup alias/stderr/expect usage.
|
output and existing binary startup alias/stderr/expect usage.
|
||||||
- `make pre-commit`: all checks passed, including nextest with 6003 passed and
|
- `make pre-commit`: all checks passed before the final clean rebase,
|
||||||
111 skipped, plus doctests.
|
including nextest with 6006 passed and 111 skipped, plus doctests.
|
||||||
|
- Post-rebase focused startup server tests, binary/lib compile checks,
|
||||||
|
formatting, migration/layer guards, and diff checks passed.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
- This slice centralizes server startup preflight bootstrap without changing
|
- This slice centralizes startup listen context and HTTP server bootstrap
|
||||||
startup ordering or fatal boundaries.
|
without changing startup ordering or shutdown handle ownership.
|
||||||
- Observability initialization failure still maps to the dedicated fatal stderr
|
- Endpoint/storage initialization remains between listen context setup and HTTP
|
||||||
sentinel in binary startup.
|
server startup.
|
||||||
|
|
||||||
## Handoff Notes
|
## Handoff Notes
|
||||||
|
|
||||||
- R-013 is complete.
|
- R-014 is complete.
|
||||||
- Next startup slices can keep using larger pure moves, but must keep startup
|
- Next startup slices can keep using larger pure moves, but must keep startup
|
||||||
ordering, fatal/non-fatal boundaries, shutdown ownership, and readiness
|
ordering, fatal/non-fatal boundaries, shutdown ownership, and readiness
|
||||||
ownership explicit in tests.
|
ownership explicit in tests.
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ pub mod startup_iam;
|
|||||||
pub mod startup_preflight;
|
pub mod startup_preflight;
|
||||||
pub mod startup_protocols;
|
pub mod startup_protocols;
|
||||||
pub mod startup_runtime;
|
pub mod startup_runtime;
|
||||||
|
pub mod startup_server;
|
||||||
pub mod startup_services;
|
pub mod startup_services;
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
pub(crate) mod table_catalog;
|
pub(crate) mod table_catalog;
|
||||||
|
|||||||
+16
-148
@@ -18,17 +18,16 @@ use rustfs::init::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use futures_util::future::join_all;
|
use futures_util::future::join_all;
|
||||||
use rustfs::capacity::capacity_integration::init_capacity_management;
|
|
||||||
use rustfs::server::{
|
use rustfs::server::{
|
||||||
ServiceState, ServiceStateManager, ShutdownHandle, ShutdownSignal, shutdown_event_notifier, start_http_server,
|
ServiceState, ServiceStateManager, ShutdownHandle, ShutdownSignal, shutdown_event_notifier, stop_audit_system,
|
||||||
stop_audit_system, wait_for_shutdown,
|
wait_for_shutdown,
|
||||||
};
|
};
|
||||||
use rustfs::startup_fs_guard::enforce_unsupported_fs_policy;
|
use rustfs::startup_fs_guard::enforce_unsupported_fs_policy;
|
||||||
use rustfs::startup_iam::{bootstrap_or_defer_iam_init, publish_ready_for_iam_bootstrap};
|
use rustfs::startup_iam::{bootstrap_or_defer_iam_init, publish_ready_for_iam_bootstrap};
|
||||||
use rustfs::startup_preflight::{StartupServerPreflightError, bootstrap_external_prefix_compat, init_startup_server_preflight};
|
use rustfs::startup_preflight::{StartupServerPreflightError, bootstrap_external_prefix_compat, init_startup_server_preflight};
|
||||||
use rustfs::startup_protocols::{ProtocolShutdownSenders, init_protocol_shutdown_senders};
|
use rustfs::startup_protocols::{ProtocolShutdownSenders, init_protocol_shutdown_senders};
|
||||||
use rustfs_common::{GlobalReadiness, SystemStage, set_global_addr};
|
use rustfs::startup_server::{StartupHttpServers, StartupListenContext, init_startup_http_servers, init_startup_listen_context};
|
||||||
use rustfs_credentials::init_global_action_credentials;
|
use rustfs_common::SystemStage;
|
||||||
use rustfs_ecstore::store::init_lock_clients;
|
use rustfs_ecstore::store::init_lock_clients;
|
||||||
use rustfs_ecstore::{
|
use rustfs_ecstore::{
|
||||||
bucket::metadata_sys::init_bucket_metadata_sys,
|
bucket::metadata_sys::init_bucket_metadata_sys,
|
||||||
@@ -36,7 +35,7 @@ use rustfs_ecstore::{
|
|||||||
bucket::replication::{get_global_replication_pool, init_background_replication},
|
bucket::replication::{get_global_replication_pool, init_background_replication},
|
||||||
config as ecconfig,
|
config as ecconfig,
|
||||||
endpoints::EndpointServerPools,
|
endpoints::EndpointServerPools,
|
||||||
global::{set_global_rustfs_port, shutdown_background_services},
|
global::shutdown_background_services,
|
||||||
set_global_endpoints,
|
set_global_endpoints,
|
||||||
store::ECStore,
|
store::ECStore,
|
||||||
store::init_local_disks,
|
store::init_local_disks,
|
||||||
@@ -51,7 +50,7 @@ use rustfs_iam::init_oidc_sys;
|
|||||||
use rustfs_obs::init_metrics_runtime;
|
use rustfs_obs::init_metrics_runtime;
|
||||||
use rustfs_scanner::init_data_scanner;
|
use rustfs_scanner::init_data_scanner;
|
||||||
use rustfs_storage_api::BucketOptions;
|
use rustfs_storage_api::BucketOptions;
|
||||||
use rustfs_utils::{get_env_bool_with_aliases, net::parse_and_resolve_address};
|
use rustfs_utils::get_env_bool_with_aliases;
|
||||||
use std::io::{Error, Result};
|
use std::io::{Error, Result};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
@@ -65,12 +64,7 @@ const LOG_COMPONENT_MAIN: &str = "main";
|
|||||||
const LOG_SUBSYSTEM_STARTUP: &str = "startup";
|
const LOG_SUBSYSTEM_STARTUP: &str = "startup";
|
||||||
const LOG_SUBSYSTEM_AUTH: &str = "auth";
|
const LOG_SUBSYSTEM_AUTH: &str = "auth";
|
||||||
const LOG_SUBSYSTEM_STORAGE: &str = "storage";
|
const LOG_SUBSYSTEM_STORAGE: &str = "storage";
|
||||||
const EVENT_DEFAULT_CREDENTIALS_DETECTED: &str = "default_credentials_detected";
|
|
||||||
const EVENT_SERVER_CONFIG_SANITIZED: &str = "server_config_sanitized";
|
|
||||||
const EVENT_SERVER_STARTING: &str = "server_starting";
|
|
||||||
const EVENT_SERVER_RUNTIME_FAILED: &str = "server_runtime_failed";
|
const EVENT_SERVER_RUNTIME_FAILED: &str = "server_runtime_failed";
|
||||||
const EVENT_ACTION_CREDENTIALS_INITIALIZED: &str = "action_credentials_initialized";
|
|
||||||
const EVENT_ACTION_CREDENTIALS_INITIALIZATION_FAILED: &str = "action_credentials_initialization_failed";
|
|
||||||
const EVENT_ENDPOINT_PARSING_STARTED: &str = "endpoint_parsing_started";
|
const EVENT_ENDPOINT_PARSING_STARTED: &str = "endpoint_parsing_started";
|
||||||
const EVENT_STARTUP_STORAGE_STAGE: &str = "startup_storage_stage";
|
const EVENT_STARTUP_STORAGE_STAGE: &str = "startup_storage_stage";
|
||||||
const EVENT_STORAGE_POOL_FORMATTING: &str = "storage_pool_formatting";
|
const EVENT_STORAGE_POOL_FORMATTING: &str = "storage_pool_formatting";
|
||||||
@@ -119,12 +113,6 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_using_default_credentials(config: &rustfs::config::Config) -> bool {
|
|
||||||
config.is_using_default_credentials()
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_CREDENTIALS_WARNING_MESSAGE: &str = "Detected default root credentials; set RUSTFS_ACCESS_KEY and RUSTFS_SECRET_KEY to non-default values for production deployments";
|
|
||||||
|
|
||||||
async fn async_main() -> Result<()> {
|
async fn async_main() -> Result<()> {
|
||||||
let env_compat_report = bootstrap_external_prefix_compat()?;
|
let env_compat_report = bootstrap_external_prefix_compat()?;
|
||||||
|
|
||||||
@@ -177,89 +165,11 @@ async fn async_main() -> Result<()> {
|
|||||||
|
|
||||||
#[instrument(skip(config))]
|
#[instrument(skip(config))]
|
||||||
async fn run(config: rustfs::config::Config) -> Result<()> {
|
async fn run(config: rustfs::config::Config) -> Result<()> {
|
||||||
debug!(
|
let StartupListenContext {
|
||||||
target: "rustfs::main::run",
|
readiness,
|
||||||
event = EVENT_SERVER_CONFIG_SANITIZED,
|
server_addr,
|
||||||
component = LOG_COMPONENT_MAIN,
|
server_address,
|
||||||
subsystem = LOG_SUBSYSTEM_STARTUP,
|
} = init_startup_listen_context(&config).await?;
|
||||||
address = %config.address,
|
|
||||||
volume_count = config.volumes.len(),
|
|
||||||
server_domain_count = config.server_domains.len(),
|
|
||||||
console_enable = config.console_enable,
|
|
||||||
console_address = %config.console_address,
|
|
||||||
tls_enabled = config.tls_path.as_deref().is_some_and(|value| !value.trim().is_empty()),
|
|
||||||
kms_enable = config.kms_enable,
|
|
||||||
kms_backend = %config.kms_backend,
|
|
||||||
region = config.region.as_deref().unwrap_or_default(),
|
|
||||||
buffer_profile = %config.buffer_profile,
|
|
||||||
"Loaded sanitized server configuration"
|
|
||||||
);
|
|
||||||
// 1. Initialize global readiness tracker
|
|
||||||
let readiness = Arc::new(GlobalReadiness::new());
|
|
||||||
|
|
||||||
if let Some(region_str) = &config.region {
|
|
||||||
region_str
|
|
||||||
.parse::<s3s::region::Region>()
|
|
||||||
.map(rustfs_ecstore::global::set_global_region)
|
|
||||||
.map_err(|e| Error::other(format!("invalid region '{}': {}", region_str, e)))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let server_addr = parse_and_resolve_address(config.address.as_str()).map_err(Error::other)?;
|
|
||||||
let server_port = server_addr.port();
|
|
||||||
let server_address = server_addr.to_string();
|
|
||||||
|
|
||||||
if is_using_default_credentials(&config) {
|
|
||||||
warn!(
|
|
||||||
target: "rustfs::main::run",
|
|
||||||
event = EVENT_DEFAULT_CREDENTIALS_DETECTED,
|
|
||||||
component = LOG_COMPONENT_MAIN,
|
|
||||||
subsystem = LOG_SUBSYSTEM_AUTH,
|
|
||||||
warning = DEFAULT_CREDENTIALS_WARNING_MESSAGE,
|
|
||||||
"{DEFAULT_CREDENTIALS_WARNING_MESSAGE}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
info!(
|
|
||||||
target: "rustfs::main::run",
|
|
||||||
event = EVENT_SERVER_STARTING,
|
|
||||||
component = LOG_COMPONENT_MAIN,
|
|
||||||
subsystem = LOG_SUBSYSTEM_STARTUP,
|
|
||||||
server_address = %server_address,
|
|
||||||
ip = %server_addr.ip(),
|
|
||||||
port = %server_port,
|
|
||||||
version = %rustfs::version::get_version(),
|
|
||||||
"Starting RustFS server"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set up AK and SK
|
|
||||||
match init_global_action_credentials(Some(config.access_key.clone()), Some(config.secret_key.clone())) {
|
|
||||||
Ok(_) => {
|
|
||||||
debug!(
|
|
||||||
target: "rustfs::main::run",
|
|
||||||
event = EVENT_ACTION_CREDENTIALS_INITIALIZED,
|
|
||||||
component = LOG_COMPONENT_MAIN,
|
|
||||||
subsystem = LOG_SUBSYSTEM_AUTH,
|
|
||||||
result = "ok",
|
|
||||||
"Initialized global action credentials"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
let msg = format!("init global action credentials failed: {e:?}");
|
|
||||||
error!(
|
|
||||||
target: "rustfs::main::run",
|
|
||||||
event = EVENT_ACTION_CREDENTIALS_INITIALIZATION_FAILED,
|
|
||||||
component = LOG_COMPONENT_MAIN,
|
|
||||||
subsystem = LOG_SUBSYSTEM_AUTH,
|
|
||||||
error = %e,
|
|
||||||
"Failed to initialize global action credentials"
|
|
||||||
);
|
|
||||||
return Err(Error::other(msg));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
set_global_rustfs_port(server_port);
|
|
||||||
|
|
||||||
set_global_addr(&config.address).await;
|
|
||||||
|
|
||||||
// For RPC
|
// For RPC
|
||||||
info!(
|
info!(
|
||||||
@@ -365,27 +275,11 @@ async fn run(config: rustfs::config::Config) -> Result<()> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Initialize capacity management system
|
let StartupHttpServers {
|
||||||
init_capacity_management().await;
|
state_manager,
|
||||||
let state_manager = Arc::new(ServiceStateManager::new());
|
s3_shutdown_tx,
|
||||||
// Update service status to Starting
|
console_shutdown_tx,
|
||||||
state_manager.update(ServiceState::Starting);
|
} = init_startup_http_servers(&config, readiness.clone()).await?;
|
||||||
|
|
||||||
let s3_shutdown_tx = {
|
|
||||||
let mut s3_config = config.clone();
|
|
||||||
s3_config.console_enable = false;
|
|
||||||
let (s3_shutdown_tx, _) = start_http_server(&s3_config, readiness.clone()).await?;
|
|
||||||
Some(s3_shutdown_tx)
|
|
||||||
};
|
|
||||||
|
|
||||||
let console_shutdown_tx = if config.console_enable && !config.console_address.is_empty() {
|
|
||||||
let mut console_config = config.clone();
|
|
||||||
console_config.address = console_config.console_address.clone();
|
|
||||||
let (console_shutdown_tx, _) = start_http_server(&console_config, readiness.clone()).await?;
|
|
||||||
Some(console_shutdown_tx)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let ctx = CancellationToken::new();
|
let ctx = CancellationToken::new();
|
||||||
|
|
||||||
@@ -909,32 +803,6 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn is_using_default_credentials_returns_true_for_default_keys() {
|
|
||||||
let mut config = rustfs::config::Config::new("127.0.0.1:9000", Vec::new());
|
|
||||||
config.console_enable = true;
|
|
||||||
config.console_address = "127.0.0.1:9001".to_string();
|
|
||||||
|
|
||||||
assert!(is_using_default_credentials(&config));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn is_using_default_credentials_returns_false_for_custom_keys() {
|
|
||||||
let mut config = rustfs::config::Config::new("127.0.0.1:9000", Vec::new());
|
|
||||||
config.access_key = "custom-access-key".to_string();
|
|
||||||
config.secret_key = "custom-secret-key".to_string();
|
|
||||||
|
|
||||||
assert!(!is_using_default_credentials(&config));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn default_credentials_messages_are_actionable_without_exposing_values() {
|
|
||||||
assert!(DEFAULT_CREDENTIALS_WARNING_MESSAGE.contains(rustfs_config::ENV_RUSTFS_ACCESS_KEY));
|
|
||||||
assert!(DEFAULT_CREDENTIALS_WARNING_MESSAGE.contains(rustfs_config::ENV_RUSTFS_SECRET_KEY));
|
|
||||||
assert!(!DEFAULT_CREDENTIALS_WARNING_MESSAGE.contains(rustfs_credentials::DEFAULT_ACCESS_KEY));
|
|
||||||
assert!(!DEFAULT_CREDENTIALS_WARNING_MESSAGE.contains(rustfs_credentials::DEFAULT_SECRET_KEY));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn background_shutdown_plan_keeps_scanner_before_ahm() {
|
fn background_shutdown_plan_keeps_scanner_before_ahm() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -0,0 +1,256 @@
|
|||||||
|
// 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::{
|
||||||
|
capacity::capacity_integration::init_capacity_management,
|
||||||
|
config::Config,
|
||||||
|
server::{ServiceState, ServiceStateManager, ShutdownHandle, start_http_server},
|
||||||
|
};
|
||||||
|
use rustfs_common::{GlobalReadiness, set_global_addr};
|
||||||
|
use rustfs_credentials::init_global_action_credentials;
|
||||||
|
use rustfs_ecstore::global::set_global_rustfs_port;
|
||||||
|
use rustfs_utils::net::parse_and_resolve_address;
|
||||||
|
use std::{
|
||||||
|
io::{Error, Result},
|
||||||
|
net::SocketAddr,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
|
const LOG_COMPONENT_MAIN: &str = "main";
|
||||||
|
const LOG_SUBSYSTEM_STARTUP: &str = "startup";
|
||||||
|
const LOG_SUBSYSTEM_AUTH: &str = "auth";
|
||||||
|
const EVENT_DEFAULT_CREDENTIALS_DETECTED: &str = "default_credentials_detected";
|
||||||
|
const EVENT_SERVER_CONFIG_SANITIZED: &str = "server_config_sanitized";
|
||||||
|
const EVENT_SERVER_STARTING: &str = "server_starting";
|
||||||
|
const EVENT_ACTION_CREDENTIALS_INITIALIZED: &str = "action_credentials_initialized";
|
||||||
|
const EVENT_ACTION_CREDENTIALS_INITIALIZATION_FAILED: &str = "action_credentials_initialization_failed";
|
||||||
|
const DEFAULT_CREDENTIALS_WARNING_MESSAGE: &str = "Detected default root credentials; set RUSTFS_ACCESS_KEY and RUSTFS_SECRET_KEY to non-default values for production deployments";
|
||||||
|
|
||||||
|
pub struct StartupListenContext {
|
||||||
|
pub readiness: Arc<GlobalReadiness>,
|
||||||
|
pub server_addr: SocketAddr,
|
||||||
|
pub server_address: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StartupHttpServers {
|
||||||
|
pub state_manager: Arc<ServiceStateManager>,
|
||||||
|
pub s3_shutdown_tx: Option<ShutdownHandle>,
|
||||||
|
pub console_shutdown_tx: Option<ShutdownHandle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn init_startup_listen_context(config: &Config) -> Result<StartupListenContext> {
|
||||||
|
log_sanitized_server_config(config);
|
||||||
|
let readiness = Arc::new(GlobalReadiness::new());
|
||||||
|
|
||||||
|
if let Some(region_str) = &config.region {
|
||||||
|
region_str
|
||||||
|
.parse::<s3s::region::Region>()
|
||||||
|
.map(rustfs_ecstore::global::set_global_region)
|
||||||
|
.map_err(|err| Error::other(format!("invalid region '{}': {}", region_str, err)))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let server_addr = parse_and_resolve_address(config.address.as_str()).map_err(Error::other)?;
|
||||||
|
let server_port = server_addr.port();
|
||||||
|
let server_address = server_addr.to_string();
|
||||||
|
|
||||||
|
if config.is_using_default_credentials() {
|
||||||
|
warn!(
|
||||||
|
target: "rustfs::main::run",
|
||||||
|
event = EVENT_DEFAULT_CREDENTIALS_DETECTED,
|
||||||
|
component = LOG_COMPONENT_MAIN,
|
||||||
|
subsystem = LOG_SUBSYSTEM_AUTH,
|
||||||
|
warning = DEFAULT_CREDENTIALS_WARNING_MESSAGE,
|
||||||
|
"{DEFAULT_CREDENTIALS_WARNING_MESSAGE}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(
|
||||||
|
target: "rustfs::main::run",
|
||||||
|
event = EVENT_SERVER_STARTING,
|
||||||
|
component = LOG_COMPONENT_MAIN,
|
||||||
|
subsystem = LOG_SUBSYSTEM_STARTUP,
|
||||||
|
server_address = %server_address,
|
||||||
|
ip = %server_addr.ip(),
|
||||||
|
port = %server_port,
|
||||||
|
version = %crate::version::get_version(),
|
||||||
|
"Starting RustFS server"
|
||||||
|
);
|
||||||
|
|
||||||
|
init_startup_action_credentials(config)?;
|
||||||
|
set_global_rustfs_port(server_port);
|
||||||
|
set_global_addr(&config.address).await;
|
||||||
|
|
||||||
|
Ok(StartupListenContext {
|
||||||
|
readiness,
|
||||||
|
server_addr,
|
||||||
|
server_address,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn init_startup_http_servers(config: &Config, readiness: Arc<GlobalReadiness>) -> Result<StartupHttpServers> {
|
||||||
|
init_capacity_management().await;
|
||||||
|
let state_manager = Arc::new(ServiceStateManager::new());
|
||||||
|
state_manager.update(ServiceState::Starting);
|
||||||
|
|
||||||
|
let s3_config = s3_http_server_config(config);
|
||||||
|
let (s3_shutdown_tx, _) = start_http_server(&s3_config, readiness.clone()).await?;
|
||||||
|
|
||||||
|
let console_shutdown_tx = match console_http_server_config(config) {
|
||||||
|
Some(console_config) => Some(start_http_server(&console_config, readiness).await?.0),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(StartupHttpServers {
|
||||||
|
state_manager,
|
||||||
|
s3_shutdown_tx: Some(s3_shutdown_tx),
|
||||||
|
console_shutdown_tx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_sanitized_server_config(config: &Config) {
|
||||||
|
debug!(
|
||||||
|
target: "rustfs::main::run",
|
||||||
|
event = EVENT_SERVER_CONFIG_SANITIZED,
|
||||||
|
component = LOG_COMPONENT_MAIN,
|
||||||
|
subsystem = LOG_SUBSYSTEM_STARTUP,
|
||||||
|
address = %config.address,
|
||||||
|
volume_count = config.volumes.len(),
|
||||||
|
server_domain_count = config.server_domains.len(),
|
||||||
|
console_enable = config.console_enable,
|
||||||
|
console_address = %config.console_address,
|
||||||
|
tls_enabled = config.tls_path.as_deref().is_some_and(|value| !value.trim().is_empty()),
|
||||||
|
kms_enable = config.kms_enable,
|
||||||
|
kms_backend = %config.kms_backend,
|
||||||
|
region = config.region.as_deref().unwrap_or_default(),
|
||||||
|
buffer_profile = %config.buffer_profile,
|
||||||
|
"Loaded sanitized server configuration"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_startup_action_credentials(config: &Config) -> Result<()> {
|
||||||
|
match init_global_action_credentials(Some(config.access_key.clone()), Some(config.secret_key.clone())) {
|
||||||
|
Ok(_) => {
|
||||||
|
debug!(
|
||||||
|
target: "rustfs::main::run",
|
||||||
|
event = EVENT_ACTION_CREDENTIALS_INITIALIZED,
|
||||||
|
component = LOG_COMPONENT_MAIN,
|
||||||
|
subsystem = LOG_SUBSYSTEM_AUTH,
|
||||||
|
result = "ok",
|
||||||
|
"Initialized global action credentials"
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
let msg = format!("init global action credentials failed: {err:?}");
|
||||||
|
error!(
|
||||||
|
target: "rustfs::main::run",
|
||||||
|
event = EVENT_ACTION_CREDENTIALS_INITIALIZATION_FAILED,
|
||||||
|
component = LOG_COMPONENT_MAIN,
|
||||||
|
subsystem = LOG_SUBSYSTEM_AUTH,
|
||||||
|
error = %err,
|
||||||
|
"Failed to initialize global action credentials"
|
||||||
|
);
|
||||||
|
Err(Error::other(msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn s3_http_server_config(config: &Config) -> Config {
|
||||||
|
let mut s3_config = config.clone();
|
||||||
|
s3_config.console_enable = false;
|
||||||
|
s3_config
|
||||||
|
}
|
||||||
|
|
||||||
|
fn console_http_server_config(config: &Config) -> Option<Config> {
|
||||||
|
if config.console_enable && !config.console_address.is_empty() {
|
||||||
|
let mut console_config = config.clone();
|
||||||
|
console_config.address = console_config.console_address.clone();
|
||||||
|
Some(console_config)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{DEFAULT_CREDENTIALS_WARNING_MESSAGE, console_http_server_config, s3_http_server_config};
|
||||||
|
use crate::config::Config;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn s3_http_server_config_disables_console_without_changing_address() {
|
||||||
|
let mut config = Config::new("127.0.0.1:9000", vec!["/tmp/rustfs-data".to_string()]);
|
||||||
|
config.console_enable = true;
|
||||||
|
config.console_address = "127.0.0.1:9001".to_string();
|
||||||
|
|
||||||
|
let s3_config = s3_http_server_config(&config);
|
||||||
|
|
||||||
|
assert!(!s3_config.console_enable);
|
||||||
|
assert_eq!(s3_config.address, "127.0.0.1:9000");
|
||||||
|
assert_eq!(s3_config.console_address, "127.0.0.1:9001");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn console_http_server_config_uses_console_address_when_enabled() {
|
||||||
|
let mut config = Config::new("127.0.0.1:9000", vec!["/tmp/rustfs-data".to_string()]);
|
||||||
|
config.console_enable = true;
|
||||||
|
config.console_address = "127.0.0.1:9001".to_string();
|
||||||
|
|
||||||
|
let Some(console_config) = console_http_server_config(&config) else {
|
||||||
|
panic!("enabled console should build config");
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(console_config.address, "127.0.0.1:9001");
|
||||||
|
assert_eq!(console_config.console_address, "127.0.0.1:9001");
|
||||||
|
assert!(console_config.console_enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn console_http_server_config_skips_disabled_or_empty_console() {
|
||||||
|
let mut config = Config::new("127.0.0.1:9000", vec!["/tmp/rustfs-data".to_string()]);
|
||||||
|
config.console_enable = false;
|
||||||
|
config.console_address = "127.0.0.1:9001".to_string();
|
||||||
|
assert!(console_http_server_config(&config).is_none());
|
||||||
|
|
||||||
|
config.console_enable = true;
|
||||||
|
config.console_address.clear();
|
||||||
|
assert!(console_http_server_config(&config).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_using_default_credentials_returns_true_for_default_keys() {
|
||||||
|
let mut config = Config::new("127.0.0.1:9000", Vec::new());
|
||||||
|
config.console_enable = true;
|
||||||
|
config.console_address = "127.0.0.1:9001".to_string();
|
||||||
|
|
||||||
|
assert!(config.is_using_default_credentials());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn is_using_default_credentials_returns_false_for_custom_keys() {
|
||||||
|
let mut config = Config::new("127.0.0.1:9000", Vec::new());
|
||||||
|
config.access_key = "custom-access-key".to_string();
|
||||||
|
config.secret_key = "custom-secret-key".to_string();
|
||||||
|
|
||||||
|
assert!(!config.is_using_default_credentials());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn default_credentials_messages_are_actionable_without_exposing_values() {
|
||||||
|
assert!(DEFAULT_CREDENTIALS_WARNING_MESSAGE.contains(rustfs_config::ENV_RUSTFS_ACCESS_KEY));
|
||||||
|
assert!(DEFAULT_CREDENTIALS_WARNING_MESSAGE.contains(rustfs_config::ENV_RUSTFS_SECRET_KEY));
|
||||||
|
assert!(!DEFAULT_CREDENTIALS_WARNING_MESSAGE.contains(rustfs_credentials::DEFAULT_ACCESS_KEY));
|
||||||
|
assert!(!DEFAULT_CREDENTIALS_WARNING_MESSAGE.contains(rustfs_credentials::DEFAULT_SECRET_KEY));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user