Add health checking system to agent
Adds a health checking endpoint on the GRPC server. This is a stream that changes whenever a health status update occurs. Reviewed-on: https://git.nikos.gg/prymn/prymn/pulls/5 Co-authored-by: Nikos Papadakis <nikos@papadakis.xyz> Co-committed-by: Nikos Papadakis <nikos@papadakis.xyz>
This commit is contained in:
parent
98fb50dcff
commit
5d948f4c19
15 changed files with 720 additions and 328 deletions
|
@ -1,6 +1,6 @@
|
|||
when:
|
||||
path: "agent/src/*"
|
||||
branch: main
|
||||
- event: push
|
||||
branch: main
|
||||
|
||||
matrix:
|
||||
BUILD_TARGET:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
when:
|
||||
path: "get_prymn.sh"
|
||||
branch: main
|
||||
- event: push
|
||||
branch: main
|
||||
|
||||
steps:
|
||||
upload:
|
||||
|
|
318
agent/Cargo.lock
generated
318
agent/Cargo.lock
generated
|
@ -19,13 +19,28 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
|
||||
checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[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.3.2"
|
||||
|
@ -67,9 +82,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
|
||||
checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
|
@ -100,18 +115,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.26",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.71"
|
||||
version = "0.1.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf"
|
||||
checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.26",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -122,9 +137,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
|||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.6.18"
|
||||
version = "0.6.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39"
|
||||
checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
|
@ -194,9 +209,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.3.3"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
|
||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
|
@ -212,9 +227,12 @@ checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -223,19 +241,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.3.12"
|
||||
name = "chrono"
|
||||
version = "0.4.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eab9e8ceb9afdade1ab3f0fd8dbce5b1b2f468ad653baf10e771781b2b67b73"
|
||||
checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"time",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.3.12"
|
||||
version = "4.3.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f2763db829349bf00cfc06251268865ed4363b93a943174f638daf3ecdba2cd"
|
||||
checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
@ -263,9 +296,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
|||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.1"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
|
@ -287,9 +320,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
|
||||
checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
|
@ -308,12 +341,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.9.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
|
@ -365,7 +395,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.26",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -404,7 +434,7 @@ checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -501,7 +531,7 @@ dependencies = [
|
|||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"socket2 0.4.9",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
|
@ -534,6 +564,29 @@ dependencies = [
|
|||
"tokio-io-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[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 = "idna"
|
||||
version = "0.4.0"
|
||||
|
@ -554,26 +607,6 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.8.0"
|
||||
|
@ -587,7 +620,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"rustix 0.38.4",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
|
@ -600,6 +633,15 @@ dependencies = [
|
|||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
|
@ -629,15 +671,9 @@ checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
|||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.3.8"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0"
|
||||
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
|
@ -647,9 +683,9 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
|
|||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.7.0"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40"
|
||||
checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
|
@ -688,7 +724,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
|
@ -731,6 +767,15 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
|
@ -780,29 +825,29 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842"
|
||||
checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
|
||||
checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.26",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.10"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
|
||||
checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
|
@ -828,9 +873,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.65"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92de25114670a878b1261c79c9f8f729fb97e95bac93f6312f583c60dd6a1dfe"
|
||||
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -853,7 +898,7 @@ checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270"
|
|||
dependencies = [
|
||||
"bytes",
|
||||
"heck",
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"multimap",
|
||||
|
@ -874,7 +919,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
|
@ -894,8 +939,10 @@ name = "prymn_agent"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"clap",
|
||||
"envy",
|
||||
"itertools 0.11.0",
|
||||
"nix",
|
||||
"once_cell",
|
||||
"prost",
|
||||
|
@ -914,9 +961,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.30"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5907a1b7c277254a8b15170f6e7c97cfa60ee7872a3217663bb81151e48184bb"
|
||||
checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -962,9 +1009,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.9.1"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
|
||||
checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -974,9 +1021,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.3.3"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310"
|
||||
checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -1051,36 +1098,22 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.37.23"
|
||||
version = "0.38.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
|
||||
checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys 0.3.8",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5"
|
||||
dependencies = [
|
||||
"bitflags 2.3.3",
|
||||
"bitflags 2.4.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.3",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.21.5"
|
||||
version = "0.21.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36"
|
||||
checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
|
@ -1099,9 +1132,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.101.1"
|
||||
version = "0.101.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15f36a6828982f422756984e47912a7a51dcbc2a197aa791158f8ca61cd8204e"
|
||||
checksum = "261e9e0888cba427c3316e6322805653c9425240b6fd96cee7cb671ab70ab8d0"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
|
@ -1131,29 +1164,29 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.173"
|
||||
version = "1.0.183"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91f70896d6720bc714a4a57d22fc91f1db634680e65c8efe13323f1fa38d53f"
|
||||
checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.173"
|
||||
version = "1.0.183"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6250dde8342e0232232be9ca3db7aa40aceb5a3e5dd9bddbc00d99a007cde49"
|
||||
checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.26",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.103"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b"
|
||||
checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
@ -1215,6 +1248,16 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
|
@ -1246,9 +1289,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.26"
|
||||
version = "2.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970"
|
||||
checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1263,9 +1306,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
|||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.29.4"
|
||||
version = "0.29.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "751e810399bba86e9326f5762b7f32ac5a085542df78da6a78d94e07d14d7c11"
|
||||
checksum = "165d6d8539689e3d3bc8b98ac59541e1f21c7de7c85d60dc80e43ae0ed2113db"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
|
@ -1277,15 +1320,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.6.0"
|
||||
version = "3.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
|
||||
checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"rustix 0.37.23",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
|
@ -1299,6 +1341,17 @@ dependencies = [
|
|||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
|
@ -1316,11 +1369,10 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.29.1"
|
||||
version = "1.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
|
||||
checksum = "2d3ce25f50619af8b0aec2eb23deebe84249e19e2ddd393a6e16e3300a6dadfd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
|
@ -1328,7 +1380,7 @@ dependencies = [
|
|||
"num_cpus",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"socket2 0.5.3",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
]
|
||||
|
@ -1351,7 +1403,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.26",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1373,6 +1425,7 @@ dependencies = [
|
|||
"futures-core",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1485,7 +1538,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.26",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1588,6 +1641,12 @@ dependencies = [
|
|||
"try-lock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
@ -1615,7 +1674,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.26",
|
||||
"syn 2.0.28",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
@ -1649,7 +1708,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.26",
|
||||
"syn 2.0.28",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
@ -1722,6 +1781,15 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
|
|
@ -5,8 +5,10 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1.0.71"
|
||||
chrono = "0.4.26"
|
||||
clap = { version = "4.3.9" }
|
||||
envy = "0.4.2"
|
||||
itertools = "0.11.0"
|
||||
nix = "0.26.2"
|
||||
once_cell = "1.18.0"
|
||||
prost = "0.11.9"
|
||||
|
@ -14,12 +16,14 @@ reqwest = { version = "0.11.18", features = ["blocking", "rustls-tls", "json"],
|
|||
serde = { version = "1.0.173", features = ["derive"] }
|
||||
serde_json = "1.0.103"
|
||||
sysinfo = { version = "0.29.2", default-features = false }
|
||||
tempfile = "3.6.0"
|
||||
tokio = { version = "1.28.2", features = ["rt-multi-thread", "io-util", "process", "macros", "signal"] }
|
||||
tokio-stream = { version = "0.1.14", features = ["net"] }
|
||||
tokio-stream = { version = "0.1.14", features = ["net", "sync"] }
|
||||
tonic = { version = "0.9.2", features = ["tls"] }
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = "0.3.17"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.6.0"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = "0.9.2"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
fn main() {
|
||||
tonic_build::configure()
|
||||
.build_client(true)
|
||||
.build_client(false)
|
||||
.compile(&["../proto/agent.proto"], &["../proto"])
|
||||
.unwrap();
|
||||
}
|
||||
|
|
|
@ -7,19 +7,13 @@ fn main() -> anyhow::Result<()> {
|
|||
|
||||
let command = clap::Command::new(env!("CARGO_BIN_NAME"))
|
||||
.version(env!("CARGO_PKG_VERSION"))
|
||||
.arg(arg!(-d --daemon "Run agent as daemon").exclusive(true))
|
||||
.arg(arg!(--install <TOKEN> "Install this agent binary to the system").exclusive(true))
|
||||
.arg_required_else_help(true)
|
||||
.try_get_matches()
|
||||
.unwrap_or_else(|e| e.exit());
|
||||
|
||||
if command.get_flag("daemon") {
|
||||
tracing::info!("starting agent");
|
||||
server::main()
|
||||
} else if let Some(token) = command.get_one::<String>("install") {
|
||||
tracing::info!("starting installation...");
|
||||
if let Some(token) = command.get_one::<String>("install") {
|
||||
self_update::install(token).context("failed to install the agent to the system")
|
||||
} else {
|
||||
unreachable!()
|
||||
server::run()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod config;
|
||||
pub mod self_update;
|
||||
pub mod server;
|
||||
pub mod system;
|
||||
|
|
|
@ -51,7 +51,7 @@ Description=Prymn Agent Service
|
|||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart={PRYMN_PATH} -d
|
||||
ExecStart={PRYMN_PATH}
|
||||
Type=simple
|
||||
Restart=always
|
||||
|
||||
|
|
117
agent/src/server/agent.rs
Normal file
117
agent/src/server/agent.rs
Normal file
|
@ -0,0 +1,117 @@
|
|||
use std::pin::Pin;
|
||||
|
||||
use tokio_stream::{
|
||||
wrappers::{ReceiverStream, WatchStream},
|
||||
Stream, StreamExt,
|
||||
};
|
||||
use tonic::{Request, Response, Status};
|
||||
|
||||
use crate::system::health::HealthMonitor;
|
||||
|
||||
use super::proto::*;
|
||||
|
||||
type AgentResult<T> = std::result::Result<Response<T>, Status>;
|
||||
|
||||
pub struct AgentService {
|
||||
health: HealthMonitor,
|
||||
}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl agent_server::Agent for AgentService {
|
||||
type HealthStream = Pin<Box<dyn Stream<Item = Result<HealthResponse, Status>> + Send>>;
|
||||
|
||||
async fn health(&self, _: Request<()>) -> AgentResult<Self::HealthStream> {
|
||||
let receiver = self.health.monitor();
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
|
||||
let output = WatchStream::new(receiver).map(|health| {
|
||||
Ok(HealthResponse {
|
||||
version: version.to_owned(),
|
||||
system: Some(health.system().into()),
|
||||
tasks: health
|
||||
.tasks()
|
||||
.into_iter()
|
||||
.map(|(key, val)| (key, TaskHealth::from(val)))
|
||||
.collect(),
|
||||
})
|
||||
});
|
||||
|
||||
Ok(Response::new(Box::pin(output)))
|
||||
}
|
||||
|
||||
async fn get_sys_info(&self, _: Request<()>) -> AgentResult<SysInfoResponse> {
|
||||
use sysinfo::{CpuExt, DiskExt, SystemExt};
|
||||
|
||||
let mut sys = crate::system::SYSTEM.lock().unwrap();
|
||||
|
||||
sys.refresh_specifics(
|
||||
sysinfo::RefreshKind::new()
|
||||
.with_disks()
|
||||
.with_memory()
|
||||
.with_processes(sysinfo::ProcessRefreshKind::everything())
|
||||
.with_cpu(sysinfo::CpuRefreshKind::everything()),
|
||||
);
|
||||
|
||||
let cpus = sys
|
||||
.cpus()
|
||||
.iter()
|
||||
.map(|cpu| sys_info_response::Cpu {
|
||||
freq_mhz: cpu.frequency(),
|
||||
usage: cpu.cpu_usage(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let disks = sys
|
||||
.disks()
|
||||
.iter()
|
||||
.map(|disk| sys_info_response::Disk {
|
||||
name: disk.name().to_string_lossy().into_owned(),
|
||||
total_bytes: disk.total_space(),
|
||||
avail_bytes: disk.available_space(),
|
||||
mount_point: disk.mount_point().to_string_lossy().into_owned(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let response = Response::new(SysInfoResponse {
|
||||
uptime: sys.uptime(),
|
||||
hostname: sys.host_name().unwrap_or_default(),
|
||||
os: sys.long_os_version().unwrap_or_default(),
|
||||
mem_total_bytes: sys.total_memory(),
|
||||
mem_avail_bytes: sys.available_memory(),
|
||||
swap_total_bytes: sys.total_swap(),
|
||||
swap_free_bytes: sys.free_swap(),
|
||||
cpus,
|
||||
disks,
|
||||
});
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
type ExecStream = Pin<Box<dyn Stream<Item = Result<ExecResponse, Status>> + Send>>;
|
||||
|
||||
async fn exec(&self, req: Request<ExecRequest>) -> AgentResult<Self::ExecStream> {
|
||||
use crate::system::exec::*;
|
||||
|
||||
let ExecRequest { program, args } = req.get_ref();
|
||||
|
||||
match exec(program, args) {
|
||||
Ok(receiver) => {
|
||||
let stream = ReceiverStream::new(receiver).map(|_inner| {
|
||||
Ok(ExecResponse {
|
||||
// TODO
|
||||
response: None,
|
||||
})
|
||||
});
|
||||
|
||||
Ok(Response::new(Box::pin(stream)))
|
||||
}
|
||||
Err(err) => Err(Status::failed_precondition(err.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server(health_monitor: HealthMonitor) -> agent_server::AgentServer<AgentService> {
|
||||
agent_server::AgentServer::new(AgentService {
|
||||
health: health_monitor,
|
||||
})
|
||||
}
|
|
@ -1,184 +1,69 @@
|
|||
mod exec;
|
||||
mod rpc;
|
||||
use std::time::Duration;
|
||||
|
||||
use std::{
|
||||
pin::Pin,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use tokio::{signal, sync::oneshot, time::sleep};
|
||||
|
||||
use anyhow::Context;
|
||||
use sysinfo::{CpuExt, DiskExt, System, SystemExt};
|
||||
use tokio::{net::TcpListener, signal, sync::oneshot};
|
||||
use tokio_stream::{
|
||||
wrappers::{ReceiverStream, TcpListenerStream},
|
||||
Stream, StreamExt,
|
||||
};
|
||||
use tonic::{transport::server::Router, Request, Response, Status};
|
||||
use crate::system::health::HealthMonitor;
|
||||
|
||||
struct Server {
|
||||
sys: Arc<Mutex<System>>,
|
||||
}
|
||||
mod agent;
|
||||
mod proto {
|
||||
tonic::include_proto!("prymn");
|
||||
|
||||
type Result<T> = std::result::Result<T, tonic::Status>;
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl rpc::agent_server::Agent for Server {
|
||||
#[tracing::instrument(skip(self))]
|
||||
async fn echo(&self, req: Request<rpc::EchoRequest>) -> Result<Response<rpc::EchoResponse>> {
|
||||
Ok(Response::new(rpc::EchoResponse {
|
||||
message: req.into_inner().message,
|
||||
}))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
async fn get_sys_info(&self, _: Request<()>) -> Result<Response<rpc::SysInfoResponse>> {
|
||||
let mut sys = self.sys.lock().unwrap();
|
||||
|
||||
sys.refresh_specifics(
|
||||
sysinfo::RefreshKind::new()
|
||||
.with_disks()
|
||||
.with_memory()
|
||||
.with_processes(sysinfo::ProcessRefreshKind::everything())
|
||||
.with_cpu(sysinfo::CpuRefreshKind::everything()),
|
||||
);
|
||||
|
||||
let cpus = sys
|
||||
.cpus()
|
||||
.iter()
|
||||
.map(|cpu| rpc::sys_info_response::Cpu {
|
||||
freq_mhz: cpu.frequency(),
|
||||
usage: cpu.cpu_usage(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let disks = sys
|
||||
.disks()
|
||||
.iter()
|
||||
.map(|disk| rpc::sys_info_response::Disk {
|
||||
name: disk.name().to_string_lossy().into_owned(),
|
||||
total_bytes: disk.total_space(),
|
||||
avail_bytes: disk.available_space(),
|
||||
mount_point: disk.mount_point().to_string_lossy().into_owned(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let response = Response::new(rpc::SysInfoResponse {
|
||||
uptime: sys.uptime(),
|
||||
hostname: sys.host_name().unwrap_or_default(),
|
||||
os: sys.long_os_version().unwrap_or_default(),
|
||||
mem_total_bytes: sys.total_memory(),
|
||||
mem_avail_bytes: sys.available_memory(),
|
||||
swap_total_bytes: sys.total_swap(),
|
||||
swap_free_bytes: sys.free_swap(),
|
||||
cpus,
|
||||
disks,
|
||||
});
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
type ExecStream = Pin<Box<dyn Stream<Item = Result<rpc::ExecResponse>> + Send + Sync>>;
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
async fn exec(&self, req: Request<rpc::ExecRequest>) -> Result<Response<Self::ExecStream>> {
|
||||
use exec::*;
|
||||
|
||||
let rpc::ExecRequest { program, args } = req.into_inner();
|
||||
|
||||
match exec(&program, &args) {
|
||||
Ok(receiver) => {
|
||||
let stream = ReceiverStream::new(receiver).map(|inner| {
|
||||
Ok(rpc::ExecResponse {
|
||||
response: Some(inner.into()),
|
||||
})
|
||||
});
|
||||
|
||||
Ok(Response::new(Box::pin(stream)))
|
||||
impl From<crate::system::health::SystemHealth> for SystemHealth {
|
||||
fn from(val: crate::system::health::SystemHealth) -> Self {
|
||||
if let crate::system::health::SystemStatus::Critical(ref reasons) = val.status {
|
||||
SystemHealth {
|
||||
status: itertools::join(reasons.iter().map(ToString::to_string), ","),
|
||||
}
|
||||
} else {
|
||||
SystemHealth {
|
||||
status: val.status.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::system::health::TaskHealth> for TaskHealth {
|
||||
fn from(val: crate::system::health::TaskHealth) -> Self {
|
||||
TaskHealth {
|
||||
status: val.status().to_string(),
|
||||
message: val.message().to_owned(),
|
||||
started_on: val.started_on().to_string(),
|
||||
progress: val.progress() as i32,
|
||||
}
|
||||
Err(err) => Err(Status::failed_precondition(err.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the server. This is the main entry point of the application.
|
||||
#[tokio::main]
|
||||
pub async fn main() -> anyhow::Result<()> {
|
||||
let address = "0.0.0.0:50012";
|
||||
|
||||
let incoming = {
|
||||
let listener = TcpListener::bind(address)
|
||||
.await
|
||||
.with_context(|| format!("can't bind deamon to address {address}"))?;
|
||||
|
||||
TcpListenerStream::new(listener)
|
||||
};
|
||||
|
||||
pub async fn run() -> anyhow::Result<()> {
|
||||
let (shutdown_tx, shutdown_rx) = oneshot::channel();
|
||||
|
||||
// Listen for shutdown signals
|
||||
tokio::spawn(async {
|
||||
signal::ctrl_c()
|
||||
.await
|
||||
.expect("could not listen to shutdown signals");
|
||||
.expect("failed to listen to a ctrl-c signal");
|
||||
|
||||
shutdown_tx.send(()).expect("bug: channel closed");
|
||||
let _ = shutdown_tx.send(());
|
||||
});
|
||||
|
||||
new_server()
|
||||
.serve_with_incoming_shutdown(incoming, async {
|
||||
let health_monitor = HealthMonitor::new();
|
||||
let agent_service = agent::server(health_monitor.clone());
|
||||
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
health_monitor.check_system().await;
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
});
|
||||
|
||||
tonic::transport::Server::builder()
|
||||
.add_service(agent_service)
|
||||
.serve_with_shutdown("[::]:50012".parse()?, async {
|
||||
let _ = shutdown_rx.await;
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new_server() -> Router {
|
||||
let server = Server {
|
||||
sys: Arc::new(Mutex::new(System::new_all())),
|
||||
};
|
||||
|
||||
let service = rpc::agent_server::AgentServer::new(server);
|
||||
|
||||
tonic::transport::Server::builder()
|
||||
.trace_fn(|_| tracing::info_span!("agent_server"))
|
||||
.add_service(service)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_stream::wrappers::TcpListenerStream;
|
||||
|
||||
use super::*;
|
||||
|
||||
async fn spawn_server() -> SocketAddr {
|
||||
let listener = TcpListener::bind("[::]:0").await.unwrap();
|
||||
let address = listener.local_addr().unwrap();
|
||||
|
||||
let listener = TcpListenerStream::new(listener);
|
||||
|
||||
tokio::spawn(async move { new_server().serve_with_incoming(listener).await.unwrap() });
|
||||
|
||||
address
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn echo_works() {
|
||||
let addr = spawn_server().await;
|
||||
|
||||
let mut client =
|
||||
rpc::agent_client::AgentClient::connect(format!("http://[::]:{}", addr.port()))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let message = "Hello!".to_owned();
|
||||
let response = client
|
||||
.echo(rpc::EchoRequest {
|
||||
message: message.clone(),
|
||||
})
|
||||
.await
|
||||
.expect("to respond");
|
||||
|
||||
assert_eq!(rpc::EchoResponse { message }, response.into_inner());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
tonic::include_proto!("prymn");
|
|
@ -9,10 +9,8 @@ use tokio::{
|
|||
sync::mpsc,
|
||||
};
|
||||
|
||||
use super::rpc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) enum ExecOutput {
|
||||
pub enum ExecOutput {
|
||||
Output {
|
||||
stdout: Option<String>,
|
||||
stderr: Option<String>,
|
||||
|
@ -21,7 +19,7 @@ pub(super) enum ExecOutput {
|
|||
Error(String),
|
||||
}
|
||||
|
||||
pub(super) fn exec<P, A>(program: P, args: &[A]) -> anyhow::Result<mpsc::Receiver<ExecOutput>>
|
||||
pub fn exec<P, A>(program: P, args: &[A]) -> anyhow::Result<mpsc::Receiver<ExecOutput>>
|
||||
where
|
||||
P: AsRef<OsStr>,
|
||||
A: AsRef<OsStr>,
|
||||
|
@ -76,19 +74,6 @@ async fn run_process(mut command: Child, sender: mpsc::Sender<ExecOutput>) {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ExecOutput> for rpc::exec_response::Response {
|
||||
fn from(value: ExecOutput) -> Self {
|
||||
match value {
|
||||
ExecOutput::Output { stdout, stderr } => Self::Output(rpc::exec_response::Output {
|
||||
stdout: stdout.unwrap_or_default(),
|
||||
stderr: stderr.unwrap_or_default(),
|
||||
}),
|
||||
ExecOutput::Exit(code) => Self::ExitCode(code.code().unwrap_or_default()),
|
||||
ExecOutput::Error(err) => Self::Error(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -104,15 +89,33 @@ mod tests {
|
|||
|
||||
assert_eq!(outputs.len(), 4);
|
||||
|
||||
let ExecOutput::Output { ref stdout, ref stderr } = outputs[0] else { panic!() };
|
||||
let ExecOutput::Output {
|
||||
ref stdout,
|
||||
ref stderr,
|
||||
} = outputs[0]
|
||||
else {
|
||||
panic!()
|
||||
};
|
||||
assert_eq!(*stdout, Some("1".to_owned()));
|
||||
assert_eq!(*stderr, None);
|
||||
|
||||
let ExecOutput::Output { ref stdout, ref stderr } = outputs[1] else { panic!() };
|
||||
let ExecOutput::Output {
|
||||
ref stdout,
|
||||
ref stderr,
|
||||
} = outputs[1]
|
||||
else {
|
||||
panic!()
|
||||
};
|
||||
assert_eq!(*stdout, Some("2".to_owned()));
|
||||
assert_eq!(*stderr, None);
|
||||
|
||||
let ExecOutput::Output { ref stdout, ref stderr } = outputs[2] else { panic!() };
|
||||
let ExecOutput::Output {
|
||||
ref stdout,
|
||||
ref stderr,
|
||||
} = outputs[2]
|
||||
else {
|
||||
panic!()
|
||||
};
|
||||
assert_eq!(*stdout, Some("3".to_owned()));
|
||||
assert_eq!(*stderr, None);
|
||||
}
|
299
agent/src/system/health.rs
Normal file
299
agent/src/system/health.rs
Normal file
|
@ -0,0 +1,299 @@
|
|||
//! System health module
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use tokio::sync::watch;
|
||||
|
||||
use super::SYSTEM;
|
||||
|
||||
const MEMORY_USAGE_CRITICAL_THRESHOLD: u64 = 90;
|
||||
const CPU_USAGE_CRITICAL_THRESHOLD: u64 = 90;
|
||||
const DISK_USAGE_CRITICAL_THRESHOLD: u64 = 90;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum CriticalReason {
|
||||
HighMemoryUsage,
|
||||
HighCpuUsage,
|
||||
HighDiskUsage,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, PartialEq)]
|
||||
pub enum SystemStatus {
|
||||
#[default]
|
||||
Normal,
|
||||
OutOfDate,
|
||||
Updating,
|
||||
Critical(Vec<CriticalReason>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SystemHealth {
|
||||
pub status: SystemStatus,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum TaskStatus {
|
||||
Normal,
|
||||
Warning,
|
||||
Error,
|
||||
Completed,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TaskHealth {
|
||||
status: TaskStatus,
|
||||
started_on: DateTime<Utc>,
|
||||
message: String,
|
||||
progress: u8,
|
||||
}
|
||||
|
||||
impl TaskHealth {
|
||||
pub fn new(message: String) -> Self {
|
||||
let started_on = chrono::Utc::now();
|
||||
|
||||
Self {
|
||||
status: TaskStatus::Normal,
|
||||
started_on,
|
||||
message,
|
||||
progress: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_normal(&mut self, message: String) {
|
||||
self.status = TaskStatus::Normal;
|
||||
self.message = message;
|
||||
}
|
||||
|
||||
pub fn set_warning(&mut self, message: String) {
|
||||
self.status = TaskStatus::Warning;
|
||||
self.message = message;
|
||||
}
|
||||
|
||||
pub fn set_error(&mut self, message: String) {
|
||||
self.status = TaskStatus::Error;
|
||||
self.message = message;
|
||||
}
|
||||
|
||||
pub fn set_completed(mut self, message: String) {
|
||||
self.status = TaskStatus::Completed;
|
||||
self.progress = 100;
|
||||
self.message = message;
|
||||
}
|
||||
|
||||
pub fn set_progress(&mut self, message: String, progress: u8) {
|
||||
self.progress = progress;
|
||||
self.message = message;
|
||||
}
|
||||
|
||||
pub fn status(&self) -> &TaskStatus {
|
||||
&self.status
|
||||
}
|
||||
|
||||
pub fn started_on(&self) -> &DateTime<Utc> {
|
||||
&self.started_on
|
||||
}
|
||||
|
||||
pub fn message(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
|
||||
pub fn progress(&self) -> u8 {
|
||||
self.progress
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Health {
|
||||
system: SystemHealth,
|
||||
tasks: HashMap<String, TaskHealth>,
|
||||
}
|
||||
|
||||
impl Health {
|
||||
pub fn system(&self) -> SystemHealth {
|
||||
self.system.clone()
|
||||
}
|
||||
|
||||
pub fn tasks(self) -> HashMap<String, TaskHealth> {
|
||||
self.tasks
|
||||
}
|
||||
}
|
||||
|
||||
/// `HealthMonitor` gives access to shared system health state, allowing to watch health and update
|
||||
/// task health status.
|
||||
///
|
||||
/// # Usage
|
||||
/// Internally `HealthMonitor` uses [Arc] so it can be cheaply cloned and shared.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use prymn_agent::system::health::{HealthMonitor, TaskHealth};
|
||||
///
|
||||
/// let health_monitor = HealthMonitor::new();
|
||||
/// let health_monitor_clone = health_monitor.clone();
|
||||
/// tokio::spawn(async move {
|
||||
/// loop {
|
||||
/// health_monitor_clone.check_system().await;
|
||||
/// }
|
||||
/// });
|
||||
/// tokio::spawn(async move {
|
||||
/// health_monitor.set_task_health("some_task".to_string(), TaskHealth::new(None)).await;
|
||||
/// });
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct HealthMonitor {
|
||||
sender: Arc<watch::Sender<Health>>,
|
||||
receiver: watch::Receiver<Health>,
|
||||
}
|
||||
|
||||
impl HealthMonitor {
|
||||
pub fn new() -> Self {
|
||||
let (sender, receiver) = watch::channel(Health::default());
|
||||
Self {
|
||||
sender: Arc::new(sender),
|
||||
receiver,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove async from here (so it can be consistent)
|
||||
// Move system checking task into it's own thing
|
||||
pub async fn check_system(&self) {
|
||||
use sysinfo::{CpuExt, DiskExt, SystemExt};
|
||||
|
||||
let status = tokio::task::spawn_blocking(|| {
|
||||
let mut status = SystemStatus::Normal;
|
||||
|
||||
// TODO: For testability, dependency inject this System struct in this function.
|
||||
let mut sys = SYSTEM.lock().unwrap();
|
||||
|
||||
// Refresh system resources usage
|
||||
sys.refresh_specifics(
|
||||
sysinfo::RefreshKind::new()
|
||||
.with_memory()
|
||||
.with_disks()
|
||||
.with_cpu(sysinfo::CpuRefreshKind::new().with_cpu_usage()),
|
||||
);
|
||||
|
||||
let mut statuses = vec![];
|
||||
|
||||
// Check for critical memory usage
|
||||
let memory_usage = sys.used_memory() * 100 / sys.total_memory();
|
||||
if memory_usage > MEMORY_USAGE_CRITICAL_THRESHOLD {
|
||||
statuses.push(CriticalReason::HighMemoryUsage);
|
||||
}
|
||||
|
||||
// Check for critical CPU usage
|
||||
let cpu_usage = sys.global_cpu_info().cpu_usage();
|
||||
if cpu_usage > CPU_USAGE_CRITICAL_THRESHOLD as f32 {
|
||||
statuses.push(CriticalReason::HighCpuUsage);
|
||||
}
|
||||
|
||||
// Check for any disk usage that is critical
|
||||
for disk in sys.disks() {
|
||||
let available_disk = disk.available_space() * 100 / disk.total_space();
|
||||
if available_disk < 100 - DISK_USAGE_CRITICAL_THRESHOLD {
|
||||
statuses.push(CriticalReason::HighDiskUsage);
|
||||
}
|
||||
}
|
||||
|
||||
if !statuses.is_empty() {
|
||||
status = SystemStatus::Critical(statuses);
|
||||
}
|
||||
|
||||
status
|
||||
})
|
||||
.await
|
||||
.expect("system checking task panicked - possibly due to panicked mutex lock");
|
||||
|
||||
self.sender.send_if_modified(|Health { system, .. }| {
|
||||
if system.status == status {
|
||||
return false;
|
||||
}
|
||||
|
||||
system.status = status;
|
||||
true
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_task_health(&self, task_name: String, health: TaskHealth) {
|
||||
// Always send a notification in this case since it is an explicit action.
|
||||
self.sender.send_modify(|Health { tasks, .. }| {
|
||||
tasks.insert(task_name, health);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn clear_task(&self, task_name: &str) {
|
||||
self.sender
|
||||
.send_if_modified(|Health { tasks, .. }| tasks.remove(task_name).is_some());
|
||||
}
|
||||
|
||||
pub fn monitor(&self) -> watch::Receiver<Health> {
|
||||
self.receiver.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for HealthMonitor {
|
||||
fn default() -> Self {
|
||||
HealthMonitor::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SystemStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SystemStatus::Normal => write!(f, "normal"),
|
||||
SystemStatus::OutOfDate => write!(f, "out of date"),
|
||||
SystemStatus::Updating => write!(f, "updating"),
|
||||
SystemStatus::Critical(_) => write!(f, "critical"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CriticalReason {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
CriticalReason::HighMemoryUsage => write!(f, "high memory usage"),
|
||||
CriticalReason::HighCpuUsage => write!(f, "high cpu usage"),
|
||||
CriticalReason::HighDiskUsage => write!(f, "high disk usage"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TaskStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TaskStatus::Normal => write!(f, "normal"),
|
||||
TaskStatus::Warning => write!(f, "warning"),
|
||||
TaskStatus::Error => write!(f, "error"),
|
||||
TaskStatus::Completed => write!(f, "completed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_task_monitor() {
|
||||
let health_monitor = HealthMonitor::new();
|
||||
let receiver = health_monitor.monitor();
|
||||
|
||||
assert!(receiver.has_changed().is_ok_and(|changed| !changed));
|
||||
|
||||
let health = TaskHealth::new("this is normal".to_owned());
|
||||
health_monitor.set_task_health("some_task".to_string(), health);
|
||||
|
||||
assert!(receiver.has_changed().is_ok_and(|changed| changed));
|
||||
|
||||
{
|
||||
let health = receiver.borrow();
|
||||
let task_health = health.tasks.get("some_task").expect("a task should exist");
|
||||
|
||||
assert_eq!(task_health.status, TaskStatus::Normal);
|
||||
assert_eq!(task_health.progress, 0);
|
||||
assert_eq!(task_health.message, "this is normal");
|
||||
}
|
||||
|
||||
health_monitor.clear_task("some_task");
|
||||
assert!(!receiver.borrow().tasks.contains_key("some_task"));
|
||||
}
|
||||
}
|
12
agent/src/system/mod.rs
Normal file
12
agent/src/system/mod.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
//! System boundary and modules that interact with the operating system and programs.
|
||||
|
||||
use std::sync::Mutex;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use sysinfo::SystemExt;
|
||||
|
||||
pub mod exec;
|
||||
pub mod health;
|
||||
|
||||
// TODO: Make this mock-able so we can test code that interacts with it
|
||||
pub static SYSTEM: Lazy<Mutex<sysinfo::System>> = Lazy::new(|| Mutex::new(sysinfo::System::new()));
|
|
@ -4,12 +4,22 @@ import "google/protobuf/empty.proto";
|
|||
|
||||
package prymn;
|
||||
|
||||
message EchoRequest {
|
||||
string message = 1;
|
||||
message SystemHealth {
|
||||
// Comma-separated statuses
|
||||
string status = 1;
|
||||
}
|
||||
|
||||
message EchoResponse {
|
||||
string message = 1;
|
||||
message TaskHealth {
|
||||
string status = 1;
|
||||
string message = 2;
|
||||
string started_on = 3;
|
||||
int32 progress = 4;
|
||||
}
|
||||
|
||||
message HealthResponse {
|
||||
string version = 1;
|
||||
SystemHealth system = 2;
|
||||
map<string, TaskHealth> tasks = 3;
|
||||
}
|
||||
|
||||
message SysInfoResponse {
|
||||
|
@ -55,7 +65,7 @@ message ExecResponse {
|
|||
}
|
||||
|
||||
service Agent {
|
||||
rpc Echo(EchoRequest) returns (EchoResponse);
|
||||
rpc Health(google.protobuf.Empty) returns (stream HealthResponse);
|
||||
rpc GetSysInfo(google.protobuf.Empty) returns (SysInfoResponse);
|
||||
rpc Exec(ExecRequest) returns (stream ExecResponse);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue