diff --git a/nexus/db-queries/src/db/queries/region_allocation.rs b/nexus/db-queries/src/db/queries/region_allocation.rs index cc201dac30..83cc7483c9 100644 --- a/nexus/db-queries/src/db/queries/region_allocation.rs +++ b/nexus/db-queries/src/db/queries/region_allocation.rs @@ -369,6 +369,7 @@ mod test { use super::*; use crate::db::datastore::REGION_REDUNDANCY_THRESHOLD; use crate::db::explain::ExplainableAsync; + use crate::db::raw_query_builder::expectorate_query_contents; use nexus_test_utils::db::test_setup_database; use omicron_test_utils::dev; use uuid::Uuid; @@ -395,15 +396,11 @@ mod test { }, REGION_REDUNDANCY_THRESHOLD, ); - let s = dev::db::format_sql( - &diesel::debug_query::(®ion_allocate).to_string(), - ) - .await - .unwrap(); - expectorate::assert_contents( + expectorate_query_contents( + ®ion_allocate, "tests/output/region_allocate_distinct_sleds.sql", - &s, - ); + ) + .await; // Second structure: "Random" @@ -415,15 +412,11 @@ mod test { &RegionAllocationStrategy::Random { seed: Some(1) }, REGION_REDUNDANCY_THRESHOLD, ); - let s = dev::db::format_sql( - &diesel::debug_query::(®ion_allocate).to_string(), - ) - .await - .unwrap(); - expectorate::assert_contents( + expectorate_query_contents( + ®ion_allocate, "tests/output/region_allocate_random_sleds.sql", - &s, - ); + ) + .await; } // Explain the possible forms of the SQL query to ensure that it diff --git a/nexus/db-queries/src/db/queries/virtual_provisioning_collection_update.rs b/nexus/db-queries/src/db/queries/virtual_provisioning_collection_update.rs index 7672d5af9a..09798e4e5d 100644 --- a/nexus/db-queries/src/db/queries/virtual_provisioning_collection_update.rs +++ b/nexus/db-queries/src/db/queries/virtual_provisioning_collection_update.rs @@ -646,3 +646,90 @@ impl Query for VirtualProvisioningCollectionUpdate { } impl RunQueryDsl for VirtualProvisioningCollectionUpdate {} + +#[cfg(test)] +mod test { + use super::*; + use crate::db::raw_query_builder::expectorate_query_contents; + use uuid::Uuid; + + // These tests are a bit of a "change detector", but they're here to help + // with debugging too. If you change this query, it can be useful to see + // exactly how the output SQL has been altered. + + #[tokio::test] + async fn expectorate_query_insert_storage() { + let id = Uuid::nil(); + let project_id = Uuid::nil(); + let disk_byte_diff = 2048.try_into().unwrap(); + let storage_type = crate::db::datastore::StorageType::Disk; + + let query = VirtualProvisioningCollectionUpdate::new_insert_storage( + id, + disk_byte_diff, + project_id, + storage_type, + ); + expectorate_query_contents( + &query, + "tests/output/virtual_provisioning_collection_update_insert_storage.sql", + ).await; + } + + #[tokio::test] + async fn expectorate_query_delete_storage() { + let id = Uuid::nil(); + let project_id = Uuid::nil(); + let disk_byte_diff = 2048.try_into().unwrap(); + + let query = VirtualProvisioningCollectionUpdate::new_delete_storage( + id, + disk_byte_diff, + project_id, + ); + + expectorate_query_contents( + &query, + "tests/output/virtual_provisioning_collection_update_delete_storage.sql", + ).await; + } + + #[tokio::test] + async fn expectorate_query_insert_instance() { + let id = Uuid::nil(); + let project_id = Uuid::nil(); + let cpus_diff = 4; + let ram_diff = 2048.try_into().unwrap(); + + let query = VirtualProvisioningCollectionUpdate::new_insert_instance( + id, cpus_diff, ram_diff, project_id, + ); + + expectorate_query_contents( + &query, + "tests/output/virtual_provisioning_collection_update_insert_instance.sql", + ).await; + } + + #[tokio::test] + async fn expectorate_query_delete_instance() { + let id = Uuid::nil(); + let project_id = Uuid::nil(); + let cpus_diff = 4; + let ram_diff = 2048.try_into().unwrap(); + let max_instance_gen = 0; + + let query = VirtualProvisioningCollectionUpdate::new_delete_instance( + id, + max_instance_gen, + cpus_diff, + ram_diff, + project_id, + ); + + expectorate_query_contents( + &query, + "tests/output/virtual_provisioning_collection_update_delete_instance.sql", + ).await; + } +} diff --git a/nexus/db-queries/src/db/raw_query_builder.rs b/nexus/db-queries/src/db/raw_query_builder.rs index c7215417c5..d108062833 100644 --- a/nexus/db-queries/src/db/raw_query_builder.rs +++ b/nexus/db-queries/src/db/raw_query_builder.rs @@ -181,3 +181,18 @@ impl RunQueryDsl for TypedSqlQuery {} impl Query for TypedSqlQuery { type SqlType = T; } + +#[cfg(test)] +pub async fn expectorate_query_contents>( + query: T, + path: &str, +) { + use omicron_test_utils::dev; + + let s = + dev::db::format_sql(&diesel::debug_query::(&query).to_string()) + .await + .expect("Failed to format SQL"); + + expectorate::assert_contents(path, &s); +} diff --git a/nexus/db-queries/tests/output/virtual_provisioning_collection_update_delete_instance.sql b/nexus/db-queries/tests/output/virtual_provisioning_collection_update_delete_instance.sql new file mode 100644 index 0000000000..fcabefef26 --- /dev/null +++ b/nexus/db-queries/tests/output/virtual_provisioning_collection_update_delete_instance.sql @@ -0,0 +1,97 @@ +WITH + parent_silo AS (SELECT project.silo_id AS id FROM project WHERE project.id = $1), + all_collections + AS ( + ((SELECT $2 AS id) UNION (SELECT parent_silo.id AS id FROM parent_silo)) + UNION (SELECT $3 AS id) + ), + quotas + AS ( + SELECT + silo_quotas.silo_id, + silo_quotas.cpus, + silo_quotas.memory_bytes AS memory, + silo_quotas.storage_bytes AS storage + FROM + silo_quotas INNER JOIN parent_silo ON silo_quotas.silo_id = parent_silo.id + ), + silo_provisioned + AS ( + SELECT + virtual_provisioning_collection.id, + virtual_provisioning_collection.cpus_provisioned, + virtual_provisioning_collection.ram_provisioned, + virtual_provisioning_collection.virtual_disk_bytes_provisioned + FROM + virtual_provisioning_collection + INNER JOIN parent_silo ON virtual_provisioning_collection.id = parent_silo.id + ), + do_update + AS ( + SELECT + ( + SELECT + count(*) + FROM + virtual_provisioning_resource + WHERE + virtual_provisioning_resource.id = $4 + LIMIT + $5 + ) + = $6 + AS update + ), + unused_cte_arm + AS ( + DELETE FROM + virtual_provisioning_resource + WHERE + virtual_provisioning_resource.id = $7 + AND virtual_provisioning_resource.id + = ( + SELECT + instance.id + FROM + instance + WHERE + instance.id = $8 AND instance.state_generation < $9 + LIMIT + $10 + ) + RETURNING + virtual_provisioning_resource.id, + virtual_provisioning_resource.time_modified, + virtual_provisioning_resource.resource_type, + virtual_provisioning_resource.virtual_disk_bytes_provisioned, + virtual_provisioning_resource.cpus_provisioned, + virtual_provisioning_resource.ram_provisioned + ), + virtual_provisioning_collection + AS ( + UPDATE + virtual_provisioning_collection + SET + time_modified = current_timestamp(), + cpus_provisioned = virtual_provisioning_collection.cpus_provisioned - $11, + ram_provisioned = virtual_provisioning_collection.ram_provisioned - $12 + WHERE + virtual_provisioning_collection.id = ANY (SELECT all_collections.id FROM all_collections) + AND (SELECT do_update.update FROM do_update LIMIT $13) + RETURNING + virtual_provisioning_collection.id, + virtual_provisioning_collection.time_modified, + virtual_provisioning_collection.collection_type, + virtual_provisioning_collection.virtual_disk_bytes_provisioned, + virtual_provisioning_collection.cpus_provisioned, + virtual_provisioning_collection.ram_provisioned + ) +SELECT + virtual_provisioning_collection.id, + virtual_provisioning_collection.time_modified, + virtual_provisioning_collection.collection_type, + virtual_provisioning_collection.virtual_disk_bytes_provisioned, + virtual_provisioning_collection.cpus_provisioned, + virtual_provisioning_collection.ram_provisioned +FROM + virtual_provisioning_collection diff --git a/nexus/db-queries/tests/output/virtual_provisioning_collection_update_delete_storage.sql b/nexus/db-queries/tests/output/virtual_provisioning_collection_update_delete_storage.sql new file mode 100644 index 0000000000..72c0b81e15 --- /dev/null +++ b/nexus/db-queries/tests/output/virtual_provisioning_collection_update_delete_storage.sql @@ -0,0 +1,86 @@ +WITH + parent_silo AS (SELECT project.silo_id AS id FROM project WHERE project.id = $1), + all_collections + AS ( + ((SELECT $2 AS id) UNION (SELECT parent_silo.id AS id FROM parent_silo)) + UNION (SELECT $3 AS id) + ), + quotas + AS ( + SELECT + silo_quotas.silo_id, + silo_quotas.cpus, + silo_quotas.memory_bytes AS memory, + silo_quotas.storage_bytes AS storage + FROM + silo_quotas INNER JOIN parent_silo ON silo_quotas.silo_id = parent_silo.id + ), + silo_provisioned + AS ( + SELECT + virtual_provisioning_collection.id, + virtual_provisioning_collection.cpus_provisioned, + virtual_provisioning_collection.ram_provisioned, + virtual_provisioning_collection.virtual_disk_bytes_provisioned + FROM + virtual_provisioning_collection + INNER JOIN parent_silo ON virtual_provisioning_collection.id = parent_silo.id + ), + do_update + AS ( + SELECT + ( + SELECT + count(*) + FROM + virtual_provisioning_resource + WHERE + virtual_provisioning_resource.id = $4 + LIMIT + $5 + ) + = $6 + AS update + ), + unused_cte_arm + AS ( + DELETE FROM + virtual_provisioning_resource + WHERE + virtual_provisioning_resource.id = $7 + RETURNING + virtual_provisioning_resource.id, + virtual_provisioning_resource.time_modified, + virtual_provisioning_resource.resource_type, + virtual_provisioning_resource.virtual_disk_bytes_provisioned, + virtual_provisioning_resource.cpus_provisioned, + virtual_provisioning_resource.ram_provisioned + ), + virtual_provisioning_collection + AS ( + UPDATE + virtual_provisioning_collection + SET + time_modified = current_timestamp(), + virtual_disk_bytes_provisioned + = virtual_provisioning_collection.virtual_disk_bytes_provisioned - $8 + WHERE + virtual_provisioning_collection.id = ANY (SELECT all_collections.id FROM all_collections) + AND (SELECT do_update.update FROM do_update LIMIT $9) + RETURNING + virtual_provisioning_collection.id, + virtual_provisioning_collection.time_modified, + virtual_provisioning_collection.collection_type, + virtual_provisioning_collection.virtual_disk_bytes_provisioned, + virtual_provisioning_collection.cpus_provisioned, + virtual_provisioning_collection.ram_provisioned + ) +SELECT + virtual_provisioning_collection.id, + virtual_provisioning_collection.time_modified, + virtual_provisioning_collection.collection_type, + virtual_provisioning_collection.virtual_disk_bytes_provisioned, + virtual_provisioning_collection.cpus_provisioned, + virtual_provisioning_collection.ram_provisioned +FROM + virtual_provisioning_collection diff --git a/nexus/db-queries/tests/output/virtual_provisioning_collection_update_insert_instance.sql b/nexus/db-queries/tests/output/virtual_provisioning_collection_update_insert_instance.sql new file mode 100644 index 0000000000..753b7f09f3 --- /dev/null +++ b/nexus/db-queries/tests/output/virtual_provisioning_collection_update_insert_instance.sql @@ -0,0 +1,154 @@ +WITH + parent_silo AS (SELECT project.silo_id AS id FROM project WHERE project.id = $1), + all_collections + AS ( + ((SELECT $2 AS id) UNION (SELECT parent_silo.id AS id FROM parent_silo)) + UNION (SELECT $3 AS id) + ), + quotas + AS ( + SELECT + silo_quotas.silo_id, + silo_quotas.cpus, + silo_quotas.memory_bytes AS memory, + silo_quotas.storage_bytes AS storage + FROM + silo_quotas INNER JOIN parent_silo ON silo_quotas.silo_id = parent_silo.id + ), + silo_provisioned + AS ( + SELECT + virtual_provisioning_collection.id, + virtual_provisioning_collection.cpus_provisioned, + virtual_provisioning_collection.ram_provisioned, + virtual_provisioning_collection.virtual_disk_bytes_provisioned + FROM + virtual_provisioning_collection + INNER JOIN parent_silo ON virtual_provisioning_collection.id = parent_silo.id + ), + do_update + AS ( + SELECT + ( + ( + ( + SELECT + count(*) + FROM + virtual_provisioning_resource + WHERE + virtual_provisioning_resource.id = $4 + LIMIT + $5 + ) + = $6 + AND CAST( + IF( + ( + $7 = $8 + OR (SELECT quotas.cpus FROM quotas LIMIT $9) + >= ( + (SELECT silo_provisioned.cpus_provisioned FROM silo_provisioned LIMIT $10) + + $11 + ) + ), + 'TRUE', + 'Not enough cpus' + ) + AS BOOL + ) + ) + AND CAST( + IF( + ( + $12 = $13 + OR (SELECT quotas.memory FROM quotas LIMIT $14) + >= ( + (SELECT silo_provisioned.ram_provisioned FROM silo_provisioned LIMIT $15) + + $16 + ) + ), + 'TRUE', + 'Not enough memory' + ) + AS BOOL + ) + ) + AND CAST( + IF( + ( + $17 = $18 + OR (SELECT quotas.storage FROM quotas LIMIT $19) + >= ( + ( + SELECT + silo_provisioned.virtual_disk_bytes_provisioned + FROM + silo_provisioned + LIMIT + $20 + ) + + $21 + ) + ), + 'TRUE', + 'Not enough storage' + ) + AS BOOL + ) + AS update + ), + unused_cte_arm + AS ( + INSERT + INTO + virtual_provisioning_resource + ( + id, + time_modified, + resource_type, + virtual_disk_bytes_provisioned, + cpus_provisioned, + ram_provisioned + ) + VALUES + ($22, DEFAULT, $23, $24, $25, $26) + ON CONFLICT + DO + NOTHING + RETURNING + virtual_provisioning_resource.id, + virtual_provisioning_resource.time_modified, + virtual_provisioning_resource.resource_type, + virtual_provisioning_resource.virtual_disk_bytes_provisioned, + virtual_provisioning_resource.cpus_provisioned, + virtual_provisioning_resource.ram_provisioned + ), + virtual_provisioning_collection + AS ( + UPDATE + virtual_provisioning_collection + SET + time_modified = current_timestamp(), + cpus_provisioned = virtual_provisioning_collection.cpus_provisioned + $27, + ram_provisioned = virtual_provisioning_collection.ram_provisioned + $28 + WHERE + virtual_provisioning_collection.id = ANY (SELECT all_collections.id FROM all_collections) + AND (SELECT do_update.update FROM do_update LIMIT $29) + RETURNING + virtual_provisioning_collection.id, + virtual_provisioning_collection.time_modified, + virtual_provisioning_collection.collection_type, + virtual_provisioning_collection.virtual_disk_bytes_provisioned, + virtual_provisioning_collection.cpus_provisioned, + virtual_provisioning_collection.ram_provisioned + ) +SELECT + virtual_provisioning_collection.id, + virtual_provisioning_collection.time_modified, + virtual_provisioning_collection.collection_type, + virtual_provisioning_collection.virtual_disk_bytes_provisioned, + virtual_provisioning_collection.cpus_provisioned, + virtual_provisioning_collection.ram_provisioned +FROM + virtual_provisioning_collection diff --git a/nexus/db-queries/tests/output/virtual_provisioning_collection_update_insert_storage.sql b/nexus/db-queries/tests/output/virtual_provisioning_collection_update_insert_storage.sql new file mode 100644 index 0000000000..040a5dc20c --- /dev/null +++ b/nexus/db-queries/tests/output/virtual_provisioning_collection_update_insert_storage.sql @@ -0,0 +1,154 @@ +WITH + parent_silo AS (SELECT project.silo_id AS id FROM project WHERE project.id = $1), + all_collections + AS ( + ((SELECT $2 AS id) UNION (SELECT parent_silo.id AS id FROM parent_silo)) + UNION (SELECT $3 AS id) + ), + quotas + AS ( + SELECT + silo_quotas.silo_id, + silo_quotas.cpus, + silo_quotas.memory_bytes AS memory, + silo_quotas.storage_bytes AS storage + FROM + silo_quotas INNER JOIN parent_silo ON silo_quotas.silo_id = parent_silo.id + ), + silo_provisioned + AS ( + SELECT + virtual_provisioning_collection.id, + virtual_provisioning_collection.cpus_provisioned, + virtual_provisioning_collection.ram_provisioned, + virtual_provisioning_collection.virtual_disk_bytes_provisioned + FROM + virtual_provisioning_collection + INNER JOIN parent_silo ON virtual_provisioning_collection.id = parent_silo.id + ), + do_update + AS ( + SELECT + ( + ( + ( + SELECT + count(*) + FROM + virtual_provisioning_resource + WHERE + virtual_provisioning_resource.id = $4 + LIMIT + $5 + ) + = $6 + AND CAST( + IF( + ( + $7 = $8 + OR (SELECT quotas.cpus FROM quotas LIMIT $9) + >= ( + (SELECT silo_provisioned.cpus_provisioned FROM silo_provisioned LIMIT $10) + + $11 + ) + ), + 'TRUE', + 'Not enough cpus' + ) + AS BOOL + ) + ) + AND CAST( + IF( + ( + $12 = $13 + OR (SELECT quotas.memory FROM quotas LIMIT $14) + >= ( + (SELECT silo_provisioned.ram_provisioned FROM silo_provisioned LIMIT $15) + + $16 + ) + ), + 'TRUE', + 'Not enough memory' + ) + AS BOOL + ) + ) + AND CAST( + IF( + ( + $17 = $18 + OR (SELECT quotas.storage FROM quotas LIMIT $19) + >= ( + ( + SELECT + silo_provisioned.virtual_disk_bytes_provisioned + FROM + silo_provisioned + LIMIT + $20 + ) + + $21 + ) + ), + 'TRUE', + 'Not enough storage' + ) + AS BOOL + ) + AS update + ), + unused_cte_arm + AS ( + INSERT + INTO + virtual_provisioning_resource + ( + id, + time_modified, + resource_type, + virtual_disk_bytes_provisioned, + cpus_provisioned, + ram_provisioned + ) + VALUES + ($22, DEFAULT, $23, $24, $25, $26) + ON CONFLICT + DO + NOTHING + RETURNING + virtual_provisioning_resource.id, + virtual_provisioning_resource.time_modified, + virtual_provisioning_resource.resource_type, + virtual_provisioning_resource.virtual_disk_bytes_provisioned, + virtual_provisioning_resource.cpus_provisioned, + virtual_provisioning_resource.ram_provisioned + ), + virtual_provisioning_collection + AS ( + UPDATE + virtual_provisioning_collection + SET + time_modified = current_timestamp(), + virtual_disk_bytes_provisioned + = virtual_provisioning_collection.virtual_disk_bytes_provisioned + $27 + WHERE + virtual_provisioning_collection.id = ANY (SELECT all_collections.id FROM all_collections) + AND (SELECT do_update.update FROM do_update LIMIT $28) + RETURNING + virtual_provisioning_collection.id, + virtual_provisioning_collection.time_modified, + virtual_provisioning_collection.collection_type, + virtual_provisioning_collection.virtual_disk_bytes_provisioned, + virtual_provisioning_collection.cpus_provisioned, + virtual_provisioning_collection.ram_provisioned + ) +SELECT + virtual_provisioning_collection.id, + virtual_provisioning_collection.time_modified, + virtual_provisioning_collection.collection_type, + virtual_provisioning_collection.virtual_disk_bytes_provisioned, + virtual_provisioning_collection.cpus_provisioned, + virtual_provisioning_collection.ram_provisioned +FROM + virtual_provisioning_collection