From 5e01807fa25d60ea17f19b19b9be7b37c2569a2a Mon Sep 17 00:00:00 2001 From: Stepan Rabotkin Date: Mon, 27 Jul 2026 14:26:39 +0300 Subject: [PATCH] vmm: api: Return 404 (not 500) for unknown device/disk id Map NoDeviceToRemove and DeviceManager(UnknownDeviceId) to 404 Not Found, update the OpenAPI 404 descriptions, and add unit tests. Signed-off-by: Stepan Rabotkin Assisted-by: Claude:Opus-4.8 --- vmm/src/api/http/mod.rs | 47 ++++++++++++++++++++++- vmm/src/api/openapi/cloud-hypervisor.yaml | 4 +- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/vmm/src/api/http/mod.rs b/vmm/src/api/http/mod.rs index 69c3f8d65..ad2062be6 100644 --- a/vmm/src/api/http/mod.rs +++ b/vmm/src/api/http/mod.rs @@ -33,6 +33,7 @@ use crate::api::{ VmPause, VmPowerButton, VmReboot, VmReceiveMigration, VmRemoveDevice, VmResize, VmResizeDisk, VmResizeZone, VmRestore, VmResume, VmSendMigration, VmShutdown, VmSnapshot, }; +use crate::device_manager::DeviceManagerError; use crate::landlock::Landlock; use crate::seccomp_filters::{Thread, get_seccomp_filter}; use crate::util::{error_chain_messages, flatten_error_chain_to_string}; @@ -87,7 +88,12 @@ impl HttpError { /// Maps an [`ApiError`] to an HTTP [`StatusCode`]. fn api_error_status_code(error: &ApiError) -> StatusCode { match error.source().and_then(|e| e.downcast_ref::()) { - Some(VmError::VmNotCreated | VmError::VmMissingConfig) => StatusCode::NotFound, + Some( + VmError::VmNotCreated + | VmError::VmMissingConfig + | VmError::NoDeviceToRemove(_) + | VmError::DeviceManager(DeviceManagerError::UnknownDeviceId(_)), + ) => StatusCode::NotFound, _ => StatusCode::InternalServerError, } } @@ -495,3 +501,42 @@ pub fn http_api_graceful_shutdown(http_handle: HttpApiHandle) -> Result<()> { api_shutdown_fd.write(1).unwrap(); api_thread.join().map_err(VmmError::ThreadCleanup)? } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_missing_target_maps_to_not_found() { + assert_eq!( + api_error_status_code(&ApiError::VmInfo(VmError::VmNotCreated)), + StatusCode::NotFound + ); + assert_eq!( + api_error_status_code(&ApiError::VmRemoveDevice(VmError::NoDeviceToRemove( + "dev0".to_string() + ))), + StatusCode::NotFound + ); + assert_eq!( + api_error_status_code(&ApiError::VmRemoveDevice(VmError::DeviceManager( + DeviceManagerError::UnknownDeviceId("dev0".to_string()) + ))), + StatusCode::NotFound + ); + assert_eq!( + api_error_status_code(&ApiError::VmResizeDisk(VmError::DeviceManager( + DeviceManagerError::UnknownDeviceId("disk0".to_string()) + ))), + StatusCode::NotFound + ); + } + + #[test] + fn test_other_errors_map_to_internal_server_error() { + assert_eq!( + api_error_status_code(&ApiError::VmRemoveDevice(VmError::VmMigrating)), + StatusCode::InternalServerError + ); + } +} diff --git a/vmm/src/api/openapi/cloud-hypervisor.yaml b/vmm/src/api/openapi/cloud-hypervisor.yaml index f4603f9ea..f7534a1f0 100644 --- a/vmm/src/api/openapi/cloud-hypervisor.yaml +++ b/vmm/src/api/openapi/cloud-hypervisor.yaml @@ -179,7 +179,7 @@ paths: 204: description: The disk was successfully resized. 404: - description: The disk could not be resized because the VM is not created. + description: The disk could not be resized because the VM is not created, or no disk with the given identifier exists. 500: description: The disk could not be resized. @@ -237,7 +237,7 @@ paths: 204: description: The device was successfully removed from the VM instance. 404: - description: The device could not be removed from the VM instance. + description: The device could not be removed because the VM is not created, or no device with the given identifier exists. /vm.add-disk: put: