feat: Inventory PhysicalHost persistence with sqlx and local sqlite db #125
| @ -0,0 +1,32 @@ | ||||
| { | ||||
|   "db_name": "SQLite", | ||||
|   "query": "SELECT id, version_id, data as \"data: Json<PhysicalHost>\" FROM physical_hosts WHERE id = ? ORDER BY version_id DESC LIMIT 1", | ||||
|   "describe": { | ||||
|     "columns": [ | ||||
|       { | ||||
|         "name": "id", | ||||
|         "ordinal": 0, | ||||
|         "type_info": "Text" | ||||
|       }, | ||||
|       { | ||||
|         "name": "version_id", | ||||
|         "ordinal": 1, | ||||
|         "type_info": "Text" | ||||
|       }, | ||||
|       { | ||||
|         "name": "data: Json<PhysicalHost>", | ||||
|         "ordinal": 2, | ||||
|         "type_info": "Null" | ||||
|       } | ||||
|     ], | ||||
|     "parameters": { | ||||
|       "Right": 1 | ||||
|     }, | ||||
|     "nullable": [ | ||||
|       false, | ||||
|       false, | ||||
|       false | ||||
|     ] | ||||
|   }, | ||||
|   "hash": "934035c7ca6e064815393e4e049a7934b0a7fac04a4fe4b2a354f0443d630990" | ||||
| } | ||||
| @ -0,0 +1,12 @@ | ||||
| { | ||||
|   "db_name": "SQLite", | ||||
|   "query": "INSERT INTO physical_hosts (id, version_id, data) VALUES (?, ?, ?)", | ||||
|   "describe": { | ||||
|     "columns": [], | ||||
|     "parameters": { | ||||
|       "Right": 3 | ||||
|     }, | ||||
|     "nullable": [] | ||||
|   }, | ||||
|   "hash": "f10f615ee42129ffa293e46f2f893d65a237d31d24b74a29c6a8d8420d255ab8" | ||||
| } | ||||
							
								
								
									
										343
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										343
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -474,6 +474,15 @@ dependencies = [ | ||||
|  "syn 2.0.105", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "atoi" | ||||
| version = "2.0.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" | ||||
| dependencies = [ | ||||
|  "num-traits", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "atomic-waker" | ||||
| version = "1.1.2" | ||||
| @ -1052,6 +1061,21 @@ dependencies = [ | ||||
|  "libc", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "crc" | ||||
| version = "3.3.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" | ||||
| dependencies = [ | ||||
|  "crc-catalog", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "crc-catalog" | ||||
| version = "2.4.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "crc32fast" | ||||
| version = "1.4.2" | ||||
| @ -1089,6 +1113,15 @@ dependencies = [ | ||||
|  "crossbeam-utils", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "crossbeam-queue" | ||||
| version = "0.3.12" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" | ||||
| dependencies = [ | ||||
|  "crossbeam-utils", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "crossbeam-utils" | ||||
| version = "0.8.21" | ||||
| @ -1409,6 +1442,12 @@ dependencies = [ | ||||
|  "syn 2.0.105", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "dotenvy" | ||||
| version = "0.15.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "dyn-clone" | ||||
| version = "1.0.19" | ||||
| @ -1471,6 +1510,9 @@ name = "either" | ||||
| version = "1.15.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" | ||||
| dependencies = [ | ||||
|  "serde", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "elliptic-curve" | ||||
| @ -1586,6 +1628,17 @@ dependencies = [ | ||||
|  "windows-sys 0.60.2", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "etcetera" | ||||
| version = "0.8.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "home", | ||||
|  "windows-sys 0.48.0", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "event-listener" | ||||
| version = "5.4.0" | ||||
| @ -1984,6 +2037,17 @@ dependencies = [ | ||||
|  "futures-util", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "futures-intrusive" | ||||
| version = "0.5.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" | ||||
| dependencies = [ | ||||
|  "futures-core", | ||||
|  "lock_api", | ||||
|  "parking_lot", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "futures-io" | ||||
| version = "0.3.31" | ||||
| @ -2241,11 +2305,13 @@ dependencies = [ | ||||
|  "serde_with", | ||||
|  "serde_yaml", | ||||
|  "similar", | ||||
|  "sqlx", | ||||
|  "strum 0.27.1", | ||||
|  "tar", | ||||
|  "temp-dir", | ||||
|  "temp-file", | ||||
|  "tempfile", | ||||
|  "thiserror 2.0.14", | ||||
|  "tokio", | ||||
|  "tokio-util", | ||||
|  "url", | ||||
| @ -2391,6 +2457,15 @@ dependencies = [ | ||||
|  "foldhash", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "hashlink" | ||||
| version = "0.10.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" | ||||
| dependencies = [ | ||||
|  "hashbrown 0.15.4", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "headers" | ||||
| version = "0.4.1" | ||||
| @ -2960,7 +3035,7 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" | ||||
| [[package]] | ||||
| name = "infisical" | ||||
| version = "0.0.2" | ||||
| source = "git+https://github.com/jggc/rust-sdk.git?branch=patch-1#5a8509ef5483a5798c5d1a7f7ebeb5ba5b783253" | ||||
| source = "git+https://github.com/jggc/rust-sdk.git?branch=patch-1#30d820194d29491411bd14f6c2e18ec500bb0b14" | ||||
| dependencies = [ | ||||
|  "base64 0.22.1", | ||||
|  "reqwest 0.12.20", | ||||
| @ -3333,6 +3408,17 @@ dependencies = [ | ||||
|  "redox_syscall", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "libsqlite3-sys" | ||||
| version = "0.30.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" | ||||
| dependencies = [ | ||||
|  "cc", | ||||
|  "pkg-config", | ||||
|  "vcpkg", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "linux-raw-sys" | ||||
| version = "0.4.15" | ||||
| @ -3429,6 +3515,16 @@ version = "0.1.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "md-5" | ||||
| version = "0.10.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" | ||||
| dependencies = [ | ||||
|  "cfg-if", | ||||
|  "digest", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "md5" | ||||
| version = "0.7.0" | ||||
| @ -5280,6 +5376,9 @@ name = "smallvec" | ||||
| version = "1.15.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" | ||||
| dependencies = [ | ||||
|  "serde", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "snafu" | ||||
| @ -5352,6 +5451,194 @@ dependencies = [ | ||||
|  "der", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sqlx" | ||||
| version = "0.8.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" | ||||
| dependencies = [ | ||||
|  "sqlx-core", | ||||
|  "sqlx-macros", | ||||
|  "sqlx-mysql", | ||||
|  "sqlx-postgres", | ||||
|  "sqlx-sqlite", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sqlx-core" | ||||
| version = "0.8.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" | ||||
| dependencies = [ | ||||
|  "base64 0.22.1", | ||||
|  "bytes", | ||||
|  "crc", | ||||
|  "crossbeam-queue", | ||||
|  "either", | ||||
|  "event-listener", | ||||
|  "futures-core", | ||||
|  "futures-intrusive", | ||||
|  "futures-io", | ||||
|  "futures-util", | ||||
|  "hashbrown 0.15.4", | ||||
|  "hashlink", | ||||
|  "indexmap 2.10.0", | ||||
|  "log", | ||||
|  "memchr", | ||||
|  "once_cell", | ||||
|  "percent-encoding", | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|  "sha2", | ||||
|  "smallvec", | ||||
|  "thiserror 2.0.14", | ||||
|  "tokio", | ||||
|  "tokio-stream", | ||||
|  "tracing", | ||||
|  "url", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sqlx-macros" | ||||
| version = "0.8.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "sqlx-core", | ||||
|  "sqlx-macros-core", | ||||
|  "syn 2.0.105", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sqlx-macros-core" | ||||
| version = "0.8.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" | ||||
| dependencies = [ | ||||
|  "dotenvy", | ||||
|  "either", | ||||
|  "heck", | ||||
|  "hex", | ||||
|  "once_cell", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|  "sha2", | ||||
|  "sqlx-core", | ||||
|  "sqlx-mysql", | ||||
|  "sqlx-postgres", | ||||
|  "sqlx-sqlite", | ||||
|  "syn 2.0.105", | ||||
|  "tokio", | ||||
|  "url", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sqlx-mysql" | ||||
| version = "0.8.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" | ||||
| dependencies = [ | ||||
|  "atoi", | ||||
|  "base64 0.22.1", | ||||
|  "bitflags 2.9.1", | ||||
|  "byteorder", | ||||
|  "bytes", | ||||
|  "crc", | ||||
|  "digest", | ||||
|  "dotenvy", | ||||
|  "either", | ||||
|  "futures-channel", | ||||
|  "futures-core", | ||||
|  "futures-io", | ||||
|  "futures-util", | ||||
|  "generic-array", | ||||
|  "hex", | ||||
|  "hkdf", | ||||
|  "hmac", | ||||
|  "itoa", | ||||
|  "log", | ||||
|  "md-5", | ||||
|  "memchr", | ||||
|  "once_cell", | ||||
|  "percent-encoding", | ||||
|  "rand 0.8.5", | ||||
|  "rsa", | ||||
|  "serde", | ||||
|  "sha1", | ||||
|  "sha2", | ||||
|  "smallvec", | ||||
|  "sqlx-core", | ||||
|  "stringprep", | ||||
|  "thiserror 2.0.14", | ||||
|  "tracing", | ||||
|  "whoami", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sqlx-postgres" | ||||
| version = "0.8.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" | ||||
| dependencies = [ | ||||
|  "atoi", | ||||
|  "base64 0.22.1", | ||||
|  "bitflags 2.9.1", | ||||
|  "byteorder", | ||||
|  "crc", | ||||
|  "dotenvy", | ||||
|  "etcetera", | ||||
|  "futures-channel", | ||||
|  "futures-core", | ||||
|  "futures-util", | ||||
|  "hex", | ||||
|  "hkdf", | ||||
|  "hmac", | ||||
|  "home", | ||||
|  "itoa", | ||||
|  "log", | ||||
|  "md-5", | ||||
|  "memchr", | ||||
|  "once_cell", | ||||
|  "rand 0.8.5", | ||||
|  "serde", | ||||
|  "serde_json", | ||||
|  "sha2", | ||||
|  "smallvec", | ||||
|  "sqlx-core", | ||||
|  "stringprep", | ||||
|  "thiserror 2.0.14", | ||||
|  "tracing", | ||||
|  "whoami", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "sqlx-sqlite" | ||||
| version = "0.8.6" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" | ||||
| dependencies = [ | ||||
|  "atoi", | ||||
|  "flume", | ||||
|  "futures-channel", | ||||
|  "futures-core", | ||||
|  "futures-executor", | ||||
|  "futures-intrusive", | ||||
|  "futures-util", | ||||
|  "libsqlite3-sys", | ||||
|  "log", | ||||
|  "percent-encoding", | ||||
|  "serde", | ||||
|  "serde_urlencoded", | ||||
|  "sqlx-core", | ||||
|  "thiserror 2.0.14", | ||||
|  "tracing", | ||||
|  "url", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "ssh-cipher" | ||||
| version = "0.2.0" | ||||
| @ -5415,6 +5702,17 @@ version = "1.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "stringprep" | ||||
| version = "0.1.5" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" | ||||
| dependencies = [ | ||||
|  "unicode-bidi", | ||||
|  "unicode-normalization", | ||||
|  "unicode-properties", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "strsim" | ||||
| version = "0.11.1" | ||||
| @ -6018,12 +6316,33 @@ version = "0.1.7" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "unicode-bidi" | ||||
| version = "0.3.18" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "unicode-ident" | ||||
| version = "1.0.18" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "unicode-normalization" | ||||
| version = "0.1.24" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" | ||||
| dependencies = [ | ||||
|  "tinyvec", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "unicode-properties" | ||||
| version = "0.1.3" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "unicode-segmentation" | ||||
| version = "1.12.0" | ||||
| @ -6147,6 +6466,12 @@ version = "0.1.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "vcpkg" | ||||
| version = "0.2.15" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "version_check" | ||||
| version = "0.9.5" | ||||
| @ -6186,6 +6511,12 @@ dependencies = [ | ||||
|  "wit-bindgen-rt", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "wasite" | ||||
| version = "0.1.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "wasm-bindgen" | ||||
| version = "0.2.100" | ||||
| @ -6306,6 +6637,16 @@ dependencies = [ | ||||
|  "rustls-pki-types", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "whoami" | ||||
| version = "1.6.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" | ||||
| dependencies = [ | ||||
|  "libredox", | ||||
|  "wasite", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "winapi" | ||||
| version = "0.2.8" | ||||
|  | ||||
| @ -65,3 +65,5 @@ directories = "6.0.0" | ||||
| thiserror = "2.0.14" | ||||
| serde = { version = "1.0.209", features = ["derive", "rc"] } | ||||
| serde_json = "1.0.127" | ||||
| askama = "0.14" | ||||
| sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite" ] } | ||||
|  | ||||
| @ -65,10 +65,12 @@ kube-derive = "1.1.0" | ||||
| bollard.workspace = true | ||||
| tar.workspace = true | ||||
| base64.workspace = true | ||||
| thiserror.workspace = true | ||||
| once_cell = "1.21.3" | ||||
| harmony_inventory_agent = { path = "../harmony_inventory_agent" } | ||||
| harmony_secret_derive = { version = "0.1.0", path = "../harmony_secret_derive" } | ||||
| askama = "0.14.0" | ||||
| askama.workspace = true | ||||
| sqlx.workspace = true | ||||
| 
 | ||||
| [dev-dependencies] | ||||
| pretty_assertions.workspace = true | ||||
|  | ||||
| @ -24,6 +24,14 @@ pub struct Id { | ||||
|     value: String, | ||||
| } | ||||
| 
 | ||||
| impl Id { | ||||
|     pub fn empty() -> Self { | ||||
|         Id { | ||||
|             value: String::new(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl FromStr for Id { | ||||
|     type Err = (); | ||||
| 
 | ||||
| @ -34,6 +42,12 @@ impl FromStr for Id { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<String> for Id { | ||||
|     fn from(value: String) -> Self { | ||||
|         Self { value } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl std::fmt::Display for Id { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         f.write_str(&self.value) | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| use std::sync::Arc; | ||||
| use std::{str::FromStr, sync::Arc}; | ||||
| 
 | ||||
| use derive_new::new; | ||||
| use harmony_types::net::MacAddress; | ||||
| use serde::{Serialize, Serializer, ser::SerializeStruct}; | ||||
| use serde::{Deserialize, Serialize, Serializer, ser::SerializeStruct}; | ||||
| use serde_value::Value; | ||||
| 
 | ||||
| pub type HostGroup = Vec<PhysicalHost>; | ||||
| @ -11,6 +11,7 @@ pub type FirewallGroup = Vec<PhysicalHost>; | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct PhysicalHost { | ||||
|     pub id: Id, | ||||
|     pub category: HostCategory, | ||||
|     pub network: Vec<NetworkInterface>, | ||||
|     pub management: Arc<dyn ManagementInterface>, | ||||
| @ -23,6 +24,7 @@ pub struct PhysicalHost { | ||||
| impl PhysicalHost { | ||||
|     pub fn empty(category: HostCategory) -> Self { | ||||
|         Self { | ||||
|             id: Id::empty(), | ||||
|             category, | ||||
|             network: vec![], | ||||
|             storage: vec![], | ||||
| @ -128,6 +130,15 @@ impl Serialize for PhysicalHost { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'de> Deserialize<'de> for PhysicalHost { | ||||
|     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||
|     where | ||||
|         D: serde::Deserializer<'de>, | ||||
|     { | ||||
|         todo!() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(new, Serialize)] | ||||
| pub struct ManualManagementInterface; | ||||
| 
 | ||||
| @ -187,6 +198,8 @@ pub struct NetworkInterface { | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| use harmony_macros::mac_address; | ||||
| 
 | ||||
| use crate::data::Id; | ||||
| #[cfg(test)] | ||||
| impl NetworkInterface { | ||||
|     pub fn dummy() -> Self { | ||||
| @ -304,6 +317,7 @@ mod tests { | ||||
|     fn test_serialize_physical_host_with_hp_ilo() { | ||||
|         // Create a PhysicalHost with HP iLO management
 | ||||
|         let host = PhysicalHost { | ||||
|             id: Id::empty(), | ||||
|             category: HostCategory::Server, | ||||
|             network: vec![NetworkInterface::dummy()], | ||||
|             management: Arc::new(MockHPIlo { | ||||
| @ -341,6 +355,7 @@ mod tests { | ||||
|     fn test_serialize_physical_host_with_dell_idrac() { | ||||
|         // Create a PhysicalHost with Dell iDRAC management
 | ||||
|         let host = PhysicalHost { | ||||
|             id: Id::empty(), | ||||
|             category: HostCategory::Server, | ||||
|             network: vec![NetworkInterface::dummy()], | ||||
|             management: Arc::new(MockDellIdrac { | ||||
| @ -375,6 +390,7 @@ mod tests { | ||||
|     fn test_different_management_implementations_produce_valid_json() { | ||||
|         // Create hosts with different management implementations
 | ||||
|         let host1 = PhysicalHost { | ||||
|             id: Id::empty(), | ||||
|             category: HostCategory::Server, | ||||
|             network: vec![], | ||||
|             management: Arc::new(MockHPIlo { | ||||
| @ -390,6 +406,7 @@ mod tests { | ||||
|         }; | ||||
| 
 | ||||
|         let host2 = PhysicalHost { | ||||
|             id: Id::empty(), | ||||
|             category: HostCategory::Server, | ||||
|             network: vec![], | ||||
|             management: Arc::new(MockDellIdrac { | ||||
|  | ||||
| @ -1,3 +1,6 @@ | ||||
| mod repository; | ||||
| pub use repository::*; | ||||
| 
 | ||||
| #[derive(Debug, new, Clone)] | ||||
| pub struct InventoryFilter { | ||||
|     target: Vec<Filter>, | ||||
|  | ||||
							
								
								
									
										25
									
								
								harmony/src/domain/inventory/repository.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								harmony/src/domain/inventory/repository.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| use async_trait::async_trait; | ||||
| 
 | ||||
| use crate::hardware::PhysicalHost; | ||||
| 
 | ||||
| /// Errors that can occur within the repository layer.
 | ||||
| #[derive(thiserror::Error, Debug)] | ||||
| pub enum RepoError { | ||||
|     #[error("Database query failed: {0}")] | ||||
|     QueryFailed(String), | ||||
|     #[error("Data serialization failed: {0}")] | ||||
|     Serialization(String), | ||||
|     #[error("Data deserialization failed: {0}")] | ||||
|     Deserialization(String), | ||||
|     #[error("Could not connect to the database: {0}")] | ||||
|     ConnectionFailed(String), | ||||
| } | ||||
| 
 | ||||
| // --- Trait and Implementation ---
 | ||||
| 
 | ||||
| /// Defines the contract for inventory persistence.
 | ||||
| #[async_trait] | ||||
| pub trait InventoryRepository: Send + Sync + 'static { | ||||
|     async fn save(&self, host: &PhysicalHost) -> Result<(), RepoError>; | ||||
|     async fn get_latest_by_id(&self, host_id: &str) -> Result<Option<PhysicalHost>, RepoError>; | ||||
|  | ||||
| } | ||||
							
								
								
									
										1
									
								
								harmony/src/infra/inventory/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								harmony/src/infra/inventory/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| mod sqlite; | ||||
							
								
								
									
										69
									
								
								harmony/src/infra/inventory/sqlite.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								harmony/src/infra/inventory/sqlite.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | ||||
| use crate::{ | ||||
|     data::Id, | ||||
|     hardware::PhysicalHost, | ||||
|     inventory::{InventoryRepository, RepoError}, | ||||
| }; | ||||
| use async_trait::async_trait; | ||||
| use log::{info, warn}; | ||||
| use sqlx::{Pool, Sqlite, SqlitePool}; | ||||
| 
 | ||||
| /// A thread-safe, connection-pooled repository using SQLite.
 | ||||
| #[derive(Debug)] | ||||
| pub struct SqliteInventoryRepository { | ||||
|     pool: Pool<Sqlite>, | ||||
| } | ||||
| 
 | ||||
| impl SqliteInventoryRepository { | ||||
|     pub async fn new(database_url: &str) -> Result<Self, RepoError> { | ||||
|         let pool = SqlitePool::connect(database_url) | ||||
|             .await | ||||
|             .map_err(|e| RepoError::ConnectionFailed(e.to_string()))?; | ||||
| 
 | ||||
|         todo!("make sure migrations are up to date"); | ||||
|         info!( | ||||
|             "SQLite inventory repository initialized at '{}'", | ||||
|             database_url, | ||||
|         ); | ||||
|         Ok(Self { pool }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[async_trait] | ||||
| impl InventoryRepository for SqliteInventoryRepository { | ||||
|     async fn save(&self, host: &PhysicalHost) -> Result<(), RepoError> { | ||||
|         let data = serde_json::to_vec(host).map_err(|e| RepoError::Serialization(e.to_string()))?; | ||||
| 
				
					
						letian
						commented  Could this struct evolve in time and that at some point we update an existing physical host instead of just saving a new one every time? Could this struct evolve in time and that at some point we update an existing physical host instead of just saving a new one every time? | ||||
| 
 | ||||
|         let id = Id::default().to_string(); | ||||
|         let host_id = host.id.to_string(); | ||||
| 
 | ||||
|         sqlx::query!( | ||||
|             "INSERT INTO physical_hosts (id, version_id, data) VALUES (?, ?, ?)", | ||||
|             host_id, | ||||
|             id, | ||||
|             data, | ||||
|         ) | ||||
|         .execute(&self.pool) | ||||
|         .await?; | ||||
| 
 | ||||
|         info!("Saved new inventory version for host '{}'", host.id); | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     async fn get_latest_by_id(&self, host_id: &str) -> Result<Option<PhysicalHost>, RepoError> { | ||||
|         let row = sqlx::query_as!( | ||||
|             DbHost, | ||||
|             r#"SELECT id, version_id, data as "data: Json<PhysicalHost>" FROM physical_hosts WHERE id = ? ORDER BY version_id DESC LIMIT 1"#, | ||||
|             host_id | ||||
|         ) | ||||
|         .fetch_optional(&self.pool) | ||||
|         .await?; | ||||
|         todo!() | ||||
| 
					
					letian marked this conversation as resolved
					
				 
				
					
						letian
						commented  Do we still need this? Do we still need this? | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| use sqlx::types::Json; | ||||
| struct DbHost { | ||||
|     data: Json<PhysicalHost>, | ||||
|     id: Id, | ||||
|     version_id: Id, | ||||
| } | ||||
| @ -1,4 +1,6 @@ | ||||
| pub mod executors; | ||||
| pub mod hp_ilo; | ||||
| pub mod intel_amt; | ||||
| pub mod inventory; | ||||
| pub mod opnsense; | ||||
| mod sqlx; | ||||
|  | ||||
							
								
								
									
										36
									
								
								harmony/src/infra/sqlx.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								harmony/src/infra/sqlx.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| use crate::inventory::RepoError; | ||||
| 
 | ||||
| impl From<sqlx::Error> for RepoError { | ||||
|     fn from(value: sqlx::Error) -> Self { | ||||
|         match value { | ||||
|             sqlx::Error::Configuration(_) | ||||
|             | sqlx::Error::Io(_) | ||||
|             | sqlx::Error::Tls(_) | ||||
|             | sqlx::Error::Protocol(_) | ||||
|             | sqlx::Error::PoolTimedOut | ||||
|             | sqlx::Error::PoolClosed | ||||
|             | sqlx::Error::WorkerCrashed => RepoError::ConnectionFailed(value.to_string()), | ||||
|             sqlx::Error::InvalidArgument(_) | ||||
|             | sqlx::Error::Database(_) | ||||
|             | sqlx::Error::RowNotFound | ||||
|             | sqlx::Error::TypeNotFound { .. } | ||||
|             | sqlx::Error::ColumnIndexOutOfBounds { .. } | ||||
|             | sqlx::Error::ColumnNotFound(_) | ||||
|             | sqlx::Error::AnyDriverError(_) | ||||
|             | sqlx::Error::Migrate(_) | ||||
|             | sqlx::Error::InvalidSavePointStatement | ||||
|             | sqlx::Error::BeginFailed => RepoError::QueryFailed(value.to_string()), | ||||
|             sqlx::Error::Encode(_) => RepoError::Serialization(value.to_string()), | ||||
|             sqlx::Error::Decode(_) | sqlx::Error::ColumnDecode { .. } => { | ||||
|                 RepoError::Deserialization(value.to_string()) | ||||
|             } | ||||
|             _ => RepoError::QueryFailed(value.to_string()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<serde_json::Error> for RepoError { | ||||
|     fn from(value: serde_json::Error) -> Self { | ||||
|         RepoError::Serialization(value.to_string()) | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	
should we use the
Idtype instead of a str here?