From 88756ea8e13eafcef46adb37baee26c2299c01db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=90=E5=B0=8F=E9=B8=AD?= Date: Mon, 10 Aug 2026 01:05:18 +0800 Subject: [PATCH] test(ecstore): decouple kubernetes endpoint tests from kernel hostname (#5900) Three Kubernetes endpoint-identity tests read the real kernel hostname and panicked when it is an IP literal (e.g. macOS without a static HostName, where DHCP/reverse-DNS sets the kernel hostname to an address like 192.168.1.11). Add a cfg(test) override seam (force_kernel_hostname_for_test, mirroring the existing force_local_host_resolution_timeout_for_test pattern) and route the production read through kernel_hostname_for_endpoint_identity() so the tests inject deterministic hostnames instead of depending on the host environment. Production behavior is unchanged. --- crates/ecstore/src/layout/endpoints.rs | 68 ++++++++++++++++++-------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/crates/ecstore/src/layout/endpoints.rs b/crates/ecstore/src/layout/endpoints.rs index 9a9b026d0..979525059 100644 --- a/crates/ecstore/src/layout/endpoints.rs +++ b/crates/ecstore/src/layout/endpoints.rs @@ -643,6 +643,48 @@ fn local_host_resolution_timeout_forced(host: &Host<&str>) -> bool { .contains(&host) } +#[cfg(test)] +static FORCED_KERNEL_HOSTNAME: LazyLock>> = LazyLock::new(|| Mutex::new(None)); + +#[cfg(test)] +struct KernelHostnameOverrideGuard; + +#[cfg(test)] +impl Drop for KernelHostnameOverrideGuard { + fn drop(&mut self) { + *FORCED_KERNEL_HOSTNAME + .lock() + .expect("kernel-hostname test override mutex poisoned") = None; + } +} + +/// Overrides the kernel hostname seen by Kubernetes endpoint-identity +/// inference so tests stay deterministic on hosts whose kernel hostname is +/// not a DNS name (e.g. macOS with a DHCP-assigned IP-literal hostname). +#[cfg(test)] +fn force_kernel_hostname_for_test(hostname: &str) -> KernelHostnameOverrideGuard { + *FORCED_KERNEL_HOSTNAME + .lock() + .expect("kernel-hostname test override mutex poisoned") = Some(hostname.to_string()); + KernelHostnameOverrideGuard +} + +fn kernel_hostname_for_endpoint_identity() -> Result { + #[cfg(test)] + if let Some(hostname) = FORCED_KERNEL_HOSTNAME + .lock() + .expect("kernel-hostname test override mutex poisoned") + .clone() + { + return Ok(hostname); + } + + hostname::get() + .map_err(|err| Error::other(format!("failed to read the kernel hostname for Kubernetes endpoint identity: {err}")))? + .into_string() + .map_err(|_| Error::new(ErrorKind::InvalidData, "kernel hostname is not valid UTF-8")) +} + fn endpoint_is_local_host(host: Host<&str>, port: u16, local_port: u16) -> Result { #[cfg(test)] if local_host_resolution_timeout_forced(&host) { @@ -1268,12 +1310,7 @@ impl EndpointServerPools { && std::env::var_os(ENV_KUBERNETES_SERVICE_HOST).is_some() && matches!(wait_mode.as_deref(), None | Some("") | Some("auto") | Some("orchestrated")); if infer_kubernetes_host { - let kernel_hostname = hostname::get() - .map_err(|err| { - Error::other(format!("failed to read the kernel hostname for Kubernetes endpoint identity: {err}")) - })? - .into_string() - .map_err(|_| Error::new(ErrorKind::InvalidData, "kernel hostname is not valid UTF-8"))?; + let kernel_hostname = kernel_hostname_for_endpoint_identity()?; let local_port = check_local_server_addr(server_addr)?.port(); match infer_kubernetes_local_endpoint_host(disks_layout, local_port, &kernel_hostname)? { Some(inferred_host) => local_endpoint_host = Some(inferred_host), @@ -2217,21 +2254,8 @@ mod test { #[serial] #[tokio::test] async fn create_server_endpoints_infers_kubernetes_pod_host_without_peer_dns() { - let raw_hostname = hostname::get() - .expect("kernel hostname should be available") - .into_string() - .expect("kernel hostname should be UTF-8"); - let Host::Domain(kernel_hostname) = Host::parse(raw_hostname.trim()).expect("kernel hostname should be a DNS name") - else { - panic!("kernel hostname should be a DNS name"); - }; - let kernel_hostname = - domain_without_optional_trailing_dot(&kernel_hostname).expect("kernel hostname should be canonical"); - let local_host = if kernel_hostname.contains('.') { - kernel_hostname.to_string() - } else { - format!("{kernel_hostname}.rustfs-headless.ns.svc.cluster.local") - }; + let _kernel_hostname = force_kernel_hostname_for_test("rustfs-0"); + let local_host = "rustfs-0.rustfs-headless.ns.svc.cluster.local"; async_with_vars( [ @@ -2287,6 +2311,7 @@ mod test { #[serial] #[tokio::test] async fn create_server_endpoints_bounds_kubernetes_alias_dns_fallback() { + let _kernel_hostname = force_kernel_hostname_for_test("unmatched-test-node"); let _resolution_timeout = force_local_host_resolution_timeout_for_test(&["unrelated-0.example.invalid", "unrelated-1.example.invalid"]); @@ -2319,6 +2344,7 @@ mod test { #[serial] #[tokio::test] async fn create_server_endpoints_preserves_resolvable_kubernetes_aliases() { + let _kernel_hostname = force_kernel_hostname_for_test("unmatched-test-node"); async_with_vars( [ (ENV_LOCAL_ENDPOINT_HOST, None),