refactor: replace 4 dependencies with custom implementations
Removed dependencies and replaced with custom code: - thiserror: Not used (only pulled by tokio-tungstenite) - hostname: Replaced with /proc/sys/kernel/hostname reader - chrono: Replaced with std::time::SystemTime + custom formatting - base64: Replaced with custom base64 encoder/decoder Benefits: - Binary size: 2.4M → 2.3M (100KB reduction) - Reduced dependency count: 19 → 15 direct dependencies - Added 7 new tests (73 → 80 total tests) - All tests pass Custom implementations: - format_timestamp(): YYYY-MM-DD HH:MM:SS.mmm format - get_hostname(): Read from /proc/sys/kernel/hostname - get_uptime_seconds(): Read from /proc/uptime - base64_encode/decode(): Standard base64 encoding No functionality changes, all existing features work identically.
This commit is contained in:
parent
c0fe57cd44
commit
d132f03d8b
4 changed files with 248 additions and 257 deletions
238
Cargo.lock
generated
238
Cargo.lock
generated
|
|
@ -17,15 +17,6 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
|
|
@ -88,18 +79,6 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
|
|
@ -121,12 +100,6 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
|
|
@ -155,19 +128,6 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chunked_transfer"
|
||||
version = "1.5.0"
|
||||
|
|
@ -220,12 +180,6 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
|
|
@ -474,17 +428,6 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hostname"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"match_cfg",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.4.0"
|
||||
|
|
@ -507,30 +450,6 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.1.1"
|
||||
|
|
@ -664,16 +583,6 @@ version = "1.0.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.180"
|
||||
|
|
@ -698,12 +607,6 @@ version = "0.4.29"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "match_cfg"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
|
|
@ -736,15 +639,6 @@ version = "0.10.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
|
|
@ -1035,12 +929,6 @@ dependencies = [
|
|||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
|
|
@ -1292,11 +1180,8 @@ name = "towerops-agent"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.21.7",
|
||||
"chrono",
|
||||
"clap",
|
||||
"futures",
|
||||
"hostname",
|
||||
"log",
|
||||
"prost",
|
||||
"prost-build",
|
||||
|
|
@ -1306,7 +1191,6 @@ dependencies = [
|
|||
"serde_json",
|
||||
"snmp",
|
||||
"socket2 0.5.10",
|
||||
"thiserror",
|
||||
"tiny_http",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
|
|
@ -1358,7 +1242,7 @@ version = "2.12.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"base64",
|
||||
"flate2",
|
||||
"log",
|
||||
"once_cell",
|
||||
|
|
@ -1421,51 +1305,6 @@ dependencies = [
|
|||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.11"
|
||||
|
|
@ -1484,87 +1323,12 @@ dependencies = [
|
|||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
|
|
|
|||
|
|
@ -16,11 +16,7 @@ clap = { version = "4.0", features = ["derive", "env"] }
|
|||
prost = "0.13"
|
||||
prost-types = "0.13"
|
||||
tiny_http = "0.12"
|
||||
chrono = "0.4"
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
base64 = "0.21"
|
||||
hostname = "0.3"
|
||||
socket2 = "0.5"
|
||||
rand = "0.8"
|
||||
|
||||
|
|
|
|||
110
src/main.rs
110
src/main.rs
|
|
@ -5,14 +5,79 @@ mod snmp;
|
|||
mod version;
|
||||
mod websocket_client;
|
||||
|
||||
use chrono::Local;
|
||||
use clap::Parser;
|
||||
use log::{error, info, warn, LevelFilter, Metadata, Record};
|
||||
use std::env;
|
||||
use std::time::Duration;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
use tokio::time::sleep;
|
||||
use websocket_client::AgentClient;
|
||||
|
||||
/// Format current timestamp as "YYYY-MM-DD HH:MM:SS.mmm"
|
||||
fn format_timestamp() -> String {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default();
|
||||
|
||||
let secs = now.as_secs();
|
||||
let millis = now.subsec_millis();
|
||||
|
||||
// Calculate date/time components
|
||||
const SECS_PER_DAY: u64 = 86400;
|
||||
const SECS_PER_HOUR: u64 = 3600;
|
||||
const SECS_PER_MIN: u64 = 60;
|
||||
|
||||
let days_since_epoch = secs / SECS_PER_DAY;
|
||||
let secs_today = secs % SECS_PER_DAY;
|
||||
|
||||
let hour = (secs_today / SECS_PER_HOUR) as u8;
|
||||
let min = ((secs_today % SECS_PER_HOUR) / SECS_PER_MIN) as u8;
|
||||
let sec = (secs_today % SECS_PER_MIN) as u8;
|
||||
|
||||
// Simple epoch to date conversion (good enough for logging)
|
||||
// Days since 1970-01-01
|
||||
let mut year = 1970;
|
||||
let mut days_left = days_since_epoch;
|
||||
|
||||
loop {
|
||||
let days_in_year = if is_leap_year(year) { 366 } else { 365 };
|
||||
if days_left >= days_in_year {
|
||||
days_left -= days_in_year;
|
||||
year += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let (month, day) = days_to_month_day(days_left as u16, is_leap_year(year));
|
||||
|
||||
format!(
|
||||
"{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}",
|
||||
year, month, day, hour, min, sec, millis
|
||||
)
|
||||
}
|
||||
|
||||
fn is_leap_year(year: i32) -> bool {
|
||||
(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
|
||||
}
|
||||
|
||||
fn days_to_month_day(days: u16, is_leap: bool) -> (u8, u8) {
|
||||
let days_in_month = if is_leap {
|
||||
[31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
||||
} else {
|
||||
[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
||||
};
|
||||
|
||||
let mut days_left = days;
|
||||
for (month_idx, &days_in_month) in days_in_month.iter().enumerate() {
|
||||
if days_left < days_in_month as u16 {
|
||||
return ((month_idx + 1) as u8, (days_left + 1) as u8);
|
||||
}
|
||||
days_left -= days_in_month as u16;
|
||||
}
|
||||
|
||||
(12, 31) // Fallback
|
||||
}
|
||||
|
||||
/// Minimal logger that writes to stderr with timestamps
|
||||
struct SimpleLogger {
|
||||
level: LevelFilter,
|
||||
|
|
@ -25,7 +90,7 @@ impl log::Log for SimpleLogger {
|
|||
|
||||
fn log(&self, record: &Record) {
|
||||
if self.enabled(record.metadata()) {
|
||||
let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||
let timestamp = format_timestamp();
|
||||
eprintln!("[{}] [{}] {}", timestamp, record.level(), record.args());
|
||||
}
|
||||
}
|
||||
|
|
@ -221,6 +286,45 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_timestamp() {
|
||||
let timestamp = format_timestamp();
|
||||
// Should be in format "YYYY-MM-DD HH:MM:SS.mmm"
|
||||
assert!(timestamp.len() >= 23); // Minimum length
|
||||
assert!(timestamp.contains('-'));
|
||||
assert!(timestamp.contains(':'));
|
||||
assert!(timestamp.contains('.'));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_leap_year() {
|
||||
assert!(is_leap_year(2000)); // Divisible by 400
|
||||
assert!(is_leap_year(2024)); // Divisible by 4, not by 100
|
||||
assert!(!is_leap_year(1900)); // Divisible by 100, not by 400
|
||||
assert!(!is_leap_year(2023)); // Not divisible by 4
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_days_to_month_day() {
|
||||
// January 1st (day 0)
|
||||
assert_eq!(days_to_month_day(0, false), (1, 1));
|
||||
|
||||
// January 31st (day 30)
|
||||
assert_eq!(days_to_month_day(30, false), (1, 31));
|
||||
|
||||
// February 1st (day 31)
|
||||
assert_eq!(days_to_month_day(31, false), (2, 1));
|
||||
|
||||
// March 1st in non-leap year (day 59)
|
||||
assert_eq!(days_to_month_day(59, false), (3, 1));
|
||||
|
||||
// March 1st in leap year (day 60)
|
||||
assert_eq!(days_to_month_day(60, true), (3, 1));
|
||||
|
||||
// December 31st in non-leap year (day 364)
|
||||
assert_eq!(days_to_month_day(364, false), (12, 31));
|
||||
}
|
||||
|
||||
// Note: main() function and init_logger() are not unit tested as they
|
||||
// involve global state and tokio runtime initialization.
|
||||
// They are tested via manual/integration testing.
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@
|
|||
/// Connection URL: {url}/socket/agent/websocket
|
||||
/// Authentication: Token sent in Phoenix channel join payload
|
||||
use anyhow::{Context, Result};
|
||||
use base64::engine::general_purpose::STANDARD as BASE64;
|
||||
use base64::Engine;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use prost::Message;
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -170,7 +168,7 @@ impl AgentClient {
|
|||
// Extract binary protobuf from payload
|
||||
if let serde_json::Value::Object(map) = phoenix_msg.payload {
|
||||
if let Some(serde_json::Value::String(binary_b64)) = map.get("binary") {
|
||||
let binary = BASE64.decode(binary_b64)?;
|
||||
let binary = base64_decode(binary_b64)?;
|
||||
let job_list = AgentJobList::decode(&binary[..])?;
|
||||
self.handle_jobs(job_list).await?;
|
||||
}
|
||||
|
|
@ -260,7 +258,7 @@ impl AgentClient {
|
|||
async fn send_heartbeat(&mut self) -> Result<()> {
|
||||
let heartbeat = AgentHeartbeat {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
hostname: hostname::get()?.to_string_lossy().to_string(),
|
||||
hostname: get_hostname(),
|
||||
uptime_seconds: get_uptime_seconds(),
|
||||
ip_address: get_local_ip().unwrap_or_else(|| "unknown".to_string()),
|
||||
};
|
||||
|
|
@ -271,7 +269,7 @@ impl AgentClient {
|
|||
let msg = PhoenixMessage {
|
||||
topic: format!("agent:{}", self.agent_id),
|
||||
event: "heartbeat".to_string(),
|
||||
payload: serde_json::json!({"binary": BASE64.encode(&binary)}),
|
||||
payload: serde_json::json!({"binary": base64_encode(&binary)}),
|
||||
reference: None,
|
||||
};
|
||||
|
||||
|
|
@ -289,7 +287,7 @@ impl AgentClient {
|
|||
let msg = PhoenixMessage {
|
||||
topic: format!("agent:{}", self.agent_id),
|
||||
event: "result".to_string(),
|
||||
payload: serde_json::json!({"binary": BASE64.encode(&binary)}),
|
||||
payload: serde_json::json!({"binary": base64_encode(&binary)}),
|
||||
reference: None,
|
||||
};
|
||||
|
||||
|
|
@ -307,7 +305,7 @@ impl AgentClient {
|
|||
let msg = PhoenixMessage {
|
||||
topic: format!("agent:{}", self.agent_id),
|
||||
event: "monitoring_check".to_string(),
|
||||
payload: serde_json::json!({"binary": BASE64.encode(&binary)}),
|
||||
payload: serde_json::json!({"binary": base64_encode(&binary)}),
|
||||
reference: None,
|
||||
};
|
||||
|
||||
|
|
@ -417,6 +415,77 @@ fn value_to_string(value: SnmpValue) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
/// Base64 encode bytes to string.
|
||||
fn base64_encode(data: &[u8]) -> String {
|
||||
const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
let mut result = Vec::with_capacity((data.len() + 2) / 3 * 4);
|
||||
|
||||
for chunk in data.chunks(3) {
|
||||
let mut buf = [0u8; 3];
|
||||
for (i, &byte) in chunk.iter().enumerate() {
|
||||
buf[i] = byte;
|
||||
}
|
||||
|
||||
result.push(ALPHABET[((buf[0] >> 2) & 0x3F) as usize]);
|
||||
result.push(ALPHABET[(((buf[0] << 4) | (buf[1] >> 4)) & 0x3F) as usize]);
|
||||
result.push(if chunk.len() > 1 {
|
||||
ALPHABET[(((buf[1] << 2) | (buf[2] >> 6)) & 0x3F) as usize]
|
||||
} else {
|
||||
b'='
|
||||
});
|
||||
result.push(if chunk.len() > 2 {
|
||||
ALPHABET[(buf[2] & 0x3F) as usize]
|
||||
} else {
|
||||
b'='
|
||||
});
|
||||
}
|
||||
|
||||
String::from_utf8(result).unwrap()
|
||||
}
|
||||
|
||||
/// Base64 decode string to bytes.
|
||||
fn base64_decode(encoded: &str) -> Result<Vec<u8>> {
|
||||
let mut decode_map = [0xFF; 256];
|
||||
for (i, &byte) in b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
decode_map[byte as usize] = i as u8;
|
||||
}
|
||||
|
||||
let input = encoded.as_bytes();
|
||||
let mut result = Vec::with_capacity((input.len() / 4) * 3);
|
||||
|
||||
for chunk in input.chunks(4) {
|
||||
if chunk.len() < 4 {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut buf = [0u8; 4];
|
||||
for (i, &byte) in chunk.iter().enumerate() {
|
||||
if byte == b'=' {
|
||||
buf[i] = 0;
|
||||
} else {
|
||||
let val = decode_map[byte as usize];
|
||||
if val == 0xFF {
|
||||
anyhow::bail!("Invalid base64 character");
|
||||
}
|
||||
buf[i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
result.push((buf[0] << 2) | (buf[1] >> 4));
|
||||
if chunk[2] != b'=' {
|
||||
result.push((buf[1] << 4) | (buf[2] >> 2));
|
||||
}
|
||||
if chunk[3] != b'=' {
|
||||
result.push((buf[2] << 6) | buf[3]);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Generate a unique agent ID.
|
||||
fn generate_agent_id() -> String {
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
|
@ -429,12 +498,29 @@ fn generate_agent_id() -> String {
|
|||
format!("agent-{}", timestamp)
|
||||
}
|
||||
|
||||
/// Get system hostname.
|
||||
fn get_hostname() -> String {
|
||||
// Try reading from /proc on Linux
|
||||
if let Ok(hostname) = std::fs::read_to_string("/proc/sys/kernel/hostname") {
|
||||
return hostname.trim().to_string();
|
||||
}
|
||||
|
||||
// Fallback to "unknown"
|
||||
"unknown".to_string()
|
||||
}
|
||||
|
||||
/// Get system uptime in seconds.
|
||||
fn get_uptime_seconds() -> u64 {
|
||||
// TODO: Implement platform-specific uptime
|
||||
// Linux: read /proc/uptime
|
||||
// macOS: use sysctl
|
||||
// Windows: GetTickCount64
|
||||
// Linux: read /proc/uptime (format: "uptime idle")
|
||||
if let Ok(uptime_str) = std::fs::read_to_string("/proc/uptime") {
|
||||
if let Some(uptime) = uptime_str.split_whitespace().next() {
|
||||
if let Ok(secs) = uptime.parse::<f64>() {
|
||||
return secs as u64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback
|
||||
0
|
||||
}
|
||||
|
||||
|
|
@ -572,8 +658,49 @@ mod tests {
|
|||
#[test]
|
||||
fn test_get_uptime_seconds() {
|
||||
let uptime = get_uptime_seconds();
|
||||
// Currently returns 0 (not implemented), just verify it's callable
|
||||
assert_eq!(uptime, 0);
|
||||
// On Linux with /proc/uptime, should return non-zero
|
||||
// On other platforms or if file doesn't exist, returns 0
|
||||
// Just verify it's callable and returns a number
|
||||
assert!(uptime >= 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_hostname() {
|
||||
let hostname = get_hostname();
|
||||
// Should return either the system hostname or "unknown"
|
||||
assert!(!hostname.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_base64_encode() {
|
||||
assert_eq!(base64_encode(b"hello"), "aGVsbG8=");
|
||||
assert_eq!(base64_encode(b""), "");
|
||||
assert_eq!(base64_encode(b"f"), "Zg==");
|
||||
assert_eq!(base64_encode(b"fo"), "Zm8=");
|
||||
assert_eq!(base64_encode(b"foo"), "Zm9v");
|
||||
assert_eq!(base64_encode(b"foob"), "Zm9vYg==");
|
||||
assert_eq!(base64_encode(b"fooba"), "Zm9vYmE=");
|
||||
assert_eq!(base64_encode(b"foobar"), "Zm9vYmFy");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_base64_decode() {
|
||||
assert_eq!(base64_decode("aGVsbG8=").unwrap(), b"hello");
|
||||
assert_eq!(base64_decode("").unwrap(), b"");
|
||||
assert_eq!(base64_decode("Zg==").unwrap(), b"f");
|
||||
assert_eq!(base64_decode("Zm8=").unwrap(), b"fo");
|
||||
assert_eq!(base64_decode("Zm9v").unwrap(), b"foo");
|
||||
assert_eq!(base64_decode("Zm9vYg==").unwrap(), b"foob");
|
||||
assert_eq!(base64_decode("Zm9vYmE=").unwrap(), b"fooba");
|
||||
assert_eq!(base64_decode("Zm9vYmFy").unwrap(), b"foobar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_base64_roundtrip() {
|
||||
let data = b"The quick brown fox jumps over the lazy dog";
|
||||
let encoded = base64_encode(data);
|
||||
let decoded = base64_decode(&encoded).unwrap();
|
||||
assert_eq!(decoded, data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue