mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-13 00:26:53 +00:00
fix(storage): add scoped timeout policy and startup fs guardrail (#3056)
* fix(storage): add RUSTFS_NETWORK_MOUNT_MODE for CIFS/NFS backends * style: fix cargo fmt formatting in disk_store.rs * fix(storage): add RUSTFS_NETWORK_MOUNT_MODE for CIFS/NFS backends Extend the TimeoutHealthAction introduced in #2996 to read_metadata, list_dir, and disk_info operations when RUSTFS_NETWORK_MOUNT_MODE=true. Also raises all drive operation timeouts to 60s (explicit per-operation overrides still take precedence). Closes #2790 * feat(startup): add unsupported filesystem policy guardrail * chore(deps): refresh lockfile and dependency pins * feat(ecstore): add scoped timeout health-action policy * docs(config): document drive timeout health-action policy * refactor(ecstore): cache timeout health policy per disk wrapper * fix(storage): add RUSTFS_NETWORK_MOUNT_MODE for CIFS/NFS backends (#2838) * fix(storage): add RUSTFS_NETWORK_MOUNT_MODE for CIFS/NFS backends * style: fix cargo fmt formatting in disk_store.rs * fix(storage): add RUSTFS_NETWORK_MOUNT_MODE for CIFS/NFS backends Extend the TimeoutHealthAction introduced in #2996 to read_metadata, list_dir, and disk_info operations when RUSTFS_NETWORK_MOUNT_MODE=true. Also raises all drive operation timeouts to 60s (explicit per-operation overrides still take precedence). Closes #2790 * fix(utils): map verified Linux filesystem magic values (#3051) * fix(utils): cover sha256 checksum validation (#3052) * fix(utils): cover sha256 checksum validation * docs: clarify sha256 checksum validation --------- Co-authored-by: houseme <housemecn@gmail.com> Co-authored-by: 安正超 <anzhengchao@gmail.com> * refactor(config): replace network mount mode with timeout profile preset * fix(review): align fallback defaults and extend fs-type detection * fix(review): cache timeout profile and restore probe timeout semantics * refactor(ecstore): cache timeout health policy lookup * perf(ecstore): cache active probe timeout per monitor task --------- Co-authored-by: mistik <mistiklord4@gmail.com> Co-authored-by: 安正超 <anzhengchao@gmail.com>
This commit is contained in:
@@ -50,6 +50,7 @@ use crate::app::context::{AppContext, init_global_app_context};
|
||||
use crate::config::Config;
|
||||
use crate::init::{add_bucket_notification_configuration, init_buffer_profile_system, init_kms_system};
|
||||
use crate::server::{init_event_notifier, shutdown_event_notifier, start_audit_system, start_http_server, stop_audit_system};
|
||||
use crate::startup_fs_guard::enforce_unsupported_fs_policy;
|
||||
use rustfs_common::{GlobalReadiness, SystemStage, set_global_addr};
|
||||
use rustfs_config::ENV_RUSTFS_ALLOW_INSECURE_DEFAULT_CREDENTIALS;
|
||||
use rustfs_credentials::init_global_action_credentials;
|
||||
@@ -350,6 +351,7 @@ impl RustFSServerBuilder {
|
||||
let (endpoint_pools, setup_type) = EndpointServerPools::from_volumes(server_addr_str.as_str(), config.volumes.clone())
|
||||
.await
|
||||
.map_err(|e| ServerError::Init(format!("endpoints: {e}")))?;
|
||||
enforce_unsupported_fs_policy(&endpoint_pools).map_err(|e| ServerError::Init(format!("unsupported fs guard: {e}")))?;
|
||||
|
||||
set_global_endpoints(endpoint_pools.as_ref().clone());
|
||||
update_erasure_type(setup_type).await;
|
||||
|
||||
@@ -67,6 +67,7 @@ pub mod profiling;
|
||||
#[cfg(any(feature = "ftps", feature = "webdav", feature = "sftp"))]
|
||||
pub mod protocols;
|
||||
pub mod server;
|
||||
pub mod startup_fs_guard;
|
||||
pub mod storage;
|
||||
pub mod update;
|
||||
pub mod version;
|
||||
|
||||
@@ -33,6 +33,7 @@ use rustfs::server::{
|
||||
SHUTDOWN_TIMEOUT, ServiceState, ServiceStateManager, ShutdownSignal, init_event_notifier, shutdown_event_notifier,
|
||||
start_audit_system, start_http_server, stop_audit_system, wait_for_shutdown,
|
||||
};
|
||||
use rustfs::startup_fs_guard::enforce_unsupported_fs_policy;
|
||||
use rustfs_common::{GlobalReadiness, SystemStage, set_global_addr};
|
||||
use rustfs_config::ENV_RUSTFS_ALLOW_INSECURE_DEFAULT_CREDENTIALS;
|
||||
use rustfs_credentials::init_global_action_credentials;
|
||||
@@ -301,6 +302,7 @@ async fn run(config: rustfs::config::Config) -> Result<()> {
|
||||
let (endpoint_pools, setup_type) = EndpointServerPools::from_volumes(server_address.clone().as_str(), config.volumes.clone())
|
||||
.await
|
||||
.map_err(Error::other)?;
|
||||
enforce_unsupported_fs_policy(&endpoint_pools)?;
|
||||
|
||||
set_global_endpoints(endpoint_pools.as_ref().clone());
|
||||
update_erasure_type(setup_type).await;
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
// 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 rustfs_config::{
|
||||
DEFAULT_RUSTFS_UNSUPPORTED_FS_POLICY, ENV_RUSTFS_UNSUPPORTED_FS_POLICY, RUSTFS_UNSUPPORTED_FS_POLICY_FAIL,
|
||||
RUSTFS_UNSUPPORTED_FS_POLICY_WARN,
|
||||
};
|
||||
use rustfs_ecstore::endpoints::EndpointServerPools;
|
||||
use std::collections::BTreeSet;
|
||||
use std::io::{Error, Result};
|
||||
use tracing::warn;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum UnsupportedFsPolicy {
|
||||
Warn,
|
||||
Fail,
|
||||
}
|
||||
|
||||
impl UnsupportedFsPolicy {
|
||||
fn parse(raw: &str) -> Option<Self> {
|
||||
match raw.trim().to_ascii_lowercase().as_str() {
|
||||
RUSTFS_UNSUPPORTED_FS_POLICY_WARN => Some(Self::Warn),
|
||||
RUSTFS_UNSUPPORTED_FS_POLICY_FAIL => Some(Self::Fail),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_unsupported_fs_policy() -> UnsupportedFsPolicy {
|
||||
let raw = rustfs_utils::get_env_str(ENV_RUSTFS_UNSUPPORTED_FS_POLICY, DEFAULT_RUSTFS_UNSUPPORTED_FS_POLICY);
|
||||
if let Some(policy) = UnsupportedFsPolicy::parse(&raw) {
|
||||
return policy;
|
||||
}
|
||||
|
||||
warn!(
|
||||
env = ENV_RUSTFS_UNSUPPORTED_FS_POLICY,
|
||||
value = %raw,
|
||||
default = DEFAULT_RUSTFS_UNSUPPORTED_FS_POLICY,
|
||||
"Invalid unsupported filesystem policy; falling back to default"
|
||||
);
|
||||
UnsupportedFsPolicy::parse(DEFAULT_RUSTFS_UNSUPPORTED_FS_POLICY).unwrap_or(UnsupportedFsPolicy::Warn)
|
||||
}
|
||||
|
||||
fn is_unsupported_fs_type(fs_type: &str) -> bool {
|
||||
let normalized = fs_type.trim().to_ascii_lowercase();
|
||||
matches!(
|
||||
normalized.as_str(),
|
||||
"nfs" | "cifs" | "smb2" | "fuse" | "fuseblk" | "overlayfs" | "9p" | "v9fs" | "ceph" | "glusterfs" | "gfs" | "gfs2"
|
||||
) || normalized.starts_with("fuse.")
|
||||
}
|
||||
|
||||
fn collect_local_paths(endpoint_pools: &EndpointServerPools) -> Vec<String> {
|
||||
let mut local_paths = BTreeSet::new();
|
||||
for pool in endpoint_pools.as_ref() {
|
||||
for endpoint in pool.endpoints.as_ref() {
|
||||
if endpoint.is_local {
|
||||
local_paths.insert(endpoint.get_file_path());
|
||||
}
|
||||
}
|
||||
}
|
||||
local_paths.into_iter().collect()
|
||||
}
|
||||
|
||||
pub fn enforce_unsupported_fs_policy(endpoint_pools: &EndpointServerPools) -> Result<()> {
|
||||
let local_paths = collect_local_paths(endpoint_pools);
|
||||
if local_paths.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut unsupported = Vec::new();
|
||||
for path in local_paths {
|
||||
let info = match rustfs_utils::os::get_info(&path) {
|
||||
Ok(info) => info,
|
||||
Err(err) => {
|
||||
warn!(
|
||||
path = %path,
|
||||
error = %err,
|
||||
"Failed to inspect filesystem type for startup boundary guard; skipping this path"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if is_unsupported_fs_type(&info.fstype) {
|
||||
unsupported.push((path, info.fstype));
|
||||
}
|
||||
}
|
||||
|
||||
if unsupported.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let detail = unsupported
|
||||
.iter()
|
||||
.map(|(path, fs_type)| format!("{path} ({fs_type})"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
let message = format!(
|
||||
"Unsupported filesystem type detected for RustFS local endpoints: {detail}. \
|
||||
RustFS only supports direct-attached local POSIX filesystems for production workloads."
|
||||
);
|
||||
|
||||
match get_unsupported_fs_policy() {
|
||||
UnsupportedFsPolicy::Warn => {
|
||||
warn!(
|
||||
env = ENV_RUSTFS_UNSUPPORTED_FS_POLICY,
|
||||
expected = format!("{RUSTFS_UNSUPPORTED_FS_POLICY_WARN}|{RUSTFS_UNSUPPORTED_FS_POLICY_FAIL}"),
|
||||
policy = RUSTFS_UNSUPPORTED_FS_POLICY_WARN,
|
||||
"{message}"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
UnsupportedFsPolicy::Fail => Err(Error::other(format!(
|
||||
"{message} Startup aborted by policy: {ENV_RUSTFS_UNSUPPORTED_FS_POLICY}={RUSTFS_UNSUPPORTED_FS_POLICY_FAIL}"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn unsupported_fs_type_matcher_covers_network_like_types() {
|
||||
assert!(is_unsupported_fs_type("NFS"));
|
||||
assert!(is_unsupported_fs_type("cifs"));
|
||||
assert!(is_unsupported_fs_type("smb2"));
|
||||
assert!(is_unsupported_fs_type("fuse"));
|
||||
assert!(is_unsupported_fs_type("fuse.sshfs"));
|
||||
assert!(is_unsupported_fs_type("v9fs"));
|
||||
assert!(is_unsupported_fs_type("overlayfs"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsupported_fs_type_matcher_keeps_local_posix_types_supported() {
|
||||
assert!(!is_unsupported_fs_type("XFS"));
|
||||
assert!(!is_unsupported_fs_type("EXT4"));
|
||||
assert!(!is_unsupported_fs_type("BTRFS"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn policy_parser_handles_valid_and_invalid_values() {
|
||||
assert_eq!(UnsupportedFsPolicy::parse("warn"), Some(UnsupportedFsPolicy::Warn));
|
||||
assert_eq!(UnsupportedFsPolicy::parse("FAIL"), Some(UnsupportedFsPolicy::Fail));
|
||||
assert_eq!(UnsupportedFsPolicy::parse("unknown"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn invalid_policy_falls_back_to_configured_default() {
|
||||
temp_env::with_var(ENV_RUSTFS_UNSUPPORTED_FS_POLICY, Some("invalid"), || {
|
||||
assert_eq!(
|
||||
get_unsupported_fs_policy(),
|
||||
UnsupportedFsPolicy::parse(DEFAULT_RUSTFS_UNSUPPORTED_FS_POLICY).unwrap_or(UnsupportedFsPolicy::Warn)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user