Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup confusion arising from mixing of unix exit codes and Rust Result types #4261

Merged
merged 4 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 10 additions & 13 deletions src/firecracker/src/api_server_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@
use vmm::logger::{error, warn, ProcessTimeReporter};
use vmm::resources::VmResources;
use vmm::rpc_interface::{
ApiRequest, ApiResponse, PrebootApiController, RuntimeApiController, VmmAction,
ApiRequest, ApiResponse, BuildMicrovmFromRequestsError, PrebootApiController,
RuntimeApiController, VmmAction,
};
use vmm::vmm_config::instance_info::InstanceInfo;
use vmm::{EventManager, FcExitCode, Vmm};

#[derive(Debug, thiserror::Error, displaydoc::Display)]
pub enum ApiServerError {
/// MicroVMStopped without an error: {0:?}
MicroVMStoppedWithoutError(FcExitCode),
/// MicroVMStopped with an error: {0:?}
/// Failed to build MicroVM: {0}.
BuildMicroVmError(BuildMicrovmFromRequestsError),
/// MicroVM stopped with an error: {0:?}
MicroVMStoppedWithError(FcExitCode),
/// Failed to open the API socket at: {0}. Check that it is not already used.
FailedToBindSocket(String),
Expand Down Expand Up @@ -52,7 +53,7 @@
vm_resources: VmResources,
vmm: Arc<Mutex<Vmm>>,
event_manager: &mut EventManager,
) -> Result<(), FcExitCode> {
) -> Result<(), ApiServerError> {

Check warning on line 56 in src/firecracker/src/api_server_adapter.rs

View check run for this annotation

Codecov / codecov/patch

src/firecracker/src/api_server_adapter.rs#L56

Added line #L56 was not covered by tests
let api_adapter = Arc::new(Mutex::new(Self {
api_event_fd,
from_api,
Expand All @@ -67,7 +68,7 @@

match vmm.lock().unwrap().shutdown_exit_code() {
Some(FcExitCode::Ok) => break,
Some(exit_code) => return Err(exit_code),
Some(exit_code) => return Err(ApiServerError::MicroVMStoppedWithError(exit_code)),

Check warning on line 71 in src/firecracker/src/api_server_adapter.rs

View check run for this annotation

Codecov / codecov/patch

src/firecracker/src/api_server_adapter.rs#L71

Added line #L71 was not covered by tests
None => continue,
}
}
Expand Down Expand Up @@ -224,10 +225,10 @@
mmds_size_limit,
metadata_json,
)
.map_err(ApiServerError::MicroVMStoppedWithError),
.map_err(ApiServerError::BuildMicroVmError),

Check warning on line 228 in src/firecracker/src/api_server_adapter.rs

View check run for this annotation

Codecov / codecov/patch

src/firecracker/src/api_server_adapter.rs#L228

Added line #L228 was not covered by tests
};

let result = build_result.map(|(vm_resources, vmm)| {
let result = build_result.and_then(|(vm_resources, vmm)| {

Check warning on line 231 in src/firecracker/src/api_server_adapter.rs

View check run for this annotation

Codecov / codecov/patch

src/firecracker/src/api_server_adapter.rs#L231

Added line #L231 was not covered by tests
firecracker_metrics
.lock()
.expect("Poisoned lock")
Expand All @@ -248,9 +249,5 @@
// shutdown-internal and returns from its function.
api_thread.join().expect("Api thread should join");

match result {
Ok(Ok(())) => Ok(()),
Ok(Err(exit_code)) => Err(ApiServerError::MicroVMStoppedWithoutError(exit_code)),
Err(exit_error) => Err(exit_error),
}
result

Check warning on line 252 in src/firecracker/src/api_server_adapter.rs

View check run for this annotation

Codecov / codecov/patch

src/firecracker/src/api_server_adapter.rs#L252

Added line #L252 was not covered by tests
}
1 change: 0 additions & 1 deletion src/firecracker/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ impl From<MainError> for ExitCode {
let exit_code = match value {
MainError::ParseArguments(_) => FcExitCode::ArgParsing,
MainError::InvalidLogLevel(_) => FcExitCode::BadConfiguration,
MainError::RunWithApi(ApiServerError::MicroVMStoppedWithoutError(code)) => code,
MainError::RunWithApi(ApiServerError::MicroVMStoppedWithError(code)) => code,
MainError::RunWithoutApiError(RunWithoutApiError::Shutdown(code)) => code,
_ => FcExitCode::GenericError,
Expand Down
36 changes: 20 additions & 16 deletions src/vmm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
use std::collections::HashMap;
use std::io;
use std::os::unix::io::AsRawFd;
use std::sync::mpsc::{RecvTimeoutError, TryRecvError};
use std::sync::mpsc::RecvTimeoutError;
use std::sync::{Arc, Barrier, Mutex};
use std::time::Duration;

Expand Down Expand Up @@ -915,23 +915,27 @@
// Exit event handling should never do anything more than call 'self.stop()'.
let _ = self.vcpus_exit_evt.read();

let mut exit_code = None;
// Query each vcpu for their exit_code.
for handle in &self.vcpus_handles {
match handle.response_receiver().try_recv() {
Ok(VcpuResponse::Exited(status)) => {
exit_code = Some(status);
// Just use the first encountered exit-code.
break;
}
Ok(_response) => {} // Don't care about these, we are exiting.
Err(TryRecvError::Empty) => {} // Nothing pending in channel
Err(err) => {
panic!("Error while looking for VCPU exit status: {}", err);
let exit_code = 'exit_code: {
// Query each vcpu for their exit_code.
for handle in &self.vcpus_handles {
// Drain all vcpu responses that are pending from this vcpu until we find an
// exit status.
for response in handle.response_receiver().try_iter() {
if let VcpuResponse::Exited(status) = response {

Check warning on line 924 in src/vmm/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

src/vmm/src/lib.rs#L924

Added line #L924 was not covered by tests
// It could be that some vcpus exited successfully while others
// errored out. Thus make sure that error exits from one vcpu always
// takes precedence over "ok" exits
if status != FcExitCode::Ok {
break 'exit_code status;
}
}

Check warning on line 931 in src/vmm/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

src/vmm/src/lib.rs#L928-L931

Added lines #L928 - L931 were not covered by tests
}
}
}
self.stop(exit_code.unwrap_or(FcExitCode::Ok));

// No CPUs exited with error status code, report "Ok"
FcExitCode::Ok
};
self.stop(exit_code);
} else {
error!("Spurious EventManager event for handler: Vmm");
}
Expand Down
40 changes: 22 additions & 18 deletions src/vmm/src/rpc_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
};
use crate::builder::StartMicrovmError;
use crate::cpu_config::templates::{CustomCpuTemplate, GuestConfigError};
use crate::logger::{error, info, warn, LoggerConfig, *};
use crate::logger::{info, warn, LoggerConfig, *};
use crate::mmds::data_store::{self, Mmds};
use crate::persist::{CreateSnapshotError, RestoreFromSnapshotError, VmInfo};
use crate::resources::VmmConfig;
Expand All @@ -42,7 +42,7 @@
use crate::vmm_config::snapshot::{CreateSnapshotParams, LoadSnapshotParams, SnapshotType};
use crate::vmm_config::vsock::{VsockConfigError, VsockDeviceConfig};
use crate::vmm_config::{self, RateLimiterUpdate};
use crate::{EventManager, FcExitCode};
use crate::EventManager;

/// This enum represents the public interface of the VMM. Each action contains various
/// bits of information (ids, paths, etc.).
Expand Down Expand Up @@ -247,7 +247,7 @@
boot_path: bool,
// Some PrebootApiRequest errors are irrecoverable and Firecracker
// should cleanly teardown if they occur.
fatal_error: Option<FcExitCode>,
fatal_error: Option<BuildMicrovmFromRequestsError>,
}

// TODO Remove when `EventManager` implements `std::fmt::Debug`.
Expand Down Expand Up @@ -287,6 +287,17 @@
/// Shorthand type for a response containing a boxed Result.
pub type ApiResponse = Box<std::result::Result<VmmData, VmmActionError>>;

/// Error type for `PrebootApiController::build_microvm_from_requests`.
#[derive(Debug, thiserror::Error, displaydoc::Display, derive_more::From)]

Check warning on line 291 in src/vmm/src/rpc_interface.rs

View check run for this annotation

Codecov / codecov/patch

src/vmm/src/rpc_interface.rs#L291

Added line #L291 was not covered by tests
pub enum BuildMicrovmFromRequestsError {
/// Populating MMDS from file failed: {0:?}.
Mmds(data_store::Error),
/// Loading snapshot failed.
Restore,
/// Resuming MicroVM after loading snapshot failed.
Resume,
}

impl<'a> PrebootApiController<'a> {
/// Constructor for the PrebootApiController.
pub fn new(
Expand Down Expand Up @@ -320,7 +331,7 @@
boot_timer_enabled: bool,
mmds_size_limit: usize,
metadata_json: Option<&str>,
) -> Result<(VmResources, Arc<Mutex<Vmm>>), FcExitCode> {
) -> Result<(VmResources, Arc<Mutex<Vmm>>), BuildMicrovmFromRequestsError> {

Check warning on line 334 in src/vmm/src/rpc_interface.rs

View check run for this annotation

Codecov / codecov/patch

src/vmm/src/rpc_interface.rs#L334

Added line #L334 was not covered by tests
let mut vm_resources = VmResources::default();
// Silence false clippy warning. Clippy suggests using
// VmResources { boot_timer: boot_timer_enabled, ..Default::default() }; but this will
Expand All @@ -333,16 +344,9 @@

// Init the data store from file, if present.
if let Some(data) = metadata_json {
vm_resources
.locked_mmds_or_default()
.put_data(
serde_json::from_str(data)
.expect("MMDS error: metadata provided not valid json"),
)
.map_err(|err| {
error!("Populating MMDS from file failed: {:?}", err);
crate::FcExitCode::GenericError
})?;
vm_resources.locked_mmds_or_default().put_data(
serde_json::from_str(data).expect("MMDS error: metadata provided not valid json"),
)?;

Check warning on line 349 in src/vmm/src/rpc_interface.rs

View check run for this annotation

Codecov / codecov/patch

src/vmm/src/rpc_interface.rs#L347-L349

Added lines #L347 - L349 were not covered by tests
roypat marked this conversation as resolved.
Show resolved Hide resolved

info!("Successfully added metadata to mmds from file");
}
Expand Down Expand Up @@ -376,8 +380,8 @@
to_api.send(Box::new(res)).expect("one-shot channel closed");

// If any fatal errors were encountered, break the loop.
if let Some(exit_code) = preboot_controller.fatal_error {
return Err(exit_code);
if let Some(preboot_error) = preboot_controller.fatal_error {
return Err(preboot_error);

Check warning on line 384 in src/vmm/src/rpc_interface.rs

View check run for this annotation

Codecov / codecov/patch

src/vmm/src/rpc_interface.rs#L383-L384

Added lines #L383 - L384 were not covered by tests
}
}

Expand Down Expand Up @@ -577,7 +581,7 @@
)
.map_err(|err| {
// If restore fails, we consider the process is too dirty to recover.
self.fatal_error = Some(FcExitCode::BadConfiguration);
self.fatal_error = Some(BuildMicrovmFromRequestsError::Restore);

Check warning on line 584 in src/vmm/src/rpc_interface.rs

View check run for this annotation

Codecov / codecov/patch

src/vmm/src/rpc_interface.rs#L584

Added line #L584 was not covered by tests
err
})?;
// Resume VM
Expand All @@ -587,7 +591,7 @@
.resume_vm()
.map_err(|err| {
// If resume fails, we consider the process is too dirty to recover.
self.fatal_error = Some(FcExitCode::BadConfiguration);
self.fatal_error = Some(BuildMicrovmFromRequestsError::Resume);

Check warning on line 594 in src/vmm/src/rpc_interface.rs

View check run for this annotation

Codecov / codecov/patch

src/vmm/src/rpc_interface.rs#L594

Added line #L594 was not covered by tests
err
})?;
}
Expand Down