Skip to content

Commit 2fa2018

Browse files
committed
it almost kind of works
1 parent 1a5a2e5 commit 2fa2018

File tree

2 files changed

+66
-25
lines changed

2 files changed

+66
-25
lines changed

nexus/src/app/metrics.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,16 @@ impl super::Nexus {
187187
query: impl AsRef<str>,
188188
) -> Result<Vec<oxql_types::Table>, Error> {
189189
// Ensure the user has read access to the project
190-
let (.., authz_project) =
190+
let (authz_silo, authz_project) =
191191
project_lookup.lookup_for(authz::Action::Read).await?;
192192

193-
let parsed_query = oximeter_db::oxql::Query::new(query.as_ref())
194-
.map_err(|e| Error::invalid_request(e.to_string()))?;
195-
196-
// Check that the query only refers to the project
193+
// Ensure the query only refers to the project
194+
let filtered_query = format!(
195+
"{} | filter silo_id == \"{}\" && project_id == \"{}\"",
196+
query.as_ref(),
197+
authz_silo.id(),
198+
authz_project.id()
199+
);
197200

198201
self.timeseries_client
199202
.get()
@@ -204,7 +207,7 @@ impl super::Nexus {
204207
e
205208
))
206209
})?
207-
.oxql_query(query)
210+
.oxql_query(filtered_query)
208211
.await
209212
.map(|result| result.tables)
210213
.map_err(|e| match e {

nexus/tests/integration_tests/metrics.rs

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use chrono::Utc;
1111
use dropshot::test_util::ClientTestContext;
1212
use dropshot::ResultsPage;
1313
use http::{Method, StatusCode};
14+
use nexus_test_utils::background::activate_background_task;
1415
use nexus_test_utils::http_testing::{AuthnMode, NexusRequest, RequestBuilder};
1516
use nexus_test_utils::resource_helpers::{
1617
create_default_ip_pool, create_disk, create_instance, create_project,
@@ -298,7 +299,7 @@ pub async fn project_timeseries_query(
298299
) -> Vec<oxql_types::Table> {
299300
execute_timeseries_query(
300301
cptestctx,
301-
&format!("/v1/timeseries/query/projects/{}", project),
302+
&format!("/v1/timeseries/query/project/{}", project),
302303
query,
303304
)
304305
.await
@@ -554,33 +555,70 @@ async fn test_project_timeseries_query(
554555
) {
555556
let client = &cptestctx.external_client;
556557

558+
create_default_ip_pool(&client).await; // needed for instance create to work
559+
557560
// Create two projects
558-
let project1 = create_project(&client, "project1").await;
559-
let project2 = create_project(&client, "project2").await;
561+
let p1 = create_project(&client, "project1").await;
562+
let _p2 = create_project(&client, "project2").await;
560563

561564
// Create resources in each project
562-
create_instance(&client, "project1", "instance1").await;
563-
create_instance(&client, "project2", "instance2").await;
565+
let i1 = create_instance(&client, "project1", "instance1").await;
566+
let _i2 = create_instance(&client, "project2", "instance2").await;
564567

565-
// Force a metrics collection
566-
cptestctx.oximeter.force_collect().await;
568+
let internal_client = &cptestctx.internal_client;
567569

568-
// Query for project1
569-
let query1 = "get virtual_machine:check"; // TODO: add project to query
570-
let result1 =
571-
project_timeseries_query(&cptestctx, "project1", query1).await;
570+
// get the instance metrics to show up
571+
let _ =
572+
activate_background_task(&internal_client, "instance_watcher").await;
572573

573-
// shouldn't work
574-
let result1 =
575-
project_timeseries_query(&cptestctx, "project2", query1).await;
574+
// Query with no project specified
575+
let q1 = "get virtual_machine:check";
576576

577-
// Query for project2
578-
let query2 = "get virtual_machine:check";
579-
let result2 =
580-
project_timeseries_query(&cptestctx, "project2", query2).await;
577+
let result = project_timeseries_query(&cptestctx, "project1", q1).await;
578+
assert_eq!(result.len(), 1);
579+
assert!(result[0].timeseries().len() > 0);
580+
581+
let result = project_timeseries_query(&cptestctx, "project2", q1).await;
582+
assert_eq!(result.len(), 1);
583+
assert!(result[0].timeseries().len() > 0);
584+
585+
// with project specified
586+
let q2 = &format!("{} | filter project_id == \"{}\"", q1, p1.identity.id);
587+
588+
let result = project_timeseries_query(&cptestctx, "project1", q2).await;
589+
assert_eq!(result.len(), 1);
590+
assert!(result[0].timeseries().len() > 0);
591+
592+
let result = project_timeseries_query(&cptestctx, "project2", q2).await;
593+
assert_eq!(result.len(), 1);
594+
assert_eq!(result[0].timeseries().len(), 0);
595+
596+
// with instance specified
597+
let q3 = &format!("{} | filter instance_id == \"{}\"", q1, i1.identity.id);
598+
599+
// project containing instance gives me something
600+
let result = project_timeseries_query(&cptestctx, "project1", q3).await;
601+
assert_eq!(result.len(), 1);
602+
assert_eq!(result[0].timeseries().len(), 1);
603+
604+
// should be empty or error
605+
let result = project_timeseries_query(&cptestctx, "project2", q3).await;
606+
assert_eq!(result.len(), 1);
607+
assert_eq!(result[0].timeseries().len(), 0);
581608

582-
// Query with no project specified
583609
// Query with nonexistent project
610+
// Also test at least once with project ID in path instead of project name
611+
612+
// try a project in your silo that you can't read
613+
// try a project in another silo
614+
615+
// now try it adversarially — use a project you can read in the path, but
616+
// try to access a fleet metric... if I can find one that will work
617+
618+
// let q4 = "get hardware_component:temperature";
619+
// let result = dbg!(timeseries_query(&cptestctx, q4).await);
620+
// let result =
621+
// dbg!(project_timeseries_query(&cptestctx, "project2", q4).await);
584622
}
585623

586624
#[nexus_test]

0 commit comments

Comments
 (0)