refactor: centralize startup command bootstrap (#3471)

This commit is contained in:
安正超
2026-06-15 14:55:33 +08:00
committed by GitHub
parent 941b8afee8
commit 57260c8314
6 changed files with 232 additions and 188 deletions
+10 -8
View File
@@ -160,19 +160,21 @@ mod tests {
#[test] #[test]
fn startup_runtime_logging_does_not_dump_full_config_debug_output() { fn startup_runtime_logging_does_not_dump_full_config_debug_output() {
let path = workspace_root().join("rustfs/src/main.rs"); for relative_path in ["rustfs/src/main.rs", "rustfs/src/startup_entrypoint.rs"] {
let source = fs::read_to_string(&path).unwrap_or_else(|err| panic!("failed to read {}: {}", path.display(), err)); let path = workspace_root().join(relative_path);
assert!( let source = fs::read_to_string(&path).unwrap_or_else(|err| panic!("failed to read {}: {}", path.display(), err));
!source.contains("debug!(\"config: {:?}\", &config)"), assert!(
"found forbidden full config debug output in {}", !source.contains("debug!(\"config: {:?}\", &config)"),
path.display() "found forbidden full config debug output in {}",
); path.display()
);
}
} }
#[test] #[test]
fn startup_fatal_stderr_uses_single_formatter_for_pre_observability_failures() { fn startup_fatal_stderr_uses_single_formatter_for_pre_observability_failures() {
assert_source_contains( assert_source_contains(
"rustfs/src/main.rs", "rustfs/src/startup_entrypoint.rs",
&[ &[
"fn format_fatal_stderr_message(context: &str, error: impl std::fmt::Display) -> String", "fn format_fatal_stderr_message(context: &str, error: impl std::fmt::Display) -> String",
"fn emit_fatal_stderr(context: &str, error: impl std::fmt::Display)", "fn emit_fatal_stderr(context: &str, error: impl std::fmt::Display)",
+46 -26
View File
@@ -5,17 +5,18 @@ 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-ready-shutdown-runtime` - Branch: `overtrue/arch-startup-command-bootstrap`
- Baseline: `origin/main` at `e26eea7f3a5ef6c272a322a47f58f5a10cb2342b0` - Baseline: `origin/main` at `941b8afee8619c6a038c20aac51565a65c959ee4`
- PR type for this branch: `pure-move` - PR type for this branch: `pure-move`
- Runtime behavior changes: no external behavior change expected; server-ready - Runtime behavior changes: no external behavior change expected; the binary
publication, global init time, scanner start, shutdown wait, and shutdown entrypoint still builds the Tokio runtime, applies preflight, dispatches
ordering still run in the same relative order after runtime services. non-server commands, and starts server/bootstrap stages in the same order.
- Rust code changes: add `startup_services::run_startup_runtime_lifecycle` and - Rust code changes: add `startup_entrypoint::run_process`, move remaining
use it from binary startup. command dispatch, server bootstrap orchestration, and fatal stderr helpers
out of `main.rs`, and keep `main.rs` as the process entry shim.
- CI/script changes: none. - CI/script changes: none.
- Docs changes: record `R-018` startup runtime lifecycle progress and - Docs changes: record `R-019` startup command/bootstrap entrypoint progress
verification. and update startup timeline ownership.
## Phase 0 Tasks ## Phase 0 Tasks
@@ -721,10 +722,26 @@ 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-019` Centralize startup command and bootstrap entrypoint.
- Do: move Tokio runtime result handling, command parsing/dispatch, server
preflight error mapping, startup run orchestration, and pre-observability
fatal stderr formatting behind `startup_entrypoint::run_process`.
- Acceptance: `main.rs` only owns the global allocator declarations and calls
the startup entrypoint; `startup_entrypoint` preserves the existing
command, preflight, listen, storage, runtime-service, ready, and shutdown
order.
- Must preserve: Tokio runtime build fatal `expect`, command parse fatal
stderr context and exit code, info/TLS subcommand behavior, observability
fatal sentinel suppression, server runtime failure log fields, startup stage
ordering, readiness publication, and shutdown ownership.
- Verification: focused startup entrypoint and observability guardrail 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 collapsing remaining binary startup glue around command 1. `pure-move`: continue shrinking startup entrypoint orchestration only where a
dispatch and server bootstrap without changing fatal stderr behavior. clean module boundary remains without changing fatal stderr behavior.
2. `ci-gate`: finish `G-006` public re-export and storage trait coverage checks 2. `ci-gate`: finish `G-006` public re-export and storage trait coverage checks
before the remaining cleanup slices. before the remaining cleanup slices.
@@ -732,40 +749,43 @@ Status values: `[ ]` not started, `[~]` in progress, `[x]` complete, `[!]` block
| Expert | Status | Notes | | Expert | Status | Notes |
|---|---|---| |---|---|---|
| Quality/architecture | passed | Pure-move slice removes ready/scanner/shutdown lifecycle details from binary startup behind the existing startup services boundary. | | Quality/architecture | passed | Pure-move slice removes command dispatch and bootstrap orchestration details from binary startup behind a dedicated startup entrypoint boundary. |
| Migration preservation | passed | Server-ready publication, scanner start, shutdown wait, background/protocol/notifier/audit/profiling/HTTP shutdown order, and stopped state are preserved. | | Migration preservation | passed | Command parse stderr, info/TLS dispatch, observability fatal sentinel, startup order, readiness publication, and shutdown ownership are preserved. |
| Testing/verification | passed | Focused startup services tests, compile checks, formatting, migration/layer guards, Rust risk scan, branch freshness check, and full `make pre-commit` passed. | | Testing/verification | passed | Focused startup entrypoint and observability guardrail 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 `e26eea7f3a5ef6c272a322a47f58f5a10cb2342b0`: Passed on `941b8afee8619c6a038c20aac51565a65c959ee4`:
- `cargo test -p rustfs startup_services --no-fail-fast`: passed. - `cargo test -p rustfs startup_entrypoint --no-fail-fast`: passed.
- `cargo test -p rustfs-obs startup_ --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.
- Added-line Rust risk scan for changed Rust files: passed. - Added-line and changed-file Rust risk scan: expected fatal-boundary
- Full-file risk scan for changed Rust files: existing `main.rs` process setup `expect`/`eprintln!` only.
entries only. - Full-file risk scan for changed Rust files: expected process exit and fatal
- `make pre-commit`: passed, including nextest `6027` passed / `111` skipped stderr entries are isolated in `startup_entrypoint.rs`.
- `make pre-commit`: passed, including nextest `6038` passed / `111` skipped
and doctests. and doctests.
- `git rev-list --left-right --count HEAD...origin/main`: returned `1 0` - `git rev-list --left-right --count HEAD...origin/main`: returned `1 0`
after commit. after commit.
Notes: Notes:
- This slice centralizes startup lifecycle completion without changing startup - This slice centralizes remaining command/bootstrap entrypoint orchestration
ordering, shutdown token ownership, readiness ownership, or shutdown handle without changing startup ordering, fatal stderr behavior, readiness ownership,
ownership. shutdown token ownership, or shutdown handle ownership.
- Ready publication and scanner start remain after runtime services; shutdown - Ready publication, scanner start, and shutdown wait remain owned by
wait remains after scanner start. `startup_services`; `startup_entrypoint` only orchestrates the existing
startup sequence and process-level fatal handling.
## Handoff Notes ## Handoff Notes
- R-018 is implemented, locally verified, and current with `origin/main`. - R-019 is implemented, locally verified, and current with `origin/main`.
- 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, readiness ownership, ordering, fatal/non-fatal boundaries, shutdown ownership, readiness ownership,
and fatal stderr behavior explicit in tests and review notes. and fatal stderr behavior explicit in tests and review notes.
+19 -18
View File
@@ -7,8 +7,9 @@ new startup semantics.
## Scope ## Scope
- Baseline commit: `ae9d25879d72bc8977f08e61062c022e2142483b` - Baseline commit: `ae9d25879d72bc8977f08e61062c022e2142483b`
- Entry points covered: `rustfs/src/main.rs::main`, `async_main`, `run`, and - Entry points covered: `rustfs/src/main.rs::main`,
`handle_shutdown` `rustfs/src/startup_entrypoint.rs::{run_process, async_main, run}`, and
startup lifecycle helpers
- Related migration task: `G-007` - Related migration task: `G-007`
- Out of scope for this baseline: embedded startup, admin route-action matrix, - Out of scope for this baseline: embedded startup, admin route-action matrix,
and any runtime/lifecycle code movement and any runtime/lifecycle code movement
@@ -17,22 +18,22 @@ new startup semantics.
| Step | Source | Current action | Side effects | Fatal boundary | Ready stage | | Step | Source | Current action | Side effects | Fatal boundary | Ready stage |
|---|---|---|---|---|---| |---|---|---|---|---|---|
| `BOOT-001` | `rustfs/src/main.rs:84` | Apply external-prefix environment compatibility before the Tokio runtime is created. | Copies supported external env aliases into canonical `RUSTFS_*` process env keys and prints warnings or info to stderr. | Non-fatal; failure is logged to stderr and startup continues. | None | | `BOOT-001` | `rustfs/src/startup_entrypoint.rs` | Apply external-prefix environment compatibility during async startup before command parsing. | Copies supported external env aliases into canonical `RUSTFS_*` process env keys and prints warnings or info to stderr. | Non-fatal; failure is logged to stderr and startup continues. | None |
| `BOOT-002` | `rustfs/src/main.rs:89` | Build the Tokio runtime. | Installs runtime configuration and any runtime telemetry guard created by the runtime builder. | Fatal through `expect`; process exits if the runtime cannot be built. | None | | `BOOT-002` | `rustfs/src/main.rs` and `rustfs/src/startup_entrypoint.rs` | Call the startup entrypoint and build the Tokio runtime. | Installs runtime configuration and any runtime telemetry guard created by the runtime builder. | Fatal through `expect`; process exits if the runtime cannot be built. | None |
| `BOOT-003` | `rustfs/src/main.rs:139` | Parse CLI command and dispatch non-server commands. | `info` and `tls` commands execute and return without server startup. | Command parse exits process with code 1; TLS command errors propagate. | None | | `BOOT-003` | `rustfs/src/startup_entrypoint.rs` | Parse CLI command and dispatch non-server commands. | `info` and `tls` commands execute and return without server startup. | Command parse exits process with code 1; TLS command errors propagate. | None |
| `BOOT-004` | `rustfs/src/main.rs:167` | Initialize config snapshot and license state. | Publishes config snapshot for later readers and initializes runtime license state. | License init is non-fallible in this path. | None | | `BOOT-004` | `rustfs/src/startup_preflight.rs` | Initialize config snapshot and license state. | Publishes config snapshot for later readers and initializes runtime license state. | License init is non-fallible in this path. | None |
| `BOOT-005` | `rustfs/src/main.rs:173` | Initialize observability and store the global guard. | Initializes tracing/observability, stores the guard globally, and logs license/runtime telemetry status. | Fatal if observability init or guard publication fails. | None | | `BOOT-005` | `rustfs/src/startup_preflight.rs` | Initialize observability and store the global guard. | Initializes tracing/observability, stores the guard globally, and logs license/runtime telemetry status. | Fatal if observability init or guard publication fails. | None |
| `BOOT-006` | `rustfs/src/main.rs:208` | Log startup logo, initialize profiling, trusted proxies, rustls provider, and outbound TLS material. | Starts optional profiling tasks, trusted proxy config, default rustls provider, outbound TLS global state, TLS generation metric, and TLS metrics when enabled. | Profiling/proxy/provider setup is non-fatal; configured TLS material load is fatal on error. | None | | `BOOT-006` | `rustfs/src/startup_runtime.rs` | Log startup logo, initialize profiling, trusted proxies, rustls provider, and outbound TLS material. | Starts optional profiling tasks, trusted proxy config, default rustls provider, outbound TLS global state, TLS generation metric, and TLS metrics when enabled. | Profiling/proxy/provider setup is non-fatal; configured TLS material load is fatal on error. | None |
| `RUN-001` | `rustfs/src/main.rs:256` | Enter `run` and create `GlobalReadiness`. | Allocates the readiness tracker shared with HTTP readiness gates. | Non-fatal. | Initial readiness state is not ready | | `RUN-001` | `rustfs/src/startup_server.rs` | Enter startup run orchestration and create `GlobalReadiness`. | Allocates the readiness tracker shared with HTTP readiness gates. | Non-fatal. | Initial readiness state is not ready |
| `RUN-002` | `rustfs/src/main.rs:261` | Parse and publish the configured region. | Updates ECStore global region when configured. | Fatal if the configured region is invalid. | None | | `RUN-002` | `rustfs/src/startup_server.rs` | Parse and publish the configured region. | Updates ECStore global region when configured. | Fatal if the configured region is invalid. | None |
| `RUN-003` | `rustfs/src/main.rs:268` | Resolve server address and warn on default credentials. | Computes server port/address and emits production credential warning when defaults are used. | Address parse is fatal; default credentials warning is non-fatal. | None | | `RUN-003` | `rustfs/src/startup_server.rs` | Resolve server address and warn on default credentials. | Computes server port/address and emits production credential warning when defaults are used. | Address parse is fatal; default credentials warning is non-fatal. | None |
| `RUN-004` | `rustfs/src/main.rs:286` | Initialize global action credentials. | Publishes root/action credentials used by auth paths. | Fatal if global credentials cannot be initialized. | None | | `RUN-004` | `rustfs/src/startup_server.rs` | Initialize global action credentials. | Publishes root/action credentials used by auth paths. | Fatal if global credentials cannot be initialized. | None |
| `RUN-005` | `rustfs/src/main.rs:298` | Publish server port and address. | Updates global RustFS port and global address. | Non-fatal in this path. | None | | `RUN-005` | `rustfs/src/startup_server.rs` | Publish server port and address. | Updates global RustFS port and global address. | Non-fatal in this path. | None |
| `RUN-006` | `rustfs/src/main.rs:302` | Build endpoint pools and enforce unsupported filesystem policy. | Derives pool/set/disk layout from configured volumes and validates unsupported filesystem policy. | Fatal on endpoint build or unsupported filesystem policy error. | None | | `RUN-006` | `rustfs/src/startup_storage.rs` | Build endpoint pools and enforce unsupported filesystem policy. | Derives pool/set/disk layout from configured volumes and validates unsupported filesystem policy. | Fatal on endpoint build or unsupported filesystem policy error. | None |
| `RUN-007` | `rustfs/src/main.rs:308` | Publish endpoints and erasure type. | Updates global endpoints and erasure type. | Non-fatal in this path. | None | | `RUN-007` | `rustfs/src/startup_storage.rs` | Publish endpoints and erasure type. | Updates global endpoints and erasure type. | Non-fatal in this path. | None |
| `RUN-008` | `rustfs/src/main.rs:311` | Initialize local disks, prewarm local disk id map, and initialize lock clients. | Opens local disk state, primes disk id lookup, and creates global lock clients. | Local disk init is fatal; prewarm and lock-client setup are non-fatal in this path. | None | | `RUN-008` | `rustfs/src/startup_storage.rs` | Initialize local disks, prewarm local disk id map, and initialize lock clients. | Opens local disk state, primes disk id lookup, and creates global lock clients. | Local disk init is fatal; prewarm and lock-client setup are non-fatal in this path. | None |
| `RUN-009` | `rustfs/src/main.rs:350` | Initialize capacity management and service state manager. | Starts capacity management and moves service state to `Starting`. | Non-fatal in this path. | None | | `RUN-009` | `rustfs/src/startup_server.rs` | Initialize capacity management and service state manager. | Starts capacity management and moves service state to `Starting`. | Non-fatal in this path. | None |
| `RUN-010` | `rustfs/src/main.rs:356` | Start S3 HTTP listener and optional console listener before storage is ready. | Starts HTTP servers with readiness gates; console listener starts only when enabled and configured. | Fatal if a configured listener cannot start. | Requests remain gated until full readiness except probe/admin/console/rpc/tonic/table-catalog exempt paths | | `RUN-010` | `rustfs/src/startup_server.rs` | Start S3 HTTP listener and optional console listener before storage is ready. | Starts HTTP servers with readiness gates; console listener starts only when enabled and configured. | Fatal if a configured listener cannot start. | Requests remain gated until full readiness except probe/admin/console/rpc/tonic/table-catalog exempt paths |
| `RUN-011` | `rustfs/src/startup_storage.rs` | Create cancellation token and initialize `ECStore`. | Creates the runtime cancellation token and storage engine. | Fatal if `ECStore::new` fails. | None | | `RUN-011` | `rustfs/src/startup_storage.rs` | Create cancellation token and initialize `ECStore`. | Creates the runtime cancellation token and storage engine. | Fatal if `ECStore::new` fails. | None |
| `RUN-012` | `rustfs/src/startup_storage.rs` | Initialize ECStore config and global config system. | Initializes ECStore config, attempts server-config migration, then retries global config init up to 15 times. | Migration attempt is non-fatal in this path; global config init becomes fatal after retries. | Marks the `GlobalReadiness` `StorageReady` stage after global config init succeeds; later runtime readiness still rechecks storage, IAM, and lock quorum before `FullReady` | | `RUN-012` | `rustfs/src/startup_storage.rs` | Initialize ECStore config and global config system. | Initializes ECStore config, attempts server-config migration, then retries global config init up to 15 times. | Migration attempt is non-fatal in this path; global config init becomes fatal after retries. | Marks the `GlobalReadiness` `StorageReady` stage after global config init succeeds; later runtime readiness still rechecks storage, IAM, and lock quorum before `FullReady` |
| `RUN-013` | `rustfs/src/startup_storage.rs` and `rustfs/src/startup_services.rs` | Start replication and KMS systems. | Starts background replication pool, then initializes KMS from startup services. | Replication init is non-fatal in this path; KMS init is fatal on error. | `StorageReady` stage is already marked; dynamic runtime storage readiness is still checked before `FullReady` | | `RUN-013` | `rustfs/src/startup_storage.rs` and `rustfs/src/startup_services.rs` | Start replication and KMS systems. | Starts background replication pool, then initializes KMS from startup services. | Replication init is non-fatal in this path; KMS init is fatal on error. | `StorageReady` stage is already marked; dynamic runtime storage readiness is still checked before `FullReady` |
+1
View File
@@ -67,6 +67,7 @@ pub mod profiling;
#[cfg(any(feature = "ftps", feature = "webdav", feature = "sftp"))] #[cfg(any(feature = "ftps", feature = "webdav", feature = "sftp"))]
pub mod protocols; pub mod protocols;
pub mod server; pub mod server;
pub mod startup_entrypoint;
pub mod startup_fs_guard; pub mod startup_fs_guard;
pub mod startup_iam; pub mod startup_iam;
pub mod startup_preflight; pub mod startup_preflight;
+1 -136
View File
@@ -12,18 +12,6 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// Ensure the correct path for parse_license is imported
use rustfs::startup_preflight::{StartupServerPreflightError, bootstrap_external_prefix_compat, init_startup_server_preflight};
use rustfs::startup_server::{StartupHttpServers, StartupListenContext, init_startup_http_servers, init_startup_listen_context};
use rustfs::startup_services::{StartupRuntimeLifecycle, init_startup_runtime_services, run_startup_runtime_lifecycle};
use rustfs::startup_storage::{StartupStorageRuntime, init_startup_storage_foundation, init_startup_storage_runtime};
use std::io::{Error, Result};
use tracing::{error, instrument};
const LOG_COMPONENT_MAIN: &str = "main";
const LOG_SUBSYSTEM_STARTUP: &str = "startup";
const EVENT_SERVER_RUNTIME_FAILED: &str = "server_runtime_failed";
const OBSERVABILITY_INIT_FATAL_ALREADY_REPORTED: &str = "observability initialization failure already reported";
#[cfg(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))] #[cfg(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))]
#[global_allocator] #[global_allocator]
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
@@ -32,129 +20,6 @@ static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
#[global_allocator] #[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
fn format_fatal_stderr_message(context: &str, error: impl std::fmt::Display) -> String {
format!("[FATAL] {context}: {error}")
}
fn emit_fatal_stderr(context: &str, error: impl std::fmt::Display) {
eprintln!("{}", format_fatal_stderr_message(context, error));
}
fn main() { fn main() {
// Build Tokio runtime with optional dial9 telemetry support rustfs::startup_entrypoint::run_process();
let runtime = rustfs::server::build_tokio_runtime().expect("Failed to build Tokio runtime");
let result = runtime.block_on(async_main());
if let Err(ref e) = result {
if e.to_string() != OBSERVABILITY_INIT_FATAL_ALREADY_REPORTED {
// Tracing may not be initialized when startup fails this early.
emit_fatal_stderr("Server runtime failed", e);
}
std::process::exit(1);
}
}
async fn async_main() -> Result<()> {
let env_compat_report = bootstrap_external_prefix_compat()?;
// Parse command line arguments
let args: Vec<String> = std::env::args().collect();
let command_result = match rustfs::config::Opt::parse_command(args) {
Ok(result) => result,
Err(e) => {
emit_fatal_stderr("Command parse failed", e);
std::process::exit(1);
}
};
// Execute subcommand, or prepare config for `server` subcommand
let config = match command_result {
rustfs::config::CommandResult::Info(opts) => {
rustfs::config::execute_info(&opts);
return Ok(());
}
rustfs::config::CommandResult::Tls(opts) => return rustfs::tls::execute_tls(&opts),
rustfs::config::CommandResult::Server(config) => config,
};
match init_startup_server_preflight(&config, &env_compat_report).await {
Ok(()) => {}
Err(StartupServerPreflightError::ObservabilityInit(err)) => {
// Structured logging is unavailable until observability initializes.
emit_fatal_stderr("Observability initialization failed", err);
return Err(Error::other(OBSERVABILITY_INIT_FATAL_ALREADY_REPORTED));
}
Err(StartupServerPreflightError::Other(err)) => return Err(err),
}
// Run parameters
match run(*config).await {
Ok(_) => Ok(()),
Err(e) => {
error!(
target: "rustfs::main",
event = EVENT_SERVER_RUNTIME_FAILED,
component = LOG_COMPONENT_MAIN,
subsystem = LOG_SUBSYSTEM_STARTUP,
error = %e,
"Server runtime failed"
);
Err(e)
}
}
}
#[instrument(skip(config))]
async fn run(config: rustfs::config::Config) -> Result<()> {
let StartupListenContext {
readiness,
server_addr,
server_address,
} = init_startup_listen_context(&config).await?;
let endpoint_pools = init_startup_storage_foundation(&server_address, &config.volumes).await?;
let StartupHttpServers {
state_manager,
s3_shutdown_tx,
console_shutdown_tx,
} = init_startup_http_servers(&config, readiness.clone()).await?;
let StartupStorageRuntime {
store,
shutdown_token: ctx,
} = init_startup_storage_runtime(server_addr, &endpoint_pools, readiness.clone()).await?;
let service_runtime = init_startup_runtime_services(
&config,
endpoint_pools,
store.clone(),
ctx.clone(),
readiness.clone(),
state_manager.clone(),
)
.await?;
run_startup_runtime_lifecycle(StartupRuntimeLifecycle {
server_address,
state_manager,
s3_shutdown_tx,
console_shutdown_tx,
service_runtime,
store,
shutdown_token: ctx,
readiness,
})
.await
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fatal_stderr_message_uses_consistent_prefix_and_context() {
assert_eq!(
format_fatal_stderr_message("Observability initialization failed", "collector unavailable"),
"[FATAL] Observability initialization failed: collector unavailable"
);
}
} }
+155
View File
@@ -0,0 +1,155 @@
// 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::{
config::{CommandResult, Config, Opt},
startup_preflight::{StartupServerPreflightError, bootstrap_external_prefix_compat, init_startup_server_preflight},
startup_server::{StartupHttpServers, StartupListenContext, init_startup_http_servers, init_startup_listen_context},
startup_services::{StartupRuntimeLifecycle, init_startup_runtime_services, run_startup_runtime_lifecycle},
startup_storage::{StartupStorageRuntime, init_startup_storage_foundation, init_startup_storage_runtime},
};
use std::io::{Error, Result};
use tracing::{error, instrument};
const LOG_COMPONENT_MAIN: &str = "main";
const LOG_SUBSYSTEM_STARTUP: &str = "startup";
const EVENT_SERVER_RUNTIME_FAILED: &str = "server_runtime_failed";
const OBSERVABILITY_INIT_FATAL_ALREADY_REPORTED: &str = "observability initialization failure already reported";
pub fn run_process() {
// Building the process runtime is a startup fatal boundary.
let runtime = crate::server::build_tokio_runtime().expect("Failed to build Tokio runtime");
let result = runtime.block_on(async_main());
if let Err(ref e) = result {
if e.to_string() != OBSERVABILITY_INIT_FATAL_ALREADY_REPORTED {
// Tracing may not be initialized when startup fails this early.
emit_fatal_stderr("Server runtime failed", e);
}
std::process::exit(1);
}
}
fn format_fatal_stderr_message(context: &str, error: impl std::fmt::Display) -> String {
format!("[FATAL] {context}: {error}")
}
fn emit_fatal_stderr(context: &str, error: impl std::fmt::Display) {
// Pre-observability startup failures cannot rely on tracing.
eprintln!("{}", format_fatal_stderr_message(context, error));
}
async fn async_main() -> Result<()> {
let env_compat_report = bootstrap_external_prefix_compat()?;
// Parse command line arguments
let args: Vec<String> = std::env::args().collect();
let command_result = match Opt::parse_command(args) {
Ok(result) => result,
Err(e) => {
emit_fatal_stderr("Command parse failed", e);
std::process::exit(1);
}
};
// Execute subcommand, or prepare config for `server` subcommand
let config = match command_result {
CommandResult::Info(opts) => {
crate::config::execute_info(&opts);
return Ok(());
}
CommandResult::Tls(opts) => return crate::tls::execute_tls(&opts),
CommandResult::Server(config) => config,
};
match init_startup_server_preflight(&config, &env_compat_report).await {
Ok(()) => {}
Err(StartupServerPreflightError::ObservabilityInit(err)) => {
// Structured logging is unavailable until observability initializes.
emit_fatal_stderr("Observability initialization failed", err);
return Err(Error::other(OBSERVABILITY_INIT_FATAL_ALREADY_REPORTED));
}
Err(StartupServerPreflightError::Other(err)) => return Err(err),
}
match run(*config).await {
Ok(_) => Ok(()),
Err(e) => {
error!(
target: "rustfs::main",
event = EVENT_SERVER_RUNTIME_FAILED,
component = LOG_COMPONENT_MAIN,
subsystem = LOG_SUBSYSTEM_STARTUP,
error = %e,
"Server runtime failed"
);
Err(e)
}
}
}
#[instrument(skip(config))]
async fn run(config: Config) -> Result<()> {
let StartupListenContext {
readiness,
server_addr,
server_address,
} = init_startup_listen_context(&config).await?;
let endpoint_pools = init_startup_storage_foundation(&server_address, &config.volumes).await?;
let StartupHttpServers {
state_manager,
s3_shutdown_tx,
console_shutdown_tx,
} = init_startup_http_servers(&config, readiness.clone()).await?;
let StartupStorageRuntime {
store,
shutdown_token: ctx,
} = init_startup_storage_runtime(server_addr, &endpoint_pools, readiness.clone()).await?;
let service_runtime = init_startup_runtime_services(
&config,
endpoint_pools,
store.clone(),
ctx.clone(),
readiness.clone(),
state_manager.clone(),
)
.await?;
run_startup_runtime_lifecycle(StartupRuntimeLifecycle {
server_address,
state_manager,
s3_shutdown_tx,
console_shutdown_tx,
service_runtime,
store,
shutdown_token: ctx,
readiness,
})
.await
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fatal_stderr_message_uses_consistent_prefix_and_context() {
assert_eq!(
format_fatal_stderr_message("Observability initialization failed", "collector unavailable"),
"[FATAL] Observability initialization failed: collector unavailable"
);
}
}