Skip to content

Commit 249cea1

Browse files
committed
fix(bootstrap): improve error message when Podman socket is not active
When Podman is installed but its API socket is not active, the error message previously said 'Install and start Podman' which is confusing since Podman is already installed. The actual issue is that the systemd socket unit needs to be enabled. Now the error detects whether the runtime binary is on PATH and, if so, provides specific systemctl commands instead of the generic install hint: - Podman: suggests 'sudo systemctl enable --now podman.socket' (rootful) and 'systemctl --user enable --now podman.socket' (rootless) - Docker: suggests 'sudo systemctl start docker' The generic 'Install and start' message is preserved as a fallback when the binary is not found on PATH.
1 parent 73909fb commit 249cea1

2 files changed

Lines changed: 109 additions & 6 deletions

File tree

crates/openshell-bootstrap/src/container_runtime.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ fn current_uid() -> Option<u32> {
228228
}
229229

230230
/// Check whether a binary is on PATH.
231-
fn has_binary(name: &str) -> bool {
231+
pub(crate) fn has_binary(name: &str) -> bool {
232232
std::process::Command::new(name)
233233
.arg("--version")
234234
.stdout(std::process::Stdio::null())

crates/openshell-bootstrap/src/docker.rs

Lines changed: 108 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -315,11 +315,36 @@ fn runtime_not_reachable_error(
315315
"No container runtime socket found and {} is not set.",
316316
host_env
317317
));
318-
hints.push(format!(
319-
"Install and start {}. See the support matrix \
320-
in the OpenShell docs for tested configurations.",
321-
runtime.display_name()
322-
));
318+
// Check if the binary is installed but the socket is missing —
319+
// this typically means the systemd socket unit needs activation.
320+
if crate::container_runtime::has_binary(runtime.binary_name()) {
321+
match runtime {
322+
ContainerRuntime::Podman => {
323+
hints.push(
324+
"Podman is installed but its API socket is not active.\n\n \
325+
Enable the Podman socket with:\n\n \
326+
sudo systemctl enable --now podman.socket\n\n \
327+
For rootless Podman, run as your regular user:\n\n \
328+
systemctl --user enable --now podman.socket"
329+
.to_string(),
330+
);
331+
}
332+
ContainerRuntime::Docker => {
333+
hints.push(
334+
"Docker is installed but its daemon is not running.\n\n \
335+
Start Docker with:\n\n \
336+
sudo systemctl start docker"
337+
.to_string(),
338+
);
339+
}
340+
}
341+
} else {
342+
hints.push(format!(
343+
"Install and start {}. See the support matrix \
344+
in the OpenShell docs for tested configurations.",
345+
runtime.display_name()
346+
));
347+
}
323348
} else {
324349
// Found sockets for possibly a different runtime
325350
let socket_list: Vec<String> = alt_sockets
@@ -1508,6 +1533,84 @@ mod tests {
15081533
);
15091534
}
15101535

1536+
#[test]
1537+
fn runtime_not_reachable_error_podman_socket_hint() {
1538+
// When Podman is installed but no socket is found, the error should
1539+
// suggest enabling the systemd socket unit instead of "Install Podman".
1540+
//
1541+
// The exact hint branch depends on system state:
1542+
// - Podman installed + sockets exist → "Found container runtime socket(s)" hint
1543+
// - Podman installed + no sockets → "systemctl enable --now podman.socket" hint
1544+
// - Podman not installed → "Install and start Podman" hint
1545+
//
1546+
// Clear env vars that would cause the function to take the "env var
1547+
// is set" branch instead of the "no socket" branch (other tests in
1548+
// this module may set DOCKER_HOST concurrently).
1549+
let prev_container_host = std::env::var("CONTAINER_HOST").ok();
1550+
let prev_docker_host = std::env::var("DOCKER_HOST").ok();
1551+
// SAFETY: test-only, single-threaded for this test
1552+
unsafe {
1553+
std::env::remove_var("CONTAINER_HOST");
1554+
std::env::remove_var("DOCKER_HOST");
1555+
}
1556+
1557+
let has_podman = crate::container_runtime::has_binary("podman");
1558+
let sockets = find_all_sockets();
1559+
1560+
let err = runtime_not_reachable_error(
1561+
ContainerRuntime::Podman,
1562+
"no Podman socket found at standard locations",
1563+
"Failed to connect to Podman",
1564+
);
1565+
let msg = format!("{err:?}");
1566+
1567+
// Restore env
1568+
// SAFETY: test-only, restoring previous state
1569+
unsafe {
1570+
match prev_container_host {
1571+
Some(val) => std::env::set_var("CONTAINER_HOST", val),
1572+
None => std::env::remove_var("CONTAINER_HOST"),
1573+
}
1574+
match prev_docker_host {
1575+
Some(val) => std::env::set_var("DOCKER_HOST", val),
1576+
None => std::env::remove_var("DOCKER_HOST"),
1577+
}
1578+
}
1579+
1580+
if !sockets.is_empty() {
1581+
// Sockets exist — the function takes the "found sockets" branch
1582+
assert!(
1583+
msg.contains("Found container runtime socket"),
1584+
"should list found sockets when they exist: {msg}"
1585+
);
1586+
} else if has_podman {
1587+
// No sockets, but Podman binary is on PATH — new hint path
1588+
assert!(
1589+
msg.contains("systemctl"),
1590+
"should suggest systemctl when Podman is installed but socket is missing: {msg}"
1591+
);
1592+
assert!(
1593+
msg.contains("podman.socket"),
1594+
"should mention podman.socket unit: {msg}"
1595+
);
1596+
assert!(
1597+
!msg.contains("Install and start Podman"),
1598+
"should NOT say 'Install and start' when Podman is already installed: {msg}"
1599+
);
1600+
} else {
1601+
// No sockets, no Podman binary — fallback install hint
1602+
assert!(
1603+
msg.contains("Install and start Podman"),
1604+
"should say 'Install and start' when Podman is not installed: {msg}"
1605+
);
1606+
}
1607+
// Regardless of system state, the verification hint must be present
1608+
assert!(
1609+
msg.contains("podman info"),
1610+
"should always suggest 'podman info' verification: {msg}"
1611+
);
1612+
}
1613+
15111614
#[test]
15121615
fn runtime_not_reachable_error_with_env_var() {
15131616
// Simulate: DOCKER_HOST is set but daemon unresponsive.

0 commit comments

Comments
 (0)