wip(inventory-agent): local presence advertisement and discovery using mdns almost working

This commit is contained in:
Jean-Gabriel Gill-Couture 2025-08-29 01:10:43 -04:00
parent 8cc7adf196
commit 6ac0e095a3
18 changed files with 624 additions and 63 deletions

252
Cargo.lock generated
View File

@ -65,7 +65,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
dependencies = [ dependencies = [
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -182,7 +182,7 @@ dependencies = [
"actix-router", "actix-router",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -249,6 +249,15 @@ dependencies = [
"zerocopy", "zerocopy",
] ]
[[package]]
name = "aho-corasick"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.1.3" version = "1.1.3"
@ -389,7 +398,7 @@ dependencies = [
"rustc-hash", "rustc-hash",
"serde", "serde",
"serde_derive", "serde_derive",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -451,7 +460,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -462,7 +471,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -849,9 +858,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.40" version = "4.5.46"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -859,9 +868,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.40" version = "4.5.46"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -871,14 +880,14 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.40" version = "4.5.45"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -1190,7 +1199,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -1214,7 +1223,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -1225,7 +1234,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -1263,7 +1272,7 @@ checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -1283,7 +1292,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
"unicode-xid", "unicode-xid",
] ]
@ -1349,7 +1358,20 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
]
[[package]]
name = "dmidecode"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e529c1bd93d69804dc1e0a0c73aacd12bb13c7a18c659497411abdc6acf5e5f"
dependencies = [
"aho-corasick 0.6.10",
"bitflags 1.3.2",
"failure",
"failure_derive",
"lazy_static",
] ]
[[package]] [[package]]
@ -1378,7 +1400,7 @@ dependencies = [
"anyhow", "anyhow",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -1435,7 +1457,7 @@ dependencies = [
"enum-ordinalize", "enum-ordinalize",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -1506,7 +1528,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -1777,6 +1799,28 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "failure"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
dependencies = [
"backtrace",
"failure_derive",
]
[[package]]
name = "failure_derive"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"synstructure 0.12.6",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "2.3.0" version = "2.3.0"
@ -1821,6 +1865,17 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "flume"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
dependencies = [
"futures-core",
"futures-sink",
"spin",
]
[[package]] [[package]]
name = "flurry" name = "flurry"
version = "0.5.2" version = "0.5.2"
@ -1941,7 +1996,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -2126,6 +2181,7 @@ dependencies = [
"fqdn", "fqdn",
"futures-util", "futures-util",
"harmony-secret-derive", "harmony-secret-derive",
"harmony_inventory_agent",
"harmony_macros", "harmony_macros",
"harmony_types", "harmony_types",
"helm-wrapper-rs", "helm-wrapper-rs",
@ -2192,7 +2248,7 @@ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -2242,9 +2298,12 @@ dependencies = [
"actix-web", "actix-web",
"env_logger", "env_logger",
"log", "log",
"mdns-sd",
"serde", "serde",
"serde_json", "serde_json",
"sysinfo", "sysinfo",
"thiserror 2.0.14",
"tokio",
] ]
[[package]] [[package]]
@ -2256,7 +2315,7 @@ dependencies = [
"quote", "quote",
"serde", "serde",
"serde_yaml", "serde_yaml",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -2811,6 +2870,16 @@ dependencies = [
"icu_properties", "icu_properties",
] ]
[[package]]
name = "if-addrs"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf39cc0423ee66021dc5eccface85580e4a001e0c5288bae8bea7ecb69225e90"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "impl-more" name = "impl-more"
version = "0.1.9" version = "0.1.9"
@ -2926,7 +2995,7 @@ dependencies = [
"indoc", "indoc",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -2998,7 +3067,7 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -3183,7 +3252,7 @@ dependencies = [
"quote", "quote",
"serde", "serde",
"serde_json", "serde_json",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -3354,6 +3423,34 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
[[package]]
name = "mdns"
version = "0.1.0"
dependencies = [
"clap",
"dmidecode",
"env_logger",
"futures",
"log",
"mdns-sd",
"tokio",
]
[[package]]
name = "mdns-sd"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e0a59b04e17a195b0674198b3182931801c4759d00f36acad51b5a97210a692"
dependencies = [
"fastrand",
"flume",
"if-addrs",
"log",
"mio 1.0.4",
"socket-pktinfo",
"socket2 0.6.0",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.5" version = "2.7.5"
@ -3607,7 +3704,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -3851,7 +3948,7 @@ dependencies = [
"pest_meta", "pest_meta",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -3881,7 +3978,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -4285,7 +4382,7 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -4294,7 +4391,7 @@ version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick 1.1.3",
"memchr", "memchr",
"regex-automata", "regex-automata",
"regex-syntax", "regex-syntax",
@ -4306,7 +4403,7 @@ version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick 1.1.3",
"memchr", "memchr",
"regex-syntax", "regex-syntax",
] ]
@ -4793,7 +4890,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde_derive_internals", "serde_derive_internals",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -4925,7 +5022,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -4936,7 +5033,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -4969,7 +5066,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -4990,7 +5087,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde", "serde",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -5034,7 +5131,7 @@ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -5176,7 +5273,18 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
]
[[package]]
name = "socket-pktinfo"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "927136cc2ae6a1b0e66ac6b1210902b75c3f726db004a73bc18686dcd0dcd22f"
dependencies = [
"libc",
"socket2 0.6.0",
"windows-sys 0.60.2",
] ]
[[package]] [[package]]
@ -5204,6 +5312,9 @@ name = "spin"
version = "0.9.8" version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]] [[package]]
name = "spki" name = "spki"
@ -5312,7 +5423,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -5325,7 +5436,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -5334,6 +5445,17 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.105" version = "2.0.105"
@ -5360,6 +5482,18 @@ dependencies = [
"futures-core", "futures-core",
] ]
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"unicode-xid",
]
[[package]] [[package]]
name = "synstructure" name = "synstructure"
version = "0.13.2" version = "0.13.2"
@ -5368,7 +5502,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -5502,7 +5636,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -5513,7 +5647,7 @@ checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -5618,7 +5752,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -5789,7 +5923,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -5999,7 +6133,7 @@ checksum = "26b682e8c381995ea03130e381928e0e005b7c9eb483c6c8682f50e07b33c2b7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -6075,7 +6209,7 @@ dependencies = [
"log", "log",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -6110,7 +6244,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -6229,7 +6363,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -6240,7 +6374,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -6584,7 +6718,7 @@ dependencies = [
"quote", "quote",
"serde", "serde",
"serde_tokenstream", "serde_tokenstream",
"syn", "syn 2.0.105",
"xml-rs", "xml-rs",
] ]
@ -6608,8 +6742,8 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
"synstructure", "synstructure 0.13.2",
] ]
[[package]] [[package]]
@ -6629,7 +6763,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]
@ -6649,8 +6783,8 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
"synstructure", "synstructure 0.13.2",
] ]
[[package]] [[package]]
@ -6689,7 +6823,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.105",
] ]
[[package]] [[package]]

View File

@ -14,7 +14,7 @@ members = [
"harmony_composer", "harmony_composer",
"harmony_inventory_agent", "harmony_inventory_agent",
"harmony_secret_derive", "harmony_secret_derive",
"harmony_secret", "harmony_secret", "adr/agent_discovery/mdns",
] ]
[workspace.package] [workspace.package]

View File

@ -0,0 +1,15 @@
[package]
name = "mdns"
edition = "2024"
version.workspace = true
readme.workspace = true
license.workspace = true
[dependencies]
mdns-sd = "0.14"
tokio = { version = "1", features = ["full"] }
futures = "0.3"
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"] }

View File

@ -0,0 +1,52 @@
// harmony-agent/src/main.rs
use log::info;
use mdns_sd::{ServiceDaemon, ServiceInfo};
use std::collections::HashMap;
use crate::SERVICE_TYPE;
// The service we are advertising.
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 instance_name = format!("harmony-agent-{}", motherboard_id);
info!("This agent's instance name: {}", instance_name);
info!("Advertising with ID: {}", motherboard_id);
// Create a new mDNS daemon.
let mdns = ServiceDaemon::new().expect("Failed to create mDNS daemon");
// Create a TXT record HashMap to hold our metadata.
let mut properties = HashMap::new();
properties.insert("id".to_string(), motherboard_id.to_string());
properties.insert("version".to_string(), "1.0".to_string());
// Create the service information.
// The instance name should be unique on the network.
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
SERVICE_PORT,
Some(properties),
)
.expect("Failed to create service info");
// Register our service with the daemon.
mdns.register(service_info)
.expect("Failed to register service");
info!("Service '{}' registered and now being advertised.", instance_name);
info!("Agent is running. Press Ctrl+C to exit.");
// Keep the agent running indefinitely.
tokio::signal::ctrl_c().await.unwrap();
info!("Shutting down agent.");
}

View File

@ -0,0 +1,110 @@
use log::debug;
use mdns_sd::{ServiceDaemon, ServiceEvent};
use crate::SERVICE_TYPE;
pub async fn discover() {
println!("Starting Harmony Master and browsing for agents...");
// Create a new mDNS daemon.
let mdns = ServiceDaemon::new().expect("Failed to create mDNS daemon");
// Start browsing for the service type.
// The receiver will be a stream of events.
let receiver = mdns.browse(SERVICE_TYPE).expect("Failed to browse");
println!(
"Listening for mDNS events for '{}'. Press Ctrl+C to exit.",
SERVICE_TYPE
);
std::thread::spawn(move || {
while let Ok(event) = receiver.recv() {
match event {
ServiceEvent::ServiceData(resolved) => {
println!("Resolved a new service: {}", resolved.fullname);
}
other_event => {
println!("Received other event: {:?}", &other_event);
}
}
}
});
// Gracefully shutdown the daemon.
std::thread::sleep(std::time::Duration::from_secs(1000000));
mdns.shutdown().unwrap();
// Process events as they come in.
// while let Ok(event) = receiver.recv_async().await {
// debug!("Received event {event:?}");
// // match event {
// // ServiceEvent::ServiceFound(svc_type, fullname) => {
// // println!("\n--- Agent Discovered ---");
// // println!(" Service Name: {}", fullname());
// // // You can now resolve this service to get its IP, port, and TXT records
// // // The resolve operation is a separate network call.
// // let receiver = mdns.browse(info.get_fullname()).unwrap();
// // if let Ok(resolve_event) = receiver.recv_timeout(Duration::from_secs(2)) {
// // if let ServiceEvent::ServiceResolved(info) = resolve_event {
// // let ip = info.get_addresses().iter().next().unwrap();
// // let port = info.get_port();
// // let motherboard_id = info.get_property("id").map_or("N/A", |v| v.val_str());
// //
// // println!(" IP: {}:{}", ip, port);
// // println!(" Motherboard ID: {}", motherboard_id);
// // println!("------------------------");
// //
// // // TODO: Add this agent to your central list of discovered hosts.
// // }
// // } else {
// // println!("Could not resolve service '{}' in time.", info.get_fullname());
// // }
// // }
// // ServiceEvent::ServiceRemoved(info) => {
// // println!("\n--- Agent Removed ---");
// // println!(" Service Name: {}", info.get_fullname());
// // println!("---------------------");
// // // TODO: Remove this agent from your list.
// // }
// // _ => {
// // // We don't care about other event types for this example
// // }
// // }
// }
}
async fn discover_example() {
use mdns_sd::{ServiceDaemon, ServiceEvent};
// Create a daemon
let mdns = ServiceDaemon::new().expect("Failed to create daemon");
// Use recently added `ServiceEvent::ServiceData`.
mdns.use_service_data(true)
.expect("Failed to use ServiceData");
// Browse for a service type.
let service_type = "_mdns-sd-my-test._udp.local.";
let receiver = mdns.browse(service_type).expect("Failed to browse");
// Receive the browse events in sync or async. Here is
// an example of using a thread. Users can call `receiver.recv_async().await`
// if running in async environment.
std::thread::spawn(move || {
while let Ok(event) = receiver.recv() {
match event {
ServiceEvent::ServiceData(resolved) => {
println!("Resolved a new service: {}", resolved.fullname);
}
other_event => {
println!("Received other event: {:?}", &other_event);
}
}
}
});
// Gracefully shutdown the daemon.
std::thread::sleep(std::time::Duration::from_secs(1));
mdns.shutdown().unwrap();
}

View File

@ -0,0 +1,31 @@
use clap::{Parser, ValueEnum};
mod advertise;
mod discover;
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
#[arg(value_enum)]
profile: Profiles,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum Profiles {
Advertise,
Discover,
}
// The service type we are looking for.
const SERVICE_TYPE: &str = "_harmory._tcp.local.";
#[tokio::main]
async fn main() {
env_logger::init();
let args = Args::parse();
match args.profile {
Profiles::Advertise => advertise::advertise().await,
Profiles::Discover => discover::discover().await,
}
}

View File

@ -1,6 +1,7 @@
#!/bin/sh #!/bin/sh
set -e set -e
rustc --version
cargo check --all-targets --all-features --keep-going cargo check --all-targets --all-features --keep-going
cargo fmt --check cargo fmt --check
cargo clippy cargo clippy

View File

@ -1,6 +1,9 @@
use harmony::{ use harmony::{
inventory::Inventory, inventory::Inventory,
modules::dummy::{ErrorScore, PanicScore, SuccessScore}, modules::{
dummy::{ErrorScore, PanicScore, SuccessScore},
inventory::DiscoverInventoryAgentScore,
},
topology::LocalhostTopology, topology::LocalhostTopology,
}; };
@ -13,6 +16,7 @@ async fn main() {
Box::new(SuccessScore {}), Box::new(SuccessScore {}),
Box::new(ErrorScore {}), Box::new(ErrorScore {}),
Box::new(PanicScore {}), Box::new(PanicScore {}),
Box::new(DiscoverInventoryAgentScore { discovery_timeout: Some(10) }),
], ],
None, None,
) )

View File

@ -67,7 +67,8 @@ bollard.workspace = true
tar.workspace = true tar.workspace = true
base64.workspace = true base64.workspace = true
once_cell = "1.21.3" once_cell = "1.21.3"
harmony-secret-derive = { version = "0.1.0", path = "../harmony_secret_derive" } harmony-secret-derive = { path = "../harmony_secret_derive" }
harmony_inventory_agent = { path = "../harmony_inventory_agent" }
[dev-dependencies] [dev-dependencies]
pretty_assertions.workspace = true pretty_assertions.workspace = true

View File

@ -32,6 +32,7 @@ pub enum InterpretName {
Lamp, Lamp,
ApplicationMonitoring, ApplicationMonitoring,
K8sPrometheusCrdAlerting, K8sPrometheusCrdAlerting,
DiscoverInventoryAgent,
} }
impl std::fmt::Display for InterpretName { impl std::fmt::Display for InterpretName {
@ -58,6 +59,7 @@ impl std::fmt::Display for InterpretName {
InterpretName::Lamp => f.write_str("LAMP"), InterpretName::Lamp => f.write_str("LAMP"),
InterpretName::ApplicationMonitoring => f.write_str("ApplicationMonitoring"), InterpretName::ApplicationMonitoring => f.write_str("ApplicationMonitoring"),
InterpretName::K8sPrometheusCrdAlerting => f.write_str("K8sPrometheusCrdAlerting"), InterpretName::K8sPrometheusCrdAlerting => f.write_str("K8sPrometheusCrdAlerting"),
InterpretName::DiscoverInventoryAgent => f.write_str("DiscoverInventoryAgent"),
} }
} }
} }

View File

@ -0,0 +1,71 @@
use async_trait::async_trait;
use harmony_inventory_agent::local_presence::DiscoveryEvent;
use log::info;
use serde::{Deserialize, Serialize};
use crate::{
data::{Id, Version},
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory,
score::Score,
topology::Topology,
};
/// This launches an harmony_inventory_agent discovery process
/// This will allow us to register/update hosts running harmony_inventory_agent
/// from LAN in the Harmony inventory
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DiscoverInventoryAgentScore {
pub discovery_timeout: Option<u64>,
}
impl<T: Topology> Score<T> for DiscoverInventoryAgentScore {
fn name(&self) -> String {
"DiscoverInventoryAgentScore".to_string()
}
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
Box::new(DiscoverInventoryAgentInterpret {
score: self.clone(),
})
}
}
#[derive(Debug)]
struct DiscoverInventoryAgentInterpret {
score: DiscoverInventoryAgentScore,
}
#[async_trait]
impl<T: Topology> Interpret<T> for DiscoverInventoryAgentInterpret {
async fn execute(
&self,
inventory: &Inventory,
topology: &T,
) -> Result<Outcome, InterpretError> {
harmony_inventory_agent::local_presence::discover_agents(
self.score.discovery_timeout,
on_discover_event,
);
}
fn get_name(&self) -> InterpretName {
InterpretName::DiscoverInventoryAgent
}
fn get_version(&self) -> Version {
todo!()
}
fn get_status(&self) -> InterpretStatus {
todo!()
}
fn get_children(&self) -> Vec<Id> {
todo!()
}
}
fn on_discover_event(event: &DiscoveryEvent) {
info!("got discovery event {event:?}");
}

View File

@ -17,3 +17,4 @@ pub mod prometheus;
pub mod storage; pub mod storage;
pub mod tenant; pub mod tenant;
pub mod tftp; pub mod tftp;
pub mod inventory;

View File

@ -10,3 +10,6 @@ serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
log.workspace = true log.workspace = true
env_logger.workspace = true env_logger.workspace = true
tokio.workspace = true
thiserror.workspace = true
mdns-sd = "0.14.1"

View File

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

View File

@ -0,0 +1,73 @@
use log::{error, info, warn};
use mdns_sd::{ServiceDaemon, ServiceInfo};
use std::collections::HashMap;
use crate::{hwinfo::PhysicalHost, local_presence::{PresenceError, SERVICE_NAME, VERSION}};
/// Advertises the agent's presence on the local network.
///
/// This function is synchronous and non-blocking. It spawns a background Tokio task
/// to handle the mDNS advertisement for the lifetime of the application.
pub fn advertise(service_port: u16) -> Result<(), PresenceError> {
let host_id = match PhysicalHost::gather() {
Ok(host) => Some(host.host_uuid),
Err(e) => {
error!("Could not build physical host, harmony presence id will be unavailable : {e}");
None
}
};
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}'.");
tokio::spawn(async move {
info!(
"Local presence task started. Advertising as '{}'.",
instance_name
);
// The ServiceDaemon must live for the entire duration of the advertisement.
// If it's dropped, the advertisement stops.
let mdns = match ServiceDaemon::new() {
Ok(daemon) => daemon,
Err(e) => {
warn!("Failed to create mDNS daemon: {}. Task shutting down.", e);
return;
}
};
let mut props = HashMap::new();
if let Some(host_id) = host_id {
props.insert("id".to_string(), host_id);
}
props.insert("version".to_string(), VERSION.to_string());
let service_info = ServiceInfo::new(
SERVICE_NAME,
&instance_name,
&format!("{}.local.", instance_name),
(), // Let the daemon determine the host IPs
service_port,
Some(props),
)
.expect("ServiceInfo creation should not fail with valid inputs");
// The registration handle must also be kept alive.
let _registration_handle = match mdns.register(service_info) {
Ok(handle) => {
info!("Service successfully registered on the local network.");
handle
}
Err(e) => {
warn!("Failed to register service: {}. Task shutting down.", e);
return;
}
};
});
info!("{spawned_msg}");
Ok(())
}

View File

@ -0,0 +1,34 @@
use mdns_sd::{ServiceDaemon, ServiceEvent};
use crate::local_presence::SERVICE_NAME;
pub type DiscoveryEvent = ServiceEvent;
pub fn discover_agents(timeout: Option<u64>, on_event: fn(&DiscoveryEvent)) {
// Create a new mDNS daemon.
let mdns = ServiceDaemon::new().expect("Failed to create mDNS daemon");
// Start browsing for the service type.
// The receiver will be a stream of events.
let receiver = mdns.browse(SERVICE_NAME).expect("Failed to browse");
std::thread::spawn(move || {
while let Ok(event) = receiver.recv() {
on_event(&event);
match event {
ServiceEvent::ServiceData(resolved) => {
println!("Resolved a new service: {}", resolved.fullname);
}
other_event => {
println!("Received other event: {:?}", &other_event);
}
}
}
});
if let Some(timeout) = timeout {
// Gracefully shutdown the daemon.
std::thread::sleep(std::time::Duration::from_secs(timeout));
mdns.shutdown().unwrap();
}
}

View File

@ -0,0 +1,16 @@
mod discover;
pub use discover::*;
mod advertise;
pub use advertise::*;
pub const SERVICE_NAME: &str = "_harmony._tcp.local.";
const VERSION: &str = env!("CARGO_PKG_VERSION");
// A specific error type for our module enhances clarity and usability.
#[derive(thiserror::Error, Debug)]
pub enum PresenceError {
#[error("Failed to create mDNS daemon")]
DaemonCreationFailed(#[from] mdns_sd::Error),
#[error("The shutdown signal has already been sent")]
ShutdownFailed,
}

View File

@ -1,9 +1,15 @@
// src/main.rs // src/main.rs
use actix_web::{App, HttpServer, Responder, get}; use actix_web::{App, HttpServer, Responder, get};
use hwinfo::PhysicalHost; use log::error;
use std::env; use std::env;
use crate::hwinfo::PhysicalHost;
mod hwinfo; mod hwinfo;
mod local_presence;
#[get("/inventory")] #[get("/inventory")]
async fn inventory() -> impl Responder { async fn inventory() -> impl Responder {
@ -26,10 +32,15 @@ async fn main() -> std::io::Result<()> {
env_logger::init(); env_logger::init();
let port = env::var("HARMONY_INVENTORY_AGENT_PORT").unwrap_or_else(|_| "8080".to_string()); 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 bind_addr = format!("0.0.0.0:{}", port); let bind_addr = format!("0.0.0.0:{}", port);
log::info!("Starting inventory agent on {}", bind_addr); log::info!("Starting inventory agent on {}", bind_addr);
if let Err(e) = local_presence::advertise(port) {
error!("Could not start advertise local presence : {e}");
}
HttpServer::new(|| App::new().service(inventory)) HttpServer::new(|| App::new().service(inventory))
.bind(&bind_addr)? .bind(&bind_addr)?
.run() .run()