mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-11 23:56:53 +00:00
test(ilm): cover manual job status after restart (#5256)
* test(ilm): cover manual job status after restart Co-Authored-By: heihutu <heihutu@gmail.com> * test(ilm): retry job status recovery after restart Co-Authored-By: heihutu <heihutu@gmail.com> --------- Co-authored-by: heihutu <heihutu@gmail.com>
This commit is contained in:
@@ -1282,6 +1282,66 @@ fn is_retryable_add_tier_error(response: &str) -> bool {
|
|||||||
response.contains("Remote tier configuration is already being replaced")
|
response.contains("Remote tier configuration is already being replaced")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn start_manual_transition_job(hot: &RustFSTestClusterEnvironment, bucket: &str) -> TestResult<String> {
|
||||||
|
let path = format!("/rustfs/admin/v3/ilm/transition/run?bucket={bucket}&async=true&dryRun=true&maxObjects=1");
|
||||||
|
let (status, response) =
|
||||||
|
signed_admin_request(&hot.nodes[0].url, Method::POST, &path, None, &hot.access_key, &hot.secret_key).await?;
|
||||||
|
assert_eq!(
|
||||||
|
status,
|
||||||
|
StatusCode::ACCEPTED,
|
||||||
|
"async manual transition run must be accepted: {}",
|
||||||
|
compact_body(&response)
|
||||||
|
);
|
||||||
|
let value: serde_json::Value = serde_json::from_str(&response)?;
|
||||||
|
assert_eq!(
|
||||||
|
value["state"].as_str(),
|
||||||
|
Some("accepted"),
|
||||||
|
"manual transition run state changed: {response}"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
value["mode"].as_str(),
|
||||||
|
Some("durable_job"),
|
||||||
|
"manual transition run mode changed: {response}"
|
||||||
|
);
|
||||||
|
value["job_id"]
|
||||||
|
.as_str()
|
||||||
|
.map(str::to_owned)
|
||||||
|
.ok_or_else(|| format!("manual transition run response omitted job_id: {response}").into())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_for_manual_transition_job_terminal(
|
||||||
|
hot: &RustFSTestClusterEnvironment,
|
||||||
|
node_index: usize,
|
||||||
|
job_id: &str,
|
||||||
|
retry_missing: bool,
|
||||||
|
) -> TestResult<serde_json::Value> {
|
||||||
|
let path = format!("/rustfs/admin/v3/ilm/transition/jobs/{job_id}");
|
||||||
|
let deadline = Instant::now() + Duration::from_secs(30);
|
||||||
|
loop {
|
||||||
|
let (status, response) =
|
||||||
|
signed_admin_request(&hot.nodes[node_index].url, Method::GET, &path, None, &hot.access_key, &hot.secret_key).await?;
|
||||||
|
if retry_missing && status == StatusCode::NOT_FOUND && Instant::now() < deadline {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
status,
|
||||||
|
StatusCode::OK,
|
||||||
|
"manual transition job status read failed: {}",
|
||||||
|
compact_body(&response)
|
||||||
|
);
|
||||||
|
let value: serde_json::Value = serde_json::from_str(&response)?;
|
||||||
|
match value["status"].as_str() {
|
||||||
|
Some("running") if Instant::now() < deadline => sleep(Duration::from_millis(200)).await,
|
||||||
|
Some("running") => {
|
||||||
|
return Err(format!("manual transition job {job_id} stayed running for 30s: {response}").into());
|
||||||
|
}
|
||||||
|
Some(_) => return Ok(value),
|
||||||
|
None => return Err(format!("manual transition job status response omitted status: {response}").into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn transition_rule(tier_name: &str) -> TestResult<LifecycleRule> {
|
fn transition_rule(tier_name: &str) -> TestResult<LifecycleRule> {
|
||||||
Ok(LifecycleRule::builder()
|
Ok(LifecycleRule::builder()
|
||||||
.id("inline-fallback-transition")
|
.id("inline-fallback-transition")
|
||||||
@@ -1722,6 +1782,62 @@ async fn four_node_add_tier_converges_after_offline_node_restart_without_second_
|
|||||||
wait_for_tier_converged(&hot, &tier_name, &add_tier_response).await
|
wait_for_tier_converged(&hot, &tier_name, &add_tier_response).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn four_node_manual_transition_job_status_survives_node_restart() -> TestResult {
|
||||||
|
init_logging();
|
||||||
|
|
||||||
|
let mut hot = RustFSTestClusterEnvironment::new(4).await?;
|
||||||
|
hot.start().await?;
|
||||||
|
|
||||||
|
let hot_client = hot.create_s3_client(0)?;
|
||||||
|
let bucket = format!("manual-transition-job-{}", Uuid::new_v4().simple());
|
||||||
|
hot_client.create_bucket().bucket(&bucket).send().await?;
|
||||||
|
|
||||||
|
let job_id = start_manual_transition_job(&hot, &bucket).await?;
|
||||||
|
let terminal = wait_for_manual_transition_job_terminal(&hot, 0, &job_id, false).await?;
|
||||||
|
assert_eq!(
|
||||||
|
terminal["status"].as_str(),
|
||||||
|
Some("completed"),
|
||||||
|
"dry-run manual transition job should complete: {terminal}"
|
||||||
|
);
|
||||||
|
assert_eq!(terminal["job_id"].as_str(), Some(job_id.as_str()));
|
||||||
|
assert_eq!(terminal["bucket"].as_str(), Some(bucket.as_str()));
|
||||||
|
assert_eq!(terminal["dry_run"].as_bool(), Some(true));
|
||||||
|
|
||||||
|
hot.stop_node(3)?;
|
||||||
|
hot.start_node(3).await?;
|
||||||
|
let after_restart = wait_for_manual_transition_job_terminal(&hot, 3, &job_id, true).await?;
|
||||||
|
assert_eq!(after_restart["status"], terminal["status"], "terminal job status changed after restart");
|
||||||
|
assert_eq!(after_restart["job_id"].as_str(), Some(job_id.as_str()));
|
||||||
|
assert_eq!(after_restart["bucket"].as_str(), Some(bucket.as_str()));
|
||||||
|
assert_eq!(after_restart["dry_run"].as_bool(), Some(true));
|
||||||
|
|
||||||
|
let missing_job_id = Uuid::new_v4();
|
||||||
|
let (missing_status, missing_body) = signed_admin_request(
|
||||||
|
&hot.nodes[3].url,
|
||||||
|
Method::GET,
|
||||||
|
&format!("/rustfs/admin/v3/ilm/transition/jobs/{missing_job_id}"),
|
||||||
|
None,
|
||||||
|
&hot.access_key,
|
||||||
|
&hot.secret_key,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
assert_eq!(
|
||||||
|
missing_status,
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
"unknown manual transition job must remain a 404 after restart: {}",
|
||||||
|
compact_body(&missing_body)
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
missing_body.contains("<Code>NoSuchKey</Code>") || missing_body.contains("NoSuchKey"),
|
||||||
|
"unknown manual transition job should return NoSuchKey, body: {}",
|
||||||
|
compact_body(&missing_body)
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn four_node_mixed_msgpack_compat_mode_preserves_fallback_controls_during_transition() -> TestResult {
|
async fn four_node_mixed_msgpack_compat_mode_preserves_fallback_controls_during_transition() -> TestResult {
|
||||||
|
|||||||
Reference in New Issue
Block a user