Merge pull request 'Miniserde refactor with custom error impls' (#11) from miniserde_refactor into main

Reviewed-on: #11
This commit is contained in:
glyph 2021-11-02 09:33:26 +00:00
commit 35a524e9db
6 changed files with 77 additions and 71 deletions

View File

@ -28,13 +28,13 @@ travis-ci = { repository = "peachcloud/peach-stats", branch = "master" }
maintenance = { status = "actively-developed" } maintenance = { status = "actively-developed" }
[dependencies] [dependencies]
env_logger = "0.6" env_logger = "0.9"
jsonrpc-core = "11" jsonrpc-core = "18"
jsonrpc-http-server = "11" jsonrpc-http-server = "18"
jsonrpc-test = "11"
log = "0.4" log = "0.4"
probes = "0.3" miniserde = "0.1.15"
serde = { version = "1", features = ["derive"] } probes = "0.4.1"
serde_json = "1" systemstat = "0.1.10"
snafu = "0.4"
systemstat = "0.1" [dev-dependencies]
jsonrpc-test = "18"

View File

@ -14,7 +14,7 @@ System statistics microservice module for PeachCloud. Provides a JSON-RPC wrappe
| `load_average` | Load average statistics | `one`, `five`, `fifteen` | | `load_average` | Load average statistics | `one`, `five`, `fifteen` |
| `mem_stats` | Memory statistics | `total`, `free`, `used` | | `mem_stats` | Memory statistics | `total`, `free`, `used` |
| `ping` | Microservice status | `success` if running | | `ping` | Microservice status | `success` if running |
| `uptime` | System uptime | `secs`, `nanos` | | `uptime` | System uptime | `secs` |
### Environment ### Environment
@ -101,7 +101,7 @@ With microservice running, open a second terminal window and use `curl` to call
Server responds with: Server responds with:
`{"jsonrpc":"2.0","result":"{\"secs\":840968,\"nanos\":0}","id":1}` `{"jsonrpc":"2.0","result":"{\"secs\":840968}","id":1}`
### Licensing ### Licensing

View File

@ -1,67 +1,69 @@
use std::{error, io}; use std::{error, fmt, io};
use jsonrpc_core::{types::error::Error, ErrorCode}; use jsonrpc_core::{types::error::Error, ErrorCode};
use probes::ProbeError; use probes::ProbeError;
use serde_json::Error as SerdeError;
use snafu::Snafu;
pub type BoxError = Box<dyn error::Error>; #[derive(Debug)]
#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
pub enum StatError { pub enum StatError {
#[snafu(display("Failed to retrieve CPU statistics: {}", source))] CpuStat { source: ProbeError },
ReadCpuStat { source: ProbeError }, DiskUsage { source: ProbeError },
LoadAvg { source: ProbeError },
MemStat { source: ProbeError },
Uptime { source: io::Error },
}
#[snafu(display("Failed to retrieve disk usage statistics: {}", source))] impl error::Error for StatError {}
ReadDiskUsage { source: ProbeError },
#[snafu(display("Failed to retrieve load average statistics: {}", source))] impl fmt::Display for StatError {
ReadLoadAvg { source: ProbeError }, fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
#[snafu(display("Failed to retrieve memory statistics: {}", source))] StatError::CpuStat { ref source } => {
ReadMemStat { source: ProbeError }, write!(f, "Failed to retrieve CPU statistics: {}", source)
}
#[snafu(display("Failed to retrieve system uptime: {}", source))] StatError::DiskUsage { ref source } => {
ReadUptime { source: io::Error }, write!(f, "Failed to retrieve disk usage statistics: {}", source)
}
#[snafu(display("JSON serialization failed: {}", source))] StatError::LoadAvg { ref source } => {
SerdeSerialize { source: SerdeError }, write!(f, "Failed to retrieve load average statistics: {}", source)
}
StatError::MemStat { ref source } => {
write!(f, "Failed to retrieve memory statistics: {}", source)
}
StatError::Uptime { ref source } => {
write!(f, "Failed to retrieve system uptime: {}", source)
}
}
}
} }
impl From<StatError> for Error { impl From<StatError> for Error {
fn from(err: StatError) -> Self { fn from(err: StatError) -> Self {
match &err { match &err {
StatError::ReadCpuStat { source } => Error { StatError::CpuStat { source } => Error {
code: ErrorCode::ServerError(-32001), code: ErrorCode::ServerError(-32001),
message: format!("Failed to retrieve CPU statistics: {}", source), message: format!("Failed to retrieve CPU statistics: {}", source),
data: None, data: None,
}, },
StatError::ReadDiskUsage { source } => Error { StatError::DiskUsage { source } => Error {
code: ErrorCode::ServerError(-32001), code: ErrorCode::ServerError(-32001),
message: format!("Failed to retrieve disk usage statistics: {}", source), message: format!("Failed to retrieve disk usage statistics: {}", source),
data: None, data: None,
}, },
StatError::ReadLoadAvg { source } => Error { StatError::LoadAvg { source } => Error {
code: ErrorCode::ServerError(-32001), code: ErrorCode::ServerError(-32001),
message: format!("Failed to retrieve load average statistics: {}", source), message: format!("Failed to retrieve load average statistics: {}", source),
data: None, data: None,
}, },
StatError::ReadMemStat { source } => Error { StatError::MemStat { source } => Error {
code: ErrorCode::ServerError(-32001), code: ErrorCode::ServerError(-32001),
message: format!("Failed to retrieve memory statistics: {}", source), message: format!("Failed to retrieve memory statistics: {}", source),
data: None, data: None,
}, },
StatError::ReadUptime { source } => Error { StatError::Uptime { source } => Error {
code: ErrorCode::ServerError(-32001), code: ErrorCode::ServerError(-32001),
message: format!("Failed to retrieve system uptime: {}", source), message: format!("Failed to retrieve system uptime: {}", source),
data: None, data: None,
}, },
StatError::SerdeSerialize { source } => Error {
code: ErrorCode::ServerError(-32002),
message: format!("JSON serialization failed: {}", source),
data: None,
},
} }
} }
} }

View File

@ -6,56 +6,56 @@ use std::{env, result::Result};
use jsonrpc_core::{IoHandler, Value}; use jsonrpc_core::{IoHandler, Value};
use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder}; use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
#[allow(unused_imports)]
use jsonrpc_test as test;
use log::info; use log::info;
use crate::error::BoxError; use crate::error::StatError;
pub fn run() -> Result<(), BoxError> { pub fn run() -> Result<(), StatError> {
info!("Starting up."); info!("Starting up.");
info!("Creating JSON-RPC I/O handler."); info!("Creating JSON-RPC I/O handler.");
let mut io = IoHandler::default(); let mut io = IoHandler::default();
io.add_method("cpu_stats", move |_| { io.add_method("cpu_stats", move |_| async {
info!("Fetching CPU statistics."); info!("Fetching CPU statistics.");
let stats = stats::cpu_stats()?; let stats = stats::cpu_stats()?;
Ok(Value::String(stats)) Ok(Value::String(stats))
}); });
io.add_method("cpu_stats_percent", move |_| { io.add_method("cpu_stats_percent", move |_| async {
info!("Fetching CPU statistics as percentages."); info!("Fetching CPU statistics as percentages.");
let stats = stats::cpu_stats_percent()?; let stats = stats::cpu_stats_percent()?;
Ok(Value::String(stats)) Ok(Value::String(stats))
}); });
io.add_method("disk_usage", move |_| { io.add_method("disk_usage", move |_| async {
info!("Fetching disk usage statistics."); info!("Fetching disk usage statistics.");
let disks = stats::disk_usage()?; let disks = stats::disk_usage()?;
Ok(Value::String(disks)) Ok(Value::String(disks))
}); });
io.add_method("load_average", move |_| { io.add_method("load_average", move |_| async {
info!("Fetching system load average statistics."); info!("Fetching system load average statistics.");
let avg = stats::load_average()?; let avg = stats::load_average()?;
Ok(Value::String(avg)) Ok(Value::String(avg))
}); });
io.add_method("mem_stats", move |_| { io.add_method("mem_stats", move |_| async {
info!("Fetching current memory statistics."); info!("Fetching current memory statistics.");
let mem = stats::mem_stats()?; let mem = stats::mem_stats()?;
Ok(Value::String(mem)) Ok(Value::String(mem))
}); });
io.add_method("ping", |_| Ok(Value::String("success".to_string()))); io.add_method("ping", |_| async {
Ok(Value::String("success".to_string()))
});
io.add_method("uptime", move |_| { io.add_method("uptime", move |_| async {
info!("Fetching system uptime."); info!("Fetching system uptime.");
let uptime = stats::uptime()?; let uptime = stats::uptime()?;
@ -85,16 +85,17 @@ pub fn run() -> Result<(), BoxError> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use jsonrpc_test as test_rpc;
// test to ensure correct success response // test to ensure correct success response
#[test] #[test]
fn rpc_success() { fn rpc_success() {
let rpc = { let rpc = {
let mut io = IoHandler::new(); let mut io = IoHandler::new();
io.add_method("rpc_success_response", |_| { io.add_method("rpc_success_response", |_| async {
Ok(Value::String("success".into())) Ok(Value::String("success".into()))
}); });
test::Rpc::from(io) test_rpc::Rpc::from(io)
}; };
assert_eq!(rpc.request("rpc_success_response", &()), r#""success""#); assert_eq!(rpc.request("rpc_success_response", &()), r#""success""#);

View File

@ -1,14 +1,14 @@
use std::result::Result; use std::result::Result;
use miniserde::json;
use probes::{cpu, disk_usage, load, memory}; use probes::{cpu, disk_usage, load, memory};
use snafu::ResultExt;
use systemstat::{Platform, System}; use systemstat::{Platform, System};
use crate::error::*; use crate::error::StatError;
use crate::structs::{CpuStat, CpuStatPercentages, DiskUsage, LoadAverage, MemStat}; use crate::structs::{CpuStat, CpuStatPercentages, DiskUsage, LoadAverage, MemStat};
pub fn cpu_stats() -> Result<String, StatError> { pub fn cpu_stats() -> Result<String, StatError> {
let cpu_stats = cpu::proc::read().context(ReadCpuStat)?; let cpu_stats = cpu::proc::read().map_err(|source| StatError::CpuStat { source })?;
let s = cpu_stats.stat; let s = cpu_stats.stat;
let cpu = CpuStat { let cpu = CpuStat {
user: s.user, user: s.user,
@ -16,13 +16,13 @@ pub fn cpu_stats() -> Result<String, StatError> {
nice: s.nice, nice: s.nice,
idle: s.idle, idle: s.idle,
}; };
let json_cpu = serde_json::to_string(&cpu).context(SerdeSerialize)?; let json_cpu = json::to_string(&cpu);
Ok(json_cpu) Ok(json_cpu)
} }
pub fn cpu_stats_percent() -> Result<String, StatError> { pub fn cpu_stats_percent() -> Result<String, StatError> {
let cpu_stats = cpu::proc::read().context(ReadCpuStat)?; let cpu_stats = cpu::proc::read().map_err(|source| StatError::CpuStat { source })?;
let s = cpu_stats.stat.in_percentages(); let s = cpu_stats.stat.in_percentages();
let cpu = CpuStatPercentages { let cpu = CpuStatPercentages {
user: s.user, user: s.user,
@ -30,13 +30,13 @@ pub fn cpu_stats_percent() -> Result<String, StatError> {
nice: s.nice, nice: s.nice,
idle: s.idle, idle: s.idle,
}; };
let json_cpu = serde_json::to_string(&cpu).context(SerdeSerialize)?; let json_cpu = json::to_string(&cpu);
Ok(json_cpu) Ok(json_cpu)
} }
pub fn disk_usage() -> Result<String, StatError> { pub fn disk_usage() -> Result<String, StatError> {
let disks = disk_usage::read().context(ReadDiskUsage)?; let disks = disk_usage::read().map_err(|source| StatError::DiskUsage { source })?;
let mut disk_usages = Vec::new(); let mut disk_usages = Vec::new();
for d in disks { for d in disks {
let disk = DiskUsage { let disk = DiskUsage {
@ -49,39 +49,42 @@ pub fn disk_usage() -> Result<String, StatError> {
}; };
disk_usages.push(disk); disk_usages.push(disk);
} }
let json_disks = serde_json::to_string(&disk_usages).context(SerdeSerialize)?; let json_disks = json::to_string(&disk_usages);
Ok(json_disks) Ok(json_disks)
} }
pub fn load_average() -> Result<String, StatError> { pub fn load_average() -> Result<String, StatError> {
let l = load::read().context(ReadLoadAvg)?; let l = load::read().map_err(|source| StatError::LoadAvg { source })?;
let load_avg = LoadAverage { let load_avg = LoadAverage {
one: l.one, one: l.one,
five: l.five, five: l.five,
fifteen: l.fifteen, fifteen: l.fifteen,
}; };
let json_load_avg = serde_json::to_string(&load_avg).context(SerdeSerialize)?; let json_load_avg = json::to_string(&load_avg);
Ok(json_load_avg) Ok(json_load_avg)
} }
pub fn mem_stats() -> Result<String, StatError> { pub fn mem_stats() -> Result<String, StatError> {
let m = memory::read().context(ReadMemStat)?; let m = memory::read().map_err(|source| StatError::MemStat { source })?;
let mem = MemStat { let mem = MemStat {
total: m.total(), total: m.total(),
free: m.free(), free: m.free(),
used: m.used(), used: m.used(),
}; };
let json_mem = serde_json::to_string(&mem).context(SerdeSerialize)?; let json_mem = json::to_string(&mem);
Ok(json_mem) Ok(json_mem)
} }
pub fn uptime() -> Result<String, StatError> { pub fn uptime() -> Result<String, StatError> {
let sys = System::new(); let sys = System::new();
let uptime = sys.uptime().context(ReadUptime)?; let uptime = sys
let json_uptime = serde_json::to_string(&uptime).context(SerdeSerialize)?; .uptime()
.map_err(|source| StatError::Uptime { source })?;
let uptime_secs = uptime.as_secs();
let json_uptime = json::to_string(&uptime_secs);
Ok(json_uptime) Ok(json_uptime)
} }

View File

@ -1,4 +1,4 @@
use serde::Serialize; use miniserde::Serialize;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct CpuStat { pub struct CpuStat {