diff --git a/lib/apps/fabro-cli/tests/it/workflow/docker.rs b/lib/apps/fabro-cli/tests/it/workflow/docker.rs index a30f476e8..42f562a89 100644 --- a/lib/apps/fabro-cli/tests/it/workflow/docker.rs +++ b/lib/apps/fabro-cli/tests/it/workflow/docker.rs @@ -1,15 +1,13 @@ -//! Sandbox providers served by sandbox-driver plugin executables, for the -//! workflow scenarios. +//! The Docker provider for the workflow scenarios: an environment on +//! [`DOCKER_IMAGE`], on an isolated server. //! -//! The executables are the driver's own `sandbox-driver-host` and -//! `sandbox-driver-docker`, found on `PATH`; CI installs them at the rev the -//! workspace pins, and a developer installs them with +//! Petri serves every provider through a sandbox-driver plugin executable it +//! finds on `PATH` (`sandbox-driver-docker` here); CI installs the +//! executables at the rev the workspace pins, and a developer installs them +//! with //! `cargo install --locked --git https://github.com/lithoscomputer/sandbox-driver --rev sandbox-driver-host sandbox-driver-docker`. -//! Each runs under a kind of the scenario's choosing (`host`, -//! `docker-plugin`): the configured kind names the plugin, whatever the -//! executable declares. A scenario configured here runs against its own -//! server so the plugin settings and the environment it creates never leak -//! into the shared session server. +//! A scenario configured here runs against its own server so the environment +//! it creates never leaks into the shared session server. #![expect( clippy::disallowed_methods, @@ -32,59 +30,27 @@ use crate::cmd::support::server_endpoint; /// skipping it. const REQUIRE_ENV: &str = "FABRO_REQUIRE_SANDBOX_PLUGINS"; const DOCKER_IMAGE: &str = "buildpack-deps:noble"; +const DOCKER_PLUGIN: &str = "sandbox-driver-docker"; +/// The environment id the scenario selects with `--environment`. +pub(crate) const ENVIRONMENT: &str = "docker"; -#[derive(Clone, Copy, Debug)] -pub(crate) enum Plugin { - /// The driver's Host executable under the non-bundled `host` kind. - Host, - /// The driver's Docker executable under the non-bundled `docker-plugin` - /// kind: the same containers, reached over stdio. - Docker, -} - -impl Plugin { - fn kind(self) -> &'static str { - match self { - Self::Host => "host", - Self::Docker => "docker-plugin", - } - } - - fn executable(self) -> &'static str { - match self { - Self::Host => "sandbox-driver-host", - Self::Docker => "sandbox-driver-docker", - } - } - - /// The environment id the scenario selects with `--environment`. - fn environment(self) -> &'static str { - match self { - Self::Host => "host-plugin", - Self::Docker => "docker-plugin", - } - } -} - -/// Point `context` at an isolated server that serves `plugin` and has an -/// environment for it. Returns the environment id, or `None` when the +/// Point `context` at an isolated server with a Docker environment on +/// [`DOCKER_IMAGE`]. Returns the environment id, or `None` when the /// prerequisites are missing and the test should skip. -pub(crate) fn configure(context: &mut TestContext, plugin: Plugin) -> Option<&'static str> { +pub(crate) fn configure(context: &mut TestContext) -> Option<&'static str> { let required = std::env::var_os(REQUIRE_ENV).is_some(); - let Some(executable) = plugin_executable(plugin) else { + if plugin_executable().is_none() { assert!( !required, - "{REQUIRE_ENV} is set but the {} executable is not built", - plugin.executable() + "{REQUIRE_ENV} is set but {DOCKER_PLUGIN} is not on PATH" ); eprintln!( - "skipping: {} is not on PATH; install the sandbox-driver executables at the rev \ - Cargo.toml pins", - plugin.executable() + "skipping: {DOCKER_PLUGIN} is not on PATH; install the sandbox-driver executables at \ + the rev Cargo.toml pins" ); return None; - }; - if matches!(plugin, Plugin::Docker) && !docker_image_available() { + } + if !docker_image_available() { assert!( !required, "{REQUIRE_ENV} is set but no Docker daemon with {DOCKER_IMAGE} is available" @@ -93,56 +59,27 @@ pub(crate) fn configure(context: &mut TestContext, plugin: Plugin) -> Option<&'s return None; } - let storage_dir = context.temp_dir.join("plugin-server-storage"); - let registry = context.temp_dir.join("host-registry"); - std::fs::create_dir_all(®istry).expect("registry dir should be created"); - let settings = match plugin { - Plugin::Host => format!( - r#"[server.storage] + let storage_dir = context.temp_dir.join("docker-server-storage"); + let settings = format!( + r#"[server.storage] root = "{storage}" [server.auth] methods = ["dev-token"] - -[server.sandbox.providers.host] -path = "{path}" -dev = true -inherit_env = ["PATH", "HOME"] - -[server.sandbox.providers.host.env] -SANDBOX_DRIVER_HOST_REGISTRY = "{registry}" "#, - storage = toml_path(&storage_dir), - path = toml_path(&executable), - registry = toml_path(®istry), - ), - Plugin::Docker => format!( - r#"[server.storage] -root = "{storage}" - -[server.auth] -methods = ["dev-token"] - -[server.sandbox.providers.docker-plugin] -path = "{path}" -dev = true -inherit_env = ["PATH", "HOME", "DOCKER_HOST", "DOCKER_CERT_PATH", "DOCKER_TLS_VERIFY"] -"#, - storage = toml_path(&storage_dir), - path = toml_path(&executable), - ), - }; + storage = toml_path(&storage_dir), + ); context.write_home(".fabro/settings.toml", settings); context.isolated_server(); - create_environment(&context.storage_dir, plugin); - Some(plugin.environment()) + create_environment(&context.storage_dir); + Some(ENVIRONMENT) } -/// The driver executable on `PATH`, when installed. -fn plugin_executable(plugin: Plugin) -> Option { +/// The Docker plugin executable on `PATH`, when installed. +fn plugin_executable() -> Option { let path = std::env::var_os("PATH")?; std::env::split_paths(&path) - .map(|dir| dir.join(plugin.executable())) + .map(|dir| dir.join(DOCKER_PLUGIN)) .find(|candidate| candidate.is_file()) } @@ -159,17 +96,11 @@ fn toml_path(path: &Path) -> String { path.display().to_string().replace('\\', "/") } -fn create_environment(storage_dir: &Path, plugin: Plugin) { +fn create_environment(storage_dir: &Path) { let body = json!({ - "id": plugin.environment(), - "provider": plugin.kind(), - "image": { - "docker": match plugin { - Plugin::Host => serde_json::Value::Null, - Plugin::Docker => json!(DOCKER_IMAGE), - }, - "dockerfile": null - }, + "id": ENVIRONMENT, + "provider": "docker", + "image": { "docker": DOCKER_IMAGE, "dockerfile": null }, "resources": { "cpu": null, "memory": null, "disk": null }, "network": { "mode": "allow_all", "allow": [] }, "lifecycle": { "preserve": false, "stop_on_terminal": true, "auto_stop": null }, diff --git a/lib/apps/fabro-cli/tests/it/workflow/mod.rs b/lib/apps/fabro-cli/tests/it/workflow/mod.rs index fab45fd90..75302e425 100644 --- a/lib/apps/fabro-cli/tests/it/workflow/mod.rs +++ b/lib/apps/fabro-cli/tests/it/workflow/mod.rs @@ -9,11 +9,11 @@ mod command_agent_mixed; mod command_pipeline; mod command_routing; mod conditional_branching; +pub(super) mod docker; mod dry_run_examples; mod full_stack; mod hooks; mod human_gate; -pub(super) mod plugin; use std::path::{Path, PathBuf}; use std::time::Duration; @@ -173,15 +173,14 @@ fn run_stream_items(run_dir: &Path) -> Vec { /// Runs a scenario against every sandbox provider fabro supports: /// -/// - `local`: the bundled Host provider in-process. -/// - `daytona`: the bundled Daytona provider, live credentials required. -/// - `host-plugin`: the driver's Host executable over stdio under the -/// non-bundled `host` kind, a clone-based managed workspace. -/// - `docker-plugin`: the driver's Docker executable over stdio under the -/// non-bundled `docker-plugin` kind. +/// - `local`: the host provider, the session server's own `local` environment. +/// - `daytona`: the Daytona provider, live credentials required. +/// - `docker`: the Docker provider, an environment on `buildpack-deps:noble` +/// created on an isolated server. /// -/// The plugin variants need the driver's executables on `PATH`; without -/// them (or without a Docker daemon) they skip, +/// Petri serves each provider through the matching sandbox-driver plugin +/// executable on `PATH`. The `docker` variant skips without +/// `sandbox-driver-docker` or without a Docker daemon that has the image, /// unless `FABRO_REQUIRE_SANDBOX_PLUGINS` is set, as CI sets it. macro_rules! sandbox_tests { ($name:ident) => { @@ -200,24 +199,10 @@ macro_rules! sandbox_tests { } #[fabro_macros::e2e_test($(live($key)),*)] - fn []() { + fn []() { let mut context = fabro_test::test_context!(); - if let Some(environment) = - $crate::workflow::plugin::configure(&mut context, $crate::workflow::plugin::Plugin::Host) - { - $crate::workflow::plugin::run_with_server_log(&context, || { - [](&context, environment); - }); - } - } - - #[fabro_macros::e2e_test($(live($key)),*)] - fn []() { - let mut context = fabro_test::test_context!(); - if let Some(environment) = - $crate::workflow::plugin::configure(&mut context, $crate::workflow::plugin::Plugin::Docker) - { - $crate::workflow::plugin::run_with_server_log(&context, || { + if let Some(environment) = $crate::workflow::docker::configure(&mut context) { + $crate::workflow::docker::run_with_server_log(&context, || { [](&context, environment); }); } @@ -230,7 +215,7 @@ pub(super) use sandbox_tests; pub(super) fn timeout_for(sandbox: &str) -> Duration { match sandbox { "daytona" => Duration::from_mins(10), - "docker-plugin" => Duration::from_mins(5), + docker::ENVIRONMENT => Duration::from_mins(5), _ => Duration::from_mins(3), } }