Igneum Miner jobs: the wsl-prover probe logs why it fails, tries the configured then the default WSL user with cargo and sp1 on PATH, and accepts the shipped wsl2\bin prover; publish-jobs --requires none|"" = no requirement
PC 2 on 0.3.3 logged 'needs wsl-prover' although the toolchain is there as user josh. Every negative probe now lands in the engine log with its exit code and the first 200 characters of output (it reaches the intake). The probe sources ~/.cargo/env and sets ~/.cargo/bin and ~/.sp1/bin itself (a login shell from a console-less process need not), runs as the job's wsl_user or IGNEUM_APP_WSL_USER first and the distro default user second, and a payload with wsl2\bin\igneum-prove-host next to the app meets the requirement when the distro answers. publish-jobs.sh: --requires none or "" publishes an empty list (none was a literal requirement, "" fell back to the kind's default). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
fa5302813e
commit
c67b2a4cfb
2 changed files with 89 additions and 19 deletions
|
|
@ -269,20 +269,34 @@ impl Jobs {
|
|||
let now = crate::platform::unix_now();
|
||||
let platform = manifest::platform_name();
|
||||
let probes: Mutex<std::collections::HashMap<String, bool>> = Mutex::new(std::collections::HashMap::new());
|
||||
let have = |req: &str| -> bool {
|
||||
if let Some(v) = probes.lock().unwrap().get(req) {
|
||||
return *v;
|
||||
}
|
||||
let v = probe(req);
|
||||
probes.lock().unwrap().insert(req.to_string(), v);
|
||||
v
|
||||
};
|
||||
let env_user = std::env::var("IGNEUM_APP_WSL_USER").unwrap_or_default();
|
||||
let mut runnable = Vec::new();
|
||||
let mut pending = Vec::new();
|
||||
for j in &f.jobs {
|
||||
if seen.contains(&j.id) {
|
||||
continue;
|
||||
}
|
||||
// the probe runs once per requirement and WSL user per poll; every negative result is logged
|
||||
// with its exit code and output (PC 2, 0.3.3: "needs wsl-prover" on a machine that had it)
|
||||
let user = { let u = j.str_param("wsl_user"); if u.is_empty() { env_user.clone() } else { u } };
|
||||
let have = |req: &str| -> bool {
|
||||
let key = format!("{req}|{user}");
|
||||
if let Some(v) = probes.lock().unwrap().get(&key) {
|
||||
return *v;
|
||||
}
|
||||
let v = match probe(req, &user) {
|
||||
Ok(how) => {
|
||||
shared2.log(&format!("probe {req}: ok ({how})"));
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
shared2.log(&format!("probe {req}{}: NOT met: {e}", if user.is_empty() { String::new() } else { format!(" (user {user})") }));
|
||||
false
|
||||
}
|
||||
};
|
||||
probes.lock().unwrap().insert(key, v);
|
||||
v
|
||||
};
|
||||
match jobs::eligibility(j, &machine_id, platform, now, &have) {
|
||||
Eligibility::Run => runnable.push(j.clone()),
|
||||
Eligibility::Needs(m) => pending.push((j.id.clone(), m)),
|
||||
|
|
@ -573,15 +587,70 @@ fn fetch_jobs(url: &str, dir: &Path) -> Result<jobs::JobsFile, String> {
|
|||
Ok(f)
|
||||
}
|
||||
|
||||
/// Does this machine meet a requirement? Probed on the fetch thread, once per poll.
|
||||
fn probe(req: &str) -> bool {
|
||||
/// What the toolchain probe runs inside the distro: the cargo and sp1 paths set by hand (a login shell from a
|
||||
/// process without a console need not source ~/.cargo/env), then the three things the prover needs.
|
||||
const PROVER_PROBE: &str = "export PATH=\"$HOME/.cargo/bin:$HOME/.sp1/bin:$PATH\"; [ -f \"$HOME/.cargo/env\" ] && . \"$HOME/.cargo/env\"; command -v cargo && ls -d \"$HOME/.sp1\" \"$HOME/igneum-prove\" && echo \"user $(id -un) home $HOME\"";
|
||||
|
||||
fn short_out(s: &str) -> String {
|
||||
let t: String = s.replace(['\r', '\n'], " ").split_whitespace().collect::<Vec<_>>().join(" ");
|
||||
t.chars().take(200).collect()
|
||||
}
|
||||
|
||||
/// Does this machine meet a requirement? Probed on the fetch thread, once per poll and WSL user. Ok carries how it
|
||||
/// was met, Err the exit code and the first 200 characters of the output, for the engine log.
|
||||
fn probe(req: &str, wsl_user: &str) -> Result<String, String> {
|
||||
let wsl = crate::platform::tool("wsl");
|
||||
match req {
|
||||
"nvidia" => run_capture(Command::new(crate::platform::tool("nvidia-smi")).arg("-L"), Duration::from_secs(20)).0 == Some(0),
|
||||
"wsl" => cfg!(windows) && run_capture(Command::new(crate::platform::tool("wsl")).arg("--status"), Duration::from_secs(30)).0 == Some(0),
|
||||
"wsl-prover" => {
|
||||
cfg!(windows) && run_capture(Command::new(crate::platform::tool("wsl")).args(["-d", DEFAULT_DISTRO, "--", "bash", "-lc", "command -v cargo && ls ~/.sp1 && ls ~/igneum-prove"]), Duration::from_secs(90)).0 == Some(0)
|
||||
"nvidia" => {
|
||||
let (code, out) = run_capture(Command::new(crate::platform::tool("nvidia-smi")).arg("-L"), Duration::from_secs(20));
|
||||
if code == Some(0) { Ok(short_out(&out)) } else { Err(format!("nvidia-smi -L exit {code:?}: {}", short_out(&out))) }
|
||||
}
|
||||
_ => false,
|
||||
"wsl" => {
|
||||
if !cfg!(windows) {
|
||||
return Err("not Windows".into());
|
||||
}
|
||||
let (code, out) = run_capture(Command::new(&wsl).arg("--status"), Duration::from_secs(30));
|
||||
if code == Some(0) { Ok("wsl --status".into()) } else { Err(format!("wsl --status exit {code:?}: {}", short_out(&out))) }
|
||||
}
|
||||
"wsl-prover" => {
|
||||
if !cfg!(windows) {
|
||||
return Err("not Windows".into());
|
||||
}
|
||||
// the configured user first (the job's wsl_user or IGNEUM_APP_WSL_USER), then the distro's default user
|
||||
let mut tried = Vec::new();
|
||||
let mut users: Vec<&str> = Vec::new();
|
||||
if !wsl_user.is_empty() {
|
||||
users.push(wsl_user);
|
||||
}
|
||||
users.push("");
|
||||
for u in users {
|
||||
let mut c = Command::new(&wsl);
|
||||
c.args(["-d", DEFAULT_DISTRO]);
|
||||
if !u.is_empty() {
|
||||
c.args(["-u", u]);
|
||||
}
|
||||
c.args(["--", "bash", "-lc", PROVER_PROBE]);
|
||||
let (code, out) = run_capture(&mut c, Duration::from_secs(90));
|
||||
if code == Some(0) {
|
||||
return Ok(format!("toolchain in {DEFAULT_DISTRO}{}: {}", if u.is_empty() { " (default user)".to_string() } else { format!(" as {u}") }, short_out(&out)));
|
||||
}
|
||||
tried.push(format!("{}: exit {code:?}: {}", if u.is_empty() { "default user".to_string() } else { format!("user {u}") }, short_out(&out)));
|
||||
}
|
||||
// the payload ships a Linux prover next to the app (wsl2\bin\igneum-prove-host): that meets it too,
|
||||
// provided WSL answers at all
|
||||
if let Some(dir) = std::env::current_exe().ok().and_then(|p| p.parent().map(|d| d.to_path_buf())) {
|
||||
let shipped = dir.join("wsl2").join("bin").join("igneum-prove-host");
|
||||
if shipped.is_file() {
|
||||
let (code, out) = run_capture(Command::new(&wsl).args(["-d", DEFAULT_DISTRO, "--", "true"]), Duration::from_secs(60));
|
||||
if code == Some(0) {
|
||||
return Ok(format!("shipped prover {} and {DEFAULT_DISTRO} answers", shipped.display()));
|
||||
}
|
||||
tried.push(format!("shipped prover present but {DEFAULT_DISTRO} does not answer: exit {code:?}: {}", short_out(&out)));
|
||||
}
|
||||
}
|
||||
Err(tried.join(" | "))
|
||||
}
|
||||
_ => Err("unknown requirement".into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@
|
|||
# packaging/ota/publish-jobs.sh add --kind update-now --target all
|
||||
# packaging/ota/publish-jobs.sh add --kind shard-benchmark --target 1ccfe586 [--zip ~/Desktop/igneum-prove-wsl2.zip] \
|
||||
# [--fixtures "block-338-shard1 block-341-shards2 block-344-shards4"] [--cap-minutes 90] [--distro Ubuntu-24.04] [--wsl-user josh]
|
||||
# common: --target <id8 or id16, comma list, or all> [--platform windows|mac|any] [--requires wsl-prover,nvidia]
|
||||
# common: --target <id8 or id16, comma list, or all> [--platform windows|mac|any] [--requires wsl-prover,nvidia | none]
|
||||
# (shard-benchmark defaults to --requires wsl-prover; --requires none or "" publishes with no requirement)
|
||||
# [--id custom-id] [--title "..."] [--expires-hours 48] [--deploy]
|
||||
# packaging/ota/publish-jobs.sh list what is published (expired jobs marked)
|
||||
# packaging/ota/publish-jobs.sh remove <id> [--deploy]
|
||||
|
|
@ -35,7 +36,7 @@ TOKEN_FILE="$HOME/.config/igneum/dl-token"
|
|||
SIGNER="$ROOT/app/igneum-app/target/release/igneum-ota-sign"
|
||||
|
||||
CMD="${1:-}"; [ $# -gt 0 ] && shift
|
||||
KIND="" TARGET="" PLATFORM="" REQUIRES="" ID="" TITLE="" EXPIRES_H="48" DEPLOY=0 BASE="" DEST=""
|
||||
KIND="" TARGET="" PLATFORM="" REQUIRES="" REQUIRES_SET=0 ID="" TITLE="" EXPIRES_H="48" DEPLOY=0 BASE="" DEST=""
|
||||
SCRIPT="" SHELL_KIND="" ELEVATED=0 STOP_MINERS=0 TIMEOUT_MIN=""
|
||||
FILE="" URL="" SHA="" SIZE="" DIR="" TO="" EXTRACT=0 EXTRACT_DIR="" FRESH=0
|
||||
GLOBS=() COMMAND="" WHAT=""
|
||||
|
|
@ -48,7 +49,7 @@ while [ $# -gt 0 ]; do
|
|||
--kind) KIND="$2"; shift 2 ;;
|
||||
--target) TARGET="$2"; shift 2 ;;
|
||||
--platform) PLATFORM="$2"; shift 2 ;;
|
||||
--requires) REQUIRES="$2"; shift 2 ;;
|
||||
--requires) REQUIRES="$2"; REQUIRES_SET=1; [ "$REQUIRES" = none ] && REQUIRES=""; shift 2 ;;
|
||||
--id) ID="$2"; shift 2 ;;
|
||||
--title) TITLE="$2"; shift 2 ;;
|
||||
--expires-hours) EXPIRES_H="$2"; shift 2 ;;
|
||||
|
|
@ -183,7 +184,7 @@ print(json.dumps(d))' "$COMMAND" "${GLOBS[@]:-}")"
|
|||
[ -n "$ZIP" ] || ZIP="$HOME/Desktop/igneum-prove-wsl2.zip"
|
||||
read -r ZURL ZSHA ZSIZE < <(hosted_file "$ZIP")
|
||||
[ -n "$PLATFORM" ] || PLATFORM=windows
|
||||
[ -n "$REQUIRES" ] || REQUIRES=wsl-prover
|
||||
[ "$REQUIRES_SET" = 1 ] || REQUIRES=wsl-prover
|
||||
PARAMS="$(python3 -c 'import json,sys; a=sys.argv; d={"zip_url": a[1], "sha256": a[2], "size": int(a[3])}
|
||||
if a[4]: d["fixtures"]=a[4].split()
|
||||
if a[5]: d["cap_minutes"]=int(a[5])
|
||||
|
|
|
|||
Loading…
Reference in a new issue