diff --git a/openapi/nexus-internal.json b/openapi/nexus-internal.json index bf5c1b3a3e5..ec02db66ca4 100644 --- a/openapi/nexus-internal.json +++ b/openapi/nexus-internal.json @@ -514,7 +514,7 @@ "/instances/{instance_id}/creation-success": { "put": { "summary": "Asynchronously report the successful result of certain instance_put calls", - "description": "(such as the potentially long-running one made during instance creation)", + "description": "(such as the potentially long-running one made during instance creation). If Nexus has already marked the instance as failed, this returns `TimedOut` so the sled-agent making the request knows to terminate the instance.", "operationId": "cpapi_handle_instance_put_success", "parameters": [ { diff --git a/sled-agent/src/instance.rs b/sled-agent/src/instance.rs index 45df6bea2c0..7016c85b6ff 100644 --- a/sled-agent/src/instance.rs +++ b/sled-agent/src/instance.rs @@ -1470,7 +1470,33 @@ mod tests { ) -> Instance { let id = Uuid::new_v4(); let propolis_id = Uuid::new_v4(); + let ticket = InstanceTicket::new_without_manager_for_test(id); + + let initial_state = + fake_instance_initial_state(propolis_id, propolis_addr); + + let services = fake_instance_manager_services( + logctx, + storage_handle, + nexus_client_with_resolver, + ); + + Instance::new( + logctx.log.new(o!("component" => "Instance")), + id, + propolis_id, + ticket, + initial_state, + services, + ) + .unwrap() + } + + fn fake_instance_initial_state( + propolis_id: Uuid, + propolis_addr: SocketAddr, + ) -> InstanceInitialState { let hardware = InstanceHardware { properties: InstanceProperties { ncpus: InstanceCpuCount(1), @@ -1495,7 +1521,7 @@ mod tests { cloud_init_bytes: None, }; - let initial_state = InstanceInitialState { + InstanceInitialState { hardware, instance_runtime: InstanceRuntimeState { propolis_id: Some(propolis_id), @@ -1510,8 +1536,14 @@ mod tests { time_updated: Default::default(), }, propolis_addr, - }; + } + } + fn fake_instance_manager_services( + logctx: &LogContext, + storage_handle: StorageHandle, + nexus_client_with_resolver: NexusClientWithResolver, + ) -> InstanceManagerServices { let vnic_allocator = VnicAllocator::new("Foo", Etherstub("mystub".to_string())); let port_manager = PortManager::new( @@ -1526,24 +1558,14 @@ mod tests { cleanup_context, ); - let services = InstanceManagerServices { + InstanceManagerServices { nexus_client: nexus_client_with_resolver, vnic_allocator, port_manager, storage: storage_handle, zone_bundler, zone_builder_factory: ZoneBuilderFactory::fake(), - }; - - Instance::new( - logctx.log.new(o!("component" => "Instance")), - id, - propolis_id, - ticket, - initial_state, - services, - ) - .unwrap() + } } #[tokio::test] @@ -1724,4 +1746,81 @@ mod tests { logctx.cleanup_successful(); } + + #[tokio::test] + async fn test_instance_manager_creation_nexus_timeout() { + let logctx = omicron_test_utils::dev::test_setup_log( + "test_instance_manager_creation_nexus_timeout", + ); + + let storage_handle = fake_storage_manager_with_u2().await; + + let (nexus_client, nexus_server, state_rx) = fake_nexus_server(&logctx); + + let (_dns_server, resolver, _dns_config_dir) = + timeout(TIMEOUT_DURATION, dns_server(&logctx, &nexus_server)) + .await + .expect("timed out making DNS server and Resolver"); + + let nexus_client_with_resolver = + NexusClientWithResolver::new_with_client(nexus_client, resolver); + + let InstanceManagerServices { + nexus_client, + vnic_allocator: _, + port_manager, + storage, + zone_bundler, + zone_builder_factory, + } = fake_instance_manager_services( + &logctx, + storage_handle, + nexus_client_with_resolver, + ); + + let etherstub = Etherstub("mystub".to_string()); + + let mgr = crate::instance_manager::InstanceManager::new( + logctx.log.new(o!("component" => "InstanceManager")), + nexus_client, + etherstub, + port_manager, + storage, + zone_bundler, + zone_builder_factory, + ) + .unwrap(); + + let (propolis_server, _propolis_client) = + propolis_mock_server(&logctx.log); + let propolis_addr = propolis_server.local_addr(); + + // automock'd things used during this test + let _mock_vnic_contexts = mock_vnic_contexts(); + let _mock_zone_contexts = mock_zone_contexts(); + + let instance_id = Uuid::new_v4(); + let propolis_id = Uuid::new_v4(); + let InstanceInitialState { + hardware, + instance_runtime, + vmm_runtime, + propolis_addr, + } = fake_instance_initial_state(propolis_id, propolis_addr); + + mgr.ensure_registered( + instance_id, + propolis_id, + hardware, + instance_runtime, + vmm_runtime, + propolis_addr, + ) + .await + .unwrap(); + + todo!(); + + logctx.cleanup_successful(); + } }