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
2,414 changes: 2,284 additions & 130 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ version = "0.1.0"
edition = "2021"

[dependencies]
clap = { version = "4.5.20", features = ["derive"] }

hickory-resolver = "0.24.1"
local-ip-address = "0.6.3"
serde = { version = "1.0.210", features = ["derive"] }
serde_json = "1.0.128"
stackable-operator = { git = "https://github.com/stackabletech/operator-rs", version = "0.81.0" }
sysinfo = "0.32.0"
tracing = "0.1.40"
users = "0.11.0"

[build-dependencies]
built = { version = "0.7", features = ["chrono", "git2"] }
3 changes: 3 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
built::write_built_file().unwrap();
}
206 changes: 61 additions & 145 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,158 +1,74 @@
mod system_information;

use local_ip_address::list_afinet_netifas;
use std::collections::{HashMap, HashSet};
use sysinfo::{Disks, System};

use crate::system_information::{SystemInformation, SystemNetworkInfo};
use hickory_resolver::system_conf::read_system_conf;
use hickory_resolver::Resolver;
use std::net::IpAddr;
use std::time::Duration;

fn main() {
// 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 disks = Disks::new_with_refreshed_list();
let disks = disks
.into_iter()
.map(|disk| system_information::Disk::from(disk))
.collect();

let system_network_information = get_network_info();

let current_uid = users::get_current_uid();
let current_gid = users::get_current_gid();
let current_user = users::get_user_by_uid(current_uid).unwrap();


let system_information = SystemInformation {
cpu_count: sys.cpus().len(),
physical_core_count: sys.physical_core_count(),

total_memory: sys.total_memory(),
free_memory: sys.free_memory(),
available_memory: sys.available_memory(),
used_memory: sys.used_memory(),

total_swap: sys.total_swap(),
free_swap: sys.free_swap(),
used_swap: sys.used_swap(),

total_memory_cgroup: sys.cgroup_limits().map(|limit| limit.total_memory),
free_memory_cgroup: sys.cgroup_limits().map(|limit| limit.free_memory),
free_swap_cgroup: sys.cgroup_limits().map(|limit| limit.free_swap),

system_name: System::name(),
kernel_version: System::kernel_version(),
os_version: System::long_os_version(),
host_name: System::host_name(),
cpu_arch: System::cpu_arch(),

disks,

network_information: system_network_information,


// Adding current user, UID, and GID info
current_user: current_user.name().to_str().unwrap().to_string(),
current_uid,
current_gid,
};

let serialized = serde_json::to_string_pretty(&system_information).unwrap();
println!("{}", serialized);

// TODO:
// Current time
// SElinux/AppArmor
// Maybe env variables (may contain secrets)
// dmesg/syslog?
// capabilities?
// downward API
// Somehow get the custom resources logged?

// Things left out for now because it doesn't seem too useful:
// - Running processes
// - Uptime/boot time
// - Load average
// - Network utilization
// - Users/Groups
use clap::{crate_description, crate_version, Parser};
use stackable_operator::logging::TracingTarget;
use std::path::PathBuf;

use crate::system_information::SystemInformation;
use std::time::Instant;

const APP_NAME: &str = "containerdebug";

/// Collects and prints helpful debugging information about the environment that it is running in.
#[derive(clap::Parser)]
struct Opts {
/// Loop every DURATION, instead of shutting down once completed (default DURATION: 1m)
#[clap(
long = "loop",
value_name = "INTERVAL",
default_missing_value = "1m",
num_args = 0..=1,
require_equals = true,
)]
loop_interval: Option<stackable_operator::time::Duration>,

#[clap(long, short = 'o')]
output: Option<PathBuf>,

/// Tracing log collector system
#[arg(long, env, default_value_t, value_enum)]
pub tracing_target: TracingTarget,
}

fn get_network_info() -> SystemNetworkInfo {
/*
let resolver = Resolver::from_system_conf()
.map_err(|e| e.to_string())
.unwrap();
*/
let (resolver_config, mut resolver_opts) = read_system_conf().unwrap();
resolver_opts.timeout = Duration::from_secs(5);
let resolver = Resolver::new(resolver_config, resolver_opts).unwrap();

let interfaces = match list_afinet_netifas() {
Ok(netifs) => {
let mut interface_map = std::collections::HashMap::new();

// Iterate over the network interfaces and group them by name
// An interface may appear multiple times if it has multiple IP addresses (e.g. IPv4 and IPv6)
for (name, ip_addr) in netifs {
interface_map
.entry(name)
.or_insert_with(Vec::new)
.push(ip_addr);
}
interface_map
}
Err(_) => HashMap::new(),
};
mod built_info {
include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

let mut ip_set: HashSet<IpAddr> = HashSet::new();
for (_, ip_addrs) in interfaces.iter() {
for ip_addr in ip_addrs {
ip_set.insert(ip_addr.clone());
fn main() {
let opts = Opts::parse();
stackable_operator::logging::initialize_logging(
"CONTAINERDEBUG_LOG",
APP_NAME,
opts.tracing_target,
);
stackable_operator::utils::print_startup_string(
crate_description!(),
crate_version!(),
built_info::GIT_VERSION,
built_info::TARGET,
built_info::BUILT_TIME_UTC,
built_info::RUSTC_VERSION,
);

let mut next_run = Instant::now();
loop {
let next_run_sleep = next_run.saturating_duration_since(Instant::now());
if !next_run_sleep.is_zero() {
tracing::info!(?next_run, "scheduling next run...");
}
}
std::thread::sleep(next_run_sleep);

let mut reverse_lookups = HashMap::new();
for ip in ip_set {
if let Ok(result) = resolver.reverse_lookup(ip) {
for ptr_record in result {
let hostname = ptr_record.to_utf8();
reverse_lookups
.entry(ip)
.or_insert_with(Vec::new)
.push(hostname);
}
}
}
let system_information = SystemInformation::collect();

let mut hostname_set: HashSet<String> = HashSet::new();
for (_, hostnames) in reverse_lookups.iter() {
for hostname in hostnames {
hostname_set.insert(hostname.clone());
let serialized = serde_json::to_string_pretty(&system_information).unwrap();
// println!("{serialized}");
if let Some(output_path) = &opts.output {
std::fs::write(output_path, &serialized).unwrap();
}
}

let mut forward_lookups = HashMap::new();
for hostname in hostname_set {
if let Ok(result) = resolver.lookup_ip(hostname.clone()) {
for ip_addr in result {
forward_lookups
.entry(hostname.clone())
.or_insert_with(Vec::new)
.push(ip_addr);
}
match opts.loop_interval {
Some(interval) => next_run += interval,
None => break,
}
}

let system_network_information = SystemNetworkInfo {
network_interfaces: interfaces,
reverse_lookups,
forward_lookups,
};
system_network_information
}
61 changes: 0 additions & 61 deletions src/system_information.rs

This file was deleted.

39 changes: 39 additions & 0 deletions src/system_information/disk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use serde::Serialize;

#[derive(Debug, Serialize)]
pub struct Disk {
pub name: String,
pub mount_point: String,
pub total_space: u64,
pub available_space: u64,
}

impl Disk {
#[tracing::instrument(name = "Disk::collect_all")]
pub fn collect_all() -> Vec<Self> {
let disks = sysinfo::Disks::new_with_refreshed_list();
if disks.into_iter().next().is_none() {
tracing::info!("no disks found");
}
disks.into_iter().map(Self::from).collect()
}
}

impl From<&sysinfo::Disk> for Disk {
fn from(sysinfo_disk: &sysinfo::Disk) -> Self {
let disk = Disk {
name: sysinfo_disk.name().to_string_lossy().into_owned(),
mount_point: sysinfo_disk.mount_point().to_string_lossy().into_owned(),
total_space: sysinfo_disk.total_space(),
available_space: sysinfo_disk.available_space(),
};
tracing::info!(
disk.mount_point,
disk.name,
disk.space.total = disk.total_space,
disk.space.available = disk.available_space,
"found disk"
);
disk
}
}
47 changes: 47 additions & 0 deletions src/system_information/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use serde::Serialize;

pub mod disk;
pub mod network;
pub mod os;
pub mod resources;
pub mod user;

#[derive(Debug, Serialize)]
pub struct SystemInformation {
pub resources: resources::Resources,
pub os: os::OperatingSystem,
pub current_user: user::User,
pub disks: Vec<disk::Disk>,
pub network: network::SystemNetworkInfo,
// TODO:
// Current time
// SElinux/AppArmor
// Maybe env variables (may contain secrets)
// dmesg/syslog?
// capabilities?
// downward API
// Somehow get the custom resources logged?

// Things left out for now because it doesn't seem too useful:
// - Running processes
// - Uptime/boot time
// - Load average
// - Network utilization
// - Users/Groups
}

impl SystemInformation {
#[tracing::instrument(name = "SystemInformation::collect")]
pub fn collect() -> Self {
tracing::info!("Starting data collection");
let info = Self {
resources: resources::Resources::collect(),
os: os::OperatingSystem::collect(),
current_user: user::User::collect_current(),
disks: disk::Disk::collect_all(),
network: network::SystemNetworkInfo::collect(),
};
tracing::info!("Data collection finished");
info
}
}
Loading