Name update
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
//! Spins up the real axum router against a temp ISO store + settings store,
|
||||
//! then walks an imaginary iPXE client through: dashboard status → upload
|
||||
//! ISO → fetch top-level boot menu → fetch per-entry script → Range-GET the
|
||||
//! ISO. Also drives the Gated Deployment flow end-to-end: two clients join,
|
||||
//! ISO. Also drives the Queued Deployment flow end-to-end: two clients join,
|
||||
//! operator assigns, both polls return the chain script with retry fallback.
|
||||
//!
|
||||
//! This is the closest we can get to "real PXE client" without QEMU; the
|
||||
@@ -12,9 +12,9 @@
|
||||
|
||||
use axum::body::Body;
|
||||
use axum::http::{header, Request, StatusCode};
|
||||
use pxeforge_core::{ClientRegistry, GateQueue, HostBindings, LogBus, Metrics, SettingsStore};
|
||||
use pxeforge_http_api::{build_router, AppState};
|
||||
use pxeforge_iso_store::{IsoStore, NfsManager};
|
||||
use openpxe_core::{ClientRegistry, DeploymentQueue, HostBindings, LogBus, Metrics, SettingsStore};
|
||||
use openpxe_http_api::{build_router, AppState};
|
||||
use openpxe_iso_store::{IsoStore, NfsManager};
|
||||
use tempfile::tempdir;
|
||||
use tower::ServiceExt;
|
||||
|
||||
@@ -87,7 +87,7 @@ async fn build_state() -> (AppState, tempfile::TempDir) {
|
||||
let iso_store = IsoStore::new(dir.path().join("isos"));
|
||||
iso_store.ensure_dirs().await.unwrap();
|
||||
let clients = ClientRegistry::new();
|
||||
let gates = GateQueue::new();
|
||||
let gates = DeploymentQueue::new();
|
||||
let settings = SettingsStore::load_or_default(dir.path());
|
||||
let nfs = NfsManager::new(dir.path(), iso_store.clone());
|
||||
iso_store.set_nfs_root(nfs.mount_root());
|
||||
@@ -97,7 +97,7 @@ async fn build_state() -> (AppState, tempfile::TempDir) {
|
||||
let state = AppState {
|
||||
iso_store,
|
||||
clients,
|
||||
gates,
|
||||
queue: gates,
|
||||
settings,
|
||||
hosts,
|
||||
metrics,
|
||||
@@ -198,7 +198,7 @@ async fn iso_range_request_slices_correctly() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn gated_deployment_full_flow() {
|
||||
async fn queued_deployment_full_flow() {
|
||||
let (state, _dir) = build_state().await;
|
||||
let app = build_router(state.clone());
|
||||
|
||||
@@ -210,22 +210,22 @@ async fn gated_deployment_full_flow() {
|
||||
.await.unwrap();
|
||||
|
||||
// Two clients join.
|
||||
let (_, join1) = get(&app, "/api/gate/join?mac=aa:bb:cc:00:00:01").await;
|
||||
let (_, join2) = get(&app, "/api/gate/join?mac=aa:bb:cc:00:00:02").await;
|
||||
let (_, join1) = get(&app, "/api/queue/join?mac=aa:bb:cc:00:00:01").await;
|
||||
let (_, join2) = get(&app, "/api/queue/join?mac=aa:bb:cc:00:00:02").await;
|
||||
let s1 = String::from_utf8(join1).unwrap();
|
||||
let s2 = String::from_utf8(join2).unwrap();
|
||||
assert!(s1.contains("Gate Position 1"));
|
||||
assert!(s2.contains("Gate Position 2"));
|
||||
|
||||
let gate1_id = s1.lines().find_map(|l| l.strip_prefix("chain http://127.0.0.1/api/gate/poll/"))
|
||||
let gate1_id = s1.lines().find_map(|l| l.strip_prefix("chain http://127.0.0.1/api/queue/poll/"))
|
||||
.unwrap().to_string();
|
||||
let gate2_id = s2.lines().find_map(|l| l.strip_prefix("chain http://127.0.0.1/api/gate/poll/"))
|
||||
let gate2_id = s2.lines().find_map(|l| l.strip_prefix("chain http://127.0.0.1/api/queue/poll/"))
|
||||
.unwrap().to_string();
|
||||
|
||||
// Kick off a long-poll for client 1 in the background. Then assign.
|
||||
let app2 = app.clone();
|
||||
let poll_future = tokio::spawn(async move {
|
||||
let uri = format!("/api/gate/poll/{gate1_id}");
|
||||
let uri = format!("/api/queue/poll/{gate1_id}");
|
||||
get(&app2, &uri).await
|
||||
});
|
||||
|
||||
@@ -233,15 +233,15 @@ async fn gated_deployment_full_flow() {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
|
||||
|
||||
// Operator assigns.
|
||||
let body = format!(r#"{{"target":"fake-alpine-linux","gate_ids":["{gate2_id}"]}}"#);
|
||||
let (s, b) = post_json(&app, "/api/gate/assign", &body).await;
|
||||
let body = format!(r#"{{"target":"fake-alpine-linux","entry_ids":["{gate2_id}"]}}"#);
|
||||
let (s, b) = post_json(&app, "/api/queue/assign", &body).await;
|
||||
assert_eq!(s, StatusCode::OK);
|
||||
let assign_json = String::from_utf8(b).unwrap();
|
||||
assert!(assign_json.contains(r#""assigned":1"#), "assign response: {assign_json}");
|
||||
|
||||
// Now assign to gate 1 too so the background poll wakes.
|
||||
let body = r#"{"target":"fake-alpine-linux","gate_ids":[]}"#;
|
||||
post_json(&app, "/api/gate/assign", body).await;
|
||||
let body = r#"{"target":"fake-alpine-linux","entry_ids":[]}"#;
|
||||
post_json(&app, "/api/queue/assign", body).await;
|
||||
|
||||
let (poll_status, poll_body) = poll_future.await.unwrap();
|
||||
assert_eq!(poll_status, StatusCode::OK);
|
||||
@@ -251,12 +251,12 @@ async fn gated_deployment_full_flow() {
|
||||
"poll response should chain the boot script:\n{poll_s}"
|
||||
);
|
||||
// Retry-on-error fallback must be present.
|
||||
assert!(poll_s.contains("|| chain http://127.0.0.1/api/gate/poll/"),
|
||||
assert!(poll_s.contains("|| chain http://127.0.0.1/api/queue/poll/"),
|
||||
"retry fallback missing");
|
||||
|
||||
// Bad target must be rejected.
|
||||
let (_, bad) = post_json(&app, "/api/gate/assign",
|
||||
r#"{"target":"does-not-exist","gate_ids":[]}"#).await;
|
||||
let (_, bad) = post_json(&app, "/api/queue/assign",
|
||||
r#"{"target":"does-not-exist","entry_ids":[]}"#).await;
|
||||
let bad_s = String::from_utf8(bad).unwrap();
|
||||
assert!(bad_s.contains(r#""ok":false"#), "expected rejection: {bad_s}");
|
||||
}
|
||||
@@ -329,7 +329,7 @@ async fn ui_assets_served_offline() {
|
||||
("/assets/app.js", "application/javascript"),
|
||||
("/assets/app.css", "text/css"),
|
||||
("/assets/logo.svg", "image/svg+xml"),
|
||||
("/assets/anvil-forge.svg", "image/svg+xml"),
|
||||
("/assets/loader.svg", "image/svg+xml"),
|
||||
] {
|
||||
let res = app
|
||||
.clone()
|
||||
@@ -406,14 +406,14 @@ async fn terminal_help_and_status_round_trip() {
|
||||
let (s, b) = post_json(&app, "/api/terminal", r#"{"command":""}"#).await;
|
||||
assert_eq!(s, StatusCode::OK);
|
||||
let v: serde_json::Value = serde_json::from_slice(&b).unwrap();
|
||||
assert!(v["output"].as_str().unwrap().contains("PXEForge terminal"));
|
||||
assert!(v["output"].as_str().unwrap().contains("OpenPXE terminal"));
|
||||
|
||||
// status -> contains the version banner.
|
||||
let (s, b) = post_json(&app, "/api/terminal", r#"{"command":"status"}"#).await;
|
||||
assert_eq!(s, StatusCode::OK);
|
||||
let v: serde_json::Value = serde_json::from_slice(&b).unwrap();
|
||||
let out = v["output"].as_str().unwrap();
|
||||
assert!(out.starts_with("PXEForge"), "unexpected status output: {out}");
|
||||
assert!(out.starts_with("OpenPXE"), "unexpected status output: {out}");
|
||||
assert!(out.contains("isos:"), "status missing iso line: {out}");
|
||||
|
||||
// Unknown command -> ok=false plus help hint.
|
||||
@@ -477,7 +477,7 @@ async fn windows_iso_renders_clean_wimboot_script_with_no_trust_store_writes() {
|
||||
.uri("/api/settings")
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from(
|
||||
r#"{"boot_menu_timeout_secs":600,"timeout_action":"gated_deployment",
|
||||
r#"{"boot_menu_timeout_secs":600,"timeout_action":"queued_deployment",
|
||||
"windows_enabled":false,"smb_host_override":"","extra_kernel_args":"",
|
||||
"default_local_hdd":true,"gate_wait_max_secs":0,"dns_server":""}"#
|
||||
.to_string(),
|
||||
@@ -623,19 +623,19 @@ async fn metrics_endpoint_emits_prometheus_format() {
|
||||
let body = String::from_utf8(body.to_vec()).unwrap();
|
||||
// Spot-check the must-have metric families.
|
||||
for name in [
|
||||
"pxeforge_dhcp_replies_total",
|
||||
"pxeforge_tftp_transfers_total",
|
||||
"pxeforge_http_requests_total",
|
||||
"pxeforge_iso_count",
|
||||
"pxeforge_uptime_seconds",
|
||||
"pxeforge_build_info",
|
||||
"openpxe_dhcp_replies_total",
|
||||
"openpxe_tftp_transfers_total",
|
||||
"openpxe_http_requests_total",
|
||||
"openpxe_iso_count",
|
||||
"openpxe_uptime_seconds",
|
||||
"openpxe_build_info",
|
||||
] {
|
||||
assert!(body.contains(name), "missing metric {name} in:\n{body}");
|
||||
}
|
||||
// Each name appears exactly once as a `# TYPE` declaration.
|
||||
for name in [
|
||||
"pxeforge_dhcp_replies_total",
|
||||
"pxeforge_iso_count",
|
||||
"openpxe_dhcp_replies_total",
|
||||
"openpxe_iso_count",
|
||||
] {
|
||||
let count = body.matches(&format!("# TYPE {name}")).count();
|
||||
assert_eq!(count, 1, "{name} TYPE line appears {count} times");
|
||||
|
||||
Reference in New Issue
Block a user