Skip to content

Commit 8524ae0

Browse files
committed
fix(admin): report global heal operations status
1 parent bbc3406 commit 8524ae0

2 files changed

Lines changed: 57 additions & 5 deletions

File tree

crates/cli/src/commands/admin/heal.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ fn print_heal_status(status: &HealStatus, formatter: &Formatter) {
313313
formatter.println(&format!(" Last Update: {}", last_update));
314314
}
315315
} else {
316-
formatter.println(" No heal operation currently running.");
316+
formatter.println(" No active heal operation.");
317317
}
318318
}
319319

@@ -586,6 +586,15 @@ mod tests {
586586
assert!(output.status.is_none());
587587
}
588588

589+
#[test]
590+
fn test_heal_task_request_rejects_token_without_bucket() {
591+
let formatter = Formatter::default();
592+
593+
let result = heal_task_request(None, None, Some("root-token".to_string()), &formatter);
594+
595+
assert!(matches!(result, Err(ExitCode::UsageError)));
596+
}
597+
589598
#[test]
590599
fn test_heal_status_output_from() {
591600
let status = HealStatus {

crates/s3/src/admin.rs

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -401,22 +401,46 @@ struct BackgroundHealStatusResponse {
401401
heal_queue_length: u64,
402402
#[serde(default)]
403403
heal_active_tasks: u64,
404+
#[serde(default)]
405+
heal_operations: Option<BackgroundHealOperations>,
406+
}
407+
408+
#[derive(Debug, Deserialize)]
409+
#[serde(rename_all = "camelCase")]
410+
struct BackgroundHealOperations {
411+
#[serde(default)]
412+
queue_length: u64,
413+
#[serde(default)]
414+
active_tasks: u64,
404415
}
405416

406417
impl From<BackgroundHealStatusResponse> for HealStatus {
407418
fn from(response: BackgroundHealStatusResponse) -> Self {
408419
let scan_mode = background_heal_scan_mode(response.current_scan_mode);
409420
let legacy_healing = scan_mode.is_none() && response.bitrot_start_time.is_some();
421+
let queue_length = response
422+
.heal_operations
423+
.as_ref()
424+
.map_or(response.heal_queue_length, |operations| {
425+
response.heal_queue_length.max(operations.queue_length)
426+
});
427+
let active_tasks = response
428+
.heal_operations
429+
.as_ref()
430+
.map_or(response.heal_active_tasks, |operations| {
431+
response.heal_active_tasks.max(operations.active_tasks)
432+
});
433+
410434
Self {
411435
healing: matches!(scan_mode, Some(HealScanMode::Deep))
412-
|| response.heal_queue_length > 0
413-
|| response.heal_active_tasks > 0
436+
|| queue_length > 0
437+
|| active_tasks > 0
414438
|| legacy_healing,
415439
started: response.bitrot_start_time,
416440
scan_mode,
417441
scan_cycle: response.bitrot_start_cycle,
418-
heal_queue_length: response.heal_queue_length,
419-
heal_active_tasks: response.heal_active_tasks,
442+
heal_queue_length: queue_length,
443+
heal_active_tasks: active_tasks,
420444
..Default::default()
421445
}
422446
}
@@ -1371,6 +1395,7 @@ mod tests {
13711395
current_scan_mode: Some(2),
13721396
heal_queue_length: 3,
13731397
heal_active_tasks: 1,
1398+
heal_operations: None,
13741399
});
13751400

13761401
assert!(status.healing);
@@ -1386,6 +1411,7 @@ mod tests {
13861411
current_scan_mode: Some(1),
13871412
heal_queue_length: 0,
13881413
heal_active_tasks: 0,
1414+
heal_operations: None,
13891415
});
13901416
assert!(!idle.healing);
13911417
assert_eq!(idle.scan_mode, Some(HealScanMode::Normal));
@@ -1397,6 +1423,7 @@ mod tests {
13971423
current_scan_mode: Some(1),
13981424
heal_queue_length: 0,
13991425
heal_active_tasks: 0,
1426+
heal_operations: None,
14001427
});
14011428
assert!(!completed.healing);
14021429
assert_eq!(completed.scan_mode, Some(HealScanMode::Normal));
@@ -1408,6 +1435,7 @@ mod tests {
14081435
current_scan_mode: None,
14091436
heal_queue_length: 0,
14101437
heal_active_tasks: 0,
1438+
heal_operations: None,
14111439
});
14121440
assert!(legacy.healing);
14131441

@@ -1417,10 +1445,25 @@ mod tests {
14171445
current_scan_mode: None,
14181446
heal_queue_length: 0,
14191447
heal_active_tasks: 1,
1448+
heal_operations: None,
14201449
});
14211450
assert!(active.healing);
14221451
}
14231452

1453+
#[test]
1454+
fn test_background_heal_status_response_maps_nested_heal_operations() {
1455+
let response: BackgroundHealStatusResponse = serde_json::from_str(
1456+
r#"{"healOperations":{"queueLength":4,"activeTasks":1,"queuedBySource":{"admin":4}}}"#,
1457+
)
1458+
.expect("background heal status response should deserialize");
1459+
1460+
let status = HealStatus::from(response);
1461+
1462+
assert!(status.healing);
1463+
assert_eq!(status.heal_queue_length, 4);
1464+
assert_eq!(status.heal_active_tasks, 1);
1465+
}
1466+
14241467
#[test]
14251468
fn test_bad_request_maps_to_general_admin_error() {
14261469
let alias = Alias::new("test", "http://localhost:9000", "access", "secret");

0 commit comments

Comments
 (0)