mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-31 09:18:28 +00:00
refactor: centralize startup command bootstrap (#3471)
This commit is contained in:
@@ -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)",
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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` |
|
||||||
|
|||||||
@@ -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
@@ -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"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user