use datestamp instead of creating a version
This commit is contained in:
parent
bd08bb3aad
commit
65fbf3bb31
7 changed files with 47 additions and 87 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -3359,6 +3359,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"async-trait",
|
||||
"cc",
|
||||
"chrono",
|
||||
"clap",
|
||||
"crossterm 0.28.1",
|
||||
"futures",
|
||||
|
|
@ -3371,6 +3372,7 @@ dependencies = [
|
|||
"prost-types",
|
||||
"rand 0.8.5",
|
||||
"ratatui",
|
||||
"regex",
|
||||
"russh",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ rand = "0.8"
|
|||
ratatui = { version = "0.30", optional = true }
|
||||
crossterm = { version = "0.28", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
regex = "1"
|
||||
|
||||
[features]
|
||||
default = ["tui"]
|
||||
tui = ["ratatui", "crossterm"]
|
||||
|
|
@ -38,6 +41,7 @@ tui = ["ratatui", "crossterm"]
|
|||
[build-dependencies]
|
||||
prost-build = "0.14"
|
||||
cc = "1.0"
|
||||
chrono = "0.4"
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
|
|
|
|||
79
build.rs
79
build.rs
|
|
@ -1,5 +1,3 @@
|
|||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
// Compile protobuf definitions
|
||||
prost_build::compile_protos(&["proto/agent.proto"], &["proto/"]).unwrap();
|
||||
|
|
@ -13,80 +11,15 @@ fn main() {
|
|||
// Link against netsnmp library
|
||||
println!("cargo:rustc-link-lib=netsnmp");
|
||||
|
||||
// Inject git-based version if available, otherwise use Cargo.toml version
|
||||
// This ensures the binary version matches the Docker image tag
|
||||
// Inject compile timestamp as version
|
||||
// This allows tracking when a specific agent binary was built
|
||||
let version = get_version();
|
||||
println!("cargo:rustc-env=BUILD_VERSION={}", version);
|
||||
}
|
||||
|
||||
fn get_version() -> String {
|
||||
// First check if BUILD_VERSION is already set (e.g., from Docker build arg)
|
||||
if let Ok(version) = std::env::var("BUILD_VERSION") {
|
||||
if !version.is_empty() && version != "0.1.0-unknown" {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
// Try git describe - gives us the most descriptive version
|
||||
// Examples:
|
||||
// v0.2.0 -> "0.2.0" (exact tag)
|
||||
// v0.2.0-5-g831588e -> "0.2.0.5.831588e" (5 commits after v0.2.0)
|
||||
if let Ok(output) = Command::new("git")
|
||||
.args(["describe", "--tags", "--always", "--dirty=-modified"])
|
||||
.output()
|
||||
{
|
||||
if output.status.success() {
|
||||
let desc = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
|
||||
// Parse git describe output
|
||||
if let Some(version) = parse_git_describe(&desc) {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: Try short commit hash only
|
||||
if let Ok(output) = Command::new("git")
|
||||
.args(["rev-parse", "--short", "HEAD"])
|
||||
.output()
|
||||
{
|
||||
if output.status.success() {
|
||||
let commit = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
return format!("{}.{}", env!("CARGO_PKG_VERSION"), commit);
|
||||
}
|
||||
}
|
||||
|
||||
// Final fallback to Cargo.toml version
|
||||
env!("CARGO_PKG_VERSION").to_string()
|
||||
}
|
||||
|
||||
fn parse_git_describe(desc: &str) -> Option<String> {
|
||||
// Strip 'v' prefix if present
|
||||
let desc = desc.strip_prefix('v').unwrap_or(desc);
|
||||
|
||||
// Check for dirty flag
|
||||
let dirty = desc.ends_with("-modified");
|
||||
let desc = desc.strip_suffix("-modified").unwrap_or(desc);
|
||||
|
||||
if let Some((base, rest)) = desc.split_once('-') {
|
||||
// Format: tag-N-ghash (e.g., "0.2.0-5-g831588e")
|
||||
let parts: Vec<&str> = rest.split('-').collect();
|
||||
if parts.len() == 2 {
|
||||
let commit_count = parts[0];
|
||||
let hash = parts[1].strip_prefix('g').unwrap_or(parts[1]);
|
||||
let version = format!("{}.{}.{}", base, commit_count, hash);
|
||||
return Some(if dirty {
|
||||
format!("{}-modified", version)
|
||||
} else {
|
||||
version
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// No commits after tag, just use the tag
|
||||
Some(if dirty {
|
||||
format!("{}-modified", desc)
|
||||
} else {
|
||||
desc.to_string()
|
||||
})
|
||||
// Generate RFC 3339 timestamp at compile time
|
||||
// Format: YYYY-MM-DDTHH:MM:SSZ
|
||||
let now = chrono::Utc::now();
|
||||
now.format("%Y-%m-%dT%H:%M:%SZ").to_string()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -355,9 +355,7 @@ impl SnmpSession {
|
|||
Err(_) => Ok(SnmpValue::OctetString(value_buf[..value_len].to_vec())),
|
||||
}
|
||||
}
|
||||
ASN_OPAQUE => {
|
||||
Ok(SnmpValue::OctetString(value_buf[..value_len].to_vec()))
|
||||
}
|
||||
ASN_OPAQUE => Ok(SnmpValue::OctetString(value_buf[..value_len].to_vec())),
|
||||
ASN_IPADDRESS => {
|
||||
// IP addresses are 4 bytes - convert to dotted notation
|
||||
if value_len == 4 {
|
||||
|
|
@ -466,7 +464,9 @@ impl SnmpSession {
|
|||
// Try UTF-8 conversion first
|
||||
match String::from_utf8(res.value[..res.value_len].to_vec()) {
|
||||
Ok(s) => SnmpValue::String(s),
|
||||
Err(_) => SnmpValue::OctetString(res.value[..res.value_len].to_vec()),
|
||||
Err(_) => {
|
||||
SnmpValue::OctetString(res.value[..res.value_len].to_vec())
|
||||
}
|
||||
}
|
||||
}
|
||||
ASN_OPAQUE => SnmpValue::OctetString(res.value[..res.value_len].to_vec()),
|
||||
|
|
@ -483,7 +483,9 @@ impl SnmpSession {
|
|||
ASN_OBJECT_ID => {
|
||||
match String::from_utf8(res.value[..res.value_len].to_vec()) {
|
||||
Ok(s) => SnmpValue::Oid(s),
|
||||
Err(_) => SnmpValue::OctetString(res.value[..res.value_len].to_vec()),
|
||||
Err(_) => {
|
||||
SnmpValue::OctetString(res.value[..res.value_len].to_vec())
|
||||
}
|
||||
}
|
||||
}
|
||||
ASN_INTEGER | ASN_COUNTER | ASN_GAUGE | ASN_TIMETICKS | ASN_UINTEGER => {
|
||||
|
|
|
|||
|
|
@ -58,20 +58,29 @@ impl DevicePoller {
|
|||
let config_clone = config.clone();
|
||||
|
||||
// Spawn the polling thread with 8MB stack for SNMPv3 crypto operations
|
||||
tracing::info!("Spawning device poller thread for {} at {}:{}", device_id, config.ip, config.port);
|
||||
tracing::info!(
|
||||
"Spawning device poller thread for {} at {}:{}",
|
||||
device_id,
|
||||
config.ip,
|
||||
config.port
|
||||
);
|
||||
std::thread::Builder::new()
|
||||
.name(format!("poller-{}", device_id))
|
||||
.stack_size(8 * 1024 * 1024) // 8MB stack (default is 2MB)
|
||||
.spawn(move || {
|
||||
tracing::info!("Device poller thread starting for {}", device_id_clone);
|
||||
if let Err(e) = run_poller_thread(device_id_clone.clone(), config_clone, request_rx) {
|
||||
if let Err(e) = run_poller_thread(device_id_clone.clone(), config_clone, request_rx)
|
||||
{
|
||||
tracing::error!("Device poller thread failed for {}: {}", device_id_clone, e);
|
||||
}
|
||||
tracing::info!("Device poller thread exited for {}", device_id_clone);
|
||||
})
|
||||
.expect("Failed to spawn device poller thread");
|
||||
|
||||
tracing::info!("Successfully spawned device poller thread for {}", device_id);
|
||||
tracing::info!(
|
||||
"Successfully spawned device poller thread for {}",
|
||||
device_id
|
||||
);
|
||||
|
||||
Self {
|
||||
device_id,
|
||||
|
|
@ -184,7 +193,11 @@ fn run_poller_thread(
|
|||
SnmpRequest::Get { oid, response_tx } => {
|
||||
tracing::debug!("Poller thread {} executing GET", device_id);
|
||||
let result = perform_get(&runtime, &client, &config, &oid);
|
||||
tracing::debug!("Poller thread {} GET result: {:?}", device_id, result.is_ok());
|
||||
tracing::debug!(
|
||||
"Poller thread {} GET result: {:?}",
|
||||
device_id,
|
||||
result.is_ok()
|
||||
);
|
||||
let _ = response_tx.send(result);
|
||||
}
|
||||
SnmpRequest::Walk {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
/// Get version at runtime - prefers BUILD_VERSION from build.rs, falls back to Cargo.toml
|
||||
/// Get compile timestamp at runtime - returns RFC 3339 formatted timestamp from build.rs
|
||||
/// Format: YYYY-MM-DDTHH:MM:SSZ (e.g., "2025-02-09T15:30:45Z")
|
||||
pub fn current_version() -> &'static str {
|
||||
option_env!("BUILD_VERSION").unwrap_or(env!("CARGO_PKG_VERSION"))
|
||||
}
|
||||
|
|
@ -25,9 +26,14 @@ mod tests {
|
|||
#[test]
|
||||
fn test_current_version_format() {
|
||||
let version = current_version();
|
||||
// Version should be in semver-like format (e.g., "0.1.0" or custom BUILD_VERSION)
|
||||
// At minimum, should have some content
|
||||
assert!(!version.is_empty());
|
||||
// Version should be RFC 3339 timestamp format (YYYY-MM-DDTHH:MM:SSZ)
|
||||
// Regex: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$
|
||||
let rfc3339_pattern = regex::Regex::new(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$").unwrap();
|
||||
assert!(
|
||||
rfc3339_pattern.is_match(version),
|
||||
"Version should be RFC 3339 timestamp, got: {}",
|
||||
version
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -686,7 +686,7 @@ impl AgentClient {
|
|||
/// Send heartbeat to server.
|
||||
async fn send_heartbeat(&mut self) -> Result<()> {
|
||||
let heartbeat = AgentHeartbeat {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
version: crate::version::current_version().to_string(),
|
||||
hostname: self.cached_hostname.clone(),
|
||||
uptime_seconds: get_uptime_seconds(),
|
||||
ip_address: get_local_ip().unwrap_or_default(),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue