Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
475 changes: 460 additions & 15 deletions desktop/src-tauri/src/acquire.rs

Large diffs are not rendered by default.

45 changes: 33 additions & 12 deletions desktop/src-tauri/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
//! documentation, and the three differ more than they look:
//!
//! - **macOS.** `podman machine` puts a Linux VM behind a socket. Inside that VM
//! `/var/run/docker.sock` is already a symlink to the rootless socket, so Compose's mount needs no
//! help and `ENGINE_SOCKET` stays unset. `applehv` is the default on Apple silicon as of Podman
//! `/var/run/docker.sock` is already a symlink to the socket of the mode the machine runs in
//! (rootful for the machine OpenBot creates, see `acquire`), so Compose's mount needs no help and
//! `ENGINE_SOCKET` stays unset. `applehv` is the default on Apple silicon as of Podman
//! 6.1, so no `--provider` is passed.
//! - **Linux.** Podman is native and rootless and there is no VM. `/var/run/docker.sock` is either
//! absent or, with `podman-docker` installed, a symlink to the *rootful* socket, which is not the
Expand Down Expand Up @@ -279,6 +280,9 @@ pub struct EngineStatus {
/// Somebody who already has a machine up is handed it rather than made to wait while a duplicate
/// boots beside it. Ours is preferred among running machines only so that repeat launches settle on
/// the same one.
///
/// Answers with the connection for the machine's own mode (see `acquire::connection_for`): a rootful
/// machine is reached through `<name>-root`, where its rootful engine is.
fn running_machine(preferred: &str) -> Option<String> {
let output = tool(Engine::Podman)
.args(["machine", "list", "--format", "json"])
Expand All @@ -292,7 +296,7 @@ fn running_machine(preferred: &str) -> Option<String> {
running()
.find(|machine| machine.name == preferred)
.or_else(|| running().next())
.map(|machine| machine.name.clone())
.map(|machine| crate::acquire::connection_of_machine(&machine.name))
Comment thread
dustywusty marked this conversation as resolved.
}

#[derive(Deserialize)]
Expand Down Expand Up @@ -381,17 +385,33 @@ does not reach Docker Desktop's `/usr/local/bin`. Appended, so a `docker` the pe
their PATH stays first; both the link and the directory it resolves to, as `command_at` does.
*/
pub fn path_with_docker() -> Option<OsString> {
let binary = program(Engine::Docker)?;
path_with_engines(&[Engine::Docker])
}

/// `path_with_docker`, for each of `engines` that is here, or None when none is.
///
/// The Intelligence CLI runs `podman` by name as it runs `docker`, and a Podman OpenBot installed
/// in this session is not on this process's PATH (see the PATH rule above).
pub fn path_with_engines(engines: &[Engine]) -> Option<OsString> {
let binaries: Vec<PathBuf> = engines
.iter()
.filter_map(|engine| program(*engine))
.collect();
if binaries.is_empty() {
return None;
}
let mut entries: Vec<PathBuf> = std::env::var_os("PATH")
.map(|path| std::env::split_paths(&path).collect())
.unwrap_or_default();
let resolved = std::fs::canonicalize(&binary).ok();
for directory in [binary.parent(), resolved.as_deref().and_then(Path::parent)]
.into_iter()
.flatten()
{
if !entries.iter().any(|entry| entry == directory) {
entries.push(directory.to_path_buf());
for binary in &binaries {
let resolved = std::fs::canonicalize(binary).ok();
for directory in [binary.parent(), resolved.as_deref().and_then(Path::parent)]
.into_iter()
.flatten()
{
if !entries.iter().any(|entry| entry == directory) {
entries.push(directory.to_path_buf());
}
}
}
std::env::join_paths(entries).ok()
Expand Down Expand Up @@ -529,7 +549,8 @@ pub fn rootless_socket() -> Option<PathBuf> {
#[cfg(not(target_os = "linux"))]
pub fn rootless_socket() -> Option<PathBuf> {
// macOS and Windows run the engine in a virtual machine, and inside it `/var/run/docker.sock`
// is already the rootless socket. Compose mounts that path, so there is nothing to override.
// is already the socket of the machine's own mode. Compose mounts that path, so there is
// nothing to override.
None
}

Expand Down
29 changes: 27 additions & 2 deletions desktop/src-tauri/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,17 +397,34 @@ pub fn install_engine_observed(
installed: impl FnMut(Engine, bool),
progress: &mut dyn FnMut(String),
) -> Result<EngineChosen, Problem> {
// Only one of the two runs, so they share the step row's lines one at a time.
let progress = std::cell::RefCell::new(progress);
install_engine_with(
cache,
Found::here(saved),
saved,
installed,
|| crate::docker_desktop::start(progress),
install_podman,
|| crate::docker_desktop::start(&mut **progress.borrow_mut()),
|into| {
(progress.borrow_mut())(podman_install_said(std::env::consts::OS).into());
install_podman(into)
},
place_compose,
)
}

/// The step row's line while Podman installs, naming the prompt the platform raises for it.
///
/// macOS asks for the person's password, because the package writes to `/opt/podman`. Windows
/// asks for nothing here: the MSI installs for this account alone. Linux asks through `pkexec`.
fn podman_install_said(os: &str) -> &'static str {
match os {
"macos" => "Installing Podman. Enter your Mac password when macOS asks for it.",
"linux" => "Installing Podman. Enter your password when your system asks for it.",
_ => "Installing Podman.",
}
}

/// What engines this machine has, read once so the choice below can be tested on any machine.
///
/// Nothing here asks what product is behind a `docker` command. A Docker that answers is used, a
Expand Down Expand Up @@ -1719,4 +1736,12 @@ esac
assert!(quoted.contains("Application Support"), "{quoted}");
assert!(!quoted.contains("\\\""), "{quoted}");
}

/// The prompt is named where there is one, and none is promised where there is not.
#[test]
fn the_podman_install_line_names_the_prompt_each_platform_raises() {
assert!(podman_install_said("macos").contains("password"));
assert!(podman_install_said("linux").contains("password"));
assert_eq!(podman_install_said("windows"), "Installing Podman.");
}
}
Loading
Loading