Skip to content

Specify log file through API #250

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

Merged
merged 5 commits into from
May 9, 2018
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
42 changes: 42 additions & 0 deletions api/swagger/firecracker-beta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,29 @@ paths:
schema:
$ref: "#/definitions/Error"

/logger:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add these to firecracker-v1.0.yaml as well.

Copy link
Member

@andreeaflorescu andreeaflorescu May 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed about it yesterday. firecracker-v1.0.yaml is very outdated. So we have here two possibilities: either update it to match what we have now in firecracker-beta, or we don't update it at all for now and only use it as a " What the future looks like" yaml. Since we are sort of time bound, I suggest we don't update it for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

put:
summary: Initializes the logging system by specifying a log path on the host.
operationId: putLogger
parameters:
- name: body
in: body
description: Logging system description
required: true
schema:
$ref: "#/definitions/Logger"
responses:
201:
description: Logger created.
400:
description: Logger cannot be initialized due to bad input.
schema:
$ref: "#/definitions/Error"
default:
description: Internal server error.
schema:
$ref: "#/definitions/Error"

/machine-config:
get:
summary: Get the machine configuration of the VM.
Expand Down Expand Up @@ -348,6 +371,25 @@ definitions:
type: string
description: Host level path to the kernel image used to boot the guest

Logger:
type: object
description:
Describes the configuration option for the logger intitialization.
properties:
path:
type: string
description: The path on the host for the log file.
level:
type: string
description: Set the level.
enum: [Error, Warning, Info, Debug]
show_level:
type: boolean
description: Whether or not to output the level in the logs.
show_log_origin:
type: boolean
description: Whether or not to include the file path and line number of the log's origin.

MachineConfiguration:
type: object
description:
Expand Down
24 changes: 23 additions & 1 deletion api_server/src/http_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ enum Error<'a> {
Generic(StatusCode, String),
// The HTTP method & request path combination is not valid.
InvalidPathMethod(&'a str, Method),
// An error occured when deserializing the json body of a request.
// An error occurred when deserializing the json body of a request.
SerdeJson(serde_json::Error),
}

Expand Down Expand Up @@ -154,6 +154,25 @@ fn parse_boot_source_req<'a>(
}
}

// Turns a GET/PUT /boot-source HTTP request into a ParsedRequest
fn parse_logger_req<'a>(
path_tokens: &Vec<&str>,
path: &'a str,
method: Method,
body: &Chunk,
) -> Result<'a, ParsedRequest> {
match path_tokens[1..].len() {
0 if method == Method::Get => Ok(ParsedRequest::Dummy),

0 if method == Method::Put => Ok(serde_json::from_slice::<request::APILoggerDescription>(
body,
).map_err(Error::SerdeJson)?
.into_parsed_request()
.map_err(|s| Error::Generic(StatusCode::BadRequest, s))?),
_ => Err(Error::InvalidPathMethod(path, method)),
}
}

// Turns a GET/PUT /drives HTTP request into a ParsedRequest
fn parse_drives_req<'a>(
path_tokens: &Vec<&str>,
Expand Down Expand Up @@ -265,6 +284,8 @@ fn parse_request<'a>(
method,
path,
str::from_utf8(body.as_ref()).unwrap()
// when time will come, we could better do
// serde_json::from_slice(&body).unwrap()
)
);
}
Expand Down Expand Up @@ -297,6 +318,7 @@ fn parse_request<'a>(
"actions" => parse_actions_req(&path_tokens, path, method, &id_from_path, body, action_map),
"boot-source" => parse_boot_source_req(&path_tokens, path, method, body),
"drives" => parse_drives_req(&path_tokens, path, method, &id_from_path, body),
"logger" => parse_logger_req(&path_tokens, path, method, body),
"machine-config" => parse_machine_config_req(&path_tokens, path, method, body),
"network-interfaces" => parse_netif_req(&path_tokens, path, method, &id_from_path, body),
"vsocks" => parse_vsocks_req(&path_tokens, path, method, &id_from_path, body),
Expand Down
4 changes: 2 additions & 2 deletions api_server/src/request/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use std::result;
use hyper::Method;
pub use self::async::{AsyncOutcome, AsyncOutcomeReceiver, AsyncOutcomeSender, AsyncRequest,
AsyncRequestBody};
pub use self::sync::{BootSourceBody, DriveDescription, NetworkInterfaceBody, SyncOutcomeReceiver,
SyncOutcomeSender, SyncRequest, VsockJsonBody};
pub use self::sync::{APILoggerDescription, BootSourceBody, DriveDescription, NetworkInterfaceBody,
SyncOutcomeReceiver, SyncOutcomeSender, SyncRequest, VsockJsonBody};

pub mod instance_info;

Expand Down
126 changes: 126 additions & 0 deletions api_server/src/request/sync/logger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use std::result;

use futures::sync::oneshot;
use hyper::{Response, StatusCode};

use http_service::{empty_response, json_fault_message, json_response};
use request::{ParsedRequest, SyncRequest};
use request::sync::GenerateResponse;

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub enum APILoggerLevel {
Error,
Warning,
Info,
Debug,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct APILoggerDescription {
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub level: Option<APILoggerLevel>,
#[serde(skip_serializing_if = "Option::is_none")]
pub show_level: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub show_log_origin: Option<bool>,
}

#[derive(Debug)]
pub enum APILoggerError {
InitializationFailure(String),
}

impl GenerateResponse for APILoggerError {
fn generate_response(&self) -> Response {
use self::APILoggerError::*;
match *self {
InitializationFailure(ref e) => json_response(
StatusCode::BadRequest,
json_fault_message(format!{"Cannot initialize logging system! {}", e}),
),
}
}
}

pub enum PutLoggerOutcome {
Initialized,
Error(APILoggerError),
}

impl GenerateResponse for PutLoggerOutcome {
fn generate_response(&self) -> Response {
use self::PutLoggerOutcome::*;
match *self {
Initialized => empty_response(StatusCode::Created),
Error(ref e) => e.generate_response(),
}
}
}

impl APILoggerDescription {
pub fn into_parsed_request(self) -> result::Result<ParsedRequest, String> {
let (sender, receiver) = oneshot::channel();
Ok(ParsedRequest::Sync(
SyncRequest::PutLogger(self, sender),
receiver,
))
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_generate_response_logger_error() {
assert_eq!(
APILoggerError::InitializationFailure("Could not initialize log system".to_string())
.generate_response()
.status(),
StatusCode::BadRequest
);
assert!(
format!(
"{:?}",
APILoggerError::InitializationFailure(
"Could not initialize log system".to_string()
)
).contains("InitializationFailure")
);
}

#[test]
fn test_generate_response_put_logger_outcome() {
assert_eq!(
PutLoggerOutcome::Initialized.generate_response().status(),
StatusCode::Created
);
assert_eq!(
PutLoggerOutcome::Error(APILoggerError::InitializationFailure(
"Could not initialize log system".to_string()
)).generate_response()
.status(),
StatusCode::BadRequest
);
}

#[test]
fn test_into_parsed_request() {
let desc = APILoggerDescription {
path: String::from(""),
level: None,
show_level: None,
show_log_origin: None,
};
format!("{:?}", desc);
assert!(&desc.clone().into_parsed_request().is_ok());
let (sender, receiver) = oneshot::channel();
assert!(&desc.clone()
.into_parsed_request()
.eq(&Ok(ParsedRequest::Sync(
SyncRequest::PutLogger(desc, sender),
receiver
))));
}
}
7 changes: 7 additions & 0 deletions api_server/src/request/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ use net_util::TapError;

pub mod boot_source;
mod drive;
mod logger;
pub mod machine_configuration;
mod net;
mod vsock;

pub use self::drive::{DriveDescription, DriveError, DrivePermissions, PutDriveOutcome};
pub use self::boot_source::{BootSourceBody, BootSourceType, LocalImage};
pub use self::logger::{APILoggerDescription, APILoggerError, APILoggerLevel, PutLoggerOutcome};
pub use self::net::NetworkInterfaceBody;
pub use self::vsock::VsockJsonBody;

Expand Down Expand Up @@ -56,6 +58,7 @@ pub enum SyncRequest {
GetMachineConfiguration(SyncOutcomeSender),
PutBootSource(BootSourceBody, SyncOutcomeSender),
PutDrive(DriveDescription, SyncOutcomeSender),
PutLogger(APILoggerDescription, SyncOutcomeSender),
PutMachineConfiguration(MachineConfiguration, SyncOutcomeSender),
PutNetworkInterface(NetworkInterfaceBody, SyncOutcomeSender),
PutVsock(VsockJsonBody, SyncOutcomeSender),
Expand Down Expand Up @@ -137,6 +140,10 @@ mod tests {
&SyncRequest::PutDrive(ref ddesc, _),
&SyncRequest::PutDrive(ref other_ddesc, _),
) => ddesc == other_ddesc,
(
&SyncRequest::PutLogger(ref logdesc, _),
&SyncRequest::PutLogger(ref other_logdesc, _),
) => logdesc == other_logdesc,
(
&SyncRequest::PutMachineConfiguration(ref mcb, _),
&SyncRequest::PutMachineConfiguration(ref other_mcb, _),
Expand Down
63 changes: 49 additions & 14 deletions logger/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ pub enum LoggerError {
NeverInitialized(String),
/// The logger does not allow reinitialization.
AlreadyInitialized,
/// Initialization has previously failed and can not be retried.
Poisoned(String),
/// Creating log file fails.
CreateLogFile(std::io::Error),
/// Writing to log file fails.
Expand All @@ -25,13 +23,20 @@ pub enum LoggerError {
impl fmt::Display for LoggerError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let printable = match *self {
LoggerError::NeverInitialized(ref e) => e,
LoggerError::AlreadyInitialized => "Reinitialization of logger not allowed",
LoggerError::Poisoned(ref e) => e,
LoggerError::CreateLogFile(ref e) => e.description(),
LoggerError::FileLogWrite(ref e) => e.description(),
LoggerError::FileLogFlush(ref e) => e.description(),
LoggerError::FileLogLock(ref e) => e,
LoggerError::NeverInitialized(ref e) => format!("{}", e),
LoggerError::AlreadyInitialized => {
format!("{}", "Reinitialization of logger not allowed.")
}
LoggerError::CreateLogFile(ref e) => {
format!("Failed to create log file. Error: {}", e.description())
}
LoggerError::FileLogWrite(ref e) => {
format!("Failed to write to log file. Error: {}", e.description())
}
LoggerError::FileLogFlush(ref e) => {
format!("Failed to flush log file. Error: {}", e.description())
}
LoggerError::FileLogLock(ref e) => format!("{}", e),
};
write!(f, "{}", printable)
}
Expand All @@ -50,30 +55,60 @@ mod tests {
LoggerError::NeverInitialized(String::from("Bad Log Path Provided"))
).contains("NeverInitialized")
);
assert!(format!("{:?}", LoggerError::AlreadyInitialized).contains("AlreadyInitialized"));
assert!(
assert_eq!(
format!(
"{:?}",
LoggerError::Poisoned(String::from("Never Initialized"))
).contains("Poisoned")
"{}",
LoggerError::NeverInitialized(String::from("Bad Log Path Provided"))
),
"Bad Log Path Provided"
);

assert!(format!("{:?}", LoggerError::AlreadyInitialized).contains("AlreadyInitialized"));
assert_eq!(
format!("{}", LoggerError::AlreadyInitialized),
"Reinitialization of logger not allowed."
);

assert!(
format!(
"{:?}",
LoggerError::FileLogWrite(std::io::Error::new(ErrorKind::Interrupted, "write"))
).contains("FileLogWrite")
);
assert_eq!(
format!(
"{}",
LoggerError::FileLogWrite(std::io::Error::new(ErrorKind::Interrupted, "write"))
),
"Failed to write to log file. Error: write"
);

assert!(
format!(
"{:?}",
LoggerError::FileLogFlush(std::io::Error::new(ErrorKind::Interrupted, "flush"))
).contains("FileLogFlush")
);
assert_eq!(
format!(
"{}",
LoggerError::FileLogFlush(std::io::Error::new(ErrorKind::Interrupted, "flush"))
),
"Failed to flush log file. Error: flush"
);

assert!(
format!(
"{:?}",
LoggerError::FileLogLock(String::from("File log lock"))
).contains("FileLogLock")
);
assert_eq!(
format!(
"{}",
LoggerError::FileLogLock(String::from("File log lock"))
),
"File log lock"
);
}
}
Loading