feat(inventory agent): Local presence advertisement and discovery now works! Must be within the same LAN to share the multicast address though

This commit is contained in:
Jean-Gabriel Gill-Couture 2025-08-29 11:22:44 -04:00
parent b857412151
commit 05e7b8075c
10 changed files with 154 additions and 30 deletions

112
Cargo.lock generated
View File

@ -641,7 +641,7 @@ dependencies = [
"tokio-util",
"tower-service",
"url",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -715,6 +715,12 @@ dependencies = [
"bytes",
]
[[package]]
name = "c_linked_list"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b"
[[package]]
name = "camino"
version = "1.1.10"
@ -1102,7 +1108,7 @@ dependencies = [
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1119,7 +1125,7 @@ dependencies = [
"rustix 0.38.44",
"signal-hook",
"signal-hook-mio",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1128,7 +1134,7 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -2049,6 +2055,12 @@ dependencies = [
"byteorder",
]
[[package]]
name = "gcc"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
[[package]]
name = "generic-array"
version = "0.14.7"
@ -2060,6 +2072,28 @@ dependencies = [
"zeroize",
]
[[package]]
name = "get_if_addrs"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abddb55a898d32925f3148bd281174a68eeb68bbfd9a5938a57b18f506ee4ef7"
dependencies = [
"c_linked_list",
"get_if_addrs-sys",
"libc",
"winapi 0.2.8",
]
[[package]]
name = "get_if_addrs-sys"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d04f9fb746cf36b191c00f3ede8bde9c8e64f9f4b05ae2694a9ccf5e3f5ab48"
dependencies = [
"gcc",
"libc",
]
[[package]]
name = "getrandom"
version = "0.2.16"
@ -2271,8 +2305,9 @@ version = "0.1.0"
dependencies = [
"actix-web",
"env_logger",
"local-ip-address",
"log",
"mdns-sd",
"mdns-sd 0.14.1 (git+https://github.com/jggc/mdns-sd.git?branch=patch-1)",
"serde",
"serde_json",
"sysinfo",
@ -2631,7 +2666,7 @@ dependencies = [
"pin-project-lite",
"tokio",
"tower-service",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -3365,6 +3400,18 @@ dependencies = [
"local-waker",
]
[[package]]
name = "local-ip-address"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "656b3b27f8893f7bbf9485148ff9a65f019e3f33bd5cdc87c83cab16b3fd9ec8"
dependencies = [
"libc",
"neli",
"thiserror 2.0.14",
"windows-sys 0.59.0",
]
[[package]]
name = "local-waker"
version = "0.1.4"
@ -3434,8 +3481,10 @@ dependencies = [
"dmidecode",
"env_logger",
"futures",
"get_if_addrs",
"local-ip-address",
"log",
"mdns-sd",
"mdns-sd 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio",
]
@ -3454,6 +3503,20 @@ dependencies = [
"socket2 0.6.0",
]
[[package]]
name = "mdns-sd"
version = "0.14.1"
source = "git+https://github.com/jggc/mdns-sd.git?branch=patch-1#9e4619599d1493ec15395d62d82d40a43fbef9e7"
dependencies = [
"fastrand",
"flume",
"if-addrs",
"log",
"mio 1.0.4",
"socket-pktinfo",
"socket2 0.6.0",
]
[[package]]
name = "memchr"
version = "2.7.5"
@ -3516,6 +3579,31 @@ dependencies = [
"tempfile",
]
[[package]]
name = "neli"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93062a0dce6da2517ea35f301dfc88184ce18d3601ec786a727a87bf535deca9"
dependencies = [
"byteorder",
"libc",
"log",
"neli-proc-macros",
]
[[package]]
name = "neli-proc-macros"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c8034b7fbb6f9455b2a96c19e6edf8dc9fc34c70449938d8ee3b4df363f61fe"
dependencies = [
"either",
"proc-macro2",
"quote",
"serde",
"syn 1.0.109",
]
[[package]]
name = "newline-converter"
version = "0.3.0"
@ -3540,7 +3628,7 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -4606,7 +4694,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fadd2c0ab350e21c66556f94ee06f766d8bdae3213857ba7610bfd8e10e51880"
dependencies = [
"libc",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -6304,6 +6392,12 @@ dependencies = [
"rustls-pki-types",
]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi"
version = "0.3.9"

View File

@ -13,3 +13,5 @@ dmidecode = "0.2" # For getting the motherboard ID on the agent
log.workspace=true
env_logger.workspace=true
clap = { version = "4.5.46", features = ["derive"] }
get_if_addrs = "0.5.3"
local-ip-address = "0.6.5"

View File

@ -10,11 +10,10 @@ use crate::SERVICE_TYPE;
const SERVICE_PORT: u16 = 43210; // A port for the service. It needs one, even if unused.
pub async fn advertise() {
info!("Starting Harmony Agent...");
// Get a unique ID for this machine.
let motherboard_id = "some motherboard id";
let motherboard_id = "some motherboard id";
let instance_name = format!("harmony-agent-{}", motherboard_id);
info!("This agent's instance name: {}", instance_name);
info!("Advertising with ID: {}", motherboard_id);
@ -29,11 +28,13 @@ pub async fn advertise() {
// Create the service information.
// The instance name should be unique on the network.
let local_ip = local_ip_address::local_ip().unwrap();
let service_info = ServiceInfo::new(
SERVICE_TYPE,
&instance_name,
"harmony-host.local.", // A hostname for the service
(), // No specific IP addresses, let the daemon figure it out
local_ip,
// "0.0.0.0",
SERVICE_PORT,
Some(properties),
)
@ -43,9 +44,16 @@ pub async fn advertise() {
mdns.register(service_info)
.expect("Failed to register service");
info!("Service '{}' registered and now being advertised.", instance_name);
info!(
"Service '{}' registered and now being advertised.",
instance_name
);
info!("Agent is running. Press Ctrl+C to exit.");
for iface in get_if_addrs::get_if_addrs().unwrap() {
println!("{:#?}", iface);
}
// Keep the agent running indefinitely.
tokio::signal::ctrl_c().await.unwrap();
info!("Shutting down agent.");

View File

@ -16,7 +16,9 @@ async fn main() {
Box::new(SuccessScore {}),
Box::new(ErrorScore {}),
Box::new(PanicScore {}),
Box::new(DiscoverInventoryAgentScore { discovery_timeout: Some(10) }),
Box::new(DiscoverInventoryAgentScore {
discovery_timeout: Some(10),
}),
],
None,
)

View File

@ -5,6 +5,7 @@ pub mod dns;
pub mod dummy;
pub mod helm;
pub mod http;
pub mod inventory;
pub mod ipxe;
pub mod k3d;
pub mod k8s;
@ -17,4 +18,3 @@ pub mod prometheus;
pub mod storage;
pub mod tenant;
pub mod tftp;
pub mod inventory;

View File

@ -12,4 +12,6 @@ log.workspace = true
env_logger.workspace = true
tokio.workspace = true
thiserror.workspace = true
mdns-sd = "0.14.1"
# mdns-sd = "0.14.1"
mdns-sd = { git = "https://github.com/jggc/mdns-sd.git", branch = "patch-1" }
local-ip-address = "0.6.5"

View File

@ -1,2 +1,2 @@
pub mod local_presence;
mod hwinfo;
pub mod local_presence;

View File

@ -1,8 +1,11 @@
use log::{error, info, warn};
use log::{debug, error, info, warn};
use mdns_sd::{ServiceDaemon, ServiceInfo};
use std::collections::HashMap;
use crate::{hwinfo::PhysicalHost, local_presence::{PresenceError, SERVICE_NAME, VERSION}};
use crate::{
hwinfo::PhysicalHost,
local_presence::{PresenceError, SERVICE_NAME, VERSION},
};
/// Advertises the agent's presence on the local network.
///
@ -17,7 +20,10 @@ pub fn advertise(service_port: u16) -> Result<(), PresenceError> {
}
};
let instance_name = format!("inventory-agent-{}", host_id.clone().unwrap_or("unknown".to_string()));
let instance_name = format!(
"inventory-agent-{}",
host_id.clone().unwrap_or("unknown".to_string())
);
let spawned_msg = format!("Spawned local presence advertisement task for '{instance_name}'.");
@ -43,11 +49,23 @@ pub fn advertise(service_port: u16) -> Result<(), PresenceError> {
}
props.insert("version".to_string(), VERSION.to_string());
let local_ip: Box<dyn mdns_sd::AsIpAddrs> = match local_ip_address::local_ip() {
Ok(ip) => Box::new(ip),
Err(e) => {
error!(
"Could not figure out local ip, mdns will have to try to figure it out by itself : {e}"
);
Box::new(())
}
};
debug!("Using local ip {:?}", local_ip.as_ip_addrs());
let service_info = ServiceInfo::new(
SERVICE_NAME,
&instance_name,
&format!("{}.local.", instance_name),
(), // Let the daemon determine the host IPs
local_ip,
service_port,
Some(props),
)
@ -64,7 +82,6 @@ pub fn advertise(service_port: u16) -> Result<(), PresenceError> {
return;
}
};
});
info!("{spawned_msg}");

View File

@ -16,7 +16,7 @@ pub fn discover_agents(timeout: Option<u64>, on_event: fn(&DiscoveryEvent)) {
while let Ok(event) = receiver.recv() {
on_event(&event);
match event {
ServiceEvent::ServiceData(resolved) => {
ServiceEvent::ServiceResolved(resolved) => {
println!("Resolved a new service: {}", resolved.fullname);
}
other_event => {
@ -27,8 +27,8 @@ pub fn discover_agents(timeout: Option<u64>, on_event: fn(&DiscoveryEvent)) {
});
if let Some(timeout) = timeout {
// Gracefully shutdown the daemon.
std::thread::sleep(std::time::Duration::from_secs(timeout));
mdns.shutdown().unwrap();
// Gracefully shutdown the daemon.
std::thread::sleep(std::time::Duration::from_secs(timeout));
mdns.shutdown().unwrap();
}
}

View File

@ -8,9 +8,6 @@ use crate::hwinfo::PhysicalHost;
mod hwinfo;
mod local_presence;
#[get("/inventory")]
async fn inventory() -> impl Responder {
log::info!("Received inventory request");
@ -32,7 +29,9 @@ async fn main() -> std::io::Result<()> {
env_logger::init();
let port = env::var("HARMONY_INVENTORY_AGENT_PORT").unwrap_or_else(|_| "8080".to_string());
let port = port.parse::<u16>().expect(&format!("Invalid port number, cannot parse to u16 {port}"));
let port = port
.parse::<u16>()
.expect(&format!("Invalid port number, cannot parse to u16 {port}"));
let bind_addr = format!("0.0.0.0:{}", port);
log::info!("Starting inventory agent on {}", bind_addr);