Skip to content
Open
20 changes: 20 additions & 0 deletions docs/reference/sandbox-compute-drivers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,26 @@ For maintainer-level implementation details, refer to the [Podman driver README]

Select Podman with `compute_drivers = ["podman"]` in `[openshell.gateway]`. Configure Podman driver values such as `socket_path`, `network_name`, `supervisor_image`, `stop_timeout_secs`, `image_pull_policy`, `grpc_endpoint`, `host_gateway_ip`, `sandbox_ssh_socket_path`, `sandbox_pids_limit`, and `guest_tls_*` in `[openshell.drivers.podman]`.

### macOS Podman Socket Path

On macOS, Homebrew-installed Podman does not create the default socket path
that the driver probes (`~/.local/share/containers/podman/machine/podman.sock`).
The actual API socket lives under `/var/folders/` in a path that macOS can
rotate after a reboot.

If the gateway fails with `Podman socket not found; is podman machine running?`
while `podman machine list` shows a running machine, set the
`OPENSHELL_PODMAN_SOCKET` environment variable to the dynamic socket path:

```shell
export OPENSHELL_PODMAN_SOCKET="$(podman machine inspect --format '{{.ConnectionInfo.PodmanSocket.Path}}')"
```

Add this to your shell profile or gateway launch environment so it resolves
correctly after each reboot. Alternatively, set `socket_path` in
`[openshell.drivers.podman]` to the current path, but note that the path may
change when macOS rotates `/var/folders/`.

Podman sandboxes default to a 45-second graceful stop window before Podman escalates from `SIGTERM` to `SIGKILL`. Set `stop_timeout_secs` in gateway config, or `OPENSHELL_STOP_TIMEOUT` for the standalone driver, when a local runtime needs a different teardown window.

Stop stops the existing Podman container while retaining its named workspace
Expand Down
51 changes: 51 additions & 0 deletions e2e/rust/tests/driver_config_volume.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,9 @@ fn podman_socket_path() -> PathBuf {

#[cfg(target_os = "macos")]
{
if let Some(path) = macos_podman_machine_socket_path() {
return path;
}
let home = std::env::var_os("HOME").unwrap_or_default();
PathBuf::from(home).join(".local/share/containers/podman/machine/podman.sock")
}
Expand All @@ -559,6 +562,34 @@ fn podman_socket_path() -> PathBuf {
}
}

#[cfg(target_os = "macos")]
fn macos_podman_machine_socket_path() -> Option<PathBuf> {
let output = std::process::Command::new("podman")
.args([
"machine",
"inspect",
"--format",
"{{.ConnectionInfo.PodmanSocket.Path}}",
])
.stderr(Stdio::null())
.output()
.ok()?;
if !output.status.success() {
return None;
}
parse_macos_podman_machine_socket_path(&output.stdout)
}

#[cfg(target_os = "macos")]
fn parse_macos_podman_machine_socket_path(stdout: &[u8]) -> Option<PathBuf> {
let stdout = String::from_utf8_lossy(stdout);
let path = stdout
.lines()
.map(str::trim)
.find(|line| !line.is_empty())?;
Some(PathBuf::from(path))
}

fn unique_volume_name(driver: &str) -> String {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
Expand Down Expand Up @@ -599,3 +630,23 @@ fn github_actions_host_work_path(path: &Path) -> Option<PathBuf> {
let mapped = Path::new("/home/runner/_work").join(relative);
mapped.exists().then_some(mapped)
}

#[cfg(target_os = "macos")]
#[test]
fn parse_macos_podman_machine_socket_path_reads_first_nonempty_line() {
let parsed = parse_macos_podman_machine_socket_path(
b"\n/var/folders/aa/bb/T/podman/podman-machine-default-api.sock\n",
);
assert_eq!(
parsed,
Some(PathBuf::from(
"/var/folders/aa/bb/T/podman/podman-machine-default-api.sock"
))
);
}

#[cfg(target_os = "macos")]
#[test]
fn parse_macos_podman_machine_socket_path_rejects_empty_output() {
assert_eq!(parse_macos_podman_machine_socket_path(b" \n\t\n"), None);
}
8 changes: 4 additions & 4 deletions e2e/support/gateway-common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,11 @@ e2e_build_gateway_binaries() {
if [ -z "${OPENSHELL_GATEWAY_BIN:-}" ]; then
echo "Building openshell-gateway..."
if [ "${OPENSHELL_E2E_EXTERNAL_COMPUTE_DRIVER:-0}" = "1" ]; then
cargo build "${jobs[@]}" \
cargo build ${jobs[@]+"${jobs[@]}"} \
-p openshell-gateway --bin openshell-gateway \
--no-default-features --features telemetry
else
cargo build "${jobs[@]}" \
cargo build ${jobs[@]+"${jobs[@]}"} \
-p openshell-gateway --bin openshell-gateway
fi
else
Expand All @@ -231,7 +231,7 @@ e2e_build_gateway_binaries() {

if [ -z "${OPENSHELL_BIN:-}" ]; then
echo "Building openshell-cli..."
cargo build "${jobs[@]}" \
cargo build ${jobs[@]+"${jobs[@]}"} \
-p openshell-cli
else
echo "Using prebuilt openshell CLI at ${OPENSHELL_BIN}"
Expand Down Expand Up @@ -265,7 +265,7 @@ e2e_build_external_driver() {
else
printf -v "${output_var}" '%s' "${target_dir}/debug/${binary}"
echo "Building external ${binary}..."
cargo build "${jobs[@]}" -p "${package}" --bin "${binary}"
cargo build ${jobs[@]+"${jobs[@]}"} -p "${package}" --bin "${binary}"
fi
if [ ! -x "${!output_var}" ]; then
echo "ERROR: expected external driver binary at ${!output_var}" >&2
Expand Down
15 changes: 11 additions & 4 deletions e2e/with-podman-gateway.sh
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,11 @@ with_podman_config() {
}

podman_cmd() {
with_podman_config podman "$@"
if [ -n "${OPENSHELL_PODMAN_SOCKET:-}" ]; then
with_podman_config podman --url "unix://${OPENSHELL_PODMAN_SOCKET}" "$@"
else
with_podman_config podman "$@"
fi
}

WORKDIR_PARENT="${TMPDIR:-/tmp}"
Expand Down Expand Up @@ -234,15 +238,17 @@ default_podman_socket_path() {

ensure_podman_api_socket() {
if [ -n "${OPENSHELL_PODMAN_SOCKET:-}" ]; then
export CONTAINER_HOST="${CONTAINER_HOST:-unix://${OPENSHELL_PODMAN_SOCKET}}"
return 0
fi

local default_socket
default_socket="$(default_podman_socket_path || true)"
if [ -n "${default_socket}" ] \
&& [ -S "${default_socket}" ] \
&& podman_cmd --url "unix://${default_socket}" info >/dev/null 2>&1; then
&& with_podman_config podman --url "unix://${default_socket}" info >/dev/null 2>&1; then
export OPENSHELL_PODMAN_SOCKET="${default_socket}"
export CONTAINER_HOST="${CONTAINER_HOST:-unix://${OPENSHELL_PODMAN_SOCKET}}"
return 0
fi

Expand All @@ -266,12 +272,13 @@ ensure_podman_api_socket() {
>"${PODMAN_SERVICE_LOG}" 2>&1 &
PODMAN_SERVICE_PID=$!
export OPENSHELL_PODMAN_SOCKET="${PODMAN_SOCKET}"
export CONTAINER_HOST="${CONTAINER_HOST:-unix://${OPENSHELL_PODMAN_SOCKET}}"

local elapsed=0
local timeout=30
while [ "${elapsed}" -lt "${timeout}" ]; do
if [ -S "${PODMAN_SOCKET}" ] \
&& podman_cmd --url "unix://${PODMAN_SOCKET}" info >/dev/null 2>&1; then
&& podman_cmd info >/dev/null 2>&1; then
return 0
fi

Expand Down Expand Up @@ -375,12 +382,12 @@ if ! command -v podman >/dev/null 2>&1; then
echo "ERROR: podman CLI is required to run Podman-backed e2e tests" >&2
exit 2
fi
ensure_podman_api_socket
if ! podman_cmd info >/dev/null 2>&1; then
echo "ERROR: podman service is not reachable (podman info failed)" >&2
echo " Start it with 'podman machine start' on macOS, or the user service on Linux." >&2
exit 2
fi
ensure_podman_api_socket

e2e_build_gateway_binaries "${ROOT}" TARGET_DIR GATEWAY_BIN CLI_BIN
export OPENSHELL_BIN="${CLI_BIN}"
Expand Down
9 changes: 9 additions & 0 deletions skills/debug-openshell-cluster/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,15 @@ podman logs <container> --tail=200
openshell status
```

On macOS, confirm the gateway is using the live Podman Machine socket rather
than the legacy `~/.local/share/containers/podman/machine/podman.sock` path:

```bash
podman machine inspect --format '{{.ConnectionInfo.PodmanSocket.Path}}'
export OPENSHELL_PODMAN_SOCKET="$(podman machine inspect --format '{{.ConnectionInfo.PodmanSocket.Path}}')"
podman info --url "unix://${OPENSHELL_PODMAN_SOCKET}"
```

Common findings:

- Podman socket unavailable: start or expose the user socket.
Expand Down
2 changes: 1 addition & 1 deletion tasks/scripts/stage-prebuilt-binaries.sh
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ build_component_for_arch() {
if [[ -n "$build_rustflags" ]]; then
export RUSTFLAGS="$build_rustflags"
fi
CARGO_INCREMENTAL=0 mise x -- "${cargo_env[@]}" "${cargo_subcommand[@]}" "${args[@]}"
CARGO_INCREMENTAL=0 mise x -- ${cargo_env[@]+"${cargo_env[@]}"} "${cargo_subcommand[@]}" "${args[@]}"
)

binary_path="${ROOT}/target/${target}/release/${binary}"
Expand Down
Loading