Skip to content

Commit aed39c9

Browse files
committed
api_server: fix error propagating
The old IOError and EventFdError in the api_server's enum Error are not actually used. They have been at some point used but we later introduced the logci where bind_and_run would call std::proces::exit on errors on HttpServer creation so in reality the IOError and EventFD error would not be thrown ever. There are still some things to figure out: bind_and_run will not return any type of error except for the one we add in this commit. All the error processing happens inside bind and run in reality. Signed-off-by: Diana Popa <dpopa@amazon.com>
1 parent 9adb2b2 commit aed39c9

File tree

5 files changed

+59
-72
lines changed

5 files changed

+59
-72
lines changed

src/api_server/src/lib.rs

Lines changed: 10 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ mod request;
1414

1515
use std::path::PathBuf;
1616
use std::sync::mpsc;
17-
use std::{fmt, io};
1817

1918
use logger::{
2019
debug, error, info, update_metric_with_elapsed_time, warn, ProcessTimeReporter, METRICS,
@@ -30,30 +29,18 @@ use vmm::rpc_interface::{VmmAction, VmmActionError, VmmData};
3029
use vmm::vmm_config::snapshot::SnapshotType;
3130

3231
use crate::parsed_request::{ParsedRequest, RequestAction};
32+
use crate::Error::ServerCreation;
3333

3434
/// Shorthand type for a request containing a boxed VmmAction.
3535
pub type ApiRequest = Box<VmmAction>;
3636
/// Shorthand type for a response containing a boxed Result.
3737
pub type ApiResponse = Box<std::result::Result<VmmData, VmmActionError>>;
3838

3939
/// Errors thrown when binding the API server to the socket path.
40-
#[derive(thiserror::Error)]
40+
#[derive(Debug)]
4141
pub enum Error {
42-
/// IO related error.
43-
#[error("IO error: {0}")]
44-
Io(io::Error),
45-
/// EventFD related error.
46-
#[error("EventFd error: {0}")]
47-
Eventfd(io::Error),
48-
}
49-
50-
impl fmt::Debug for Error {
51-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52-
match *self {
53-
Error::Io(ref err) => write!(f, "IO error: {}", err),
54-
Error::Eventfd(ref err) => write!(f, "EventFd error: {}", err),
55-
}
56-
}
42+
/// HTTP Server creation related error.
43+
ServerCreation(ServerError),
5744
}
5845

5946
type Result<T> = std::result::Result<T, Error>;
@@ -137,7 +124,7 @@ impl ApiServer {
137124
/// .spawn(move || {
138125
/// ApiServer::new(api_request_sender, vmm_response_receiver, to_vmm_fd)
139126
/// .bind_and_run(
140-
/// PathBuf::from(api_thread_path_to_socket),
127+
/// &PathBuf::from(api_thread_path_to_socket),
141128
/// time_reporter,
142129
/// seccomp_filters.get("api").unwrap(),
143130
/// payload_limit,
@@ -161,16 +148,14 @@ impl ApiServer {
161148
/// ```
162149
pub fn bind_and_run(
163150
&mut self,
164-
path: PathBuf,
151+
path: &PathBuf,
165152
process_time_reporter: ProcessTimeReporter,
166153
seccomp_filter: BpfProgramRef,
167154
api_payload_limit: usize,
168155
socket_ready: mpsc::Sender<bool>,
169156
) -> Result<()> {
170-
let mut server = HttpServer::new(path).unwrap_or_else(|err| {
171-
error!("Error creating the HTTP server: {}", err);
172-
std::process::exit(vmm::FcExitCode::GenericError as i32);
173-
});
157+
let mut server = HttpServer::new(path).map_err(ServerCreation)?;
158+
174159
// Announce main thread that the socket path was created.
175160
// As per the doc, "A send operation can only fail if the receiving end of a channel is
176161
// disconnected". so this means that the main thread has exited.
@@ -338,34 +323,6 @@ mod tests {
338323

339324
use super::*;
340325

341-
#[test]
342-
fn test_error_messages() {
343-
let err = Error::Io(io::Error::from_raw_os_error(0));
344-
assert_eq!(
345-
format!("{}", err),
346-
format!("IO error: {}", io::Error::from_raw_os_error(0))
347-
);
348-
let err = Error::Eventfd(io::Error::from_raw_os_error(0));
349-
assert_eq!(
350-
format!("{}", err),
351-
format!("EventFd error: {}", io::Error::from_raw_os_error(0))
352-
);
353-
}
354-
355-
#[test]
356-
fn test_error_debug() {
357-
let err = Error::Io(io::Error::from_raw_os_error(0));
358-
assert_eq!(
359-
format!("{:?}", err),
360-
format!("IO error: {}", io::Error::from_raw_os_error(0))
361-
);
362-
let err = Error::Eventfd(io::Error::from_raw_os_error(0));
363-
assert_eq!(
364-
format!("{:?}", err),
365-
format!("EventFd error: {}", io::Error::from_raw_os_error(0))
366-
);
367-
}
368-
369326
#[test]
370327
fn test_serve_vmm_action_request() {
371328
let to_vmm_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap();
@@ -491,7 +448,7 @@ mod tests {
491448
.spawn(move || {
492449
ApiServer::new(api_request_sender, vmm_response_receiver, to_vmm_fd)
493450
.bind_and_run(
494-
PathBuf::from(api_thread_path_to_socket),
451+
&PathBuf::from(api_thread_path_to_socket),
495452
ProcessTimeReporter::new(Some(1), Some(1), Some(1)),
496453
seccomp_filters.get("api").unwrap(),
497454
vmm::HTTP_MAX_PAYLOAD_SIZE,
@@ -539,7 +496,7 @@ mod tests {
539496
.spawn(move || {
540497
ApiServer::new(api_request_sender, vmm_response_receiver, to_vmm_fd)
541498
.bind_and_run(
542-
PathBuf::from(api_thread_path_to_socket),
499+
&PathBuf::from(&api_thread_path_to_socket),
543500
ProcessTimeReporter::new(Some(1), Some(1), Some(1)),
544501
seccomp_filters.get("api").unwrap(),
545502
50,

src/api_server/src/parsed_request.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,10 @@ pub(crate) fn method_to_error(method: Method) -> Result<ParsedRequest, Error> {
257257

258258
#[derive(Debug, derive_more::From)]
259259
pub(crate) enum Error {
260-
// A generic error, with a given status code and message to be turned into a fault message.
261-
Generic(StatusCode, String),
262260
// The resource ID is empty.
263261
EmptyID,
262+
// A generic error, with a given status code and message to be turned into a fault message.
263+
Generic(StatusCode, String),
264264
// The resource ID must only contain alphanumeric characters and '_'.
265265
InvalidID,
266266
// The HTTP method & request path combination is not valid.
@@ -272,8 +272,8 @@ pub(crate) enum Error {
272272
impl std::fmt::Display for Error {
273273
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
274274
match self {
275-
Error::Generic(_, ref desc) => write!(f, "{}", desc),
276275
Error::EmptyID => write!(f, "The ID cannot be empty."),
276+
Error::Generic(_, ref desc) => write!(f, "{desc}"),
277277
Error::InvalidID => write!(
278278
f,
279279
"API Resource IDs can only contain alphanumeric characters and underscores."
@@ -286,8 +286,7 @@ impl std::fmt::Display for Error {
286286
),
287287
Error::SerdeJson(ref err) => write!(
288288
f,
289-
"An error occurred when deserializing the json body of a request: {}.",
290-
err
289+
"An error occurred when deserializing the json body of a request: {err}."
291290
),
292291
}
293292
}

src/arch/src/x86_64/interrupts.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use kvm_bindings::kvm_lapic_state;
99
use kvm_ioctls::VcpuFd;
1010
use utils::byte_order;
11+
1112
/// Errors thrown while configuring the LAPIC.
1213
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
1314
pub enum Error {

src/firecracker/src/api_server_adapter.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
99
use std::sync::{Arc, Mutex};
1010
use std::thread;
1111

12-
use api_server::{ApiRequest, ApiResponse, ApiServer};
12+
use api_server::{ApiRequest, ApiResponse, ApiServer, ServerError};
1313
use event_manager::{EventOps, Events, MutEventSubscriber, SubscriberOps};
1414
use logger::{error, warn, ProcessTimeReporter};
1515
use seccompiler::BpfThreadMap;
@@ -149,25 +149,26 @@ pub(crate) fn run_with_api(
149149
.name("fc_api".to_owned())
150150
.spawn(move || {
151151
match ApiServer::new(to_vmm, from_vmm, to_vmm_event_fd).bind_and_run(
152-
api_bind_path,
152+
&api_bind_path,
153153
process_time_reporter,
154154
&api_seccomp_filter,
155155
api_payload_limit,
156156
socket_ready_sender,
157157
) {
158158
Ok(_) => (),
159-
Err(api_server::Error::Io(inner)) => match inner.kind() {
160-
std::io::ErrorKind::AddrInUse => panic!(
161-
"Failed to open the API socket: {:?}",
162-
api_server::Error::Io(inner)
163-
),
164-
_ => panic!(
165-
"Failed to communicate with the API socket: {:?}",
166-
api_server::Error::Io(inner)
167-
),
168-
},
169-
Err(eventfd_err @ api_server::Error::Eventfd(_)) => {
170-
panic!("Failed to open the API socket: {:?}", eventfd_err)
159+
Err(api_server::Error::ServerCreation(ServerError::IOError(inner)))
160+
if inner.kind() == std::io::ErrorKind::AddrInUse =>
161+
{
162+
let sock_path = api_bind_path.display().to_string();
163+
error!(
164+
"Failed to open the API socket at: {sock_path}. Check that it is not \
165+
already used."
166+
);
167+
std::process::exit(vmm::FcExitCode::GenericError as i32);
168+
}
169+
Err(api_server::Error::ServerCreation(err)) => {
170+
error!("Failed to bind and run the HTTP server: {err}");
171+
std::process::exit(vmm::FcExitCode::GenericError as i32);
171172
}
172173
}
173174
})
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""Tests scenario exercising api server functionality."""
4+
5+
import socket
6+
from framework.utils import run_cmd
7+
8+
9+
def test_api_socket_in_use(test_microvm_with_api):
10+
"""
11+
Test error message when api socket is already in use.
12+
13+
This is a very frequent scenario when Firecracker cannot
14+
start due to the socket being left open from previous runs.
15+
Check that the error message is a fixed one and that it also
16+
contains the name of the path.
17+
18+
@type: functional
19+
"""
20+
microvm = test_microvm_with_api
21+
22+
cmd = "mkdir {}/run".format(microvm.chroot())
23+
run_cmd(cmd)
24+
25+
sock = socket.socket(socket.AF_UNIX)
26+
sock.bind(microvm.jailer.api_socket_path())
27+
microvm.spawn()
28+
msg = "Failed to open the API socket at: /run/firecracker.socket. Check that it is not already used."
29+
microvm.check_log_message(msg)

0 commit comments

Comments
 (0)