From 1d646bc3c93c96aa7a87561885f3f3a9999576d8 Mon Sep 17 00:00:00 2001 From: Eric Curtin Date: Sat, 7 Nov 2026 21:16:40 +0000 Subject: [PATCH] fix(docker): warn on unsupported Docker Engine versions Signed-off-by: Eric Curtin --- crates/openshell-driver-docker/src/lib.rs | 32 +++++++++++++++++++-- crates/openshell-driver-docker/src/tests.rs | 32 +++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/crates/openshell-driver-docker/src/lib.rs b/crates/openshell-driver-docker/src/lib.rs index aa1d7de706..e5421431f8 100644 --- a/crates/openshell-driver-docker/src/lib.rs +++ b/crates/openshell-driver-docker/src/lib.rs @@ -14,7 +14,7 @@ use bollard::models::{ ContainerCreateBody, ContainerState, ContainerStateStatusEnum, ContainerSummary, ContainerSummaryStateEnum, ContainerWaitResponse, CreateImageInfo, DeviceRequest, HealthConfig, HealthStatusEnum, HostConfig, Mount, MountTmpfsOptions, MountTypeEnum, MountVolumeOptions, - ProgressDetail, SystemInfo, VolumeCreateRequest, + ProgressDetail, SystemInfo, SystemVersion, VolumeCreateRequest, }; use bollard::query_parameters::{ CreateContainerOptionsBuilder, CreateImageOptions, DownloadFromContainerOptionsBuilder, @@ -917,9 +917,12 @@ impl DockerComputeDriver { .map_err(|err| { Error::execution(format!("failed to create Docker client: {err}")) })?; - docker.version().await.map_err(|err| { + let version = docker.version().await.map_err(|err| { Error::execution(format!("failed to query Docker daemon version: {err}")) })?; + if let Some(warning) = unsupported_docker_engine_warning(&version) { + warn!("{warning}"); + } let info = docker.info().await.map_err(|err| { Error::execution(format!("failed to query Docker daemon info: {err}")) })?; @@ -6033,6 +6036,31 @@ fn docker_cdi_gpu_inventory(info: &SystemInfo) -> CdiGpuInventory { ) } +/// Oldest Docker Engine major version the driver is supported on. +const MIN_DOCKER_ENGINE_MAJOR: u64 = 28; + +/// Warn text when the daemon is a Docker Engine older than the supported +/// minimum. Older engines fail sandbox bundle staging with opaque errors. +fn unsupported_docker_engine_warning(version: &SystemVersion) -> Option { + let engine = version + .components + .iter() + .flatten() + .find(|component| component.name == "Engine")?; + let major: u64 = engine + .version + .split(|c: char| !c.is_ascii_digit()) + .next()? + .parse() + .ok()?; + (major < MIN_DOCKER_ENGINE_MAJOR).then(|| { + format!( + "Docker Engine {} is older than the supported minimum {MIN_DOCKER_ENGINE_MAJOR}.0; sandbox creation may fail. See the support matrix in the OpenShell docs.", + engine.version + ) + }) +} + fn docker_info_reports_wsl2(info: &SystemInfo) -> bool { [ info.kernel_version.as_deref(), diff --git a/crates/openshell-driver-docker/src/tests.rs b/crates/openshell-driver-docker/src/tests.rs index 70fc34f256..c022adc923 100644 --- a/crates/openshell-driver-docker/src/tests.rs +++ b/crates/openshell-driver-docker/src/tests.rs @@ -3274,6 +3274,38 @@ fn driver_default_gpu_selection_consumes_distinct_devices_for_creates() { ); } +fn engine_version(name: &str, version: &str) -> SystemVersion { + SystemVersion { + components: Some(vec![bollard::models::SystemVersionComponents { + name: name.to_string(), + version: version.to_string(), + details: None, + }]), + ..Default::default() + } +} + +#[test] +fn warns_on_docker_engine_older_than_minimum() { + let warning = unsupported_docker_engine_warning(&engine_version("Engine", "26.1.5+dfsg1")) + .expect("old engine warns"); + assert!(warning.contains("26.1.5+dfsg1"), "{warning}"); + assert!(warning.contains("28.0"), "{warning}"); +} + +#[test] +fn no_warning_for_supported_unknown_or_other_engines() { + for version in [ + engine_version("Engine", "28.0.0"), + engine_version("Engine", "29.9.3"), + engine_version("Engine", "garbage"), + engine_version("Other Engine", "5.8.2"), + SystemVersion::default(), + ] { + assert!(unsupported_docker_engine_warning(&version).is_none()); + } +} + #[test] fn docker_info_reports_wsl2_from_kernel_version() { let info = SystemInfo {