2026-08-17 22:51:08 +00:00
|
|
|
//! Two-runtime proof: the orchestrator FSM actor and the node job actor live on
|
|
|
|
|
//! **separate swactor runtimes**, meshed by the transport seam (codec encode →
|
|
|
|
|
//! `TransportRouter` → `Transport` → codec decode → `deliver_raw`) — the same
|
|
|
|
|
//! seam iroh realizes in production. Control, workspace bytes, output bytes, and
|
|
|
|
|
//! the supervised-process exit all cross the runtime boundary over the actor
|
|
|
|
|
//! plane; `setup`/`run` execute via `swactor-process`. No shared filesystem for
|
|
|
|
|
//! the job, no SSH.
|
|
|
|
|
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
use std::time::{Duration, Instant};
|
|
|
|
|
|
|
|
|
|
use swactor::actor::{ActorAddress, Message};
|
|
|
|
|
use swactor::runtime::{Inbox, Runtime, RuntimeConfig, RuntimeParts};
|
|
|
|
|
use swactor::std::StdExtension;
|
|
|
|
|
use swactor_engine::{Engine, TokioBackend, TokioConfig};
|
|
|
|
|
use swactor_transport::{CodecRegistry, CodecRemoteSink, Transport, TransportRouter, WireEnvelope};
|
|
|
|
|
|
refactor(myelin): rework control and runtime integration
Add actor-backed manual node provisioning, control-plane endpoints, and fleet UI assets with durable provider lifecycle handling.
Simplify Myelin orchestration, node runtime, staging, and telemetry paths while removing obsolete engine-builder, dashboard-view, and local-mock implementations.
Align runtime delivery, data-plane, distribution, job-runner, process, telemetry, dashboard, Vast.ai integrations, and their tests with the revised actor and transport contracts.
2026-08-19 10:20:01 +00:00
|
|
|
use swactor_job_runner::model;
|
2026-08-17 22:51:08 +00:00
|
|
|
use swactor_job_runner::{
|
refactor(myelin): rework control and runtime integration
Add actor-backed manual node provisioning, control-plane endpoints, and fleet UI assets with durable provider lifecycle handling.
Simplify Myelin orchestration, node runtime, staging, and telemetry paths while removing obsolete engine-builder, dashboard-view, and local-mock implementations.
Align runtime delivery, data-plane, distribution, job-runner, process, telemetry, dashboard, Vast.ai integrations, and their tests with the revised actor and transport contracts.
2026-08-19 10:20:01 +00:00
|
|
|
Job, JobDone, JobState, NodeJobActor, OrchestratorJobActor, OrchestratorJobMsg, Workspace,
|
|
|
|
|
register_job_codecs,
|
|
|
|
|
}; // ensure model path compiles; not used directly below
|
2026-08-17 22:51:08 +00:00
|
|
|
|
|
|
|
|
const POLL: Duration = Duration::from_millis(15);
|
|
|
|
|
const DEADLINE: Duration = Duration::from_secs(20);
|
|
|
|
|
|
|
|
|
|
type BoxError = Box<dyn std::error::Error + Send + Sync>;
|
|
|
|
|
|
|
|
|
|
/// Stands in for the iroh transport: carries a `WireEnvelope` from one runtime
|
|
|
|
|
/// to another, decoding via the shared codec and performing the production
|
|
|
|
|
/// ingress (`deliver_raw`). This is exactly the seam the iroh driver fills.
|
|
|
|
|
struct Link {
|
|
|
|
|
dst: Runtime,
|
|
|
|
|
codec: Arc<CodecRegistry>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Transport for Link {
|
|
|
|
|
fn send(&self, wire: WireEnvelope) -> Result<(), swactor::Error> {
|
|
|
|
|
let msg = self.codec.decode(&wire.type_tag, &wire.payload)?;
|
|
|
|
|
self.dst.deliver_raw(wire.dest, msg)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn build_runtime(codec: Arc<CodecRegistry>) -> (RuntimeParts, Runtime, Arc<TransportRouter>) {
|
refactor(myelin): rework control and runtime integration
Add actor-backed manual node provisioning, control-plane endpoints, and fleet UI assets with durable provider lifecycle handling.
Simplify Myelin orchestration, node runtime, staging, and telemetry paths while removing obsolete engine-builder, dashboard-view, and local-mock implementations.
Align runtime delivery, data-plane, distribution, job-runner, process, telemetry, dashboard, Vast.ai integrations, and their tests with the revised actor and transport contracts.
2026-08-19 10:20:01 +00:00
|
|
|
let parts =
|
|
|
|
|
RuntimeParts::new(RuntimeConfig::default()).with_extension(Arc::new(StdExtension::new()));
|
2026-08-17 22:51:08 +00:00
|
|
|
let rt = parts.runtime().clone();
|
|
|
|
|
let router = Arc::new(TransportRouter::new());
|
|
|
|
|
rt.set_remote_sink(Arc::new(CodecRemoteSink::new(codec, router.clone())));
|
|
|
|
|
(parts, rt, router)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn job_runs_across_two_swactor_runtimes_over_the_actor_plane() {
|
|
|
|
|
let mut codec = CodecRegistry::new();
|
|
|
|
|
register_job_codecs(&mut codec);
|
|
|
|
|
let codec = Arc::new(codec);
|
|
|
|
|
|
|
|
|
|
let ws = tempfile::tempdir().expect("ws");
|
|
|
|
|
std::fs::write(ws.path().join("seed.txt"), "seed-value").expect("seed");
|
|
|
|
|
let node_workdir = tempfile::tempdir().expect("node workdir");
|
|
|
|
|
let landing = tempfile::tempdir().expect("landing");
|
|
|
|
|
|
|
|
|
|
// Two independent runtimes, each driven by its own engine.
|
|
|
|
|
let (parts_a, rt_a, router_a) = build_runtime(codec.clone());
|
|
|
|
|
let (parts_b, rt_b, router_b) = build_runtime(codec.clone());
|
refactor(myelin): rework control and runtime integration
Add actor-backed manual node provisioning, control-plane endpoints, and fleet UI assets with durable provider lifecycle handling.
Simplify Myelin orchestration, node runtime, staging, and telemetry paths while removing obsolete engine-builder, dashboard-view, and local-mock implementations.
Align runtime delivery, data-plane, distribution, job-runner, process, telemetry, dashboard, Vast.ai integrations, and their tests with the revised actor and transport contracts.
2026-08-19 10:20:01 +00:00
|
|
|
let engine_a = Engine::new(
|
|
|
|
|
parts_a,
|
|
|
|
|
TokioBackend::new(TokioConfig::default()).expect("tokio"),
|
|
|
|
|
)
|
|
|
|
|
.expect("engine A");
|
|
|
|
|
let engine_b = Engine::new(
|
|
|
|
|
parts_b,
|
|
|
|
|
TokioBackend::new(TokioConfig::default()).expect("tokio"),
|
|
|
|
|
)
|
|
|
|
|
.expect("engine B");
|
2026-08-17 22:51:08 +00:00
|
|
|
|
|
|
|
|
let done = rt_a.new_inbox::<JobDone>().expect("done inbox");
|
|
|
|
|
let orch = rt_a
|
refactor(myelin): rework control and runtime integration
Add actor-backed manual node provisioning, control-plane endpoints, and fleet UI assets with durable provider lifecycle handling.
Simplify Myelin orchestration, node runtime, staging, and telemetry paths while removing obsolete engine-builder, dashboard-view, and local-mock implementations.
Align runtime delivery, data-plane, distribution, job-runner, process, telemetry, dashboard, Vast.ai integrations, and their tests with the revised actor and transport contracts.
2026-08-19 10:20:01 +00:00
|
|
|
.spawn(OrchestratorJobActor::new(
|
|
|
|
|
*done.addr(),
|
|
|
|
|
landing.path().to_path_buf(),
|
|
|
|
|
))
|
2026-08-17 22:51:08 +00:00
|
|
|
.expect("spawn orchestrator on A");
|
|
|
|
|
let node = rt_b
|
refactor(myelin): rework control and runtime integration
Add actor-backed manual node provisioning, control-plane endpoints, and fleet UI assets with durable provider lifecycle handling.
Simplify Myelin orchestration, node runtime, staging, and telemetry paths while removing obsolete engine-builder, dashboard-view, and local-mock implementations.
Align runtime delivery, data-plane, distribution, job-runner, process, telemetry, dashboard, Vast.ai integrations, and their tests with the revised actor and transport contracts.
2026-08-19 10:20:01 +00:00
|
|
|
.spawn(NodeJobActor::new(
|
|
|
|
|
orch,
|
|
|
|
|
node_workdir.path().to_path_buf(),
|
|
|
|
|
rt_b.create_sender(),
|
|
|
|
|
0,
|
|
|
|
|
))
|
2026-08-17 22:51:08 +00:00
|
|
|
.expect("spawn node on B");
|
|
|
|
|
|
|
|
|
|
// Cross-runtime routes: A routes the node address → B; B routes the
|
|
|
|
|
// orchestrator address → A. Each Link delivers to wire.dest on the peer.
|
refactor(myelin): rework control and runtime integration
Add actor-backed manual node provisioning, control-plane endpoints, and fleet UI assets with durable provider lifecycle handling.
Simplify Myelin orchestration, node runtime, staging, and telemetry paths while removing obsolete engine-builder, dashboard-view, and local-mock implementations.
Align runtime delivery, data-plane, distribution, job-runner, process, telemetry, dashboard, Vast.ai integrations, and their tests with the revised actor and transport contracts.
2026-08-19 10:20:01 +00:00
|
|
|
router_a.add_route(
|
|
|
|
|
node,
|
|
|
|
|
Arc::new(Link {
|
|
|
|
|
dst: rt_b.clone(),
|
|
|
|
|
codec: codec.clone(),
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
router_b.add_route(
|
|
|
|
|
orch,
|
|
|
|
|
Arc::new(Link {
|
|
|
|
|
dst: rt_a.clone(),
|
|
|
|
|
codec: codec.clone(),
|
|
|
|
|
}),
|
|
|
|
|
);
|
2026-08-17 22:51:08 +00:00
|
|
|
|
|
|
|
|
let job = Job {
|
|
|
|
|
name: "cross-runtime-probe".to_owned(),
|
|
|
|
|
setup: Some("echo setup-ok > setup_done.txt".to_owned()),
|
|
|
|
|
run: "echo hello-across-runtimes > greeting.txt".to_owned(),
|
refactor(myelin): rework control and runtime integration
Add actor-backed manual node provisioning, control-plane endpoints, and fleet UI assets with durable provider lifecycle handling.
Simplify Myelin orchestration, node runtime, staging, and telemetry paths while removing obsolete engine-builder, dashboard-view, and local-mock implementations.
Align runtime delivery, data-plane, distribution, job-runner, process, telemetry, dashboard, Vast.ai integrations, and their tests with the revised actor and transport contracts.
2026-08-19 10:20:01 +00:00
|
|
|
workspace: Some(Workspace {
|
|
|
|
|
workdir: ws.path().to_path_buf(),
|
|
|
|
|
exclude: vec![],
|
|
|
|
|
}),
|
2026-08-17 22:51:08 +00:00
|
|
|
outputs: vec![
|
|
|
|
|
"greeting.txt".to_owned(),
|
|
|
|
|
"setup_done.txt".to_owned(),
|
|
|
|
|
"seed.txt".to_owned(),
|
|
|
|
|
],
|
|
|
|
|
env: std::collections::BTreeMap::new(),
|
|
|
|
|
};
|
refactor(myelin): rework control and runtime integration
Add actor-backed manual node provisioning, control-plane endpoints, and fleet UI assets with durable provider lifecycle handling.
Simplify Myelin orchestration, node runtime, staging, and telemetry paths while removing obsolete engine-builder, dashboard-view, and local-mock implementations.
Align runtime delivery, data-plane, distribution, job-runner, process, telemetry, dashboard, Vast.ai integrations, and their tests with the revised actor and transport contracts.
2026-08-19 10:20:01 +00:00
|
|
|
rt_a.send_to(
|
|
|
|
|
orch,
|
|
|
|
|
OrchestratorJobMsg::Submit {
|
|
|
|
|
job,
|
|
|
|
|
node_actor: node,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
.expect("submit");
|
2026-08-17 22:51:08 +00:00
|
|
|
|
|
|
|
|
let started = Instant::now();
|
|
|
|
|
let mut outcome = None;
|
|
|
|
|
while started.elapsed() < DEADLINE {
|
|
|
|
|
if let Some(d) = done.try_recv() {
|
|
|
|
|
outcome = Some(d);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
std::thread::sleep(POLL);
|
|
|
|
|
}
|
|
|
|
|
drop(engine_a);
|
|
|
|
|
drop(engine_b);
|
|
|
|
|
let done = outcome.expect("job did not reach a terminal state across runtimes");
|
refactor(myelin): rework control and runtime integration
Add actor-backed manual node provisioning, control-plane endpoints, and fleet UI assets with durable provider lifecycle handling.
Simplify Myelin orchestration, node runtime, staging, and telemetry paths while removing obsolete engine-builder, dashboard-view, and local-mock implementations.
Align runtime delivery, data-plane, distribution, job-runner, process, telemetry, dashboard, Vast.ai integrations, and their tests with the revised actor and transport contracts.
2026-08-19 10:20:01 +00:00
|
|
|
assert_eq!(
|
|
|
|
|
done.state,
|
|
|
|
|
JobState::Completed,
|
|
|
|
|
"expected COMPLETED across runtimes, got {:?}",
|
|
|
|
|
done
|
|
|
|
|
);
|
2026-08-17 22:51:08 +00:00
|
|
|
assert_eq!(done.exit_code, Some(0));
|
|
|
|
|
|
|
|
|
|
let greeting =
|
|
|
|
|
std::fs::read_to_string(landing.path().join("greeting.txt")).expect("collected greeting");
|
refactor(myelin): rework control and runtime integration
Add actor-backed manual node provisioning, control-plane endpoints, and fleet UI assets with durable provider lifecycle handling.
Simplify Myelin orchestration, node runtime, staging, and telemetry paths while removing obsolete engine-builder, dashboard-view, and local-mock implementations.
Align runtime delivery, data-plane, distribution, job-runner, process, telemetry, dashboard, Vast.ai integrations, and their tests with the revised actor and transport contracts.
2026-08-19 10:20:01 +00:00
|
|
|
assert!(
|
|
|
|
|
greeting.contains("hello-across-runtimes"),
|
|
|
|
|
"greeting: {greeting}"
|
|
|
|
|
);
|
2026-08-17 22:51:08 +00:00
|
|
|
let seed = std::fs::read_to_string(landing.path().join("seed.txt")).expect("collected seed");
|
refactor(myelin): rework control and runtime integration
Add actor-backed manual node provisioning, control-plane endpoints, and fleet UI assets with durable provider lifecycle handling.
Simplify Myelin orchestration, node runtime, staging, and telemetry paths while removing obsolete engine-builder, dashboard-view, and local-mock implementations.
Align runtime delivery, data-plane, distribution, job-runner, process, telemetry, dashboard, Vast.ai integrations, and their tests with the revised actor and transport contracts.
2026-08-19 10:20:01 +00:00
|
|
|
assert_eq!(
|
|
|
|
|
seed, "seed-value",
|
|
|
|
|
"workspace crossed the runtime boundary through swactor"
|
|
|
|
|
);
|
2026-08-17 22:51:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
fn _ensure_paths_compile(_a: ActorAddress, _e: BoxError, _m: model::Job) {}
|