Expose task placement reasons in CLI summaries
This commit is contained in:
parent
fe6c2d5042
commit
218ab65e47
4 changed files with 161 additions and 33 deletions
|
|
@ -1783,8 +1783,12 @@ fn human_report(value: &Value) -> String {
|
|||
if let Some(current_task_count) = value.get("current_task_count").and_then(Value::as_u64) {
|
||||
lines.push(format!("tasks: {current_task_count}"));
|
||||
}
|
||||
if let Some(tasks) = value.get("current_tasks").and_then(Value::as_array) {
|
||||
push_task_placement_reasons(&mut lines, tasks);
|
||||
}
|
||||
if let Some(tasks) = value.get("tasks").and_then(Value::as_array) {
|
||||
lines.push(format!("tasks: {}", tasks.len()));
|
||||
push_task_placement_reasons(&mut lines, tasks);
|
||||
}
|
||||
if let Some(log_entries) = value.get("log_entries").and_then(Value::as_array) {
|
||||
lines.push(format!("log entries: {}", log_entries.len()));
|
||||
|
|
@ -1976,6 +1980,33 @@ fn push_string_field(lines: &mut Vec<String>, value: &Value, key: &str, label: &
|
|||
}
|
||||
}
|
||||
|
||||
fn push_task_placement_reasons(lines: &mut Vec<String>, tasks: &[Value]) {
|
||||
for task in tasks {
|
||||
let Some(placement) = task.get("node_placement") else {
|
||||
continue;
|
||||
};
|
||||
let Some(reasons) = placement.get("reasons").and_then(Value::as_array) else {
|
||||
continue;
|
||||
};
|
||||
let reasons = reasons.iter().filter_map(Value::as_str).collect::<Vec<_>>();
|
||||
if reasons.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let task_name = task
|
||||
.get("task")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("unknown");
|
||||
let node = placement
|
||||
.get("node")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("unknown");
|
||||
lines.push(format!(
|
||||
"placement {task_name}: {node} ({})",
|
||||
reasons.join(", ")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn push_nested_string_field(lines: &mut Vec<String>, value: &Value, key: &str, label: &str) {
|
||||
if let Some(text) = value.get(key).and_then(Value::as_str) {
|
||||
lines.push(format!("{label}: {text}"));
|
||||
|
|
@ -2514,6 +2545,15 @@ fn task_summaries(task_events: Option<&Value>) -> Value {
|
|||
let terminal_state =
|
||||
event_string(event, "terminal_state").unwrap_or_else(|| "unknown".to_owned());
|
||||
let node = event_string(event, "node");
|
||||
let placement = event.get("placement");
|
||||
let placement_reasons = placement
|
||||
.and_then(|placement| placement.get("reasons"))
|
||||
.cloned()
|
||||
.unwrap_or_else(|| json!([]));
|
||||
let placement_score = placement
|
||||
.and_then(|placement| placement.get("score"))
|
||||
.cloned()
|
||||
.unwrap_or(Value::Null);
|
||||
json!({
|
||||
"process": event_string(event, "process"),
|
||||
"task": task,
|
||||
|
|
@ -2523,6 +2563,9 @@ fn task_summaries(task_events: Option<&Value>) -> Value {
|
|||
"node_placement": {
|
||||
"node": node,
|
||||
"source": "coordinator_task_event",
|
||||
"score": placement_score,
|
||||
"reasons": placement_reasons,
|
||||
"explanation_available": placement.is_some(),
|
||||
},
|
||||
"failure_reason": task_failure_reason(event),
|
||||
"stdout_bytes": event_u64(event, "stdout_bytes").unwrap_or(0),
|
||||
|
|
@ -5345,7 +5388,7 @@ mod tests {
|
|||
let server = std::thread::spawn(move || {
|
||||
let response = concat!(
|
||||
r#"{"type":"task_events","events":["#,
|
||||
r#"{"tenant":"tenant","project":"project","process":"vp","node":"node-a","task":"task-a","terminal_state":"completed","status_code":0,"stdout_bytes":12,"stderr_bytes":0,"stdout_tail":"ok","stderr_tail":"","stdout_truncated":false,"stderr_truncated":false,"artifact_path":"/vfs/artifacts/app.txt","artifact_digest":"sha256:artifact","artifact_size_bytes":12},"#,
|
||||
r#"{"tenant":"tenant","project":"project","process":"vp","node":"node-a","task":"task-a","placement":{"node":"node-a","score":120,"reasons":["warm environment cache","source snapshot already local"]},"terminal_state":"completed","status_code":0,"stdout_bytes":12,"stderr_bytes":0,"stdout_tail":"ok","stderr_tail":"","stdout_truncated":false,"stderr_truncated":false,"artifact_path":"/vfs/artifacts/app.txt","artifact_digest":"sha256:artifact","artifact_size_bytes":12},"#,
|
||||
r#"{"tenant":"tenant","project":"project","process":"vp","node":"node-b","task":"task-b","terminal_state":"failed","status_code":1,"stdout_bytes":0,"stderr_bytes":7,"stdout_tail":"","stderr_tail":"boom","stdout_truncated":false,"stderr_truncated":false,"artifact_path":null,"artifact_digest":null,"artifact_size_bytes":null}"#,
|
||||
r#"]}"#
|
||||
);
|
||||
|
|
@ -5397,6 +5440,14 @@ mod tests {
|
|||
process["current_tasks"][0]["node_placement"]["node"],
|
||||
"node-a"
|
||||
);
|
||||
assert_eq!(
|
||||
process["current_tasks"][0]["node_placement"]["reasons"][0],
|
||||
"warm environment cache"
|
||||
);
|
||||
assert_eq!(tasks["tasks"][0]["node_placement"]["score"], 120);
|
||||
let rendered_tasks = human_report(&tasks);
|
||||
assert!(rendered_tasks.contains("placement task-a: node-a"));
|
||||
assert!(rendered_tasks.contains("source snapshot already local"));
|
||||
assert_eq!(tasks["tasks"][1]["failure_reason"], "boom");
|
||||
assert_eq!(logs["log_entries"].as_array().unwrap().len(), 1);
|
||||
assert_eq!(logs["log_entries"][0]["task"], "task-a");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue