diff --git a/.gitignore b/.gitignore index a13555c..31219eb 100644 --- a/.gitignore +++ b/.gitignore @@ -109,6 +109,7 @@ bindings/*/nydusd bindings/*/unionfs bindings/*/nydusd-bootstrap bindings/*/index.node +bindings/*/rapid_deamon .idea packages/cli/test/fixtures/project-scripts/apps/a/1 @@ -117,3 +118,4 @@ packages/downloader/test/nginx.conf # napi-rs generate wrong type ignore for tmp packages/binding/index.d.ts .DS_Store +package-lock.json diff --git a/Cargo.lock b/Cargo.lock index daf1829..f8171c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,6 +53,12 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dabe5a181f83789739c194cbe5a897dde195078fac08568d09221fd6137a7ba8" +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + [[package]] name = "arrayref" version = "0.3.6" @@ -86,8 +92,8 @@ dependencies = [ "flate2", "futures-core", "memchr", - "pin-project-lite 0.2.7", - "tokio 1.13.0", + "pin-project-lite 0.2.13", + "tokio 1.29.1", ] [[package]] @@ -134,7 +140,7 @@ dependencies = [ "parking", "polling", "slab", - "socket2 0.4.2", + "socket2 0.4.10", "waker-fn", "winapi 0.3.9", ] @@ -165,7 +171,7 @@ checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.81", ] [[package]] @@ -189,7 +195,7 @@ dependencies = [ "memchr", "num_cpus", "once_cell", - "pin-project-lite 0.2.7", + "pin-project-lite 0.2.13", "pin-utils", "slab", "wasm-bindgen-futures", @@ -203,13 +209,13 @@ checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" [[package]] name = "async-trait" -version = "0.1.52" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.48", ] [[package]] @@ -231,9 +237,64 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" +dependencies = [ + "async-trait", + "axum-core", + "bytes 1.1.0", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.1.0", + "hyper-util", + "itoa 1.0.10", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite 0.2.13", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio 1.29.1", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes 1.1.0", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite 0.2.13", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] [[package]] name = "backtrace" @@ -272,7 +333,7 @@ dependencies = [ "serde", "serde_json", "simple_logger 2.1.0", - "tokio 1.13.0", + "tokio 1.29.1", ] [[package]] @@ -396,6 +457,7 @@ dependencies = [ "libc", "num-integer", "num-traits", + "serde", "time 0.1.44", "winapi 0.3.9", ] @@ -534,7 +596,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" dependencies = [ "quote", - "syn", + "syn 1.0.81", ] [[package]] @@ -558,7 +620,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn", + "syn 1.0.81", ] [[package]] @@ -569,7 +631,7 @@ checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" dependencies = [ "darling_core", "quote", - "syn", + "syn 1.0.81", ] [[package]] @@ -590,9 +652,15 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.81", ] +[[package]] +name = "destructure_traitobject" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" + [[package]] name = "digest" version = "0.9.0" @@ -630,7 +698,7 @@ dependencies = [ "serde_json", "serde_with", "sha2", - "tokio 1.13.0", + "tokio 1.29.1", "tokio-tar", "tokio-util 0.6.9", "uuid", @@ -659,6 +727,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "event-listener" version = "2.5.1" @@ -689,7 +763,7 @@ checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.10", "winapi 0.3.9", ] @@ -838,7 +912,7 @@ dependencies = [ "futures-io", "memchr", "parking", - "pin-project-lite 0.2.7", + "pin-project-lite 0.2.13", "waker-fn", ] @@ -852,7 +926,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2", "quote", - "syn", + "syn 1.0.81", ] [[package]] @@ -887,7 +961,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.7", + "pin-project-lite 0.2.13", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -912,7 +986,7 @@ checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.10.0+wasi-snapshot-preview1", ] [[package]] @@ -964,7 +1038,7 @@ dependencies = [ "futures-timer", "no-std-compat", "nonzero_ext", - "parking_lot", + "parking_lot 0.11.2", "quanta", "rand", "smallvec", @@ -981,8 +1055,8 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", - "indexmap", + "http 0.2.5", + "indexmap 1.7.0", "slab", "tokio 0.2.25", "tokio-util 0.3.1", @@ -1001,14 +1075,33 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", - "indexmap", + "http 0.2.5", + "indexmap 1.7.0", "slab", - "tokio 1.13.0", + "tokio 1.29.1", "tokio-util 0.6.9", "tracing", ] +[[package]] +name = "h2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" +dependencies = [ + "bytes 1.1.0", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 1.0.0", + "indexmap 2.1.0", + "slab", + "tokio 1.29.1", + "tokio-util 0.7.8", + "tracing", +] + [[package]] name = "hashbrown" version = "0.8.2" @@ -1025,6 +1118,12 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1034,6 +1133,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hmac" version = "0.8.1" @@ -1044,6 +1149,20 @@ dependencies = [ "digest", ] +[[package]] +name = "homedir" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22074da8bba2ef26fc1737ae6c777b5baab5524c2dc403b5c6a76166766ccda5" +dependencies = [ + "cfg-if 1.0.0", + "nix 0.26.4", + "serde", + "widestring", + "windows-sys", + "wmi", +] + [[package]] name = "http" version = "0.2.5" @@ -1055,6 +1174,17 @@ dependencies = [ "itoa 0.4.8", ] +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes 1.1.0", + "fnv", + "itoa 1.0.10", +] + [[package]] name = "http-body" version = "0.3.1" @@ -1062,7 +1192,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" dependencies = [ "bytes 0.5.6", - "http", + "http 0.2.5", ] [[package]] @@ -1072,15 +1202,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" dependencies = [ "bytes 1.1.0", - "http", - "pin-project-lite 0.2.7", + "http 0.2.5", + "pin-project-lite 0.2.13", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes 1.1.0", + "http 1.0.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes 1.1.0", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "pin-project-lite 0.2.13", ] [[package]] name = "httparse" -version = "1.5.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -1111,7 +1264,7 @@ dependencies = [ "futures-core", "futures-util", "h2 0.2.7", - "http", + "http 0.2.5", "http-body 0.3.1", "httparse", "httpdate 0.3.2", @@ -1135,19 +1288,39 @@ dependencies = [ "futures-core", "futures-util", "h2 0.3.7", - "http", + "http 0.2.5", "http-body 0.4.4", "httparse", "httpdate 1.0.1", "itoa 0.4.8", - "pin-project-lite 0.2.7", - "socket2 0.4.2", - "tokio 1.13.0", + "pin-project-lite 0.2.13", + "socket2 0.4.10", + "tokio 1.29.1", "tower-service", "tracing", "want", ] +[[package]] +name = "hyper" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" +dependencies = [ + "bytes 1.1.0", + "futures-channel", + "futures-util", + "h2 0.4.2", + "http 1.0.0", + "http-body 1.0.0", + "httparse", + "httpdate 1.0.1", + "itoa 1.0.10", + "pin-project-lite 0.2.13", + "tokio 1.29.1", + "want", +] + [[package]] name = "hyper-rustls" version = "0.22.1" @@ -1160,7 +1333,7 @@ dependencies = [ "log", "rustls", "rustls-native-certs", - "tokio 1.13.0", + "tokio 1.29.1", "tokio-rustls", "webpki 0.21.4", ] @@ -1187,10 +1360,43 @@ dependencies = [ "bytes 1.1.0", "hyper 0.14.14", "native-tls", - "tokio 1.13.0", + "tokio 1.29.1", "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67" +dependencies = [ + "bytes 1.1.0", + "futures-channel", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.1.0", + "pin-project-lite 0.2.13", + "socket2 0.5.5", + "tokio 1.29.1", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "hyperlocal" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c" +dependencies = [ + "futures-util", + "hex", + "hyper 0.14.14", + "pin-project", + "tokio 1.29.1", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1218,6 +1424,16 @@ dependencies = [ "hashbrown 0.11.2", ] +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + [[package]] name = "instant" version = "0.1.12" @@ -1250,9 +1466,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jobserver" @@ -1299,9 +1515,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.107" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libgit2-sys" @@ -1337,25 +1553,64 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "lock_api" -version = "0.4.5" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.16" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" dependencies = [ - "cfg-if 1.0.0", + "serde", "value-bag", ] +[[package]] +name = "log-mdc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" + +[[package]] +name = "log4rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d36ca1786d9e79b8193a68d480a0907b612f109537115c6ff655a3a1967533fd" +dependencies = [ + "anyhow", + "arc-swap 1.6.0", + "chrono", + "derivative", + "fnv", + "humantime", + "libc", + "log", + "log-mdc", + "parking_lot 0.12.1", + "serde", + "serde-value", + "serde_json", + "serde_yaml", + "thiserror", + "thread-id", + "typemap-ors", + "winapi 0.3.9", +] + [[package]] name = "lz4-sys" version = "1.9.2" @@ -1372,6 +1627,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.4.1" @@ -1387,6 +1648,15 @@ dependencies = [ "autocfg", ] +[[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" @@ -1436,7 +1706,7 @@ dependencies = [ "kernel32-sys", "libc", "log", - "miow 0.2.2", + "miow", "net2", "slab", "winapi 0.2.8", @@ -1444,15 +1714,13 @@ dependencies = [ [[package]] name = "mio" -version = "0.7.14" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", - "log", - "miow 0.3.7", - "ntapi", - "winapi 0.3.9", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", ] [[package]] @@ -1467,15 +1735,6 @@ dependencies = [ "ws2_32-sys", ] -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "napi" version = "2.0.3" @@ -1485,8 +1744,8 @@ dependencies = [ "ctor", "lazy_static", "napi-sys", - "tokio 1.13.0", - "windows", + "tokio 1.29.1", + "windows 0.29.0", ] [[package]] @@ -1505,7 +1764,7 @@ dependencies = [ "napi-derive-backend", "proc-macro2", "quote", - "syn", + "syn 1.0.81", ] [[package]] @@ -1519,7 +1778,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn", + "syn 1.0.81", ] [[package]] @@ -1573,7 +1832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "987f12c91eb6ce0b67819f7c5fb4d391de64cf411c605ed027f03507a33943b2" dependencies = [ "quote", - "syn", + "syn 1.0.81", ] [[package]] @@ -1621,7 +1880,34 @@ dependencies = [ "cc", "cfg-if 1.0.0", "libc", - "memoffset", + "memoffset 0.6.4", +] + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags", + "cfg-if 1.0.0", + "libc", + "memoffset 0.6.4", + "pin-utils", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "libc", + "memoffset 0.7.1", + "pin-utils", ] [[package]] @@ -1639,15 +1925,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44a1290799eababa63ea60af0cbc3f03363e328e58f32fb0294798ed3e85f444" -[[package]] -name = "ntapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "num-integer" version = "0.1.44" @@ -1752,9 +2029,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.8.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" @@ -1795,6 +2072,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + [[package]] name = "parking" version = "2.0.0" @@ -1809,7 +2095,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core", + "parking_lot_core 0.8.5", +] + +[[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 0.9.9", ] [[package]] @@ -1821,11 +2117,24 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.10", "smallvec", "winapi 0.3.9", ] +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -1849,7 +2158,7 @@ checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.81", ] [[package]] @@ -1860,9 +2169,9 @@ checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] name = "pin-project-lite" -version = "0.2.7" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1909,11 +2218,11 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.32" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -1928,9 +2237,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.10" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1940,7 +2249,7 @@ name = "rafs" version = "0.1.0" dependencies = [ "anyhow", - "arc-swap", + "arc-swap 0.4.8", "base64", "bitflags", "blake3", @@ -2010,6 +2319,32 @@ dependencies = [ name = "rapid-rs" version = "0.1.0" +[[package]] +name = "rapid_deamon" +version = "0.1.0" +dependencies = [ + "anyhow", + "axum", + "chrono", + "homedir", + "http-body-util", + "httparse", + "hyper 0.14.14", + "hyper 1.1.0", + "hyper-util", + "hyperlocal", + "lazy_static", + "log", + "log4rs", + "mime", + "nix 0.25.1", + "regex", + "serde", + "serde_json", + "tokio 1.29.1", + "tower", +] + [[package]] name = "redox_syscall" version = "0.2.10" @@ -2019,6 +2354,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.5.4" @@ -2056,7 +2400,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "http", + "http 0.2.5", "http-body 0.3.1", "hyper 0.13.10", "hyper-tls 0.4.3", @@ -2068,7 +2412,7 @@ dependencies = [ "mime_guess", "native-tls", "percent-encoding", - "pin-project-lite 0.2.7", + "pin-project-lite 0.2.13", "serde", "serde_json", "serde_urlencoded", @@ -2092,7 +2436,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "http", + "http 0.2.5", "http-body 0.4.4", "hyper 0.14.14", "hyper-rustls", @@ -2104,12 +2448,12 @@ dependencies = [ "mime", "native-tls", "percent-encoding", - "pin-project-lite 0.2.7", + "pin-project-lite 0.2.13", "rustls", "serde", "serde_json", "serde_urlencoded", - "tokio 1.13.0", + "tokio 1.29.1", "tokio-native-tls", "tokio-rustls", "url", @@ -2168,9 +2512,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.5" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" @@ -2254,22 +2598,32 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.130" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + [[package]] name = "serde_derive" -version = "1.0.130" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.48", ] [[package]] @@ -2283,6 +2637,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" +dependencies = [ + "itoa 1.0.10", + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.0" @@ -2315,7 +2679,19 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 1.0.81", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap 1.7.0", + "ryu", + "serde", + "yaml-rust", ] [[package]] @@ -2404,14 +2780,24 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.2" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi 0.3.9", ] +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "spin" version = "0.5.2" @@ -2453,6 +2839,23 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "tar" version = "0.4.37" @@ -2473,7 +2876,7 @@ dependencies = [ "cfg-if 1.0.0", "libc", "rand", - "redox_syscall", + "redox_syscall 0.2.10", "remove_dir_all", "winapi 0.3.9", ] @@ -2513,7 +2916,17 @@ checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.81", +] + +[[package]] +name = "thread-id" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0ec81c46e9eb50deaa257be2f148adf052d1fb7701cfd55ccfab2525280b70b" +dependencies = [ + "libc", + "winapi 0.3.9", ] [[package]] @@ -2523,7 +2936,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", - "wasi", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi 0.3.9", ] @@ -2533,7 +2946,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" dependencies = [ - "itoa 1.0.1", + "itoa 1.0.10", "libc", "num_threads", "time-macros", @@ -2580,33 +2993,33 @@ dependencies = [ [[package]] name = "tokio" -version = "1.13.0" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "588b2d10a336da58d877567cd8fb8a14b463e2104910f8132cd054b4b96e29ee" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes 1.1.0", "libc", - "memchr", - "mio 0.7.14", + "mio 0.8.10", "num_cpus", - "once_cell", - "parking_lot", - "pin-project-lite 0.2.7", + "parking_lot 0.12.1", + "pin-project-lite 0.2.13", "signal-hook-registry", + "socket2 0.4.10", "tokio-macros", - "winapi 0.3.9", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "1.5.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114383b041aa6212c579467afa0075fbbdd0718de036100bc0ba7961d8cb9095" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.48", ] [[package]] @@ -2616,7 +3029,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" dependencies = [ "native-tls", - "tokio 1.13.0", + "tokio 1.29.1", ] [[package]] @@ -2626,7 +3039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" dependencies = [ "rustls", - "tokio 1.13.0", + "tokio 1.29.1", "webpki 0.21.4", ] @@ -2637,8 +3050,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" dependencies = [ "futures-core", - "pin-project-lite 0.2.7", - "tokio 1.13.0", + "pin-project-lite 0.2.13", + "tokio 1.29.1", ] [[package]] @@ -2649,9 +3062,9 @@ dependencies = [ "filetime", "futures-core", "libc", - "redox_syscall", + "redox_syscall 0.2.10", "tempfile", - "tokio 1.13.0", + "tokio 1.29.1", "tokio-stream", "xattr", ] @@ -2690,8 +3103,22 @@ dependencies = [ "futures-core", "futures-sink", "log", - "pin-project-lite 0.2.7", - "tokio 1.13.0", + "pin-project-lite 0.2.13", + "tokio 1.29.1", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes 1.1.0", + "futures-core", + "futures-sink", + "pin-project-lite 0.2.13", + "tokio 1.29.1", + "tracing", ] [[package]] @@ -2703,6 +3130,28 @@ dependencies = [ "serde", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite 0.2.13", + "tokio 1.29.1", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.1" @@ -2711,23 +3160,22 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.29" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if 1.0.0", "log", - "pin-project-lite 0.2.7", + "pin-project-lite 0.2.13", "tracing-core", ] [[package]] name = "tracing-core" -version = "0.1.21" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ - "lazy_static", + "once_cell", ] [[package]] @@ -2746,6 +3194,15 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "typemap-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867" +dependencies = [ + "unsafe-any-ors", +] + [[package]] name = "typenum" version = "1.14.0" @@ -2767,6 +3224,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + [[package]] name = "unicode-normalization" version = "0.1.19" @@ -2788,6 +3251,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "unsafe-any-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad" +dependencies = [ + "destructure_traitobject", +] + [[package]] name = "untrusted" version = "0.7.1" @@ -2817,13 +3289,9 @@ dependencies = [ [[package]] name = "value-bag" -version = "1.0.0-alpha.8" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f" -dependencies = [ - "ctor", - "version_check", -] +checksum = "7cdbaf5e132e593e9fc1de6a15bbec912395b11fb9719e061cf64f804524c503" [[package]] name = "vcpkg" @@ -2881,6 +3349,12 @@ 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.78" @@ -2904,7 +3378,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 1.0.81", "wasm-bindgen-shared", ] @@ -2938,7 +3412,7 @@ checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.81", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3006,6 +3480,12 @@ dependencies = [ "cc", ] +[[package]] +name = "widestring" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" + [[package]] name = "winapi" version = "0.2.8" @@ -3055,43 +3535,142 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aac7fef12f4b59cd0a29339406cc9203ab44e440ddff6b3f5a41455349fa9cf3" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.29.0", + "windows_i686_gnu 0.29.0", + "windows_i686_msvc 0.29.0", + "windows_x86_64_gnu 0.29.0", + "windows_x86_64_msvc 0.29.0", +] + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.81", ] +[[package]] +name = "windows-interface" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.81", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_msvc" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_i686_gnu" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8793f59f7b8e8b01eda1a652b2697d87b93097198ae85f823b969ca5b89bba58" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_msvc" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_x86_64_gnu" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3d615f419543e0bd7d2b3323af0d86ff19cbc4f816e6453f36a2c2ce889c354" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_msvc" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d95421d9ed3672c280884da53201a5c46b7b2765ca6faf34b0d71cf34a3561" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "winreg" version = "0.7.0" @@ -3101,6 +3680,20 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "wmi" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced703d10188571ce53582c2932ce640ed3c413cff7ee6e2d961f9abdb6a63d1" +dependencies = [ + "chrono", + "futures", + "log", + "serde", + "thiserror", + "windows 0.48.0", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -3120,6 +3713,15 @@ dependencies = [ "libc", ] +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "yansi" version = "0.5.0" diff --git a/Cargo.toml b/Cargo.toml index ecfce7a..3490236 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,5 +21,6 @@ members = [ "packages/fcntl", "packages/rafs", "packages/tokio-tar", - "packages/utils" + "packages/utils", + "packages/deamon" ] diff --git a/integration/fixtures/utils/pids.js b/integration/fixtures/utils/pids.js new file mode 100644 index 0000000..4e41408 --- /dev/null +++ b/integration/fixtures/utils/pids.js @@ -0,0 +1,57 @@ +'use strict'; +const execa = require('execa'); + +class Pids { + constructor(nodeModulesDir) { + this.nodeModulesDir = nodeModulesDir; + } + + async getPsSnapshot() { + try { + const { stdout } = await execa.command('ps aux'); + return stdout; + } catch (error) { + throw new Error(`Failed to execute 'ps aux': ${error.message}`, { cause: error }); + } + } + + async getPids() { + const pids = []; + + try { + const snapshot = await this.getPsSnapshot(); + console.log(snapshot); + if (process.platform === 'darwin') { + const overlayPattern = new RegExp(`unionfs.*?${this.nodeModulesDir}`, 'i'); + const nfsPattern = new RegExp( + `/usr/local/bin/go-nfsv4.*?${this.nodeModulesDir}`, 'i' + ); + for (const line of snapshot.split('\n')) { + if (overlayPattern.test(line)) { + const fields = line.split(/\s+/); + console.log(fields); + if (fields.length >= 11) { + const pid = parseInt(fields[1], 10) || 0; + pids.push(pid); + } + } + + if (nfsPattern.test(line)) { + const fields = line.split(/\s+/); + console.log(fields); + if (fields.length >= 11) { + const pid = parseInt(fields[1], 10) || 0; + pids.push(pid); + } + } + } + } + + return pids; + } catch (error) { + throw new Error(`Failed to get PIDs: ${error.message}`, { cause: error }); + } + } +} + +exports.Pids = Pids; diff --git a/integration/index.2.test.js b/integration/index.2.test.js index 50e9070..bdd2346 100644 --- a/integration/index.2.test.js +++ b/integration/index.2.test.js @@ -6,6 +6,7 @@ const assert = require('node:assert'); const coffee = require('coffee'); const semver = require('semver'); const execa = require('execa'); +const { setTimeout: setTimeoutPromise } = require('node:timers/promises'); const rapid = path.join(__dirname, '../node_modules/.bin/rapid'); const { clean, @@ -203,4 +204,57 @@ describe('test/index.v2.test.js', () => { assert(res.stdout.indexOf('integration/fixtures/esbuild/node_modules') === res.stdout.lastIndexOf('integration/fixtures/esbuild/node_modules')); }); + + describe('deamon', async () => { + it('should work', async () => { + cwd = path.join(__dirname, './fixtures/esbuild'); + await coffee + .fork(rapid, [ + 'install', + '--ignore-scripts', + ], { + cwd, + }) + .debug() + .expect('code', 0) + .end(); + + const dirs = await fs.readdir(path.join(cwd, 'node_modules')); + assert.strictEqual(dirs.filter(dir => dir.includes('esbuild')).length, 2); + await assert.doesNotReject(fs.stat(path.join(cwd, 'node_modules/esbuild'))); + assert.strictEqual(require(path.join(cwd, 'node_modules', 'esbuild/package.json')).version, '0.15.14'); + const nodeModulesDir = path.join(cwd, 'node_modules'); + + await execa.command(`umount -f ${nodeModulesDir}`); + await setTimeoutPromise(20000); + assert.strictEqual(require(path.join(cwd, 'node_modules', 'esbuild/package.json')).version, '0.15.14'); + }); + + it('should not work', async () => { + cwd = path.join(__dirname, './fixtures/esbuild'); + await coffee + .fork(rapid, [ + 'install', + '--ignore-scripts', + '--daemon=false', + ], { + cwd, + }) + .debug() + .expect('code', 0) + .end(); + + const dirs = await fs.readdir(path.join(cwd, 'node_modules')); + assert.strictEqual(dirs.filter(dir => dir.includes('esbuild')).length, 2); + await assert.doesNotReject(fs.stat(path.join(cwd, 'node_modules/esbuild'))); + assert.strictEqual(require(path.join(cwd, 'node_modules', 'esbuild/package.json')).version, '0.15.14'); + const nodeModulesDir = path.join(cwd, 'node_modules'); + + await execa.command(`umount -f ${nodeModulesDir}`); + await setTimeoutPromise(20000); + + await assert.rejects(fs.stat(path.join(cwd, 'node_modules', 'esbuild/package.json'))); + }); + }); + }); diff --git a/package.json b/package.json index 8c492a1..7428ec7 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "dl:nydusd": "bash scripts/download_nydusd.sh", "cp:binary": "node scripts/prepareBootstrap.js", "build": "npm run build:binary && npm run build:binding && npm run dl:nydusd && npm run cp:binary", - "build:binary": "cargo build --package nydus-rs --release", + "build:binary": "cargo build --package nydus-rs --release && cargo build --package rapid_deamon --release", "build:binding": "npm run build-release --workspace @cnpmjs/binding", "clean": "rm -rf mochawesome-reports coverage", "lint": "eslint integration/**/*.test.js packages/cli/bin packages/cli/lib packages/cli/test/**/*.test.js", @@ -29,7 +29,6 @@ "version": "lerna version --conventional-commits --sign-git-commit --sign-git-tag --no-push --no-private" }, "devDependencies": { - "lerna": "^7.1.4", "@eggjs/tsconfig": "^1.0.0", "@types/mocha": "^8.2.0", "@types/node": "^18.16.3", @@ -38,6 +37,7 @@ "eslint-config-egg": "^12.0.0", "espower-typescript": "^9.0.2", "intelli-espower-loader": "^1.0.1", + "lerna": "^7.1.4", "mm": "^2.2.0", "mocha": "^8.2.1", "nyc": "^15.1.0", diff --git a/packages/cli/bin/rapid.js b/packages/cli/bin/rapid.js index 135f831..0fedcf2 100755 --- a/packages/cli/bin/rapid.js +++ b/packages/cli/bin/rapid.js @@ -33,6 +33,11 @@ const argv = yargs type: 'array', default: [], }) + .option('daemon', { + describe: 'Will not run deamon', + type: 'boolean', + default: true, + }) .option('package-lock', { describe: 'Whether to generate package-lock.json file', type: 'boolean', @@ -43,6 +48,7 @@ const argv = yargs const ignoreScripts = argv['ignore-scripts']; const mode = argv.by || NpmFsMode.NPM; const productionMode = argv.production || argv.omit.includes('dev') || process.env.NODE_ENV === 'production'; + const daemon = argv.daemon; const noPackageLock = !argv['package-lock']; const cwd = process.cwd(); @@ -63,6 +69,7 @@ const argv = yargs nydusMode: NYDUS_TYPE.FUSE, ignoreScripts, productionMode, + daemon, noPackageLock, }); @@ -79,9 +86,18 @@ const argv = yargs command: 'clean [path]', aliases: [ 'c', 'unmount', 'uninstall' ], describe: 'Clean up the project', + builder: yargs => { + return yargs + .option('daemon', { + describe: 'Will run deamon', + type: 'boolean', + default: true, + }); + }, handler: async argv => { const cwd = argv.path || process.cwd(); - await clean({ nydusMode: NYDUS_TYPE.FUSE, cwd, force: true }); + const daemon = argv.daemon; + await clean({ nydusMode: NYDUS_TYPE.FUSE, cwd, force: true, daemon }); console.log('[rapid] clean finished'); }, }) diff --git a/packages/cli/lib/deamon.js b/packages/cli/lib/deamon.js new file mode 100644 index 0000000..29dfb30 --- /dev/null +++ b/packages/cli/lib/deamon.js @@ -0,0 +1,215 @@ +const urllib = require('urllib'); +const debug = require('node:util').debuglog('rapid:deamon'); +const AutoLaunch = require('auto-launch'); +const path = require('node:path'); +const fs = require('node:fs/promises'); +const { rsBindingPath } = require('@cnpmjs/binding'); +const execa = require('execa'); + +const { + baseRapidModeDir, + nydusd, + nydusdConfigFile, + nydusdMnt, + socketPath, + nydusdLogFile, +} = require('./constants'); + +const deamonDir = path.join(baseRapidModeDir(), 'project'); + +const metadataDir = path.join(deamonDir, 'metadata'); + +const deamonSocketPath = path.join(deamonDir, 'socket_path'); + +const rapidDeamon = rsBindingPath + ? path.join(rsBindingPath, 'rapid_deamon') + : undefined; + + +const destinationFilePath = path.join(deamonDir, 'rapid_deamon'); + +const daemonPoint = 'http://unix'; +const aliveUrl = `${daemonPoint}/alive`; +const killUrl = `${daemonPoint}/kill`; +const projectUrl = `${daemonPoint}/project`; + +const checkDeamonAlive = async () => { + try { + const result = await urllib.request(`${aliveUrl}`, { + method: 'GET', + socketPath: deamonSocketPath, + timeout: 1000, + }); + return result.status === 200; + } catch (error) { + debug('checkDeamonAlive error: ', error); + return false; + } +}; + +const delProject = async projectName => { + let config; + try { + await fs.stat(metadataDir); + } catch (error) { + debug('delProject error: ', error); + return false; + } + const configPath = path.join(metadataDir, `${projectName}.json`); + try { + const configBuffer = await fs.readFile(configPath); + config = JSON.parse(configBuffer.toString()); + } catch (error) { + debug('parse json error: ', error); + } + + try { + await fs.rm(`${configPath}`); + } catch (error) { + debug('rm json error: ', error); + return false; + } + + try { + const result = await urllib.request(`${projectUrl}`, { + method: 'DELETE', + data: { projectPath: config.projectPath }, + dataType: 'json', + contentType: 'json', + socketPath: deamonSocketPath, + }); + return result.status === 200 && result.data?.code === 0; + } catch (error) { + debug('delProject error: ', error); + return false; + } +}; + +const addProject = async config => { + try { + await fs.mkdir(metadataDir, { recursive: true }); + await fs.writeFile(path.join(metadataDir, `${config.projectName}.json`), JSON.stringify(config, null, 2)); + const result = await urllib.request(`${projectUrl}`, { + method: 'POST', + data: config, + dataType: 'json', + contentType: 'json', + socketPath: deamonSocketPath, + }); + return result.status === 200 && result.data?.code === 0; + } catch (error) { + debug('addProject error: ', error); + return false; + } +}; + + +const runDeamon = async () => { + const subprocess = execa(destinationFilePath, [], { + detached: true, + stdio: 'ignore', + }); + + subprocess.unref(); + + let count = 0; + + while (count < 10) { + const res = await checkDeamonAlive(); + if (res) { + return true; + } + count++; + } + + return false; +}; + + +const killDeamon = async () => { + try { + const result = await urllib.request(`${killUrl}`, { + method: 'GET', + socketPath: deamonSocketPath, + }); + return result.status === 200; + } catch (error) { + debug('killDeamon error: ', error); + return false; + } +}; + +const registerDeamon = async () => { + const nydusConfigPath = path.join(deamonDir, 'nydus_config.json'); + + await fs.writeFile(nydusConfigPath, JSON.stringify({ + nydusdBin: nydusd, + nydusdConfigFile, + nydusdMnt, + socketPath, + nydusdLogFile, + }, null, 2)); + + const logConfigPath = path.join(deamonDir, 'log4rs.yaml'); + + await fs.writeFile(logConfigPath, ` +refresh_rate: 86400 seconds + +appenders: + file: + kind: file + path: "${path.join(deamonDir, '/logs/rapid-deamon-output.log')}" + encoder: + pattern: "{d} - {l} - {m}{n}" + +root: + level: info + appenders: + - file + `); + await fs.copyFile(rapidDeamon, destinationFilePath); + + await fs.chmod(destinationFilePath, '777'); + + const deamonAutoLauncher = new AutoLaunch({ + name: 'rapid_deamon', + path: destinationFilePath, + mac: { + useLaunchAgent: true, + }, + }); + + deamonAutoLauncher.enable(); + + try { + const isEnabled = deamonAutoLauncher.isEnabled(); + if (isEnabled) return; + deamonAutoLauncher.enable(); + } catch (e) { + console.log(e); + } +}; + +const initDeamon = async () => { + const isRunning = await checkDeamonAlive(); + if (isRunning) { + console.info('[rapid] rapid daemon is running already.'); + return; + } + await fs.mkdir(deamonDir, { recursive: true }); + + await fs.mkdir(nydusdMnt, { recursive: true }); + + try { + await fs.stat(destinationFilePath); + } catch (e) { + await registerDeamon(); + } finally { + await runDeamon(); + } +}; + +exports.initDeamon = initDeamon; +exports.delProject = delProject; +exports.addProject = addProject; +exports.killDeamon = killDeamon; diff --git a/packages/cli/lib/index.js b/packages/cli/lib/index.js index a114cb0..7486f0c 100644 --- a/packages/cli/lib/index.js +++ b/packages/cli/lib/index.js @@ -41,6 +41,7 @@ exports.install = async options => { cwd: mountedInfo.mountPoint, pkg: options.pkg, force: true, + daemon: options.daemon, }); console.timeEnd(`[rapid] ${nodeModulesDir} already mounted, try to clean`); } @@ -62,7 +63,7 @@ exports.install = async options => { await downloadDependency.download(options); assert(Object.keys(packageLock).length, '[rapid] depsJSON invalid.'); - await nydusd.startNydusFs(options.nydusMode, options.cwd, options.pkg); + await nydusd.startNydusFs(options.nydusMode, options.cwd, options.pkg, options.daemon); await util.ensureAccess(options.cwd, packageLock); @@ -75,7 +76,7 @@ exports.install = async options => { console.timeEnd('[rapid] run lifecycle scripts'); }; -exports.clean = async function clean({ nydusMode = NYDUS_TYPE.FUSE, cwd, force, pkg }) { +exports.clean = async function clean({ nydusMode = NYDUS_TYPE.FUSE, cwd, force, pkg, daemon }) { const listInfo = await util.listMountInfo(); if (!listInfo.length) { console.log('[rapid] no mount info found.'); @@ -91,7 +92,7 @@ exports.clean = async function clean({ nydusMode = NYDUS_TYPE.FUSE, cwd, force, pkg = pkgRes.pkg; } - await nydusd.endNydusFs(nydusMode, cwd, pkg, force); + await nydusd.endNydusFs(nydusMode, cwd, pkg, force, daemon); }; exports.list = async () => { diff --git a/packages/cli/lib/nydusd/fuse_mode.js b/packages/cli/lib/nydusd/fuse_mode.js index a952f5b..4b5c577 100644 --- a/packages/cli/lib/nydusd/fuse_mode.js +++ b/packages/cli/lib/nydusd/fuse_mode.js @@ -3,11 +3,13 @@ const fs = require('node:fs/promises'); const os = require('node:os'); const path = require('node:path'); +const crypto = require('node:crypto'); const execa = require('execa'); const { tarBucketsDir, unionfs, BOOTSTRAP_BIN, + socketPath, } = require('../constants'); const { wrapSudo, @@ -18,35 +20,67 @@ const { } = require('../util'); const nydusdApi = require('./nydusd_api'); const { Bar } = require('../logger'); +const { addProject, delProject, initDeamon } = require('../deamon'); -async function startNydusFs(cwd, pkg) { +const getProjectName = cwd => { + const folderName = path.basename(cwd); + const hash = crypto.createHash('md5').update(folderName).digest('hex'); + const hashedFolderName = `${folderName}_${hash}`; + + return hashedFolderName; +}; + +async function startNydusFs(cwd, pkg, daemon) { await nydusdApi.initDaemon(); + if (daemon) { + await initDeamon(); + } + + const deamonConfig = { + projectName: getProjectName(cwd), + projectPath: cwd, + }; + console.log('[rapid] generate bootstrap'); - await generateBootstrapFile(cwd, pkg); + await generateBootstrapFile(cwd, pkg, deamonConfig); console.log('[rapid] mount nydusd'); - await mountNydus(cwd, pkg); + await mountNydus(cwd, pkg, deamonConfig); console.log('[rapid] mount overlay, it may take a few seconds'); - await mountOverlay(cwd, pkg); + await mountOverlay(cwd, pkg, deamonConfig); + + if (daemon) { + await addProject(deamonConfig); + } } -async function generateBootstrapFile(cwd, pkg) { +async function generateBootstrapFile(cwd, pkg, config) { const allPkgs = await getAllPkgPaths(cwd, pkg); const bar = new Bar({ type: 'bootstrap', total: allPkgs.length }); + const bootstraps = []; await Promise.all(allPkgs.map(async pkgPath => { const { bootstrap, tarIndex, nodeModulesDir } = await getWorkdir(cwd, pkgPath); await fs.mkdir(path.dirname(bootstrap), { recursive: true }); await execa.command(`${BOOTSTRAP_BIN} --stargz-config-path=${tarIndex} --stargz-dir=${tarBucketsDir} --bootstrap=${bootstrap}`); + bootstraps.push({ + bootstrapBin: BOOTSTRAP_BIN, + stargzConfigPath: tarIndex, + stargzDir: tarBucketsDir, + bootstrap, + }); bar.update(nodeModulesDir); })); bar.stop(); + config.bootstraps = bootstraps; } -async function mountNydus(cwd, pkg) { +async function mountNydus(cwd, pkg, config) { const allPkgs = await getAllPkgPaths(cwd, pkg); + const mounts = []; + const bar = new Bar({ type: 'mount', total: allPkgs.length, @@ -56,17 +90,25 @@ async function mountNydus(cwd, pkg) { for (const pkgPath of allPkgs) { const { dirname, bootstrap } = await getWorkdir(cwd, pkgPath); await nydusdApi.mount(`/${dirname}`, cwd, bootstrap); + mounts.push({ + mountpoint: `/${dirname}`, + socketPath, + bootstrap, + nydusdConfig: JSON.parse(nydusdApi.nydusdConfig), + }); bar.update(dirname); } bar.stop(); + config.nydusdApiMount = mounts; } -async function mountOverlay(cwd, pkg) { +async function mountOverlay(cwd, pkg, config) { const allPkgs = await getAllPkgPaths(cwd, pkg); const bar = new Bar({ type: 'overlay', total: allPkgs.length, }); + const overlays = []; await Promise.all(allPkgs.map(async pkgPath => { const { upper, @@ -103,11 +145,19 @@ async function mountOverlay(cwd, pkg) { } await fs.mkdir(upper, { recursive: true }); await fs.mkdir(workdir, { recursive: true }); - + let overlayConfig = {}; let shScript = wrapSudo(`mount \ -t overlay overlay \ -o lowerdir=${mnt},upperdir=${upper},workdir=${workdir} \ ${nodeModulesDir}`); + overlayConfig = { + workdir, + upper, + mnt, + nodeModulesDir, + tmpDmg, + overlay, + }; if (os.type() === 'Darwin') { shScript = `${unionfs} \ @@ -115,17 +165,30 @@ ${nodeModulesDir}`); -o allow_other,use_ino,suid,dev,nobrowse \ ${upper}=RW:${mnt}=RO \ ${nodeModulesDir}`; + overlayConfig = { + unionfs, + upper, + mnt, + nodeModulesDir, + tmpDmg, + overlay, + }; } // console.log('[rapid] mountOverlay: `%s`', shScript); await execa.command(shScript); bar.update(nodeModulesDir); + overlays.push(overlayConfig); })); bar.stop(); + config.overlays = overlays; } -async function endNydusFs(cwd, pkg, force = true) { +async function endNydusFs(cwd, pkg, force = true, daemon) { const allPkgs = await getAllPkgPaths(cwd, pkg); const umountCmd = force ? 'umount -f' : 'umount'; + if (daemon) { + await delProject(getProjectName(cwd)); + } await Promise.all(allPkgs.map(async pkgPath => { const { dirname, overlay, baseDir, nodeModulesDir } = await getWorkdir( cwd, diff --git a/packages/cli/lib/nydusd/index.js b/packages/cli/lib/nydusd/index.js index 70f22e6..914c628 100644 --- a/packages/cli/lib/nydusd/index.js +++ b/packages/cli/lib/nydusd/index.js @@ -31,20 +31,20 @@ exports.unregisterMode = function(mode) { fsImplMap[mode] = null; }; -exports.startNydusFs = async function(mode, cwd, pkg) { +exports.startNydusFs = async function(mode, cwd, pkg, daemon) { const impl = fsImplMap[mode]; assert(impl, `can not find fs impl for mode: ${mode}`); - await impl.start(cwd, pkg); + await impl.start(cwd, pkg, daemon); }; -exports.endNydusFs = async function(mode, cwd, pkg, force) { +exports.endNydusFs = async function(mode, cwd, pkg, force, daemon) { if (!mode || mode === NYDUS_TYPE.NATIVE) { console.log('[rapid] nydusd is not running, skip clean'); return; } const impl = fsImplMap[mode]; assert(impl, `can not find fs impl for mode: ${mode}`); - await impl.end(cwd, pkg, force); + await impl.end(cwd, pkg, force, daemon); }; exports.getNydusMode = async function(cwd) { diff --git a/packages/cli/lib/nydusd/nydusd_api.js b/packages/cli/lib/nydusd/nydusd_api.js index 3a652bd..7b3adb4 100644 --- a/packages/cli/lib/nydusd/nydusd_api.js +++ b/packages/cli/lib/nydusd/nydusd_api.js @@ -16,6 +16,7 @@ const { tarBucketsDir, } = require('../constants'); const { wrapSudo, getWorkdir } = require('../util'); +const { killDeamon } = require('../deamon'); // see all APIs at: https://github.com/dragonflyoss/image-service/blob/master/api/openapi/nydus-rs.yaml const endpoint = 'http://unix/api/v1'; @@ -139,6 +140,7 @@ async function checkDaemon() { // 优雅退出 nydusd daemon async function exitDaemon() { try { + await killDeamon(); await urllib.request(`${daemonUrl}/exit`, { method: 'PUT', socketPath, @@ -155,6 +157,7 @@ async function exitDaemon() { // 强制杀掉进程 async function forceExitDaemon() { try { + await killDeamon(); await execa.command(`umount -f ${nydusdMnt}`); await execa.command('killall -9 nydusd'); } catch (e) { @@ -164,6 +167,7 @@ async function forceExitDaemon() { } try { + await killDeamon(); await execa.command('killall -9 nydusd'); } catch (e) { // ignore, nydusd quits with error, but it's ok @@ -235,3 +239,4 @@ exports.exitDaemon = exitDaemon; exports.forceExitDaemon = forceExitDaemon; exports.isDaemonRunning = isDaemonRunning; exports.list = list; +exports.nydusdConfig = nydusdConfig; diff --git a/packages/cli/package.json b/packages/cli/package.json index da9aa90..901a0fa 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -14,6 +14,7 @@ "@cnpmjs/binding": "^0.3.10", "@npmcli/arborist": "^6.1.5", "@npmcli/map-workspaces": "^3.0.0", + "auto-launch": "^5.0.6", "await-event": "^2.1.0", "binary-mirror-config": "^2.5.0", "boxen": "^5.1.2", diff --git a/packages/deamon/Cargo.toml b/packages/deamon/Cargo.toml new file mode 100644 index 0000000..62a33ab --- /dev/null +++ b/packages/deamon/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "rapid_deamon" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +homedir = "0.2.1" +lazy_static = "1.4.0" +tokio = { version = "1", features = ["full"] } +serde = "1.0" +serde_json = "1.0" +regex = "1.5" +anyhow = "1.0" +nix = "0.25" +chrono = "0.4" +log = "0.4" +log4rs = "1" +axum = "0.7" +http-body-util = "0.1" +hyper = { version = "0.14", features = ["full"] } +hyper_v1 = { package = "hyper", version = "1", features = ["full"] } +hyperlocal = "0.8" +hyper-util = { version = "0.1", features = [ + "client", + "client-legacy", + "http1", + "server-auto", + "tokio", +] } +httparse = "1.8" +tower = { version = "0.4", features = ["util"] } + +[dev-dependencies] +mime = "0.3" diff --git a/packages/deamon/src/config.rs b/packages/deamon/src/config.rs new file mode 100644 index 0000000..ed2bb74 --- /dev/null +++ b/packages/deamon/src/config.rs @@ -0,0 +1,567 @@ +use anyhow::{anyhow, Result}; +use homedir::get_my_home; +use hyper::{body::to_bytes, Body, Client, Request, StatusCode}; +use hyperlocal::{UnixClientExt, Uri}; +use log::{error, info}; +use regex::Regex; +use serde::{Deserialize, Serialize}; +use serde_json::Error; +use std::{path::PathBuf, process::Command}; + +use crate::utils::{get_ps_snapshot, start_command}; + +// { +// projectName: "", +// bootstrap: { +// projectPath: "", +// stargzConfigPath:"", +// stargzDir: "", +// bootstrap: "", +// }, +// nydusdApiMount: [ +// { +// mountpoint: "", +// socketPath: "", // string | null | undefined +// bootstrap: "", +// nydusdConfig: "" +// } +// ], +// overlay: { +// unionfs: "", +// upper: "", +// mnt: "", +// nodeModulesDir: "", +// workdir: "", +// } +// } + +lazy_static! { + static ref END_POINT: &'static str = "http://unix/api/v1"; + static ref MOUNT_URL: String = format!("{}/mount", END_POINT.to_string()); + static ref DAEMON_URL: String = format!("{}/daemon", END_POINT.to_string()); +} + +#[derive(Debug, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct NydusdApiMount { + mountpoint: String, + socket_path: String, + bootstrap: String, + nydusd_config: NydusdConfig, +} + +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub struct NydusdConfig { + device: DeviceConfig, + mode: String, + digest_validate: bool, + iostats_files: bool, +} + +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub struct DeviceConfig { + backend: Backend, +} + +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub struct Backend { + #[serde(rename = "type")] + _type: String, + config: BackendConfig, +} + +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub struct BackendConfig { + dir: String, + readahead: bool, +} +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub struct NydusdApiMountReset { + source: String, + fs_type: String, + config: String, +} + +impl NydusdApiMount { + fn get_reset_json(&self) -> Result { + let config = serde_json::to_string(&self.nydusd_config)?; + let reset_struce = NydusdApiMountReset { + source: self.bootstrap.clone(), + fs_type: "rafs".to_string(), + config: config.clone(), + }; + + Ok(serde_json::to_string(&reset_struce)?) + } + pub async fn restart(&self) -> Result<()> { + let url = Uri::new( + &self.socket_path, + &format!("/api/v1/mount?mountpoint={}", self.mountpoint), + ); + + let client = Client::unix(); + + let request = Request::builder() + .method("POST") + .uri(url) + .header("Content-Type", "application/json") + .body(Body::from(self.get_reset_json()?)) + .unwrap(); + + let response = client.request(request).await?; + + if (response.status() == StatusCode::OK) || (response.status() == StatusCode::NO_CONTENT) { + return Ok(()); + } + + let binding = to_bytes(response.into_body()).await?; + let body = String::from_utf8_lossy(&binding).to_string(); + + if body.contains("object or filesystem already exists") { + return Ok(()); + } + + return Err(anyhow!( + "Error NydusdApiMount restart: {:?}, mountpoint: {:?}", + body, + self.mountpoint + )); + } +} + +#[derive(Debug, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct Overlay { + unionfs: Option, + workdir: Option, + upper: String, + mnt: String, + node_modules_dir: String, + tmp_dmg: String, + overlay: String, +} + +impl Overlay { + #[cfg(target_os = "macos")] + pub fn get_pids(&self) -> Result> { + let mut pids = vec![]; + + let snapshot = get_ps_snapshot()?; + + let unionfs = match &self.unionfs { + Some(s) => s, + None => { + return Err(anyhow!( + "unionfs is empty, node_modules_dir is {}", + self.node_modules_dir + )) + } + }; + + let overlay_pattern = + Regex::new(&format!(r#"(?i){}.*?{}"#, unionfs, self.node_modules_dir))?; + + for line in snapshot.clone().lines() { + if overlay_pattern.is_match(line) { + let fields: Vec<&str> = line.split_whitespace().collect(); + if fields.len() >= 11 { + let _user = fields[0].to_string(); + let pid = fields[1].parse::().unwrap_or(0); + let _cpu = fields[2].to_string(); + let _command = fields[10].to_string(); + + pids.push(pid); + } + } + } + + let nfs_pattern = Regex::new(&format!( + r#"(?i)/usr/local/bin/go-nfsv4.*?{}"#, + self.node_modules_dir + ))?; + + for line in snapshot.clone().lines() { + if nfs_pattern.is_match(line) { + let fields: Vec<&str> = line.split_whitespace().collect(); + if fields.len() >= 11 { + let _user = fields[0].to_string(); + let pid = fields[1].parse::().unwrap_or(0); + let _cpu = fields[2].to_string(); + let _command = fields[10].to_string(); + + pids.push(pid); + } + } + } + + Ok(pids) + } + + #[cfg(target_os = "linux")] + pub fn restart(&self) -> Result> { + let unmount_modules_str = format!(r#"umount -f {}"#, self.node_modules_dir); + + let _ = start_command(&unmount_modules_str); + + let unmount_overlay_str = format!(r#"umount -f {}"#, self.overlay); + + let _ = start_command(&unmount_overlay_str); + + let tmp_str = format!(r#"mount -t tmpfs tmpfs {}"#, self.overlay); + + match start_command(&tmp_str) { + Ok(output) => { + if output.status.success() { + info!( + "Overlay restart executed successfully, mountpoint: {:?}, tmp_str: {:?}", + self.node_modules_dir, tmp_str + ); + } else { + return Err(anyhow!( + "Error executing Overlay restart: {:?}, mountpoint: {:?}, tmp_str: {:?}", + output.status, + self.node_modules_dir, + tmp_str + )); + } + } + Err(e) => { + return Err(anyhow!( + "Error executing Overlay restart command: {:?}, mountpoint: {:?}, tmp_str: {:?}", + e, + self.node_modules_dir, + tmp_str + )); + } + } + + let workdir = match &self.workdir { + Some(s) => s, + None => { + return Err(anyhow!( + "workdir is empty, node_modules_dir is {}", + self.node_modules_dir + )) + } + }; + + let mount_str = format!( + r#"mount -t overlay overlay -o lowerdir={},upperdir={},workdir={} {}"#, + self.mnt, self.upper, workdir, self.node_modules_dir + ); + match start_command(&mount_str) { + Ok(output) => { + if output.status.success() { + info!( + "Overlay restart executed successfully, mountpoint: {:?}", + self.node_modules_dir + ); + } else { + return Err(anyhow!( + "Error executing Overlay restart: {:?}, mountpoint: {:?}", + output.status, + self.node_modules_dir + )); + } + } + Err(e) => { + return Err(anyhow!( + "Error executing Overlay restart command: {:?}, mountpoint: {:?}", + e, + self.node_modules_dir + )); + } + } + + let res = vec![]; + Ok(res) + } + + #[cfg(target_os = "macos")] + pub fn restart(&self) -> Result> { + let tmp_str = format!( + r#"hdiutil attach -nobrowse -mountpoint {} {}"#, + self.overlay, self.tmp_dmg + ); + + match start_command(&tmp_str) { + Ok(output) => { + if output.status.success() { + info!( + "Overlay restart executed successfully, mountpoint: {:?}, tmp_str: {:?}", + self.node_modules_dir, tmp_str + ); + } else { + return Err(anyhow!( + "Error executing Overlay restart: {:?}, mountpoint: {:?}, tmp_str: {:?}", + output.status, + self.node_modules_dir, + tmp_str + )); + } + } + Err(e) => { + return Err(anyhow!( + "Error executing Overlay restart command: {:?}, mountpoint: {:?}, tmp_str: {:?}", + e, + self.node_modules_dir, + tmp_str + )); + } + } + + let unionfs = match &self.unionfs { + Some(s) => s, + None => { + return Err(anyhow!( + "unionfs is empty, node_modules_dir is {}", + self.node_modules_dir + )) + } + }; + + let mount_str = format!( + r#"{} -o cow,max_files=32768 -o allow_other,use_ino,suid,dev,nobrowse {}=RW:{}=RO {}"#, + unionfs, self.upper, self.mnt, self.node_modules_dir + ); + + match start_command(&mount_str) { + Ok(output) => { + if output.status.success() { + info!( + "Overlay restart executed successfully, mountpoint: {:?}", + self.node_modules_dir + ); + } else { + return Err(anyhow!( + "Error executing Overlay restart: {:?}, mountpoint: {:?}", + output.status, + self.node_modules_dir + )); + } + } + Err(e) => { + return Err(anyhow!( + "Error executing Overlay restart command: {:?}, mountpoint: {:?}", + e, + self.node_modules_dir + )); + } + } + + let res = self.get_pids()?; + Ok(res) + } +} + +#[derive(Debug, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct Bootstrap { + bootstrap_bin: String, + stargz_config_path: String, + stargz_dir: String, + bootstrap: String, +} + +impl Bootstrap { + pub fn restart(&self) -> Result<()> { + let str = format!( + "{} --stargz-config-path={} --stargz-dir={} --bootstrap={}", + self.bootstrap_bin, self.stargz_config_path, self.stargz_dir, self.bootstrap + ); + match start_command(&str) { + Ok(output) => { + if output.status.success() { + info!( + "bootstrap restart executed successfully, stargz_config_path: {:?}", + self.stargz_config_path + ); + Ok(()) + } else { + return Err(anyhow!( + "Error executing bootstrap restart: {:?}, stargz_config_path: {:?}", + output.status, + self.stargz_config_path + )); + } + } + Err(e) => Err(anyhow!( + "Error executing bootstrap restart: {:?}, stargz_config_path: {:?}", + e, + self.stargz_config_path + )), + } + } +} + +#[derive(Debug, Deserialize, PartialEq, Eq)] +#[allow(dead_code)] +#[serde(rename_all = "camelCase")] +pub struct ProjectConfig { + project_name: String, + project_path: String, + bootstraps: Vec, + nydusd_api_mount: Vec, + overlays: Vec, +} + +impl ProjectConfig { + pub fn new( + project_path: String, + bootstraps: Vec, + nydusd_api_mount: Vec, + overlays: Vec, + ) -> Self { + Self { + project_name: project_path.clone(), + project_path, + bootstraps, + nydusd_api_mount, + overlays, + } + } + pub fn get_project_path(&self) -> &str { + return &self.project_path; + } + + pub fn get_pids(&self) -> Result> { + let mut pids = vec![]; + + #[cfg(target_os = "macos")] + for overlay in self.overlays.iter() { + let ps = overlay.get_pids()?; + pids.extend(ps); + } + return Ok(pids); + } + + pub async fn restart(&self) -> Result> { + for bootstrap in self.bootstraps.iter() { + bootstrap.restart()?; + } + for mount in self.nydusd_api_mount.iter() { + mount.restart().await?; + } + + let mut pids = vec![]; + + for overlay in self.overlays.iter() { + let ps = overlay.restart()?; + pids.extend(ps); + } + + Ok(pids) + } +} + +pub async fn process_json_files_in_folder( + folder_path: &str, +) -> Result, Box> { + let mut entries = tokio::fs::read_dir(folder_path).await?; + let mut handles = vec![]; + + while let Some(entry) = entries.next_entry().await? { + if entry.file_type().await?.is_file() { + let file_path = entry.path(); + + if let Some(extension) = file_path.extension() { + if extension == "json" { + let handle = tokio::spawn(read_json_file(file_path.clone())); + handles.push(handle); + } + } + } + } + + let mut results = vec![]; + + for handle in handles { + let res = handle.await??; + results.push(res); + } + + Ok(results) +} + +async fn read_json_file(file_path: PathBuf) -> Result { + let content = tokio::fs::read_to_string(&file_path).await.unwrap(); + let json_value: ProjectConfig = serde_json::from_str(&content)?; + + Ok(json_value) +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NydusConfig { + nydusd_bin: String, + nydusd_config_file: String, + nydusd_mnt: String, + socket_path: String, + nydusd_log_file: String, +} + +impl NydusConfig { + pub async fn new() -> Self { + let content = tokio::fs::read_to_string( + get_my_home() + .unwrap() + .unwrap() + .join(".rapid/cache/project/nydus_config.json"), + ) + .await + .unwrap(); + let json_value: NydusConfig = serde_json::from_str(&content).unwrap(); + json_value + } + pub fn init_daemon(&self) -> Result<()> { + let str = format!( + r#"curl -X GET \ + --unix-socket {} \ + {}"#, + self.socket_path, + DAEMON_URL.to_string() + ); + match start_command(&str) { + Ok(output) => { + if output.status.success() { + info!("init_daemon executed successfully"); + return Ok(()); + } else { + error!( + "Error executing init_daemon, status: {:?}, stdout: {:?}, stderr: {:?}", + output.status, + std::str::from_utf8(&output.stdout)?, + std::str::from_utf8(&output.stderr)?, + ); + } + } + Err(e) => error!("Error executing init_daemon: {:?}", e), + } + + #[cfg(target_os = "linux")] + let mut command = Command::new("sudo"); + #[cfg(target_os = "macos")] + let mut command = Command::new(&self.nydusd_bin); + command.args([ + #[cfg(target_os = "linux")] + &self.nydusd_bin, + "--config", + &self.nydusd_config_file, + "--mountpoint", + &self.nydusd_mnt, + "--apisock", + &self.socket_path, + "--log-file", + &self.nydusd_log_file, + ]); + std::thread::spawn(move || { + let _ = command.output(); + }); + Ok(()) + } +} diff --git a/packages/deamon/src/main.rs b/packages/deamon/src/main.rs new file mode 100644 index 0000000..1875f73 --- /dev/null +++ b/packages/deamon/src/main.rs @@ -0,0 +1,93 @@ +#[macro_use] +extern crate lazy_static; + +use anyhow::{anyhow, Result}; +use config::{process_json_files_in_folder, NydusConfig}; +use homedir::get_my_home; +use log::error; +use pid::{check_projects, init_projects}; +use server::start_server; +use tokio::{select, sync::mpsc}; +use utils::create_folder_if_not_exists; + +mod config; +mod pid; +mod server; +mod utils; + +fn setup_logger() -> Result<()> { + let log_dir = get_my_home()? + .ok_or(anyhow!("get log_dir my_home fail"))? + .join(".rapid/cache/project/logs/"); + + create_folder_if_not_exists(log_dir.to_str().ok_or(anyhow!("log_dir to str fail"))?).unwrap(); + + let log_config_path = get_my_home()? + .ok_or(anyhow!("get log_config_path my_home fail"))? + .join(".rapid/cache/project/log4rs.yaml"); + + log4rs::init_file(log_config_path, Default::default())?; + + Ok(()) +} + +#[tokio::main] +async fn main() { + let cache_dir = get_my_home() + .unwrap() + .unwrap() + .join(".rapid/cache/project/"); + + create_folder_if_not_exists(cache_dir.to_str().unwrap()).unwrap(); + + let metadata_dir = get_my_home() + .unwrap() + .unwrap() + .join(".rapid/cache/project/metadata/"); + + create_folder_if_not_exists(metadata_dir.to_str().unwrap()).unwrap(); + + let socket_path = get_my_home() + .unwrap() + .unwrap() + .join(".rapid/cache/project/socket_path"); + + let _ = setup_logger(); + + let configs = process_json_files_in_folder(metadata_dir.to_str().unwrap()) + .await + .unwrap(); + + let nydus = NydusConfig::new().await; + + let _ = nydus.init_daemon(); + + let project_tree = init_projects(configs).await.unwrap(); + + let (sender, mut receiver) = mpsc::channel::(1); + + { + let project_tree = project_tree.clone(); + std::thread::spawn(move || { + tokio::runtime::Runtime::new() + .unwrap() + .block_on(start_server(project_tree, sender, socket_path)); + }); + } + + loop { + select! { + _ = receiver.recv() => { + return; + } + _ = tokio::time::sleep(tokio::time::Duration::from_secs(5)) => { + if let Err(e) = nydus.init_daemon() { + error!("init_daemon err: {}", e); + }; + if let Err(e) = check_projects(project_tree.clone()).await { + error!("check_projects err: {}", e); + } + } + } + } +} diff --git a/packages/deamon/src/pid.rs b/packages/deamon/src/pid.rs new file mode 100644 index 0000000..5e73c08 --- /dev/null +++ b/packages/deamon/src/pid.rs @@ -0,0 +1,215 @@ +use super::config::ProjectConfig; +use anyhow::{anyhow, Result}; +use log::{debug, error, info}; +use nix::sys::signal::{self, Signal}; +use nix::unistd; +use std::sync::Arc; +use std::{collections::BTreeMap, process::Command}; +use tokio::{ + fs::read_dir, + sync::Mutex, + time::{timeout, Duration}, +}; + +#[derive(Debug, PartialEq, Eq)] +pub struct ProjectInfo { + pub config: ProjectConfig, + pub pids: Vec, +} + +impl ProjectInfo { + pub fn kill_pids(&mut self) -> Result<()> { + for pid in self.pids.iter() { + info!( + "{:?} be killing, project path {}", + pid, + self.config.get_project_path() + ); + match signal::kill(unistd::Pid::from_raw(pid.clone() as i32), Signal::SIGKILL) { + Ok(_) => { + info!( + "{:?} killed, project path {}", + pid, + self.config.get_project_path() + ); + } + Err(err) => { + return Err(anyhow!( + "Failed to kill process with PID {}. Error: {:?}", + pid, + err + )); + } + } + } + + self.pids = vec![]; + + Ok(()) + } + + async fn restart(&mut self) -> Result<()> { + match self.config.restart().await { + Ok(pids) => { + info!( + "restart project path {}, pids: {:?}", + self.config.get_project_path(), + pids + ); + self.pids = pids; + return Ok(()); + } + Err(err) => { + return Err(anyhow!( + "Failed to restart project path {}. Error: {:?}", + self.config.get_project_path(), + err + )); + } + }; + } +} + +pub async fn init_projects( + projects: Vec, +) -> Result>>> { + let lock_map = Arc::new(Mutex::new(BTreeMap::new())); + + for project in projects.into_iter() { + match add_project(project, lock_map.clone()).await { + Ok(_) => continue, + Err(e) => { + error!("init_project error : {:?}", e); + } + }; + } + + Ok(lock_map) +} + +pub async fn kill_projects(process_map: Arc>>) -> Result<()> { + let mut map = process_map.lock().await; + + for (_, info) in map.iter_mut() { + info.kill_pids()?; + } + + Ok(()) +} + +pub async fn init_project( + project: ProjectConfig, + process_map: Arc>>, +) -> Result<()> { + let pids = project.restart().await?; + let mut map = process_map.lock().await; + map.insert( + project.get_project_path().to_string(), + ProjectInfo { + config: project, + pids, + }, + ); + Ok(()) +} + +pub async fn add_project( + project: ProjectConfig, + process_map: Arc>>, +) -> Result<()> { + let pids = project.get_pids()?; + let mut map = process_map.lock().await; + map.insert( + project.get_project_path().to_string(), + ProjectInfo { + config: project, + pids, + }, + ); + Ok(()) +} + +// check all rapid project status +pub async fn check_projects(process_map: Arc>>) -> Result<()> { + let mut p_map = process_map.lock().await; + + for project in p_map.iter_mut() { + info!("{:?} be checked", project.0); + if let Err(e) = check_project(project.1).await { + error!("project {} err: {}", project.0, e); + } + } + + Ok(()) +} + +async fn check_project(project: &mut ProjectInfo) -> Result<()> { + info!( + "{:?} checking, pids is {:?}", + project.config.get_project_path(), + &project.pids + ); + + #[cfg(target_os = "macos")] + if (&project.pids).len() == 0 { + project.restart().await?; + return Ok(()); + } + if !is_alive(&project.pids, project.config.get_project_path()).await { + info!("{:?} will be check", project.config.get_project_path()); + if let Err(e) = project.kill_pids() { + error!( + "project {} kill fail {}", + project.config.get_project_path(), + e + ) + } + project.restart().await?; + } + + Ok(()) +} + +async fn is_alive(pids: &Vec, directory_path: &str) -> bool { + let mut is_pid_alive = true; + for pid in pids.iter() { + debug!("directory_path: {:?}, pid: {:?}", directory_path, pid); + let output = Command::new("kill").arg("-0").arg(pid.to_string()).output(); + if output.is_ok() && output.unwrap().status.success() { + continue; + } else { + is_pid_alive = false; + break; + } + } + + if !is_pid_alive { + return false; + } + + match timeout(Duration::from_secs(1), read_directory(directory_path)).await { + Ok(result) => match result { + Ok(_) => true, + Err(e) => { + error!("read {:?} result error, error is {:?}", directory_path, e); + return false; + } + }, + Err(e) => { + error!("read {:?} error, error is {:?}", directory_path, e); + return false; + } + } +} + +async fn read_directory(path: &str) -> Result> { + let mut dir = read_dir(path).await?; + + let mut entries = vec![]; + + while let Some(entry) = dir.next_entry().await? { + entries.push(entry); + } + + Ok(entries) +} diff --git a/packages/deamon/src/server.rs b/packages/deamon/src/server.rs new file mode 100644 index 0000000..f17237e --- /dev/null +++ b/packages/deamon/src/server.rs @@ -0,0 +1,335 @@ +use std::{collections::BTreeMap, convert::Infallible, path::PathBuf, sync::Arc}; + +use anyhow::Result; +use axum::{ + extract::{connect_info, State}, + http::{Request, StatusCode}, + response::Json, + routing::{delete, get, post}, + Router, +}; +use hyper_util::{ + rt::{TokioExecutor, TokioIo}, + server, +}; +use hyper_v1::body::Incoming; +use log::{error, info}; +use serde::{Deserialize, Serialize}; +use tokio::{ + net::{unix::UCred, UnixListener, UnixStream}, + sync::{mpsc::Sender, Mutex}, +}; +use tower::Service; + +use crate::{ + config::ProjectConfig, + pid::{add_project, ProjectInfo}, +}; + +#[derive(Serialize, Deserialize)] +struct Res { + code: i32, + msg: String, +} + +struct RouteState { + pub project_tree: Arc>>, + pub sender: Arc>>, +} + +async fn handle_add( + State(state): State>>, + Json(config): Json, +) -> Result, (StatusCode, String)> { + let path = config.get_project_path().to_string(); + let state = state.lock().await; + info!("handle add project path is {}", path); + match add_project(config, state.project_tree.clone()).await { + Ok(_) => Ok(Json(Res { + code: 0, + msg: format!("add project {} config success!", path), + })), + Err(e) => Ok(Json(Res { + code: -1, + msg: format!("add project {} config fail, err is {:?}", path, e), + })), + } +} + +#[derive(Debug, Deserialize)] +#[allow(dead_code)] +#[serde(rename_all = "camelCase")] +pub struct DelReq { + project_path: String, +} + +async fn handle_del( + State(state): State>>, + Json(request): Json, +) -> Result, (StatusCode, String)> { + let state = state.lock().await; + let mut project_tree = state.project_tree.lock().await; + + info!("handle del project path is {}", request.project_path); + if let Some(mut project) = project_tree.remove(&request.project_path) { + // project.kill_pids(); + } + + Ok(Json(Res { + code: 0, + msg: format!("del project {} config success!", request.project_path), + })) +} + +async fn handle_alive() -> Result, (StatusCode, String)> { + info!("echo alive"); + Ok(Json(Res { + code: 0, + msg: format!("deamon is alive"), + })) +} + +async fn handle_kill( + State(state): State>>, +) -> Result, (StatusCode, String)> { + info!("echo kill"); + let state = state.lock().await; + let sender = state.sender.lock().await; + match sender.send(0).await { + Ok(_) => Ok(Json(Res { + code: 0, + msg: format!("deamon be killed"), + })), + Err(e) => Ok(Json(Res { + code: -1, + msg: format!("deamon kill err {:?}", e), + })), + } +} + +fn app( + project_tree: Arc>>, + sender: Arc>>, +) -> Router { + let state = RouteState { + project_tree, + sender, + }; + Router::new() + .route("/project", post(handle_add)) + .route("/project", delete(handle_del)) + .route("/alive", get(handle_alive)) + .route("/kill", get(handle_kill)) + .with_state(Arc::new(Mutex::new(state))) +} + +#[derive(Clone, Debug)] +#[allow(dead_code)] +struct UdsConnectInfo { + peer_addr: Arc, + peer_cred: UCred, +} + +impl connect_info::Connected<&UnixStream> for UdsConnectInfo { + fn connect_info(target: &UnixStream) -> Self { + let peer_addr = target.peer_addr().unwrap(); + let peer_cred = target.peer_cred().unwrap(); + + Self { + peer_addr: Arc::new(peer_addr), + peer_cred, + } + } +} + +fn unwrap_infallible(result: Result) -> T { + match result { + Ok(value) => value, + Err(err) => { + error!("unwrap_infallible err {:?}", err); + match err {} + } + } +} + +pub async fn start_server( + project_tree: Arc>>, + sender: Sender, + socket_path: PathBuf, +) { + let sender = Arc::new(Mutex::new(sender)); + let app = app(project_tree, sender.clone()); + + let _ = tokio::fs::remove_file(&socket_path).await; + + let uds = match UnixListener::bind(socket_path.clone()) { + Ok(uds) => uds, + Err(e) => { + error!("create uds error: {:?}", e); + sender.lock().await.send(0).await; + return; + } + }; + + let mut make_service = app.into_make_service_with_connect_info::(); + + loop { + let (socket, _remote_addr) = uds.accept().await.unwrap(); + + let tower_service = unwrap_infallible(make_service.call(&socket).await); + + tokio::spawn(async move { + let socket = TokioIo::new(socket); + + let hyper_service = hyper_v1::service::service_fn(move |request: Request| { + tower_service.clone().call(request) + }); + + if let Err(err) = server::conn::auto::Builder::new(TokioExecutor::new()) + .serve_connection_with_upgrades(socket, hyper_service) + .await + { + eprintln!("failed to serve connection: {err:#}"); + } + }); + } +} + +#[cfg(test)] +mod test { + use super::*; + use axum::{ + body::Body, + http::{self, Request, StatusCode}, + }; + use http_body_util::BodyExt; + use tower::ServiceExt; + + use tokio::sync::mpsc; + + async fn create_mock_tree() -> Arc>> { + let mut tree = BTreeMap::new(); + + tree.insert( + "mock_project".to_string(), + ProjectInfo { + config: ProjectConfig::new("mock_project".to_string(), vec![], vec![], vec![]), + pids: vec![], + }, + ); + + Arc::new(Mutex::new(tree)) + } + + #[tokio::test] + async fn test_handle_del() { + let tree = create_mock_tree().await; + let tree_config = tree.lock().await; + assert_eq!( + tree_config + .get("mock_project") + .unwrap() + .config + .get_project_path(), + "mock_project" + ); + drop(tree_config); + + let (sender, _) = mpsc::channel::(1); + let sender = Arc::new(Mutex::new(sender)); + let app = app(tree.clone(), sender); + + let response = app + .oneshot( + Request::builder() + .method(http::Method::DELETE) + .uri("/project") + .header(http::header::CONTENT_TYPE, mime::APPLICATION_JSON.as_ref()) + .body(Body::from(r#"{"projectPath":"mock_project"}"#)) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK); + let tree_config = tree.lock().await; + + assert_eq!(tree_config.get("mock_project"), None); + } + + #[tokio::test] + async fn test_handle_add() { + let tree = create_mock_tree().await; + + let (sender, _) = mpsc::channel::(1); + let sender = Arc::new(Mutex::new(sender)); + let app = app(tree.clone(), sender); + + let response = app + .oneshot( + Request::builder() + .method(http::Method::POST) + .uri("/project") + .header(http::header::CONTENT_TYPE, mime::APPLICATION_JSON.as_ref()) + .body(Body::from(r#"{"projectName":"mock_project","projectPath":"test_project","bootstraps":[],"nydusdApiMount":[],"overlays":[]}"#)) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK); + let body = response.into_body().collect().await.unwrap().to_bytes(); + let body: Res = serde_json::from_slice(&body).unwrap(); + assert_eq!(body.code, 0); + } + + #[tokio::test] + async fn test_handle_alive() { + let tree = create_mock_tree().await; + + let (sender, _) = mpsc::channel::(1); + let sender = Arc::new(Mutex::new(sender)); + let app = app(tree.clone(), sender); + + let response = app + .oneshot( + Request::builder() + .method(http::Method::GET) + .uri("/alive") + .body(Body::from("")) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + let body = response.into_body().collect().await.unwrap().to_bytes(); + let body: Res = serde_json::from_slice(&body).unwrap(); + assert_eq!(body.code, 0); + } + + #[tokio::test] + async fn test_handle_kill() { + let tree = create_mock_tree().await; + + let (sender, mut receiver) = mpsc::channel::(1); + let sender = Arc::new(Mutex::new(sender)); + let app = app(tree.clone(), sender); + + let response = app + .oneshot( + Request::builder() + .method(http::Method::GET) + .uri("/kill") + .body(Body::from("")) + .unwrap(), + ) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + let body = response.into_body().collect().await.unwrap().to_bytes(); + let body: Res = serde_json::from_slice(&body).unwrap(); + assert_eq!(body.code, 0); + let msg = receiver.recv().await.unwrap(); + assert_eq!(msg, 0); + } +} diff --git a/packages/deamon/src/utils.rs b/packages/deamon/src/utils.rs new file mode 100644 index 0000000..6cb4016 --- /dev/null +++ b/packages/deamon/src/utils.rs @@ -0,0 +1,98 @@ +use anyhow::Result; +use log::info; +use regex::Regex; +use std::fs::File; +use std::io; +use std::process::{Command, Output}; + +pub fn get_ps_snapshot() -> Result { + let ps_output = Command::new("ps").arg("aux").output()?; + + let ps_output_str = String::from_utf8_lossy(&ps_output.stdout).to_string(); + Ok(ps_output_str) +} + +pub fn create_folder_if_not_exists(folder_path: &str) -> Result<()> { + if !std::fs::metadata(folder_path).is_ok() { + std::fs::create_dir_all(folder_path)?; + info!("Folder created: {}", folder_path); + } else { + info!("Folder already exists: {}", folder_path); + } + + Ok(()) +} + +pub fn split_args(comm: &str) -> (String, Option) { + let split_comm: Vec<_> = comm.splitn(2, ' ').collect(); + + if split_comm.len() > 1 { + let cleaned_str = Regex::new(r#"\\[\s]*\n"#) + .unwrap() + .replace_all(split_comm[1], " "); + let arg = Regex::new(r#"\s+"#).unwrap().replace_all(&cleaned_str, " "); + return (split_comm[0].to_string(), Some(arg.to_string())); + } + + (split_comm[0].to_string(), None) +} + +pub fn start_command(comm: &str) -> io::Result { + let (com, args) = split_args(comm); + + let mut command = Command::new(&com); + + match args { + Some(arg) => { + command.args(arg.split(' ').collect::>()); + } + _ => (), + }; + command.output() +} + +pub fn check_and_create_file(file_path: &str) -> io::Result { + if !std::path::Path::new(file_path).exists() { + let file = File::create(file_path)?; + + return Ok(file); + } else { + let file = File::open(file_path)?; + return Ok(file); + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_split_args() { + let (command, args) = split_args("echo Hello World"); + assert_eq!(command, "echo"); + assert_eq!(args, Some("Hello World".to_string())); + + let (command, args) = split_args("ls"); + assert_eq!(command, "ls"); + assert_eq!(args, None); + + let (command, args) = split_args("echo \"Hello World\""); + assert_eq!(command, "echo"); + assert_eq!(args, Some("\"Hello World\"".to_string())); + } + + #[tokio::test] + async fn test_start_command() { + match start_command("echo Hello World") { + Ok(output) => { + assert!(output.status.success()); + + assert!(String::from_utf8_lossy(&output.stdout).contains("Hello World")); + } + Err(err) => { + eprintln!("Error running command: {:?}", err); + assert!(false); + } + } + } +} diff --git a/scripts/prepareBootstrap.js b/scripts/prepareBootstrap.js index cc558d4..1484b4c 100644 --- a/scripts/prepareBootstrap.js +++ b/scripts/prepareBootstrap.js @@ -27,6 +27,10 @@ console.log('root folder: ', rootFolder); const bootstrapBinPath = process.env.CARGO_BUILD_TARGET ? path.resolve(rootFolder, 'target', process.env.CARGO_BUILD_TARGET, 'release/bootstrap') : path.resolve(rootFolder, 'target/release/bootstrap'); const targetBinPath = path.resolve(targetFolder, 'nydusd-bootstrap'); + +const deamonBinPath = process.env.CARGO_BUILD_TARGET ? path.resolve(rootFolder, 'target', process.env.CARGO_BUILD_TARGET, 'release/rapid_deamon') : path.resolve(rootFolder, 'target/release/rapid_deamon'); +const targetDeamonBinPath = path.resolve(targetFolder, 'rapid_deamon'); + const nodeBindingPath = path.resolve(rootFolder, 'packages/binding/index.node'); const targetNodeBindingPath = path.resolve(targetFolder, 'index.node'); @@ -34,6 +38,8 @@ const targetNodeBindingPath = path.resolve(targetFolder, 'index.node'); try { console.log(`mv ${bootstrapBinPath} to ${targetBinPath}`); await fs.rename(bootstrapBinPath, targetBinPath); + console.log(`mv ${deamonBinPath} to ${targetDeamonBinPath}`); + await fs.rename(deamonBinPath, targetDeamonBinPath); console.log(`mv ${nodeBindingPath} to ${targetNodeBindingPath}`); await fs.rename(nodeBindingPath, targetNodeBindingPath); } catch (err) {