mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-05 02:41:45 +00:00
Run the workflow scenarios on the Docker provider instead of the stdio plugins
The host_plugin_ and docker_plugin_ variants ran each scenario under Fabro's old plugin transport with the provider kinds `host` and `docker-plugin`, which Petri's Fabro frontend rejects. Under Petri every provider is already served by a sandbox-driver plugin, so those variants test nothing distinct. A single docker_ variant replaces them: an environment with provider `docker` on buildpack-deps:noble, created on an isolated server, skipping without the sandbox-driver-docker executable or a daemon with the image unless FABRO_REQUIRE_SANDBOX_PLUGINS is set. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
49a647e3a4
commit
beac547a1f
2 changed files with 46 additions and 130 deletions
|
|
@ -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 <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<PathBuf> {
|
||||
/// The Docker plugin executable on `PATH`, when installed.
|
||||
fn plugin_executable() -> Option<PathBuf> {
|
||||
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 },
|
||||
|
|
|
|||
|
|
@ -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<RunStreamItem> {
|
|||
|
||||
/// 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 [<host_plugin_ $name>]() {
|
||||
fn [<docker_ $name>]() {
|
||||
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, || {
|
||||
[<scenario_ $name>](&context, environment);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[fabro_macros::e2e_test($(live($key)),*)]
|
||||
fn [<docker_plugin_ $name>]() {
|
||||
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, || {
|
||||
[<scenario_ $name>](&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),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue