Move iroh_driver and the relay binary out of distribution into a dedicated crates/iroh-driver (lib re-exports IrohDriver; relay bin renamed). Remove the node crate and the single-gpu-inference example; drop the docker/datastream demo. Slim pipeline-parallel vastai. Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
143 lines
4.9 KiB
Rust
143 lines
4.9 KiB
Rust
use crate::types::{InstanceResponse, LifecyclePolicy, RunningInstance};
|
|
|
|
/// Historical env-backed polling wrapper.
|
|
pub async fn wait_for_running(
|
|
client: &reqwest::Client,
|
|
base_url: &str,
|
|
api_key: &str,
|
|
contract_id: u64,
|
|
poll_interval: std::time::Duration,
|
|
max_polls: u32,
|
|
) -> Result<RunningInstance, String> {
|
|
let policy = LifecyclePolicy::from_env(poll_interval, max_polls);
|
|
wait_for_running_with_policy(client, base_url, api_key, contract_id, &policy).await
|
|
}
|
|
|
|
/// Poll vast.ai until an instance reaches `running`, or fail on terminal/stalled state.
|
|
pub async fn wait_for_running_with_policy(
|
|
client: &reqwest::Client,
|
|
base_url: &str,
|
|
api_key: &str,
|
|
contract_id: u64,
|
|
policy: &LifecyclePolicy,
|
|
) -> Result<RunningInstance, String> {
|
|
let url = format!("{base_url}/api/v0/instances/{contract_id}/");
|
|
let mut state_since = std::time::Instant::now();
|
|
let mut progress_since = std::time::Instant::now();
|
|
let mut last_state: Option<String> = None;
|
|
let mut last_msg: Option<String> = None;
|
|
let mut last_disk: Option<f64> = None;
|
|
|
|
for poll in 0..policy.max_polls {
|
|
let resp = match client
|
|
.get(&url)
|
|
.header("Authorization", format!("Bearer {api_key}"))
|
|
.send()
|
|
.await
|
|
{
|
|
Ok(r) => r,
|
|
Err(e) => {
|
|
eprintln!(
|
|
" contract {contract_id} poll {}/{}: request error: {e} (retrying)",
|
|
poll + 1,
|
|
policy.max_polls,
|
|
);
|
|
tokio::time::sleep(policy.poll_interval).await;
|
|
continue;
|
|
}
|
|
};
|
|
|
|
if !resp.status().is_success() {
|
|
let status = resp.status();
|
|
let body = resp.text().await.unwrap_or_default();
|
|
eprintln!(
|
|
" contract {contract_id} poll {}/{}: HTTP {status} (retrying): {}",
|
|
poll + 1,
|
|
policy.max_polls,
|
|
body.chars().take(80).collect::<String>(),
|
|
);
|
|
tokio::time::sleep(policy.poll_interval).await;
|
|
continue;
|
|
}
|
|
|
|
let wrapper: InstanceResponse = resp
|
|
.json()
|
|
.await
|
|
.map_err(|e| format!("wait_for_running parse failed: {e}"))?;
|
|
let status = wrapper.instances;
|
|
|
|
let actual = status.actual_status.as_deref().unwrap_or("unknown");
|
|
let intended = status.intended_status.as_deref().unwrap_or("unknown");
|
|
|
|
let msg = status.status_msg.clone();
|
|
let disk = status.disk_usage;
|
|
if msg != last_msg || disk != last_disk {
|
|
progress_since = std::time::Instant::now();
|
|
}
|
|
if last_state.as_deref() != Some(actual) {
|
|
state_since = std::time::Instant::now();
|
|
}
|
|
last_state = Some(actual.to_string());
|
|
last_msg = msg.clone();
|
|
last_disk = disk;
|
|
|
|
let stalled = policy
|
|
.pull_stall
|
|
.is_some_and(|stall| progress_since.elapsed() >= stall);
|
|
let in_state = state_since.elapsed().as_secs();
|
|
let msg_disp = match msg.as_deref() {
|
|
Some(m) if !m.is_empty() => format!(" msg=\"{m}\""),
|
|
_ => String::new(),
|
|
};
|
|
let disk_disp = match disk {
|
|
Some(d) if d >= 0.0 => format!(" disk={d:.2}GB"),
|
|
_ => String::new(),
|
|
};
|
|
eprintln!(
|
|
" contract {contract_id} poll {}/{}: status={actual} in-state={in_state}s {}{msg_disp}{disk_disp}",
|
|
poll + 1,
|
|
policy.max_polls,
|
|
if stalled { "STALLED" } else { "progressing" },
|
|
);
|
|
|
|
if let Some(m) = &msg {
|
|
if m.contains("Error") || m.contains("failed") {
|
|
return Err(format!("instance {contract_id} error: {m}"));
|
|
}
|
|
}
|
|
if intended == "stopped" && actual != "running" {
|
|
return Err(format!(
|
|
"instance {contract_id} stopped: {}",
|
|
msg.unwrap_or_default()
|
|
));
|
|
}
|
|
|
|
match actual {
|
|
"running" => {
|
|
let ip = status
|
|
.public_ipaddr
|
|
.unwrap_or_else(|| "unknown".to_string());
|
|
let port = status.ssh_port.unwrap_or(0);
|
|
return Ok(RunningInstance { ip, port });
|
|
}
|
|
"exited" | "error" => {
|
|
return Err(format!(
|
|
"instance {contract_id} reached terminal status: {actual}"
|
|
));
|
|
}
|
|
_ => {
|
|
if stalled {
|
|
return Err(format!(
|
|
"instance {contract_id} stalled in '{actual}' for {}s with no status_msg/disk_usage progress",
|
|
progress_since.elapsed().as_secs()
|
|
));
|
|
}
|
|
tokio::time::sleep(policy.poll_interval).await;
|
|
}
|
|
}
|
|
}
|
|
|
|
Err(format!(
|
|
"instance {contract_id} did not reach running within poll limit"
|
|
))
|
|
}
|