mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-12-22 13:45:20 +00:00
vmm: http: graceful shutdown of the http api thread
This commit ensures that the HttpApi thread flushes all the responses before the application shuts down. Without this step, in case of a VmmShutdown request the application might terminate before the thread sends a response. Fixes: #6247 Signed-off-by: Alexandru Matei <alexandru.matei@uipath.com>
This commit is contained in:
parent
3f2ca5375e
commit
1091494320
@ -20,6 +20,7 @@ use std::sync::{Arc, Mutex};
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
#[cfg(feature = "dbus_api")]
|
#[cfg(feature = "dbus_api")]
|
||||||
use vmm::api::dbus::{dbus_api_graceful_shutdown, DBusApiOptions};
|
use vmm::api::dbus::{dbus_api_graceful_shutdown, DBusApiOptions};
|
||||||
|
use vmm::api::http::http_api_graceful_shutdown;
|
||||||
use vmm::api::ApiAction;
|
use vmm::api::ApiAction;
|
||||||
use vmm::config;
|
use vmm::config;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
@ -82,6 +83,8 @@ enum Error {
|
|||||||
LogFileCreation(std::io::Error),
|
LogFileCreation(std::io::Error),
|
||||||
#[error("Error setting up logger: {0}")]
|
#[error("Error setting up logger: {0}")]
|
||||||
LoggerSetup(log::SetLoggerError),
|
LoggerSetup(log::SetLoggerError),
|
||||||
|
#[error("Failed to gracefully shutdown http api: {0}")]
|
||||||
|
HttpApiShutdown(#[source] vmm::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Logger {
|
struct Logger {
|
||||||
@ -760,6 +763,10 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
|||||||
.map_err(Error::ThreadJoin)?
|
.map_err(Error::ThreadJoin)?
|
||||||
.map_err(Error::VmmThread)?;
|
.map_err(Error::VmmThread)?;
|
||||||
|
|
||||||
|
if let Some(api_handle) = vmm_thread_handle.http_api_handle {
|
||||||
|
http_api_graceful_shutdown(api_handle).map_err(Error::HttpApiShutdown)?
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "dbus_api")]
|
#[cfg(feature = "dbus_api")]
|
||||||
if let Some(chs) = vmm_thread_handle.dbus_shutdown_chs {
|
if let Some(chs) = vmm_thread_handle.dbus_shutdown_chs {
|
||||||
dbus_api_graceful_shutdown(chs);
|
dbus_api_graceful_shutdown(chs);
|
||||||
|
@ -16,7 +16,9 @@ use crate::seccomp_filters::{get_seccomp_filter, Thread};
|
|||||||
use crate::{Error as VmmError, Result};
|
use crate::{Error as VmmError, Result};
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use hypervisor::HypervisorType;
|
use hypervisor::HypervisorType;
|
||||||
use micro_http::{Body, HttpServer, MediaType, Method, Request, Response, StatusCode, Version};
|
use micro_http::{
|
||||||
|
Body, HttpServer, MediaType, Method, Request, Response, ServerError, StatusCode, Version,
|
||||||
|
};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use seccompiler::{apply_filter, SeccompAction};
|
use seccompiler::{apply_filter, SeccompAction};
|
||||||
use serde_json::Error as SerdeError;
|
use serde_json::Error as SerdeError;
|
||||||
@ -33,6 +35,8 @@ use vmm_sys_util::eventfd::EventFd;
|
|||||||
|
|
||||||
pub mod http_endpoint;
|
pub mod http_endpoint;
|
||||||
|
|
||||||
|
pub type HttpApiHandle = (thread::JoinHandle<Result<()>>, EventFd);
|
||||||
|
|
||||||
/// Errors associated with VMM management
|
/// Errors associated with VMM management
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum HttpError {
|
pub enum HttpError {
|
||||||
@ -297,12 +301,19 @@ fn start_http_thread(
|
|||||||
seccomp_action: &SeccompAction,
|
seccomp_action: &SeccompAction,
|
||||||
exit_evt: EventFd,
|
exit_evt: EventFd,
|
||||||
hypervisor_type: HypervisorType,
|
hypervisor_type: HypervisorType,
|
||||||
) -> Result<thread::JoinHandle<Result<()>>> {
|
) -> Result<HttpApiHandle> {
|
||||||
// Retrieve seccomp filter for API thread
|
// Retrieve seccomp filter for API thread
|
||||||
let api_seccomp_filter = get_seccomp_filter(seccomp_action, Thread::HttpApi, hypervisor_type)
|
let api_seccomp_filter = get_seccomp_filter(seccomp_action, Thread::HttpApi, hypervisor_type)
|
||||||
.map_err(VmmError::CreateSeccompFilter)?;
|
.map_err(VmmError::CreateSeccompFilter)?;
|
||||||
|
|
||||||
thread::Builder::new()
|
let api_shutdown_fd = EventFd::new(libc::EFD_NONBLOCK).map_err(VmmError::EventFdCreate)?;
|
||||||
|
let api_shutdown_fd_clone = api_shutdown_fd.try_clone().unwrap();
|
||||||
|
|
||||||
|
server
|
||||||
|
.add_kill_switch(api_shutdown_fd_clone)
|
||||||
|
.map_err(VmmError::CreateApiServer)?;
|
||||||
|
|
||||||
|
let thread = thread::Builder::new()
|
||||||
.name("http-server".to_string())
|
.name("http-server".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
// Apply seccomp filter for API thread.
|
// Apply seccomp filter for API thread.
|
||||||
@ -329,6 +340,10 @@ fn start_http_thread(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(ServerError::ShutdownEvent) => {
|
||||||
|
server.flush_outgoing_writes();
|
||||||
|
return;
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(
|
error!(
|
||||||
"HTTP server error on retrieving incoming request. Error: {}",
|
"HTTP server error on retrieving incoming request. Error: {}",
|
||||||
@ -346,7 +361,9 @@ fn start_http_thread(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.map_err(VmmError::HttpThreadSpawn)
|
.map_err(VmmError::HttpThreadSpawn)?;
|
||||||
|
|
||||||
|
Ok((thread, api_shutdown_fd))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_http_path_thread(
|
pub fn start_http_path_thread(
|
||||||
@ -356,12 +373,13 @@ pub fn start_http_path_thread(
|
|||||||
seccomp_action: &SeccompAction,
|
seccomp_action: &SeccompAction,
|
||||||
exit_evt: EventFd,
|
exit_evt: EventFd,
|
||||||
hypervisor_type: HypervisorType,
|
hypervisor_type: HypervisorType,
|
||||||
) -> Result<thread::JoinHandle<Result<()>>> {
|
) -> Result<HttpApiHandle> {
|
||||||
let socket_path = PathBuf::from(path);
|
let socket_path = PathBuf::from(path);
|
||||||
let socket_fd = UnixListener::bind(socket_path).map_err(VmmError::CreateApiServerSocket)?;
|
let socket_fd = UnixListener::bind(socket_path).map_err(VmmError::CreateApiServerSocket)?;
|
||||||
// SAFETY: Valid FD just opened
|
// SAFETY: Valid FD just opened
|
||||||
let server = unsafe { HttpServer::new_from_fd(socket_fd.into_raw_fd()) }
|
let server = unsafe { HttpServer::new_from_fd(socket_fd.into_raw_fd()) }
|
||||||
.map_err(VmmError::CreateApiServer)?;
|
.map_err(VmmError::CreateApiServer)?;
|
||||||
|
|
||||||
start_http_thread(
|
start_http_thread(
|
||||||
server,
|
server,
|
||||||
api_notifier,
|
api_notifier,
|
||||||
@ -379,7 +397,7 @@ pub fn start_http_fd_thread(
|
|||||||
seccomp_action: &SeccompAction,
|
seccomp_action: &SeccompAction,
|
||||||
exit_evt: EventFd,
|
exit_evt: EventFd,
|
||||||
hypervisor_type: HypervisorType,
|
hypervisor_type: HypervisorType,
|
||||||
) -> Result<thread::JoinHandle<Result<()>>> {
|
) -> Result<HttpApiHandle> {
|
||||||
// SAFETY: Valid FD
|
// SAFETY: Valid FD
|
||||||
let server = unsafe { HttpServer::new_from_fd(fd) }.map_err(VmmError::CreateApiServer)?;
|
let server = unsafe { HttpServer::new_from_fd(fd) }.map_err(VmmError::CreateApiServer)?;
|
||||||
start_http_thread(
|
start_http_thread(
|
||||||
@ -391,3 +409,10 @@ pub fn start_http_fd_thread(
|
|||||||
hypervisor_type,
|
hypervisor_type,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn http_api_graceful_shutdown(http_handle: HttpApiHandle) -> Result<()> {
|
||||||
|
let (api_thread, api_shutdown_fd) = http_handle;
|
||||||
|
|
||||||
|
api_shutdown_fd.write(1).unwrap();
|
||||||
|
api_thread.join().map_err(VmmError::ThreadCleanup)?
|
||||||
|
}
|
||||||
|
@ -27,6 +27,7 @@ use crate::vm::{Error as VmError, Vm, VmState};
|
|||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
#[cfg(feature = "dbus_api")]
|
#[cfg(feature = "dbus_api")]
|
||||||
use api::dbus::{DBusApiOptions, DBusApiShutdownChannels};
|
use api::dbus::{DBusApiOptions, DBusApiShutdownChannels};
|
||||||
|
use api::http::HttpApiHandle;
|
||||||
use libc::{tcsetattr, termios, EFD_NONBLOCK, SIGINT, SIGTERM, TCSANOW};
|
use libc::{tcsetattr, termios, EFD_NONBLOCK, SIGINT, SIGTERM, TCSANOW};
|
||||||
use memory_manager::MemoryManagerSnapshotData;
|
use memory_manager::MemoryManagerSnapshotData;
|
||||||
use pci::PciBdf;
|
use pci::PciBdf;
|
||||||
@ -455,25 +456,27 @@ pub fn start_vmm_thread(
|
|||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(http_path) = http_path {
|
let http_api_handle = if let Some(http_path) = http_path {
|
||||||
api::start_http_path_thread(
|
Some(api::start_http_path_thread(
|
||||||
http_path,
|
http_path,
|
||||||
api_event_clone,
|
api_event_clone,
|
||||||
api_sender,
|
api_sender,
|
||||||
seccomp_action,
|
seccomp_action,
|
||||||
exit_event,
|
exit_event,
|
||||||
hypervisor_type,
|
hypervisor_type,
|
||||||
)?;
|
)?)
|
||||||
} else if let Some(http_fd) = http_fd {
|
} else if let Some(http_fd) = http_fd {
|
||||||
api::start_http_fd_thread(
|
Some(api::start_http_fd_thread(
|
||||||
http_fd,
|
http_fd,
|
||||||
api_event_clone,
|
api_event_clone,
|
||||||
api_sender,
|
api_sender,
|
||||||
seccomp_action,
|
seccomp_action,
|
||||||
exit_event,
|
exit_event,
|
||||||
hypervisor_type,
|
hypervisor_type,
|
||||||
)?;
|
)?)
|
||||||
}
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "guest_debug")]
|
#[cfg(feature = "guest_debug")]
|
||||||
if let Some(debug_path) = debug_path {
|
if let Some(debug_path) = debug_path {
|
||||||
@ -493,6 +496,7 @@ pub fn start_vmm_thread(
|
|||||||
thread_handle: thread,
|
thread_handle: thread,
|
||||||
#[cfg(feature = "dbus_api")]
|
#[cfg(feature = "dbus_api")]
|
||||||
dbus_shutdown_chs,
|
dbus_shutdown_chs,
|
||||||
|
http_api_handle,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -523,6 +527,7 @@ pub struct VmmThreadHandle {
|
|||||||
pub thread_handle: thread::JoinHandle<Result<()>>,
|
pub thread_handle: thread::JoinHandle<Result<()>>,
|
||||||
#[cfg(feature = "dbus_api")]
|
#[cfg(feature = "dbus_api")]
|
||||||
pub dbus_shutdown_chs: Option<DBusApiShutdownChannels>,
|
pub dbus_shutdown_chs: Option<DBusApiShutdownChannels>,
|
||||||
|
pub http_api_handle: Option<HttpApiHandle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Vmm {
|
pub struct Vmm {
|
||||||
|
@ -838,6 +838,7 @@ fn http_api_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, BackendError>
|
|||||||
(libc::SYS_sched_yield, vec![]),
|
(libc::SYS_sched_yield, vec![]),
|
||||||
(libc::SYS_sigaltstack, vec![]),
|
(libc::SYS_sigaltstack, vec![]),
|
||||||
(libc::SYS_write, vec![]),
|
(libc::SYS_write, vec![]),
|
||||||
|
(libc::SYS_rt_sigprocmask, vec![]),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user