diff --git a/nexus/src/app/sagas/snapshot_create.rs b/nexus/src/app/sagas/snapshot_create.rs index 9e665a1de1b..8061c4c81d4 100644 --- a/nexus/src/app/sagas/snapshot_create.rs +++ b/nexus/src/app/sagas/snapshot_create.rs @@ -1625,9 +1625,11 @@ mod test { use omicron_common::api::external::InstanceCpuCount; use omicron_common::api::external::Name; use omicron_common::api::external::NameOrId; + use omicron_test_utils::dev::poll; use sled_agent_client::types::CrucibleOpts; use sled_agent_client::TestInterfaces as SledAgentTestInterfaces; use std::str::FromStr; + use std::time::Duration; type DiskTest<'a> = nexus_test_utils::resource_helpers::DiskTest<'a, crate::Server>; @@ -2184,6 +2186,31 @@ mod test { PROJECT_NAME, ) .await; + // Wait until the instance has advanced to the `NoVmm` + // state before deleting it. This may not happen + // immediately, as the `Nexus::cpapi_instances_put` API + // endpoint simply writes the new VMM state to the + // database and *starts* an `instance-update` saga, and + // the instance record isn't updated until that saga + // completes. + poll::wait_for_condition( + || async { + let new_state = test_helpers::instance_fetch_by_name( + cptestctx, + INSTANCE_NAME, + PROJECT_NAME, + ) + .await; + if new_state.instance().runtime().nexus_state != nexus_db_model::InstanceState::NoVmm { + Err(poll::CondCheckError::<()>::NotYet) + } else { + Ok(()) + } + }, + &Duration::from_secs(5), + &Duration::from_secs(300), + ) + .await.expect("instance did not advance to NoVmm after 400 seconds"); test_helpers::instance_delete_by_name( cptestctx, INSTANCE_NAME, diff --git a/nexus/src/app/sagas/test_helpers.rs b/nexus/src/app/sagas/test_helpers.rs index a5d9d0a8437..0ae85ec6ad2 100644 --- a/nexus/src/app/sagas/test_helpers.rs +++ b/nexus/src/app/sagas/test_helpers.rs @@ -188,6 +188,38 @@ pub async fn instance_fetch( db_state } +pub async fn instance_fetch_by_name( + cptestctx: &ControlPlaneTestContext, + name: &str, + project_name: &str, +) -> InstanceAndActiveVmm { + let datastore = cptestctx.server.server_context().nexus.datastore().clone(); + + let nexus = &cptestctx.server.server_context().nexus; + let opctx = test_opctx(&cptestctx); + let instance_selector = + nexus_types::external_api::params::InstanceSelector { + project: Some(project_name.to_string().try_into().unwrap()), + instance: name.to_string().try_into().unwrap(), + }; + + let instance_lookup = + nexus.instance_lookup(&opctx, instance_selector).unwrap(); + let (_, _, authz_instance, ..) = instance_lookup.fetch().await.unwrap(); + + let db_state = datastore + .instance_fetch_with_vmm(&opctx, &authz_instance) + .await + .expect("test instance's info should be fetchable"); + + info!(&cptestctx.logctx.log, "fetched instance info from db"; + "instance_name" => %name, + "project_name" => %project_name, + "instance_id" => %authz_instance.id(), + "instance_and_vmm" => ?db_state); + + db_state +} pub async fn no_virtual_provisioning_resource_records_exist( cptestctx: &ControlPlaneTestContext, ) -> bool {