Skip to content
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
13 changes: 2 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ hickory-resolver = "0.24.1"
local-ip-address = "0.6.3"
serde = { version = "1.0.210", features = ["derive"] }
serde_json = "1.0.128"
snafu = "0.8.5"
stackable-operator = { git = "https://github.com/stackabletech/operator-rs", tag = "stackable-operator-0.83.0" }
sysinfo = "0.32.0"
sysinfo = { version = "0.32.0", features = ["serde"] }
tracing = "0.1.40"
users = "0.11.0"

[build-dependencies]
built = { version = "0.7", features = ["chrono", "git2"] }
51 changes: 51 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use serde::Serialize;
use snafu::Snafu;

/// Wrapped version of the errors returned by [`sysinfo`], since they are bare [`str`]s.
#[derive(Debug, Snafu)]
#[snafu(display("{msg}"))]
pub struct SysinfoError {
pub msg: &'static str,
}

/// Wraps errors returned by a component to present them consistently for serialization.
#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum ComponentResult<T> {
Ok(T),
Err {
#[serde(rename = "$error")]
inner: ComponentError,
},
}
impl<T> ComponentResult<T> {
#[track_caller]
pub fn report_from_result<E: std::error::Error + 'static>(
component: &str,
result: Result<T, E>,
) -> ComponentResult<T> {
match result {
Ok(x) => ComponentResult::Ok(x),
Err(err) => {
tracing::error!(
error = &err as &dyn std::error::Error,
"error reported by {component}, ignoring...",
);
err.source();
ComponentResult::Err {
inner: ComponentError {
message: err.to_string(),
causes: std::iter::successors(err.source(), |err| err.source())
.map(|err| err.to_string())
.collect(),
},
}
}
}
}
}
#[derive(Debug, Serialize)]
pub struct ComponentError {
message: String,
causes: Vec<String>,
}
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod error;
mod system_information;

use clap::{crate_description, crate_version, Parser};
Expand Down
17 changes: 14 additions & 3 deletions src/system_information/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use serde::Serialize;

use crate::error::ComponentResult;

pub mod disk;
pub mod network;
pub mod os;
Expand All @@ -10,7 +12,7 @@ pub mod user;
pub struct SystemInformation {
pub resources: resources::Resources,
pub os: os::OperatingSystem,
pub current_user: user::User,
pub current_user: ComponentResult<user::User>,
pub disks: Vec<disk::Disk>,
pub network: network::SystemNetworkInfo,
// TODO:
Expand All @@ -34,10 +36,19 @@ impl SystemInformation {
#[tracing::instrument(name = "SystemInformation::collect")]
pub fn collect() -> Self {
tracing::info!("Starting data collection");

// Please note that we use "new_all" to ensure that all list of
// components, network interfaces, disks and users are already
// filled!
let sys = sysinfo::System::new_all();

let info = Self {
resources: resources::Resources::collect(),
resources: resources::Resources::collect(&sys),
os: os::OperatingSystem::collect(),
current_user: user::User::collect_current(),
current_user: ComponentResult::report_from_result(
"User::collect_current",
user::User::collect_current(&sys),
),
disks: disk::Disk::collect_all(),
network: network::SystemNetworkInfo::collect(),
};
Expand Down
9 changes: 2 additions & 7 deletions src/system_information/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,11 @@ pub struct Resources {
}

impl Resources {
#[tracing::instrument(name = "Resources::collect")]
pub fn collect() -> Self {
#[tracing::instrument(name = "Resources::collect", skip(sys))]
pub fn collect(sys: &System) -> Self {
// This style of "declare-then-log-then-merge becomes a bit verbose,
// but should help keep each log statement local to where that info is collected.

// Please note that we use "new_all" to ensure that all list of
// components, network interfaces, disks and users are already
// filled!
let sys = System::new_all();

let cpu_count = sys.cpus().len();
let physical_core_count = sys.physical_core_count();
tracing::info!(
Expand Down
44 changes: 35 additions & 9 deletions src/system_information/user.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,47 @@
use serde::Serialize;
use snafu::{OptionExt, ResultExt, Snafu};
use sysinfo::{Gid, Pid, Uid};

use crate::error::SysinfoError;

#[derive(Debug, Snafu)]
pub enum Error {
#[snafu(display("failed to get pid of the current process"))]
GetCurrentPid { source: SysinfoError },
#[snafu(display("current pid {pid} could not be resolved to a proess"))]
ResolveCurrentProcess { pid: Pid },
}
type Result<T, E = Error> = std::result::Result<T, E>;

#[derive(Debug, Serialize)]
pub struct User {
pub name: Option<String>, // The name of the current user
pub uid: u32, // The user ID (UID)
pub gid: u32, // The group ID (GID)
pub uid: Option<Uid>, // The user ID (UID)
pub gid: Option<Gid>, // The group ID (GID)
}

impl User {
pub fn collect_current() -> Self {
let uid = users::get_current_uid();
#[tracing::instrument(name = "User::collect_current", skip(sys))]
pub fn collect_current(sys: &sysinfo::System) -> Result<Self> {
let pid = sysinfo::get_current_pid()
.map_err(|msg| SysinfoError { msg })
.context(GetCurrentPidSnafu)?;
let current_process = sys
.process(pid)
.context(ResolveCurrentProcessSnafu { pid })?;
let uid = current_process.user_id();
let os_users = sysinfo::Users::new_with_refreshed_list();
let user = Self {
name: users::get_user_by_uid(uid).map(|user| user.name().to_string_lossy().to_string()),
uid,
gid: users::get_current_gid(),
name: uid.and_then(|uid| Some(os_users.get_user_by_id(uid)?.name().to_string())),
uid: uid.cloned(),
gid: current_process.group_id(),
};
tracing::info!(user.name, user.uid, user.gid, "current user");
user
tracing::info!(
user.name,
user.uid = user.uid.as_ref().map(|uid| format!("{uid:?}")),
user.gid = user.uid.as_ref().map(|gid| format!("{gid:?}")),
"current user"
);
Ok(user)
}
}
Loading