OXYGENHUB

A WASM job's placement names no image d7050703d2f4

The job has none; the line now starts with what the runner is doing.

closes #323
Author
Andy McCall @andymccall <[email protected]>
When
· 2026-10-26 02:36 UTC
Signature
◈ sealed by Andy McCall
Hash
d7050703d2f4c830367a32398629c87fe74160c58d1937c32b71eb313d29517f
Depends on
42fa95540468 501d8b268dd4 8501fd5815b9 a7e6e8e6094c b5668108487f c2be148cfe83
Depended on by
c26267b3aba6 1ec29691b67a dc5c64296b0a 1000b40a2629 b6dfcb61cf5e
In bookmarks
api-unknown-fields diffusion-pin-bookmark main proposal-list-fast
In tags
v0.9.11 v0.9.15 v0.9.16 v0.9.19 v0.9.20 v0.9.21 v0.9.22 v0.9.23 v0.9.24 v0.9.6 v0.9.7 v0.9.8 v0.9.9
Edits
4 atoms, 0 tree operations, 3 files

Browse the tree at this change

modified CHANGELOG.md +1 −0
…
3 unchanged lines
4 4
5 5
## Unreleased
6 6
7 +
- **A WASM job's placement names no image (#323):** a job on the WASM tier was placed saying its runner "fetches the image from the hub"; it has none, so the line now starts with what the runner is doing (andymccall/windows-runner-demo run #1).
7 8
- **Runner updates: policy, test groups and the offer (#322):** Admin › Runners › Updates sets where runner builds come from (a repository whose releases carry `flow-linux-ARCH` files) and who takes them. Updates are off unless chosen; the site, an owner, a test group and a runner may each set updates on or off, a channel (`stable`, `canary` or a version), a window of hours (UTC) and dry run, and the nearest setting wins, each setting on its own. A test group is chosen runners or a percentage of the site's (or an owner's) runners, by a stable hash of each runner's id, so a share is the same machines every rollout and 10% sits inside 50%. `GET /api/v1/flow/runner/update` tells a runner its policy, where each setting came from, and the build to install (its BLAKE3, to check the download against); `POST` takes what the runner did, shown on the page and in the audit log. Pins OxygenFlow's main.
8 9
9 10
## 0.9.5 (2026-10-26)
…
335 unchanged lines
modified crates/hub/src/flow.rs +9 −5
…
1109 unchanged lines
1110 1110
    let waited =
1111 1111
        HELD.lock().unwrap_or_else(|p| p.into_inner()).as_mut().and_then(|h| h.remove(&(hub_key.clone(), job.id))).map(|t| now - t);
1112 1112
    let rootfs = rootfs_of(&job);
1113 -
    let mut why = vec![if me_now.image_list().is_some_and(|have| have.contains(&rootfs)) {
1114 -
        "holds the image".to_string()
1115 -
    } else {
1116 -
        "fetches the image from the hub".to_string()
1117 -
    }];
1113 +
    // A WASM job has no image (#323), so nothing to hold or fetch.
1114 +
    let mut why = Vec::new();
1115 +
    if !rootfs.is_empty() {
1116 +
        why.push(if me_now.image_list().is_some_and(|have| have.contains(&rootfs)) {
1117 +
            "holds the image".to_string()
1118 +
        } else {
1119 +
            "fetches the image from the hub".to_string()
1120 +
        });
1121 +
    }
1118 1122
    if let Some(kept) = slots.get(&job.id).and_then(|slot| cache_kept(&hub_key, me.id, slot)) {
1119 1123
        why.insert(
1120 1124
            0,
…
1919 unchanged lines
modified crates/hub/tests/flow.rs +24 −0
…
6709 unchanged lines
6710 6710
    assert!(page.contains("Test group canary removed"), "{page}");
6711 6711
    assert!(!ask(0).await.policy.updates, "back to the site's off");
6712 6712
}
6713 +
6714 +
/// #323: a WASM job has no image, so its placement says nothing of one.
6715 +
#[tokio::test(flavor = "multi_thread")]
6716 +
async fn a_wasm_jobs_placement_names_no_image() {
6717 +
    let h = hub().await;
6718 +
    create(&h.app, "andy", "wasm").await;
6719 +
    let work = tempfile::tempdir().unwrap();
6720 +
    let c = client(&format!("{}/andy/wasm", h.base), work.path());
6721 +
    write(
6722 +
        work.path(),
6723 +
        ".flow/pipeline.kdl",
6724 +
        "schema-version 1\nsandbox \"w\" {\n    runtime \"wasm\"\n}\njob \"count\" {\n    sandbox \"w\"\n    exec \"count.wasm\"\n}\n",
6725 +
    );
6726 +
    write(work.path(), "count.wasm", "\0asm");
6727 +
    c.commit("A WASM job", &[]).unwrap();
6728 +
    assert!(c.push("origin", false).unwrap().iter().all(|r| r.ok));
6729 +
    runs_of(&h.app, "andy", "wasm").await;
6730 +
    let (st, body) =
6731 +
        call(&h.app, "POST", "/api/v1/flow/claim", Some("runner-secret"), Some(r#"{"runner":"test-1","runtimes":["wasm"]}"#.into())).await;
6732 +
    assert_eq!(st, StatusCode::OK, "{body}");
6733 +
    let id = serde_json::from_str::<serde_json::Value>(&body).unwrap()["job_id"].as_i64().unwrap();
6734 +
    let placed = db::flow::job(&h.app.db, id).await.unwrap().unwrap().placed;
6735 +
    assert!(placed.starts_with("idle") && !placed.contains("image"), "{placed}");
6736 +
}