Skip to content

Commit f4b2313

Browse files
committed
Merge branch 'main' into project-scoped-oxql
# Conflicts: # nexus/tests/integration_tests/metrics.rs
2 parents 683df3e + 9285a7c commit f4b2313

1 file changed

Lines changed: 98 additions & 29 deletions

File tree

nexus/tests/integration_tests/metrics.rs

Lines changed: 98 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -301,32 +301,71 @@ async fn test_system_timeseries_schema_list(
301301
.expect("Failed to find HTTP request latency histogram schema");
302302
}
303303

304-
pub async fn timeseries_query(
304+
pub async fn timeseries_query_until_success(
305305
cptestctx: &ControlPlaneTestContext<omicron_nexus::Server>,
306306
query: impl ToString,
307307
) -> Vec<oxql_types::Table> {
308-
execute_timeseries_query(cptestctx, "/v1/system/timeseries/query", query)
309-
.await
308+
timeseries_query_until_success_(
309+
cptestctx,
310+
"/v1/system/timeseries/query",
311+
query,
312+
)
313+
.await
310314
}
311315

312-
pub async fn project_timeseries_query(
316+
pub async fn project_timeseries_query_until_success(
313317
cptestctx: &ControlPlaneTestContext<omicron_nexus::Server>,
314318
project: &str,
315319
query: impl ToString,
316320
) -> Vec<oxql_types::Table> {
317-
execute_timeseries_query(
321+
timeseries_query_until_success_(
318322
cptestctx,
319323
&format!("/v1/timeseries/query?project={}", project),
320324
query,
321325
)
322326
.await
323327
}
324328

325-
async fn execute_timeseries_query(
329+
/// Run an OxQL query until it succeeds or panics.
330+
async fn timeseries_query_until_success_(
326331
cptestctx: &ControlPlaneTestContext<omicron_nexus::Server>,
327332
endpoint: &str,
328333
query: impl ToString,
329334
) -> Vec<oxql_types::Table> {
335+
const POLL_INTERVAL: Duration = Duration::from_secs(1);
336+
const POLL_MAX: Duration = Duration::from_secs(30);
337+
let query_ = query.to_string();
338+
wait_for_condition(
339+
|| async {
340+
match execute_timeseries_query(cptestctx, endpoint, &query_).await {
341+
Some(r) => Ok(r),
342+
None => Err(CondCheckError::<()>::NotYet),
343+
}
344+
},
345+
&POLL_INTERVAL,
346+
&POLL_MAX,
347+
)
348+
.await
349+
.unwrap_or_else(|_| {
350+
panic!(
351+
"Timeseries named in query are not available \
352+
after {:?}, query: '{}'",
353+
POLL_MAX,
354+
query.to_string(),
355+
)
356+
})
357+
}
358+
359+
/// Run an OxQL query.
360+
///
361+
/// This returns `None` if the query resulted in client error and the body
362+
/// indicates that a timeseries named in the query could not be found. In all
363+
/// other cases, it either succeeds or panics.
364+
pub async fn execute_timeseries_query(
365+
cptestctx: &ControlPlaneTestContext<omicron_nexus::Server>,
366+
endpoint: &str,
367+
query: impl ToString,
368+
) -> Option<Vec<oxql_types::Table>> {
330369
// first, make sure the latest timeseries have been collected.
331370
cptestctx
332371
.oximeter
@@ -353,14 +392,29 @@ async fn execute_timeseries_query(
353392
.unwrap_or_else(|e| {
354393
panic!("timeseries query failed: {e:?}\nquery: {query}")
355394
});
356-
rsp.parsed_body::<OxqlQueryResult>()
357-
.unwrap_or_else(|e| {
358-
panic!(
359-
"could not parse timeseries query response: {e:?}\n\
360-
query: {query}\nresponse: {rsp:#?}"
361-
);
362-
})
363-
.tables
395+
396+
// Check for a timeseries-not-found error specifically.
397+
if rsp.status.is_client_error() {
398+
let text = std::str::from_utf8(&rsp.body)
399+
.expect("Timeseries query response body should be UTF-8");
400+
if text.contains("Schema for timeseries") && text.contains("not found")
401+
{
402+
return None;
403+
}
404+
}
405+
406+
// Try to parse the query as usual, which will fail on other kinds of
407+
// errors.
408+
Some(
409+
rsp.parsed_body::<OxqlQueryResult>()
410+
.unwrap_or_else(|e| {
411+
panic!(
412+
"could not parse timeseries query response: {e:?}\n\
413+
query: {query}\nresponse: {rsp:#?}"
414+
);
415+
})
416+
.tables,
417+
)
364418
}
365419

366420
#[nexus_test]
@@ -467,7 +521,7 @@ async fn test_instance_watcher_metrics(
467521
// activate the instance watcher background task.
468522
activate_instance_watcher().await;
469523

470-
let metrics = timeseries_query(&cptestctx, OXQL_QUERY).await;
524+
let metrics = timeseries_query_until_success(&cptestctx, OXQL_QUERY).await;
471525
let checks = metrics
472526
.iter()
473527
.find(|t| t.name() == "virtual_machine:check")
@@ -483,7 +537,7 @@ async fn test_instance_watcher_metrics(
483537
// activate the instance watcher background task.
484538
activate_instance_watcher().await;
485539

486-
let metrics = timeseries_query(&cptestctx, OXQL_QUERY).await;
540+
let metrics = timeseries_query_until_success(&cptestctx, OXQL_QUERY).await;
487541
let checks = metrics
488542
.iter()
489543
.find(|t| t.name() == "virtual_machine:check")
@@ -500,7 +554,7 @@ async fn test_instance_watcher_metrics(
500554
// activate the instance watcher background task.
501555
activate_instance_watcher().await;
502556

503-
let metrics = timeseries_query(&cptestctx, OXQL_QUERY).await;
557+
let metrics = timeseries_query_until_success(&cptestctx, OXQL_QUERY).await;
504558
let checks = metrics
505559
.iter()
506560
.find(|t| t.name() == "virtual_machine:check")
@@ -525,7 +579,7 @@ async fn test_instance_watcher_metrics(
525579
// activate the instance watcher background task.
526580
activate_instance_watcher().await;
527581

528-
let metrics = timeseries_query(&cptestctx, OXQL_QUERY).await;
582+
let metrics = timeseries_query_until_success(&cptestctx, OXQL_QUERY).await;
529583
let checks = metrics
530584
.iter()
531585
.find(|t| t.name() == "virtual_machine:check")
@@ -554,7 +608,7 @@ async fn test_instance_watcher_metrics(
554608
// activate the instance watcher background task.
555609
activate_instance_watcher().await;
556610

557-
let metrics = timeseries_query(&cptestctx, OXQL_QUERY).await;
611+
let metrics = timeseries_query_until_success(&cptestctx, OXQL_QUERY).await;
558612
let checks = metrics
559613
.iter()
560614
.find(|t| t.name() == "virtual_machine:check")
@@ -599,42 +653,57 @@ async fn test_project_timeseries_query(
599653
// Query with no project specified
600654
let q1 = "get virtual_machine:check";
601655

602-
let result = project_timeseries_query(&cptestctx, "project1", q1).await;
656+
let result =
657+
project_timeseries_query_until_success(&cptestctx, "project1", q1)
658+
.await;
603659
assert_eq!(result.len(), 1);
604660
assert!(result[0].timeseries().len() > 0);
605661

606662
// also works with project ID
607-
let result =
608-
project_timeseries_query(&cptestctx, &p1.identity.id.to_string(), q1)
609-
.await;
663+
let result = project_timeseries_query_until_success(
664+
&cptestctx,
665+
&p1.identity.id.to_string(),
666+
q1,
667+
)
668+
.await;
610669
assert_eq!(result.len(), 1);
611670
assert!(result[0].timeseries().len() > 0);
612671

613-
let result = project_timeseries_query(&cptestctx, "project2", q1).await;
672+
let result =
673+
project_timeseries_query_until_success(&cptestctx, "project2", q1)
674+
.await;
614675
assert_eq!(result.len(), 1);
615676
assert!(result[0].timeseries().len() > 0);
616677

617678
// with project specified
618679
let q2 = &format!("{} | filter project_id == \"{}\"", q1, p1.identity.id);
619680

620-
let result = project_timeseries_query(&cptestctx, "project1", q2).await;
681+
let result =
682+
project_timeseries_query_until_success(&cptestctx, "project1", q2)
683+
.await;
621684
assert_eq!(result.len(), 1);
622685
assert!(result[0].timeseries().len() > 0);
623686

624-
let result = project_timeseries_query(&cptestctx, "project2", q2).await;
687+
let result =
688+
project_timeseries_query_until_success(&cptestctx, "project2", q2)
689+
.await;
625690
assert_eq!(result.len(), 1);
626691
assert_eq!(result[0].timeseries().len(), 0);
627692

628693
// with instance specified
629694
let q3 = &format!("{} | filter instance_id == \"{}\"", q1, i1.identity.id);
630695

631696
// project containing instance gives me something
632-
let result = project_timeseries_query(&cptestctx, "project1", q3).await;
697+
let result =
698+
project_timeseries_query_until_success(&cptestctx, "project1", q3)
699+
.await;
633700
assert_eq!(result.len(), 1);
634701
assert_eq!(result[0].timeseries().len(), 1);
635702

636703
// should be empty or error
637-
let result = project_timeseries_query(&cptestctx, "project2", q3).await;
704+
let result =
705+
project_timeseries_query_until_success(&cptestctx, "project2", q3)
706+
.await;
638707
assert_eq!(result.len(), 1);
639708
assert_eq!(result[0].timeseries().len(), 0);
640709

@@ -868,7 +937,7 @@ async fn test_mgs_metrics(
868937
.try_force_collect()
869938
.await
870939
.expect("Could not force oximeter collection");
871-
let table = timeseries_query(&cptestctx, &query)
940+
let table = timeseries_query_until_success(&cptestctx, &query)
872941
.await
873942
.into_iter()
874943
.find(|t| t.name() == name)

0 commit comments

Comments
 (0)