diff --git a/Cargo.lock b/Cargo.lock index 6ba4b84..31624f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[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 = "atty" version = "0.2.14" @@ -19,12 +34,48 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + [[package]] name = "cc" version = "1.0.79" @@ -37,6 +88,22 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "serde", + "time", + "wasm-bindgen", + "winapi", +] + [[package]] name = "clap" version = "4.1.4" @@ -81,11 +148,27 @@ dependencies = [ "clap", "colored", "nvml-wrapper", + "podman-api", "pretty-bytes", + "rand", + "reqwest", + "serde", + "serde_json", "sysinfo", + "tokio", "users", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "colored" version = "2.0.0" @@ -97,12 +180,55 @@ dependencies = [ "winapi", ] +[[package]] +name = "containers-api" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56082e32f18a6d60f06c736b49e234deffb93b13cb87091a39e0dec053d03819" +dependencies = [ + "chrono", + "flate2", + "futures-util", + "http", + "hyper", + "hyperlocal", + "log", + "mime", + "paste", + "pin-project 1.0.12", + "serde", + "serde_json", + "tar", + "thiserror", + "tokio", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-channel" version = "0.5.6" @@ -146,6 +272,50 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "cxx" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90d59d9acd2a682b4e40605a242f6670eaa58c5957471cbf85e8aa6a0b97a5e8" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebfa40bda659dd5c864e65f4c9a2b0aff19bea56b017b9b77c73d3766a453a38" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457ce6757c5c70dc6ecdbda6925b958aae7f959bda7d8fb9bde889e34a09dc03" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebf883b7aacd7b2aeb2a7b338648ee19f57c140d4ee8e52c68979c6b2f7f2263" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "darling" version = "0.10.2" @@ -187,6 +357,15 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + [[package]] name = "errno" version = "0.2.8" @@ -208,6 +387,37 @@ dependencies = [ "libc", ] +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "filetime" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.45.0", +] + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -215,265 +425,1065 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "getopts" -version = "0.2.21" +name = "foreign-types" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "unicode-width", + "foreign-types-shared", ] [[package]] -name = "heck" -version = "0.4.1" +name = "foreign-types-shared" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "form_urlencoded" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "libc", + "percent-encoding", ] [[package]] -name = "hermit-abi" -version = "0.2.6" +name = "futures" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" dependencies = [ - "libc", + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] -name = "hermit-abi" -version = "0.3.0" +name = "futures-channel" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +dependencies = [ + "futures-core", + "futures-sink", +] [[package]] -name = "ident_case" -version = "1.0.1" +name = "futures-core" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] -name = "io-lifetimes" -version = "1.0.5" +name = "futures-executor" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" dependencies = [ - "libc", - "windows-sys", + "futures-core", + "futures-task", + "futures-util", ] [[package]] -name = "is-terminal" -version = "0.4.3" +name = "futures-io" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" + +[[package]] +name = "futures-macro" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ - "hermit-abi 0.3.0", - "io-lifetimes", - "rustix", - "windows-sys", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "futures-sink" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] -name = "libc" -version = "0.2.139" +name = "futures-task" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] -name = "libloading" -version = "0.7.4" +name = "futures-util" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ - "cfg-if", - "winapi", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", ] [[package]] -name = "linux-raw-sys" -version = "0.1.4" +name = "futures_codec" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "ce54d63f8b0c75023ed920d46fd71d0cbbb830b0ee012726b5b4f506fb6dea5b" +dependencies = [ + "bytes 0.5.6", + "futures", + "memchr", + "pin-project 0.4.30", +] [[package]] -name = "log" -version = "0.4.17" +name = "getopts" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" dependencies = [ - "cfg-if", + "unicode-width", ] [[package]] -name = "memoffset" -version = "0.7.1" +name = "getrandom" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ - "autocfg", + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] -name = "ntapi" -version = "0.4.0" +name = "h2" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" dependencies = [ - "winapi", + "bytes 1.4.0", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", ] [[package]] -name = "num_cpus" -version = "1.15.0" +name = "hashbrown" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] -name = "nvml-wrapper" -version = "0.9.0" +name = "heck" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd21b9f5a1cce3c3515c9ffa85f5c7443e07162dae0ccf4339bb7ca38ad3454" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ - "bitflags", - "libloading", - "nvml-wrapper-sys", - "static_assertions", - "thiserror", - "wrapcenum-derive", + "libc", ] [[package]] -name = "nvml-wrapper-sys" -version = "0.7.0" +name = "hermit-abi" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c961a2ea9e91c59a69b78e69090f6f5b867bb46c0c56de9482da232437c4987e" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ - "libloading", + "libc", ] [[package]] -name = "once_cell" -version = "1.17.0" +name = "hermit-abi" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" [[package]] -name = "os_str_bytes" -version = "6.4.1" +name = "hex" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "pretty-bytes" -version = "0.2.2" +name = "http" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "009d6edd2c1dbf2e1c0cd48a2f7766e03498d49ada7109a01c6911815c685316" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ - "atty", - "getopts", + "bytes 1.4.0", + "fnv", + "itoa", ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "http-body" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", + "bytes 1.4.0", + "http", + "pin-project-lite", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "httparse" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +dependencies = [ + "bytes 1.4.0", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes 1.4.0", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "hyperlocal" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" +dependencies = [ + "futures-util", + "hex", + "hyper", + "pin-project 1.0.12", + "tokio", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "ipnet" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + +[[package]] +name = "is-terminal" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +dependencies = [ + "hermit-abi 0.3.0", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.42.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ntapi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "nvml-wrapper" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd21b9f5a1cce3c3515c9ffa85f5c7443e07162dae0ccf4339bb7ca38ad3454" +dependencies = [ + "bitflags", + "libloading", + "nvml-wrapper-sys", + "static_assertions", + "thiserror", + "wrapcenum-derive", +] + +[[package]] +name = "nvml-wrapper-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c961a2ea9e91c59a69b78e69090f6f5b867bb46c0c56de9482da232437c4987e" +dependencies = [ + "libloading", +] + +[[package]] +name = "once_cell" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" + +[[package]] +name = "openssl" +version = "0.10.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "paste" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" +dependencies = [ + "pin-project-internal 0.4.30", +] + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal 1.0.12", +] + +[[package]] +name = "pin-project-internal" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "podman-api" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b8a428a3381f5e1fcfa1fb94092017f193c75f2d7959bc799ee82dadf0f72d" +dependencies = [ + "base64 0.13.1", + "byteorder", + "bytes 1.4.0", + "chrono", + "containers-api", + "flate2", + "futures-util", + "futures_codec", + "log", + "paste", + "podman-api-stubs", + "serde", + "serde_json", + "tar", + "thiserror", + "tokio", + "url", +] + +[[package]] +name = "podman-api-stubs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9a63943c0c6e69efcd252ad61f4914270b7e3fec170e6a04ffdb2fc8dcd6a9" +dependencies = [ + "chrono", + "serde", + "serde_json", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "pretty-bytes" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "009d6edd2c1dbf2e1c0cd48a2f7766e03498d49ada7109a01c6911815c685316" +dependencies = [ + "atty", + "getopts", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ "proc-macro2", "quote", "version_check", ] [[package]] -name = "proc-macro2" -version = "1.0.50" +name = "proc-macro2" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +dependencies = [ + "base64 0.21.0", + "bytes 1.4.0", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustix" +version = "0.36.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "ryu" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "unicode-ident", + "windows-sys 0.42.0", ] [[package]] -name = "quote" -version = "1.0.23" +name = "scopeguard" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" + +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", + "quote", + "syn", ] [[package]] -name = "rayon" -version = "1.6.1" +name = "serde_json" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" dependencies = [ - "either", - "rayon-core", + "itoa", + "ryu", + "serde", ] [[package]] -name = "rayon-core" -version = "1.10.2" +name = "serde_urlencoded" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", + "form_urlencoded", + "itoa", + "ryu", + "serde", ] [[package]] -name = "rustix" -version = "0.36.8" +name = "signal-hook-registry" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ - "bitflags", - "errno", - "io-lifetimes", "libc", - "linux-raw-sys", - "windows-sys", ] [[package]] -name = "scopeguard" -version = "1.1.0" +name = "slab" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] [[package]] name = "static_assertions" @@ -519,6 +1529,31 @@ dependencies = [ "winapi", ] +[[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -548,18 +1583,157 @@ dependencies = [ "syn", ] +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +dependencies = [ + "autocfg", + "bytes 1.4.0", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.42.0", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes 1.4.0", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "unicode-bidi" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" + [[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "users" version = "0.11.0" @@ -570,12 +1744,116 @@ dependencies = [ "log", ] +[[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.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -607,6 +1885,21 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -673,6 +1966,15 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "wrapcenum-derive" version = "0.4.0" @@ -684,3 +1986,12 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] diff --git a/Cargo.toml b/Cargo.toml index 284e0af..6fd51e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,17 @@ nvml-wrapper = {version = "0.9.0", features = ["legacy-functions"]} pretty-bytes = "0.2.2" sysinfo = "0.27.7" users = "0.11.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +podman-api = "0.8" +tokio = {version = "1.25.0", features=["full"]} +rand = "0.8.5" +reqwest = { version = "0.11.14", features = ["blocking"] } + +[[bin]] +name = "clobber" +path = "src/clobber.rs" + +[[bin]] +name = "clobberd" +path = "src/clobberd.rs" diff --git a/res/clobberd.service b/res/clobberd.service new file mode 100644 index 0000000..655635e --- /dev/null +++ b/res/clobberd.service @@ -0,0 +1,9 @@ +[Unit] +Description=Clobberd (@wilnil, @ethanf108). Beats u up if u use GPU when its not ur turn :) + +[Service] +Type=simple +ExecStart=/usr/local/sbin/clobberd + +[Install] +WantedBy=default.target diff --git a/res/install.sh b/res/install.sh index 07838d4..11b82d4 100755 --- a/res/install.sh +++ b/res/install.sh @@ -3,5 +3,6 @@ set -e cargo build --release -sudo install target/release/clobber /usr/local/sbin; echo "Installed clobber." +sudo install target/release/clobber /usr/local/sbin && chmod u+s /usr/local/sbin/clobber && echo "Installed clobber." +sudo install target/release/clobberd /usr/local/sbin && echo "Installed clobberd." sudo cp res/clobber.sh /etc/profile.d; echo "Added profile.d script." diff --git a/src/clobber.rs b/src/clobber.rs new file mode 100644 index 0000000..6725c85 --- /dev/null +++ b/src/clobber.rs @@ -0,0 +1,227 @@ +use clap::{Parser, Subcommand}; +use colored::Colorize; +use pod::*; +use std::os::unix::net::UnixStream; +use users::get_user_by_uid; + +mod comms; +mod pod; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + #[command(subcommand)] + command: Option, +} + +#[derive(Subcommand, Debug)] +enum Commands { + Status, + Kill, + Watch { + #[arg(short, long, required = true)] + device: u8, + }, + Unwatch { + #[arg(short, long, required = true)] + device: u8, + }, + Queue { + #[arg(short, long, required = true)] + image: String, + #[arg(short, long, required = true)] + gpus: Vec, + }, + Jobs { + #[arg(short, long)] + active: bool, + }, +} + +#[link(name = "c")] +extern "C" { + fn getuid() -> u32; +} + +fn print_response(response: comms::Response) { + use comms::Response::*; + match response { + Success => {} + Error(e) => eprintln!("Error: {}", e.red()), + GPUStatus { locks } => { + for (index, gpu) in locks.iter().enumerate() { + if let Some(user) = gpu { + println!( + "{} {} {} {}", + "GPU".yellow(), + index.to_string().yellow().bold(), + "is being used by".red(), + user.name.yellow().bold() + ); + } else { + println!( + "{} {} {}", + "GPU".yellow(), + index.to_string().yellow().bold(), + "is not being used".green() + ); + } + } + } + ActiveJobs(jobs) => { + for job in jobs { + println!( + "{} {} {} {} {}", + "Job".yellow(), + job.id.to_string().yellow().bold(), + format!("({})", job.owner.name).green().bold(), + "is using GPU(s)".yellow(), + job.requested_gpus + .iter() + .map(|g| g.to_string()) + .reduce(|a, b| { + let mut str = a.to_owned(); + str.push_str(&b.to_owned()); + str.push_str(", "); + str + }) + .unwrap_or("NONE".into()) + .green() + ); + } + } + MyJobs(jobs) => { + for (index, job) in jobs { + println!( + "{} {} {} {}", + "Job".yellow(), + job.id.to_string().yellow().bold(), + "is in queue position".yellow(), + (index + 1).to_string().yellow().bold() + ); + } + } + } +} + +fn send_command(command: comms::Command) { + use std::io::{Read, Write}; + let mut sock = match UnixStream::connect("/run/clobberd.sock") { + Ok(s) => s, + Err(e) => { + eprintln!("Error connecting to socket. Is clobberd running?\n{}", e); + return; + } + }; + + let json = match serde_json::to_string(&command) { + Ok(j) => j, + Err(e) => { + eprintln!("Error serializing to JSON? somehow: {}", e); + return; + } + }; + + if let Err(e) = sock.write(json.as_bytes()) { + eprintln!("Error writing to socket: {}", e); + } + + sock.flush().unwrap(); + sock.shutdown(std::net::Shutdown::Write).unwrap(); + + let mut buf = String::with_capacity(10 * 1024); + match sock.read_to_string(&mut buf) { + Ok(_size) => match serde_json::from_str::(&buf) { + Ok(response) => print_response(response), + Err(e) => eprintln!("Error parsing response: {}", e), + }, + Err(e) => { + eprintln!("Error reading from socket: {}", e); + } + } +} + +async fn find_image(uid: u32, image: String) -> Result, String> { + let pod = Pod::new(uid); + if let Err(e) = pod.ping().await { + eprintln!( + "Error connecting to podman: {}\n\nMaybe try {}", + e, + "systemctl enable --user --now podman.socket".green().bold() + ); + return Err("".to_string()); + } + pod.image_exists(image.clone()) + .await + .map(|e| if e { Some(image) } else { None }) + .map_err(|e| e.to_string()) +} + +#[tokio::main] +async fn main() { + let args = Args::parse(); + + let (uid, is_root) = unsafe { (getuid(), getuid() == 0) }; + + let username = match get_user_by_uid(uid) { + Some(user) => user.name().to_string_lossy().to_string(), + None => { + eprintln!("Error obtaining username."); + return; + } + }; + + use Commands::*; + + let command = args.command.unwrap_or(Status); + + match command { + Status => send_command(comms::Command::Status), + Kill => { + if !is_root { + eprintln!("Permission denied."); + return; + } + send_command(comms::Command::Kill); + } + Watch { device } => { + if !is_root { + eprintln!("Permission denied."); + return; + } + send_command(comms::Command::SetWatch { + device_number: device as u32, + watching: true, + }); + } + Unwatch { device } => { + if !is_root { + eprintln!("Permission denied."); + return; + } + send_command(comms::Command::SetWatch { + device_number: device as u32, + watching: false, + }); + } + Queue { image, gpus } => match find_image(uid, image).await { + Ok(Some(image)) => send_command(comms::Command::QueueJob { + user: comms::User { + uid: uid as usize, + name: username.to_string(), + }, + image_id: image, + gpus: gpus, + }), + Ok(None) => eprintln!("Cannot find image"), + Err(e) => eprintln!("Error finding image: {}", e), + }, + Jobs { active } => { + if active { + send_command(comms::Command::ActiveJobs); + } else { + send_command(comms::Command::MyJobs { uid: uid }); + } + } + } +} diff --git a/src/clobberd.rs b/src/clobberd.rs new file mode 100644 index 0000000..1163b89 --- /dev/null +++ b/src/clobberd.rs @@ -0,0 +1,373 @@ +use comms::ClobberConfig; +use comms::Response::*; +use comms::{Command, Response, User}; +use gpu::GPU; +use pings::Ping::*; +use pings::*; +use pod::*; +use rand; +use std::collections::VecDeque; +use std::io::prelude::*; +use std::os::unix::net::UnixListener; +use std::path::Path; +use std::thread; +use std::time::Duration; +use tokio::*; + +mod comms; +mod gpu; +mod pings; +mod pod; + +struct DeviceState { + device_number: u32, + watching: bool, //false if there should be no scheduling on this GPU +} + +#[derive(Clone)] +struct Job { + id: u16, + owner: User, + image_id: String, + requested_gpus: Vec, + container: Option, +} + +impl PartialEq for Job { + fn eq(&self, other: &Job) -> bool { + self.id == other.id + } +} + +struct SharedState { + devices: Vec, + queued_jobs: VecDeque, + active_jobs: Vec, + config: ClobberConfig, +} + +impl SharedState { + fn device_owner(&self, device_number: u32) -> Option<&Job> { + self.active_jobs + .iter() + .find(|job| job.requested_gpus.contains(&device_number)) + } +} + +fn sock_communicate(shared_state: &mut SharedState, command: Command) -> Response { + use comms::Command::*; + match command { + Status => GPUStatus { + locks: shared_state + .devices + .iter() + .map(|d| d.device_number) + .map(|d| shared_state.device_owner(d)) + .map(|j| j.map(|j| j.owner.clone())) + .collect(), + }, + Kill => { + println!("Received Kill command. Exiting."); + std::process::exit(0); + } + SetWatch { + device_number, + watching, + } => { + println!( + "Set watching status of GPU {} to {}", + device_number, watching + ); + for device in &mut shared_state.devices { + if device.device_number == device_number { + device.watching = watching; + return Success; + } + } + println!("No device with device number {}", device_number); + Error("Invalid device number".to_string()) + } + QueueJob { + user, + image_id, + gpus, + } => { + if gpus.len() == 0 { + return Error("Must request at least one GPU".to_string()); + } + for gpu in gpus.clone() { + if shared_state + .devices + .iter() + .map(|d| d.device_number) + .find(|n| n == &gpu) + .is_none() + { + return Error(format!("GPU {} not found", gpu)); + } + } + let job = Job { + id: rand::random(), + owner: user, + image_id: image_id, + requested_gpus: gpus, + container: None, + }; + shared_state.queued_jobs.push_back(job.clone()); + println!( + "Queued job {} from uid {} ({:?}) from image {}", + job.id, job.owner.uid, job.owner.name, job.image_id + ); + Success + } + ActiveJobs => comms::Response::ActiveJobs( + shared_state + .active_jobs + .iter() + .map(|j| comms::JobDesc { + id: j.id, + owner: j.owner.clone(), + requested_gpus: j.requested_gpus.clone(), + }) + .collect(), + ), + MyJobs { uid } => comms::Response::MyJobs( + shared_state + .queued_jobs + .iter() + .enumerate() + .filter(|(_, j)| j.owner.uid == uid as usize) + .map(|(i, j)| { + ( + i as u32, + comms::JobDesc { + id: j.id, + owner: j.owner.clone(), + requested_gpus: j.requested_gpus.clone(), + }, + ) + }) + .collect(), + ), + } +} + +fn bind(path: impl AsRef) -> std::io::Result { + let path = path.as_ref(); + let _ = std::fs::remove_file(path); + UnixListener::bind(path) +} + +fn pop_first_qualified_job(shared_state: &mut SharedState) -> Option { + let current_gpus = shared_state + .active_jobs + .iter() + .map(|j| j.requested_gpus.clone()) + .flatten() + .collect::>(); + let mut job_index = 0; + let mut found_job = false; + for (index, job) in shared_state.queued_jobs.iter().enumerate() { + if job.requested_gpus.iter().all(|g| !current_gpus.contains(g)) { + job_index = index; + found_job = true; + break; + } + } + if found_job { + shared_state.queued_jobs.remove(job_index) + } else { + None + } +} + +async fn remove_finished_jobs(shared_state: &mut SharedState) -> Vec { + let mut remove: Vec = vec![]; + for job in &shared_state.active_jobs { + let pod = Pod::new(job.owner.uid as u32); + match pod + .container_finished(job.container.as_ref().unwrap()) + .await + { + Ok(finished) => { + if finished { + println!("Container finished for Job {}", job.id); + remove.push(job.clone()); + } + } + Err(e) => { + eprintln!("Error determining container state: {}", e); + remove.push(job.clone()); + } + } + } + shared_state.active_jobs.retain(|j| !remove.contains(&j)); + for job in &remove { + send_ping( + shared_state.config.pings_api_key.clone(), + job.owner.clone(), + JobFinished { id: job.id }, + ); + } + remove +} + +async fn start_job<'a>( + shared_state: &'a mut SharedState, + pod: &Pod, + job: &mut Job, +) -> Result<&'a Job, String> { + let container_id = match pod.create_container(job.image_id.clone()).await { + Ok(id) => id, + Err(e) => return Err(e), + }; + job.container = Some(container_id); + shared_state.active_jobs.push(job.clone()); + Ok(shared_state.active_jobs.last().unwrap()) +} + +async fn try_start_job(shared_state: &mut SharedState) { + if let Some(mut job) = pop_first_qualified_job(shared_state) { + let pod = Pod::new(job.owner.uid as u32); + match pod.image_exists(&job.image_id).await { + Ok(exists) => { + let pings_api_key = shared_state.config.pings_api_key.clone(); + if exists { + match start_job(shared_state, &pod, &mut job).await { + Ok(job) => { + println!("Starting Job {}", job.id); + send_ping(pings_api_key, job.owner.clone(), JobStarted { id: job.id }); + } + Err(e) => { + eprintln!("Error starting container for image {}: {}", job.image_id, e); + } + } + } else { + println!("Image {} for uid {} not found", job.image_id, job.owner.uid); + send_ping( + pings_api_key, + job.owner, + JobCancelled { + id: job.id, + reason: format!("Cannot find image {}", job.image_id), + }, + ); + } + } + Err(e) => { + eprintln!( + "Error finding image {} for uid {}: {}", + job.image_id, job.owner.uid, e + ); + send_ping( + shared_state.config.pings_api_key.clone(), + job.owner, + JobCancelled { + id: job.id, + reason: format!("Error finding image {}: {}", job.image_id, e), + }, + ); + } + } + } +} + +fn kill_rogue_gpu_processes(gpu: &mut GPU, shared_state: &mut SharedState) { + for device in &shared_state.devices { + if !device.watching { + continue; + } + if let Some(current_job) = shared_state.device_owner(device.device_number) { + let processes = gpu.get_processes(device.device_number).unwrap_or(vec![]); + for p in processes.iter().filter(|p| p.user.uid != 0) { + if p.user.uid != current_job.owner.uid { + gpu.kill_process(p.pid); + println!( + "Killed process on GPU {}: pid: {}, uid: {} ({})", + device.device_number, p.pid, p.user.uid, p.user.name + ); + } + } + } + } +} + +fn accept_socket(sock: &UnixListener, shared_state: &mut SharedState) { + match sock.accept() { + Ok((mut socket, addr)) => { + socket + .set_read_timeout(Some(Duration::from_millis(1000))) + .unwrap(); + socket + .set_write_timeout(Some(Duration::from_millis(1000))) + .unwrap(); + println!("Accepted connection from {:?}", addr); + let mut buf = String::with_capacity(1024); + match socket.read_to_string(&mut buf) { + Ok(_size) => { + let result = match serde_json::from_str::(&buf) { + Ok(command) => sock_communicate(shared_state, command), + Err(e) => { + let msg = format!("Error parsing command: {:?}", e); + eprintln!("{}", msg); + Error(msg) + } + }; + if let Err(e) = socket.write( + serde_json::to_string(&result) + .unwrap_or_else(|e| { + eprintln!("Error serializing result: {}", e); + "".to_string() + }) + .as_bytes(), + ) { + eprintln!("Error sending response: {}", e); + } + } + Err(e) => match e.kind() { + io::ErrorKind::WouldBlock => eprintln!("Timeout while reading from socket"), + _ => eprintln!("Error reading from socket: {:?}", e), + }, + } + } + Err(e) => match e.kind() { + io::ErrorKind::WouldBlock => {} + _ => eprintln!("Error accepting socket connection: {:?}", e), + }, + } +} + +#[tokio::main] +async fn main() { + let config = comms::get_config().unwrap(); + + let mut gpu = GPU::new(); + + let server_sock = bind("/run/clobberd.sock").unwrap(); + println!("Started socket"); + if let Err(e) = server_sock.set_nonblocking(true) { + panic!("Error setting socket to non-blocking: {}", e); + } + + let mut shared_state = SharedState { + devices: (0..gpu.device_count()) + .map(|n| DeviceState { + device_number: n, + watching: true, + }) + .collect(), + queued_jobs: VecDeque::from([]), + active_jobs: vec![], + config: config, + }; + + println!("Started Server"); + + loop { + accept_socket(&server_sock, &mut shared_state); + kill_rogue_gpu_processes(&mut gpu, &mut shared_state); + remove_finished_jobs(&mut shared_state).await; + try_start_job(&mut shared_state).await; + thread::sleep(time::Duration::from_millis(250)); + } +} diff --git a/src/comms.rs b/src/comms.rs new file mode 100644 index 0000000..9a39968 --- /dev/null +++ b/src/comms.rs @@ -0,0 +1,61 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone)] +pub struct ClobberConfig { + pub pings_api_key: String, +} + +#[derive(Serialize, Deserialize)] +pub enum Command { + Status, + Kill, + SetWatch { + device_number: u32, + watching: bool, + }, + QueueJob { + user: User, + image_id: String, + gpus: Vec, + }, + ActiveJobs, + MyJobs { + uid: u32, + }, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct JobDesc { + pub id: u16, + pub owner: User, + pub requested_gpus: Vec, +} + +#[derive(Serialize, Deserialize)] +pub enum Response { + Success, + Error(String), + GPUStatus { locks: Vec> }, + ActiveJobs(Vec), + MyJobs(Vec<(u32, JobDesc)>), //(position in queue, job) +} + +#[derive(Clone, Eq, Serialize, Deserialize)] +pub struct User { + pub name: String, + pub uid: usize, +} + +impl PartialEq for User { + fn eq(&self, other: &User) -> bool { + self.uid == other.uid + } +} + +pub fn get_config() -> Result { + let file = match std::fs::read_to_string("/etc/clobber/config.json") { + Ok(f) => f, + Err(e) => return Err(e.to_string()), + }; + serde_json::from_str::(&file).map_err(|e| e.to_string()) +} diff --git a/src/gpu.rs b/src/gpu.rs new file mode 100644 index 0000000..405ab0e --- /dev/null +++ b/src/gpu.rs @@ -0,0 +1,68 @@ +use crate::comms::User; +use nvml_wrapper::{error::NvmlError, Nvml}; +use sysinfo::{Pid, ProcessExt, ProcessRefreshKind, Signal, System, SystemExt}; +use users::get_user_by_uid; + +pub struct GPUprocess { + pub name: String, + pub pid: usize, + pub device_number: usize, + pub user: User, +} + +pub struct GPU { + nvml: Nvml, + system: System, +} + +impl GPU { + pub fn new() -> GPU { + GPU { + nvml: Nvml::init().unwrap(), + system: System::new_all(), + } + } + + pub fn device_count(&self) -> u32 { + self.nvml.device_count().unwrap() + } + + pub fn kill_process(&self, pid: usize) -> bool { + self.system + .process(Pid::from(pid)) + .map(|process| process.kill_with(Signal::Term)) + .is_some() + } + + pub fn get_processes(&mut self, device_index: u32) -> Result, NvmlError> { + self.system + .refresh_processes_specifics(ProcessRefreshKind::everything()); + self.system.refresh_users_list(); + let mut gpu_processes = vec![]; + let device = self.nvml.device_by_index(device_index).unwrap(); + let nvml_processes = device.running_compute_processes_v2().unwrap(); + for proc in nvml_processes { + if let Some(process) = self.system.process(Pid::from(proc.pid as usize)) { + gpu_processes.push(GPUprocess { + name: process.name().to_string(), + pid: proc.pid as usize, + device_number: device_index as usize, + user: match process.user_id() { + Some(user_id) => { + let user = get_user_by_uid(**user_id).unwrap(); + User { + uid: **user_id as usize, + name: user.name().to_string_lossy().to_string(), + } + } + None => User { + uid: 0, + name: "Unknown".to_string(), + }, + }, + }); + } + } + Ok(gpu_processes) + } +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index c50ae8c..0000000 --- a/src/main.rs +++ /dev/null @@ -1,215 +0,0 @@ -use std::{collections::{HashMap, HashSet}, process::{Command, Stdio}}; -use nvml_wrapper::{Nvml,error::NvmlError}; -use users::get_user_by_uid; -use sysinfo::{Pid, ProcessExt, System, SystemExt, Signal}; -use colored::Colorize; -use clap::Parser; - -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -struct Args { - #[clap(long, short, action)] - summary: bool, - #[clap(long, short, action)] - bug_users: bool, - #[clap(long, short, action)] - term_offenders: bool, - #[clap(long, short, action)] - kill_offenders: bool, -} - -pub struct GPUprocess { - name: String, - pid: usize, - start_time: u64, - device_number: usize, - uid: usize, - user: String -} - -fn main() -> Result<(), NvmlError> { - let args = Args::parse(); - let nvml = Nvml::init()?; - let mut s = System::new_all(); - - let running_gpu_processes = get_processes(&nvml, &mut s)?; - if args.summary { - print_banner_summary(&nvml, &running_gpu_processes); - } else { - print_usage(&running_gpu_processes); - } - - print_warnings(&running_gpu_processes, args.bug_users); - - if args.term_offenders { - end_offenders(&mut s, &running_gpu_processes, Signal::Term); - } else if args.kill_offenders { - end_offenders(&mut s, &running_gpu_processes, Signal::Kill); - } - Ok(()) -} - -// Use the `write` command to annoy a user with a message -// FIXME: Get rid of `expect`s -fn write_to_user(user: String, message: String) { - let echo_child = Command::new("echo") - .arg(message) - .stdout(Stdio::piped()) - .spawn() - .expect("Failed to start echo process while writing to user"); - - let echo_out = echo_child.stdout.expect("Failed to open echo stdout"); - - let write_child = Command::new("write") - .arg(user) - .stdin(Stdio::from(echo_out)) - .spawn() - .expect("Failed to start wall process while writing to user"); - - let _output = write_child.wait_with_output(); -} - -// Query NVML for the processes running on all GPUs and build a list of them -fn get_processes(nvml: &Nvml, system: &mut System) -> Result, NvmlError>{ - let nvml_device_count = nvml.device_count().unwrap(); - system.refresh_users_list(); - let mut gpu_processes = vec![]; - for device_number in 0..nvml_device_count { - let device = nvml.device_by_index(device_number).unwrap(); - let nvml_processes = device.running_compute_processes_v2().unwrap(); - for proc in nvml_processes { - if let Some(process) = system.process(Pid::from(proc.pid as usize)) { - let mut gpu_process = GPUprocess { - name: process.name().to_string(), - pid: proc.pid as usize, - start_time: process.start_time(), - device_number: device_number as usize, - uid: 0, - user: "Unknown".to_string() - }; - - // Sometimes, it's not a sure bet that a UID will be found. So we have to handle - // that. - if let Some(user_id) = process.user_id() { - let user = get_user_by_uid(**user_id).unwrap(); - - gpu_process.uid = **user_id as usize; - gpu_process.user = user.name().to_string_lossy().to_string(); - } - gpu_processes.push(gpu_process); - } - } - } - Ok(gpu_processes) -} - -fn end_offenders(system: &mut System, processes: &Vec, signal: Signal) { - let mut gpus = HashMap::new(); - for e in processes { - gpus.entry(e.device_number).or_insert(vec!()).push(e); - } - - for (_gpu, processes) in gpus { - let oldest = processes.iter().min_by_key(|p| p.start_time).unwrap().start_time; - for process in processes { - if process.start_time > oldest { - kill_process(system, process.pid, signal); - } - } - } -} - -fn kill_process(system: &mut System, pid: usize, signal: Signal) -> bool { - println!("{} {}", signal.to_string().red().bold(), pid.to_string().red().bold()); - if let Some(process) = system.process(Pid::from(pid)) { - process.kill_with(signal); - } - true -} - -// Print number of devices (GPUs) installed in the system -fn print_device_count(nvml: &Nvml) { - let nvml_device_count = nvml.device_count().unwrap(); - println!("Found {} devices.", nvml_device_count); -} - -// Show a small banner, and print some more detailed info about what processes -// are running on the GPU and who owns them -fn print_banner_summary(nvml: &Nvml, processes: &Vec) { - println!("== CLOBBER =="); - print_device_count(&nvml); - for proc in processes { - println!( - "Found process \"{}\" ({}) on GPU {} started by {}!", - proc.name, proc.pid, proc.device_number, proc.user.red() - ); - } - - if processes.len() == 0 { - println!("{}", "There are no running GPU processes.".green()); - } -} - -// Print matter-of-fact information about who is using what GPU -fn print_usage(processes: &Vec) { - let mut users = HashMap::new(); - for e in processes { - users.entry(e.user.to_string()).or_insert(vec!()).push(&e.device_number); - } - - for (user, mut gpus) in users { - let set: HashSet<_> = gpus.drain(..).collect(); // dedup - gpus.extend(set.into_iter()); - - let mut gpu_string = format!("{:?}", gpus); - gpu_string = gpu_string.trim_start_matches('[').trim_end_matches(']').to_string(); - println!("{} {} {}", user.yellow().bold(), "is currently using GPUs".yellow(), gpu_string.yellow().bold()); - } - - if processes.len() == 0 { - println!("{}", "There are no running GPU processes.".green()); - } -} - -// Print a scary warning about colliding GPU processes, and optionally, use -// the `write` command to annoy the users directly -fn print_warnings(processes: &Vec, bug_users: bool) -> bool { - let mut warned = false; - // List of GPUs that have multiple processes running on them - // We can count on processes being sorted, since we go through the GPU IDs - // sequentially - let mut mult_proc = HashMap::new(); - for e in processes { - mult_proc.entry(e.device_number).or_insert(vec!()).push(&e.user); - } - - for (gpu_num, mut names) in mult_proc { - let mut write_message: String; - if names.len() > 1 { - write_message = format!( - "{} {}", - "WARNING! MULTIPLE PROCESSES DETECTED ON GPU".red().bold(), gpu_num.to_string().red().bold() - ); - - // Delete duplicate names in case someone has multiple processes running - let mut uniques = HashSet::new(); - names.retain(|e| uniques.insert(*e)); - - write_message += &format!("{}", "\nPLEASE CONTACT THE FOLLOWING USERS TO COORDINATE WORKLOADS:".red()).to_string(); - for user in &names { - write_message += &format!("\n- {}", user.red().bold()); - } - - println!("{}", write_message); - - if bug_users { - let names_string = &format!("{:?}", names).trim_start_matches('[').trim_end_matches(']').to_string(); - for user in &names { - write_to_user(user.to_string(), format!("WARNING! MULTIPLE PROCESSES DETECTED ON GPU {}\nPLEASE CONTACT THE FOLLOWING USERS TO COORDINATE WORKLOADS:\n{}", gpu_num.to_string(), names_string)); - } - } - warned = true; // If this does something, then don't run show_usage - } - } - warned -} diff --git a/src/pings.rs b/src/pings.rs new file mode 100644 index 0000000..2a6637f --- /dev/null +++ b/src/pings.rs @@ -0,0 +1,57 @@ +use crate::User; +use core::fmt; + +#[derive(Debug)] +pub enum Ping { + JobCancelled { id: u16, reason: String }, + JobFinished { id: u16 }, + JobStarted { id: u16 }, +} + +impl fmt::Display for Ping { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use crate::Ping::*; + match self { + JobCancelled { id, reason } => write!(f, "Job {} cancelled: {}", id, reason), + JobStarted { id } => write!(f, "Job {} started", id), + JobFinished { id } => write!(f, "Job {} finished", id), + } + } +} + +impl Ping { + //find a better way to do this + fn get_route_uuid(&self) -> &str { + use crate::Ping::*; + match self { + JobCancelled { .. } => "97f166a0-d9f9-4888-b8ad-b09656f19330", + JobStarted { .. } => "f810410a-0dcd-4271-8b75-b6e6ce5ead7e", + JobFinished { .. } => "aab2a0c6-001c-455a-956f-10c8b685fb87", + } + } +} + +pub fn send_ping(api_token: String, user: User, ping: Ping) { + println!("Sending ping to {}: {:?}", user.name, ping); + std::thread::spawn(move || { + let client = reqwest::blocking::Client::new(); + if let Err(e) = client + .post(format!( + "https://pings.csh.rit.edu/service/route/{}/ping", + ping.get_route_uuid() + )) + .header("Authorization", format!("Bearer {}", api_token)) + .header("Content-Type", "application/json") + .body(format!( + "{{\"username\":\"{}\",\"body\":\"{}\"}}", + user.name, ping + )) + .send() + { + eprintln!( + "Error sending ping to user {} ({}): {:?}", + user.uid, user.name, e + ); + } + }); +} diff --git a/src/pod.rs b/src/pod.rs new file mode 100644 index 0000000..569ba36 --- /dev/null +++ b/src/pod.rs @@ -0,0 +1,87 @@ +use podman_api::opts::ContainerCreateOpts; +use podman_api::{Id, Podman}; +use rand::{distributions::Alphanumeric, Rng}; + +pub struct Pod { + podman: Podman, +} + +impl Pod { + pub fn new(uid: u32) -> Pod { + Pod { + podman: Podman::unix(format!("/run/user/{}/podman/podman.sock", uid)), + } + } + + pub async fn ping(&self) -> Result<(), String> { + self.podman + .ping() + .await + .map(|_| ()) + .map_err(|e| e.to_string()) + } + + pub async fn image_exists(&self, image_id: impl Into) -> podman_api::Result { + self.podman.images().get(image_id).exists().await + } + + fn gen_container_name() -> String { + format!( + "clobber_{}", + rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(8) + .map(char::from) + .collect::() + ) + } + + pub async fn create_container(&self, image_id: String) -> Result { + let container_id = match self + .podman + .containers() + .create( + &ContainerCreateOpts::builder() + .image(image_id) + .name(Pod::gen_container_name()) + .remove(false) + .build(), + ) + .await + { + Ok(body) => body.id, + Err(e) => return Err(format!("Error creating container: {}", e)), + }; + let container = self.podman.containers().get(container_id.clone()); + container + .start(None) //Maybe change detach keys? not sure + .await + .map_err(|e| format!("Error starting container: {}", e)) + .map(|_| container_id) + } + + pub async fn container_exists(&self, container: Id) -> podman_api::Result { + self.podman.containers().get(container).exists().await + } + + pub async fn container_finished(&self, container: impl Into) -> Result { + let container_id = container.into(); + let ret = self + .podman + .containers() + .get(container_id.clone()) + .inspect() + .await + .map(|i| i.state.map(|s| s.status).flatten()); + match ret { + Ok(val) => match val { + Some(r) => Ok(r == "exited"), + None => Err("Could not determine state".into()), + }, + Err(e) => Err(format!( + "Error inspecting container {:?}: {}", + container_id, e + )), + } + } +}