mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-08 22:33:22 +00:00
244 lines
7.8 KiB
Rust
244 lines
7.8 KiB
Rust
// 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::storage::ECStore;
|
|
use crate::{
|
|
server::{ServiceStateManager, ShutdownHandle, wait_for_shutdown},
|
|
startup_iam::{IamBootstrapDisposition, publish_ready_for_iam_bootstrap},
|
|
startup_runtime_sources,
|
|
startup_services::StartupServiceRuntime,
|
|
startup_shutdown::run_startup_shutdown_sequence,
|
|
};
|
|
use rustfs_common::GlobalReadiness;
|
|
use rustfs_scanner::init_data_scanner;
|
|
use std::{
|
|
io::Result,
|
|
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
|
sync::{
|
|
Arc,
|
|
atomic::{AtomicBool, Ordering},
|
|
},
|
|
};
|
|
use tokio_util::sync::CancellationToken;
|
|
use tracing::info;
|
|
|
|
const LOG_COMPONENT_MAIN: &str = "main";
|
|
const LOG_COMPONENT_EMBEDDED: &str = "embedded";
|
|
const LOG_SUBSYSTEM_STARTUP: &str = "startup";
|
|
const LOG_SUBSYSTEM_EMBEDDED: &str = "embedded";
|
|
const EVENT_SERVER_READY: &str = "server_ready";
|
|
const EVENT_SERVER_SHUTDOWN_STATE: &str = "server_shutdown_state";
|
|
const EVENT_EMBEDDED_SERVER_STATE: &str = "embedded_server_state";
|
|
|
|
static EMBEDDED_SERVER_STARTED: AtomicBool = AtomicBool::new(false);
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub(crate) struct EmbeddedStartupAlreadyStarted;
|
|
|
|
pub(crate) struct EmbeddedStartupGuard {
|
|
global_init_started: bool,
|
|
}
|
|
|
|
impl EmbeddedStartupGuard {
|
|
pub(crate) fn new() -> Self {
|
|
Self {
|
|
global_init_started: false,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn mark_global_init_started(&mut self) -> std::result::Result<(), EmbeddedStartupAlreadyStarted> {
|
|
mark_embedded_global_init_started(&EMBEDDED_SERVER_STARTED, &mut self.global_init_started)
|
|
}
|
|
}
|
|
|
|
impl Default for EmbeddedStartupGuard {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
fn mark_embedded_global_init_started(
|
|
server_started: &AtomicBool,
|
|
global_init_started: &mut bool,
|
|
) -> std::result::Result<(), EmbeddedStartupAlreadyStarted> {
|
|
if *global_init_started {
|
|
return Ok(());
|
|
}
|
|
|
|
server_started
|
|
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
|
|
.map_err(|_| EmbeddedStartupAlreadyStarted)?;
|
|
*global_init_started = true;
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) struct StartupRuntimeLifecycle {
|
|
pub(crate) server_address: String,
|
|
pub(crate) state_manager: Arc<ServiceStateManager>,
|
|
pub(crate) s3_shutdown_tx: Option<ShutdownHandle>,
|
|
pub(crate) console_shutdown_tx: Option<ShutdownHandle>,
|
|
pub(crate) service_runtime: StartupServiceRuntime,
|
|
pub(crate) store: Arc<ECStore>,
|
|
pub(crate) shutdown_token: CancellationToken,
|
|
pub(crate) readiness: Arc<GlobalReadiness>,
|
|
}
|
|
|
|
pub(crate) async fn run_startup_runtime_lifecycle(lifecycle: StartupRuntimeLifecycle) -> Result<()> {
|
|
let StartupRuntimeLifecycle {
|
|
server_address,
|
|
state_manager,
|
|
s3_shutdown_tx,
|
|
console_shutdown_tx,
|
|
service_runtime,
|
|
store,
|
|
shutdown_token,
|
|
readiness,
|
|
} = lifecycle;
|
|
let StartupServiceRuntime {
|
|
optional_runtimes,
|
|
iam_bootstrap,
|
|
enable_scanner,
|
|
} = service_runtime;
|
|
|
|
info!(
|
|
target: "rustfs::main::run",
|
|
event = EVENT_SERVER_READY,
|
|
component = LOG_COMPONENT_MAIN,
|
|
subsystem = LOG_SUBSYSTEM_STARTUP,
|
|
version = %crate::version::get_version(),
|
|
server_address = %server_address,
|
|
started_at = %jiff::Zoned::now(),
|
|
iam_bootstrap = ?iam_bootstrap,
|
|
"RustFS server ready"
|
|
);
|
|
publish_ready_for_iam_bootstrap(iam_bootstrap, readiness.as_ref(), Some(state_manager.as_ref())).await?;
|
|
startup_runtime_sources::publish_init_time_now().await;
|
|
|
|
if enable_scanner {
|
|
init_data_scanner(shutdown_token.clone(), store).await;
|
|
}
|
|
|
|
let shutdown_signal = wait_for_shutdown().await;
|
|
run_startup_shutdown_sequence(
|
|
&state_manager,
|
|
shutdown_signal,
|
|
s3_shutdown_tx,
|
|
console_shutdown_tx,
|
|
optional_runtimes,
|
|
shutdown_token,
|
|
)
|
|
.await;
|
|
|
|
info!(
|
|
target: "rustfs::main::run",
|
|
event = EVENT_SERVER_SHUTDOWN_STATE,
|
|
component = LOG_COMPONENT_MAIN,
|
|
subsystem = LOG_SUBSYSTEM_STARTUP,
|
|
state = ?state_manager.current_state(),
|
|
result = "stopped",
|
|
"RustFS server stopped"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) async fn publish_embedded_startup_ready(
|
|
iam_bootstrap: IamBootstrapDisposition,
|
|
readiness: &GlobalReadiness,
|
|
) -> Result<()> {
|
|
publish_ready_for_iam_bootstrap(iam_bootstrap, readiness, None).await?;
|
|
startup_runtime_sources::publish_init_time_now().await;
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn embedded_endpoint_address(address: SocketAddr) -> SocketAddr {
|
|
let ip = match address.ip() {
|
|
ip @ IpAddr::V4(v4) if !v4.is_unspecified() => ip,
|
|
IpAddr::V4(_) => IpAddr::V4(Ipv4Addr::LOCALHOST),
|
|
ip @ IpAddr::V6(v6) if !v6.is_unspecified() => ip,
|
|
IpAddr::V6(_) => IpAddr::V6(Ipv6Addr::LOCALHOST),
|
|
};
|
|
|
|
SocketAddr::new(ip, address.port())
|
|
}
|
|
|
|
pub(crate) fn log_embedded_server_ready(endpoint_address: SocketAddr) {
|
|
info!(
|
|
target: "rustfs::embedded",
|
|
component = LOG_COMPONENT_EMBEDDED,
|
|
subsystem = LOG_SUBSYSTEM_EMBEDDED,
|
|
event = EVENT_EMBEDDED_SERVER_STATE,
|
|
state = "ready",
|
|
"RustFS embedded server ready at http://{}",
|
|
endpoint_address
|
|
);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::{embedded_endpoint_address, mark_embedded_global_init_started};
|
|
use std::{
|
|
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
|
sync::atomic::{AtomicBool, Ordering},
|
|
};
|
|
|
|
#[test]
|
|
fn embedded_global_init_guard_allows_local_retry_before_mark() {
|
|
let server_started = AtomicBool::new(false);
|
|
let mut global_init_started = false;
|
|
|
|
mark_embedded_global_init_started(&server_started, &mut global_init_started)
|
|
.expect("first irreversible startup should mark global init");
|
|
mark_embedded_global_init_started(&server_started, &mut global_init_started)
|
|
.expect("repeated mark in same startup should be idempotent");
|
|
|
|
assert!(global_init_started);
|
|
assert!(server_started.load(Ordering::SeqCst));
|
|
}
|
|
|
|
#[test]
|
|
fn embedded_global_init_guard_rejects_second_startup_after_mark() {
|
|
let server_started = AtomicBool::new(true);
|
|
let mut global_init_started = false;
|
|
|
|
let result = mark_embedded_global_init_started(&server_started, &mut global_init_started);
|
|
|
|
assert!(result.is_err());
|
|
assert!(!global_init_started);
|
|
}
|
|
|
|
#[test]
|
|
fn embedded_endpoint_address_rewrites_unspecified_hosts() {
|
|
assert_eq!(
|
|
embedded_endpoint_address(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 9000)),
|
|
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9000)
|
|
);
|
|
assert_eq!(
|
|
embedded_endpoint_address(SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 9001)),
|
|
SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 9001)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn embedded_endpoint_address_preserves_bound_hosts() {
|
|
assert_eq!(
|
|
embedded_endpoint_address(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 0, 2, 10)), 9000)),
|
|
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 0, 2, 10)), 9000)
|
|
);
|
|
assert_eq!(
|
|
embedded_endpoint_address(SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 9001)),
|
|
SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 9001)
|
|
);
|
|
}
|
|
}
|