fix(server): fall through compiled-out auto-detected drivers

Signed-off-by: Evan Lezar <elezar@nvidia.com>
This commit is contained in:
Evan Lezar
2026-07-24 16:57:41 +02:00
parent b6df03c09f
commit 169b826877
6 changed files with 54 additions and 26 deletions
+2 -2
View File
@@ -252,7 +252,7 @@ OpenShell collects anonymous telemetry to help improve the project for developer
Disable telemetry at runtime by setting `OPENSHELL_TELEMETRY_ENABLED=false` on the gateway deployment. OpenShell propagates this deployment setting into sandbox supervisor environments so sandbox-side telemetry collection is disabled as well.
You can also compile telemetry out entirely. Telemetry support is a default-on `telemetry` Cargo feature; building `openshell-server` with `--no-default-features` and selecting only the driver features you need produces a gateway that contains no telemetry endpoint, no telemetry HTTP client, and no emission code. The gateway requires at least one compute driver to link; for example, `cargo build --release -p openshell-server --bin openshell-gateway --no-default-features --features driver-kubernetes,driver-docker,driver-podman,driver-vm` builds a telemetry-free gateway with every driver. Build telemetry-free `openshell-sandbox` and `openshell-driver-vm` binaries with a plain `--no-default-features`. With telemetry compiled out, the gateway emits nothing and reports telemetry disabled to the sandboxes it launches.
You can also compile telemetry out entirely. Telemetry support is a default-on `telemetry` Cargo feature; building `openshell-server` with `--no-default-features` produces a gateway that contains no telemetry endpoint, no telemetry HTTP client, and no emission code. Enable the built-in driver features you need, or enable none for an extension-only gateway; for example, `cargo build --release -p openshell-server --bin openshell-gateway --no-default-features --features driver-kubernetes,driver-docker,driver-podman,driver-vm` builds a telemetry-free gateway with every built-in driver. Build telemetry-free `openshell-sandbox` and `openshell-driver-vm` binaries with a plain `--no-default-features`. With telemetry compiled out, the gateway emits nothing and reports telemetry disabled to the sandboxes it launches.
Telemetry events are limited to anonymous operational categories and counts, such as sandbox lifecycle outcomes, provider profile buckets, policy decision counts, and aggregate network activity denial categories. OpenShell telemetry does not collect sandbox names or IDs, hostnames, file paths, binary paths, prompts, credentials, provider names, model names, or user content.
@@ -269,7 +269,7 @@ cargo build --release -p openshell-server --bin openshell-gateway \
--no-default-features --features driver-kubernetes,telemetry
```
Any non-empty driver subset is a valid build, including zero built-in drivers (an extension-only gateway that reaches an out-of-tree driver over `[openshell.drivers.<name>].socket_path`). What the gateway needs at *runtime* is a `compute_drivers` entry it can serve: either a built-in this binary was built with, or an extension driver name with a matching socket_path entry. Naming a built-in that this gateway was not built with fails at startup with a message naming both the driver and the Cargo flag that re-enables it. Extension drivers reached via `--compute-driver-socket` work regardless of the feature set. See the [gateway configuration reference](docs/reference/gateway-config.mdx) for details.
Any built-in driver subset is a valid build, including zero built-in drivers (an extension-only gateway that reaches an out-of-tree driver over `[openshell.drivers.<name>].socket_path`). What the gateway needs at *runtime* is a `compute_drivers` entry it can serve: either a built-in this binary was built with, or an extension driver name with a matching socket path entry. Naming a built-in that this gateway was not built with fails at startup with a message naming the unsupported driver. Extension drivers reached via `--compute-driver-socket` work regardless of the feature set. See the [gateway configuration reference](docs/reference/gateway-config.mdx) for details.
## Notice and Disclaimer
+4 -4
View File
@@ -208,10 +208,10 @@ carries no in-tree driver code and speaks `compute_driver.proto` only to
out-of-tree drivers named in `compute_drivers` with a matching
`[openshell.drivers.<name>].socket_path`. A driver named in
`gateway.toml` that this binary was not built with fails at startup with
a message naming both the driver and the Cargo flag that re-enables it;
auto-detection skips drivers that were compiled out and falls through to
a "no suitable driver" error rather than silently picking a
different backend. Extension drivers reached via
a message naming the unsupported driver. Auto-detection skips detected
drivers that were compiled out and falls through to the next detected
compiled-in backend; if none remain, startup fails with the standard "no
suitable driver" error. Extension drivers reached via
`--compute-driver-socket` are always available regardless of driver
features — they run out-of-process and the gateway only needs to speak
`compute_driver.proto`.
+19 -10
View File
@@ -149,25 +149,32 @@ impl FromStr for ComputeDriverKind {
/// Priority order: Kubernetes → Podman → Docker.
/// VM is never auto-detected (requires explicit `--drivers vm`).
///
/// Returns the first driver where the environment check passes.
/// Returns `None` if no compatible driver is found.
pub fn detect_driver() -> Option<ComputeDriverKind> {
/// Returns every driver where the environment check passes, in priority order.
pub fn detect_drivers() -> Vec<ComputeDriverKind> {
let mut drivers = Vec::new();
// Kubernetes: check for KUBERNETES_SERVICE_HOST env var (set inside pods)
if std::env::var_os("KUBERNETES_SERVICE_HOST").is_some() {
return Some(ComputeDriverKind::Kubernetes);
drivers.push(ComputeDriverKind::Kubernetes);
}
// Podman: check for a reachable local API socket.
if is_podman_available() {
return Some(ComputeDriverKind::Podman);
drivers.push(ComputeDriverKind::Podman);
}
// Docker: check for a reachable local API socket.
if is_docker_available() {
return Some(ComputeDriverKind::Docker);
drivers.push(ComputeDriverKind::Docker);
}
None
drivers
}
/// Returns the first driver where the environment check passes.
/// Returns `None` if no compatible driver is found.
pub fn detect_driver() -> Option<ComputeDriverKind> {
detect_drivers().into_iter().next()
}
fn is_podman_available() -> bool {
@@ -982,9 +989,9 @@ mod tests {
ComputeDriverKind, Config, DEFAULT_SERVICE_ROUTING_DOMAIN, GatewayInterceptorBindingPolicy,
GatewayInterceptorConfig, GatewayInterceptorFailurePolicy, GatewayJwtConfig,
GatewayProviderProfileSourceConfig, detect_docker_socket_from_candidates, detect_driver,
detect_podman_socket_from_candidates, docker_host_unix_socket_path, docker_socket_responds,
is_unix_socket, normalize_compute_driver_name, podman_socket_candidates_from_env,
podman_socket_responds,
detect_drivers, detect_podman_socket_from_candidates, docker_host_unix_socket_path,
docker_socket_responds, is_unix_socket, normalize_compute_driver_name,
podman_socket_candidates_from_env, podman_socket_responds,
};
#[cfg(unix)]
use std::io::{Read as _, Write as _};
@@ -1401,6 +1408,8 @@ mod tests {
let result = detect_driver();
assert_eq!(result, Some(ComputeDriverKind::Kubernetes));
let detected = detect_drivers();
assert_eq!(detected.first(), Some(&ComputeDriverKind::Kubernetes));
// Restore the original env var
unsafe {
+1 -1
View File
@@ -757,7 +757,7 @@ fn normalize_compute_driver_socket_args(args: &mut RunArgs, matches: &ArgMatches
fn effective_single_driver(args: &RunArgs) -> Option<ComputeDriverKind> {
match args.drivers.as_slice() {
[] => openshell_core::config::detect_driver(),
[] => crate::first_compiled_driver(openshell_core::config::detect_drivers()),
[driver] => driver.parse().ok(),
_ => None,
}
+27 -8
View File
@@ -983,7 +983,7 @@ fn resolve_configured_compute_driver(
///
/// Third-party drivers reached via `--compute-driver-socket` are handled
/// through the remote path and are always available regardless of features.
const fn driver_compiled_in(kind: ComputeDriverKind) -> bool {
pub(crate) const fn driver_compiled_in(kind: ComputeDriverKind) -> bool {
match kind {
ComputeDriverKind::Kubernetes => cfg!(feature = "driver-kubernetes"),
ComputeDriverKind::Docker => cfg!(feature = "driver-docker"),
@@ -992,15 +992,19 @@ const fn driver_compiled_in(kind: ComputeDriverKind) -> bool {
}
}
pub(crate) fn first_compiled_driver(
detected: impl IntoIterator<Item = ComputeDriverKind>,
) -> Option<ComputeDriverKind> {
detected.into_iter().find(|kind| driver_compiled_in(*kind))
}
/// Auto-detect a driver, ignoring any that were compiled out.
///
/// Wraps [`openshell_core::config::detect_driver`]; if the environment's
/// preferred driver is not compiled in, the caller falls back to the
/// standard "no driver configured" error rather than silently starting a
/// different backend.
/// Walks [`openshell_core::config::detect_drivers`] in priority order so
/// a compiled-out driver detected first does not hide a later compiled-in
/// backend from automatic startup.
fn filtered_detect_driver() -> Option<ComputeDriverKind> {
let kind = openshell_core::config::detect_driver()?;
driver_compiled_in(kind).then_some(kind)
first_compiled_driver(openshell_core::config::detect_drivers())
}
fn builtin_compute_driver(name: &str) -> Option<ComputeDriverKind> {
@@ -1090,7 +1094,8 @@ mod tests {
use super::{
ConfiguredComputeDriver, ConnectionProtocol, MultiplexService, ServerState, TlsAcceptor,
allow_plaintext_service_http, classify_initial_bytes, configured_compute_driver,
gateway_listener_addresses, is_benign_tls_handshake_failure, serve_gateway_listener,
first_compiled_driver, gateway_listener_addresses, is_benign_tls_handshake_failure,
serve_gateway_listener,
};
use openshell_core::{
ComputeDriverKind, Config,
@@ -1661,4 +1666,18 @@ mod tests {
}
}
}
#[test]
fn first_compiled_driver_skips_detected_drivers_not_in_this_build() {
let detected = [
ComputeDriverKind::Kubernetes,
ComputeDriverKind::Podman,
ComputeDriverKind::Docker,
];
let expected = detected
.into_iter()
.find(|kind| super::driver_compiled_in(*kind));
assert_eq!(first_compiled_driver(detected), expected);
}
}
+1 -1
View File
@@ -488,6 +488,6 @@ cargo build --release -p openshell-server --bin openshell-gateway --no-default-f
This build carries no built-in driver code. `compute_drivers` must name an
extension driver with a matching `[openshell.drivers.<name>].socket_path`.
Configuring a driver in `gateway.toml` that this gateway was not built with fails at startup with a
message naming both the driver and the Cargo flag that re-enables it.
message naming the unsupported driver.
Extension drivers reached via `--compute-driver-socket` do not depend on any
driver feature and work regardless.