diff --git a/.gitattributes b/.gitattributes index 77bebfc3..1fa4830c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ runtime/image/* filter=lfs diff=lfs merge=lfs -text runtime/poc/runtime/* filter=lfs diff=lfs merge=lfs -text +runtime/poc/squashfs/* filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9fa584a7..dabb504b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: #os: [macos-latest, windows-latest, ubuntu-latest] - os: [macos-latest, ubuntu-latest] + os: [windows-latest, ubuntu-latest] steps: - name: Checkout diff --git a/.gitignore b/.gitignore index 70354600..2480777d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ *.d /.idea/ /.vscode/ +/tmp +/runtime/tmp diff --git a/Cargo.lock b/Cargo.lock index 537808bc..78a9a024 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,14 +16,14 @@ dependencies = [ "memchr", "pin-project-lite", "tokio", - "tokio-util 0.7.3", + "tokio-util 0.7.4", ] [[package]] name = "actix-http" -version = "3.2.1" +version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f9ffb6db08c1c3a1f4aef540f1a63193adc73c4fbd40b75a95fc8c5258f6e51" +checksum = "0c83abf9903e1f0ad9973cc4f7b9767fd5a03a583f51a5b7a339e07987cd2724" dependencies = [ "actix-codec", "actix-rt", @@ -43,13 +43,13 @@ dependencies = [ "http", "httparse", "httpdate", - "itoa 1.0.2", + "itoa", "language-tags", "local-channel", "mime", "percent-encoding", "pin-project-lite", - "rand 0.8.4", + "rand 0.8.5", "sha1", "smallvec", "tracing", @@ -102,7 +102,7 @@ dependencies = [ "http", "log", "pin-project-lite", - "tokio-util 0.7.3", + "tokio-util 0.7.4", ] [[package]] @@ -134,24 +134,24 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] [[package]] name = "alloc-no-stdlib" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ef4730490ad1c4eae5c4325b2a95f521d023e5c885853ff7aca0a6a1631db3" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" [[package]] name = "alloc-stdlib" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697ed7edc0f1711de49ce108c541623a0af97c6c60b2f6e2b65229847ac843c2" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" dependencies = [ "alloc-no-stdlib", ] @@ -165,15 +165,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - [[package]] name = "ansi_term" version = "0.12.1" @@ -185,9 +176,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.45" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee10e43ae4a853c0a3591d4e2ada1719e553be18199d9da9d4a83f5927c2f5c7" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" + +[[package]] +name = "arc-swap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164" [[package]] name = "arrayvec" @@ -197,9 +194,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "async-trait" -version = "0.1.51" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" +checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" dependencies = [ "proc-macro2", "quote", @@ -225,9 +222,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "awc" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65c60c44fbf3c8cee365e86b97d706e513b733c4eeb16437b45b88d2fffe889a" +checksum = "80ca7ff88063086d2e2c70b9f3b29b2fcd999bac68ac21731e66781970d68519" dependencies = [ "actix-codec", "actix-http", @@ -245,12 +242,12 @@ dependencies = [ "futures-util", "h2", "http", - "itoa 1.0.2", + "itoa", "log", "mime", "percent-encoding", "pin-project-lite", - "rand 0.8.4", + "rand 0.8.5", "serde", "serde_json", "serde_urlencoded", @@ -271,9 +268,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ "generic-array", ] @@ -340,17 +337,11 @@ dependencies = [ "alloc-stdlib", ] -[[package]] -name = "build_const" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" - [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" [[package]] name = "byteorder" @@ -360,9 +351,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" [[package]] name = "bytestring" @@ -375,9 +366,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.72" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" dependencies = [ "jobserver", ] @@ -397,20 +388,20 @@ dependencies = [ "iana-time-zone", "js-sys", "num-integer", - "num-traits", + "num-traits 0.2.15", "serde", - "time 0.1.43", + "time 0.1.44", "wasm-bindgen", "winapi", ] [[package]] name = "clap" -version = "2.33.3" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "ansi_term 0.11.0", + "ansi_term", "atty", "bitflags", "strsim 0.8.0", @@ -440,13 +431,13 @@ dependencies = [ [[package]] name = "console" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" +checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c" dependencies = [ "encode_unicode", + "lazy_static", "libc", - "once_cell", "terminal_size", "winapi", ] @@ -459,12 +450,12 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "cookie" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" +checksum = "344adc371239ef32293cb1c4fe519592fcf21206c79c02854320afcdf3ab4917" dependencies = [ "percent-encoding", - "time 0.3.11", + "time 0.3.15", "version_check", ] @@ -476,29 +467,29 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.2.1" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] [[package]] name = "crc" -version = "1.8.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23" dependencies = [ - "build_const", + "crc-catalog 1.1.1", ] [[package]] name = "crc" -version = "2.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23" +checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" dependencies = [ - "crc-catalog", + "crc-catalog 2.1.0", ] [[package]] @@ -507,20 +498,26 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" +[[package]] +name = "crc-catalog" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" + [[package]] name = "crc32fast" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if", ] [[package]] name = "crypto-common" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ccfd8c0ee4cce11e45b3fd6f9d5e69e0cc62912aa6a0cb1bf4617b0eba5a12f" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", @@ -528,9 +525,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f39818dcfc97d45b03953c1292efc4e80954e1583c4aa770bac1383e2310a4" +checksum = "3f83d0ebf42c6eafb8d7c52f7e5f2d3003b89c7aa4fd2b79229209459a849af8" dependencies = [ "cc", "cxxbridge-flags", @@ -540,13 +537,13 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.62" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b49af8e551e84f85d6def97c42b8d93bc5bb0817cce96b56a472b1b19b5bfc2" +checksum = "07d050484b55975889284352b0ffc2ecbda25c0c55978017c132b29ba0818a86" dependencies = [ "cc", "codespan-reporting", - "lazy_static", + "once_cell", "proc-macro2", "quote", "scratch", @@ -555,15 +552,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56a46460b88d1cec95112c8c363f0e2c39afdb237f60583b0b36343bf627ea9c" +checksum = "99d2199b00553eda8012dfec8d3b1c75fce747cf27c169a270b3b99e3448ab78" [[package]] name = "cxxbridge-macro" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747b608fecf06b0d72d440f27acc99288207324b793be2c17991839f3d4995ea" +checksum = "dcb67a6de1f602736dd7eaead0080cf3435df806c61b24b13328db128c58868f" dependencies = [ "proc-macro2", "quote", @@ -572,9 +569,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.13.0" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ "darling_core", "darling_macro", @@ -582,9 +579,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.13.0" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", @@ -596,9 +593,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.13.0" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core", "quote", @@ -611,11 +608,22 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_more" -version = "0.99.16" +version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", "proc-macro2", @@ -624,11 +632,17 @@ dependencies = [ "syn", ] +[[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.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ "block-buffer", "crypto-common", @@ -655,9 +669,9 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", @@ -675,11 +689,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "dunce" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" + [[package]] name = "either" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "encode_unicode" @@ -689,9 +709,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.29" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74ea89a0a1b98f6332de42c95baff457ada66d1cb4030f9ff151b2041a1c746" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" dependencies = [ "cfg-if", ] @@ -708,11 +728,20 @@ dependencies = [ "syn", ] +[[package]] +name = "enum_primitive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" +dependencies = [ + "num-traits 0.1.43", +] + [[package]] name = "env_logger" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" dependencies = [ "atty", "humantime", @@ -721,16 +750,25 @@ dependencies = [ "termcolor", ] +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + [[package]] name = "filetime" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" +checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" dependencies = [ "cfg-if", "libc", "redox_syscall", - "winapi", + "windows-sys 0.36.1", ] [[package]] @@ -741,13 +779,11 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" dependencies = [ - "cfg-if", "crc32fast", - "libc", "miniz_oxide", ] @@ -757,7 +793,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2d3febd79960eb01b008e874319f1268b53dd5e7e54fca89b547b8a9685cebc" dependencies = [ - "ansi_term 0.12.1", + "ansi_term", "atty", "chrono", "glob", @@ -791,9 +827,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "futures" -version = "0.3.17" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" dependencies = [ "futures-channel", "futures-core", @@ -806,9 +842,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.17" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ "futures-core", "futures-sink", @@ -816,15 +852,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.17" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" [[package]] name = "futures-executor" -version = "0.3.17" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" dependencies = [ "futures-core", "futures-task", @@ -833,18 +869,16 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.17" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" [[package]] name = "futures-macro" -version = "0.3.17" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ - "autocfg", - "proc-macro-hack", "proc-macro2", "quote", "syn", @@ -852,23 +886,22 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.17" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" [[package]] name = "futures-task" -version = "0.3.17" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" [[package]] name = "futures-util" -version = "0.3.17" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ - "autocfg", "futures-channel", "futures-core", "futures-io", @@ -878,16 +911,14 @@ dependencies = [ "memchr", "pin-project-lite", "pin-utils", - "proc-macro-hack", - "proc-macro-nested", "slab", ] [[package]] name = "generic-array" -version = "0.14.4" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", @@ -895,13 +926,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -940,9 +971,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" dependencies = [ "bytes", "fnv", @@ -953,7 +984,7 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.7.3", + "tokio-util 0.7.4", "tracing", ] @@ -1006,13 +1037,13 @@ dependencies = [ [[package]] name = "http" -version = "0.2.5" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa 0.4.8", + "itoa", ] [[package]] @@ -1028,9 +1059,9 @@ dependencies = [ [[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" @@ -1046,9 +1077,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.16" +version = "0.14.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" dependencies = [ "bytes", "futures-channel", @@ -1059,7 +1090,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 0.4.8", + "itoa", "pin-project-lite", "socket2", "tokio", @@ -1097,9 +1128,9 @@ dependencies = [ [[package]] name = "iana-time-zone-haiku" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde6edd6cef363e9359ed3c98ba64590ba9eecba2293eb5a723ab32aee8926aa" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" dependencies = [ "cxx", "cxx-build", @@ -1154,6 +1185,15 @@ dependencies = [ "regex", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "ipconfig" version = "0.3.0" @@ -1192,39 +1232,33 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" [[package]] name = "jobserver" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] [[package]] name = "keccak" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" [[package]] name = "language-tags" @@ -1240,9 +1274,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" [[package]] name = "link-cplusplus" @@ -1255,9 +1289,9 @@ dependencies = [ [[package]] name = "linked-hash-map" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "local-channel" @@ -1279,9 +1313,9 @@ checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", @@ -1289,11 +1323,44 @@ dependencies = [ [[package]] name = "log" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", + "serde", +] + +[[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", + "chrono", + "derivative", + "fnv", + "humantime", + "libc", + "log", + "log-mdc", + "parking_lot", + "serde", + "serde-value", + "serde_json", + "serde_yaml 0.8.26", + "thiserror", + "thread-id", + "typemap-ors", + "winapi", ] [[package]] @@ -1319,9 +1386,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" @@ -1331,12 +1398,11 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "miniz_oxide" -version = "0.4.4" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" dependencies = [ "adler", - "autocfg", ] [[package]] @@ -1348,7 +1414,7 @@ dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -1365,28 +1431,37 @@ checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", - "num-traits", + "num-traits 0.2.15", ] [[package]] name = "num-traits" -version = "0.2.14" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.15", +] + +[[package]] +name = "num-traits" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ "hermit-abi", "libc", @@ -1409,9 +1484,18 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "once_cell" -version = "1.8.0" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + +[[package]] +name = "ordered-float" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" +dependencies = [ + "num-traits 0.2.15", +] [[package]] name = "parking_lot" @@ -1425,22 +1509,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] name = "paste" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" [[package]] name = "percent-encoding" @@ -1448,15 +1532,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - [[package]] name = "petgraph" version = "0.6.2" @@ -1469,18 +1544,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", @@ -1489,9 +1564,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" @@ -1590,9 +1665,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "proc-macro-error" @@ -1618,23 +1693,11 @@ dependencies = [ "version_check", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] @@ -1724,14 +1787,13 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core 0.6.3", - "rand_hc", + "rand_core 0.6.4", ] [[package]] @@ -1741,7 +1803,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -1761,27 +1823,18 @@ checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core 0.6.3", -] - [[package]] name = "raw-cpuid" -version = "10.2.0" +version = "10.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "929f54e29691d4e6a9cc558479de70db7aa3d98cd6fe7ab86d7507aa2886b9d2" +checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb" dependencies = [ "bitflags", ] @@ -1797,21 +1850,22 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.10" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "redox_users" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", "redox_syscall", + "thiserror", ] [[package]] @@ -1852,24 +1906,24 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] [[package]] name = "rustversion" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "scopeguard" @@ -1885,29 +1939,27 @@ checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" [[package]] name = "semver" -version = "0.11.0" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" [[package]] -name = "semver-parser" -version = "0.10.2" +name = "serde" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ - "pest", + "serde_derive", ] [[package]] -name = "serde" -version = "1.0.145" +name = "serde-value" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" dependencies = [ - "serde_derive", + "ordered-float", + "serde", ] [[package]] @@ -1923,43 +1975,42 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.70" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e277c495ac6cd1a01a58d0a0c574568b4d1ddf14f59965c6a58b8d96400b54f3" +checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" dependencies = [ - "itoa 0.4.8", + "itoa", "ryu", "serde", ] [[package]] name = "serde_urlencoded" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 0.4.8", + "itoa", "ryu", "serde", ] [[package]] name = "serde_with" -version = "1.11.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad6056b4cb69b6e43e3a0f055def223380baecc99da683884f205bf347f7c4b3" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" dependencies = [ - "rustversion", "serde", "serde_with_macros", ] [[package]] name = "serde_with_macros" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e47be9471c72889ebafb5e14d5ff930d89ae7a67bbdb5f8abb564f845a927e" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ "darling", "proc-macro2", @@ -1967,6 +2018,18 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + [[package]] name = "serde_yaml" version = "0.9.13" @@ -1974,7 +2037,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8613d593412a0deb7bbd8de9d908efff5a0cb9ccd8f62c641e7b2ed2f57291d1" dependencies = [ "indexmap", - "itoa 1.0.2", + "itoa", "ryu", "serde", "unsafe-libyaml", @@ -1982,9 +2045,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", @@ -1993,9 +2056,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881bf8156c87b6301fc5ca6b27f11eeb2761224c7081e69b409d5a1951a70c86" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" dependencies = [ "digest", "keccak", @@ -2012,21 +2075,24 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" -version = "1.7.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi", @@ -2055,9 +2121,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "structopt" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" dependencies = [ "clap", "lazy_static", @@ -2079,9 +2145,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.101" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" dependencies = [ "proc-macro2", "quote", @@ -2090,9 +2156,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f5515d3add52e0bbdcad7b83c388bb36ba7b754dda3b5f5bc2d38640cdba5c" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" dependencies = [ "filetime", "libc", @@ -2111,13 +2177,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ "cfg-if", + "fastrand", "libc", - "rand 0.8.4", "redox_syscall", "remove_dir_all", "winapi", @@ -2125,9 +2191,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] @@ -2153,41 +2219,53 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "thread-id" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fdfe0627923f7411a43ec9ec9c39c3a9b4151be313e0922042581fb6c9b717f" +dependencies = [ + "libc", + "redox_syscall", + "winapi", +] + [[package]] name = "time" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] [[package]] name = "time" -version = "0.3.11" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" +checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" dependencies = [ - "itoa 1.0.2", + "itoa", "libc", "num_threads", "time-macros", @@ -2201,9 +2279,9 @@ checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" [[package]] name = "tinyvec" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -2255,6 +2333,17 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-stream" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.6.10" @@ -2271,9 +2360,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" dependencies = [ "bytes", "futures-core", @@ -2285,24 +2374,24 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] [[package]] name = "tower-service" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.34" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "log", @@ -2313,9 +2402,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", @@ -2324,11 +2413,11 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.26" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ - "lazy_static", + "once_cell", ] [[package]] @@ -2347,7 +2436,7 @@ dependencies = [ "idna 0.2.3", "ipnet", "lazy_static", - "rand 0.8.4", + "rand 0.8.5", "smallvec", "thiserror", "tinyvec", @@ -2383,49 +2472,61 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] -name = "typenum" -version = "1.14.0" +name = "typemap-ors" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" +checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867" +dependencies = [ + "unsafe-any-ors", +] [[package]] -name = "ucd-trie" -version = "0.1.3" +name = "typenum" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unsafe-any-ors" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad" +dependencies = [ + "destructure_traitobject", +] [[package]] name = "unsafe-libyaml" @@ -2452,9 +2553,9 @@ checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" [[package]] name = "uuid" -version = "1.1.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" +checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" dependencies = [ "getrandom", ] @@ -2467,9 +2568,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vte" @@ -2504,9 +2605,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wasi" @@ -2516,9 +2617,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2526,13 +2627,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -2541,9 +2642,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2551,9 +2652,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", @@ -2564,9 +2665,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "which" @@ -2622,43 +2723,100 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + [[package]] name = "winreg" version = "0.7.0" @@ -2670,9 +2828,9 @@ dependencies = [ [[package]] name = "xattr" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" dependencies = [ "libc", ] @@ -2691,7 +2849,7 @@ dependencies = [ "serde", "serde_json", "tokio", - "tokio-util 0.7.3", + "tokio-util 0.7.4", "url", ] @@ -2708,7 +2866,7 @@ dependencies = [ "log", "serde", "serde_json", - "serde_yaml", + "serde_yaml 0.9.13", "structopt", "tokio", "toml", @@ -2732,12 +2890,13 @@ version = "0.3.0" dependencies = [ "anyhow", "bollard-stubs", - "crc 1.8.1", + "crc 3.0.0", + "dunce", "env_logger", "futures", "log", "pnet", - "rand 0.8.4", + "rand 0.8.5", "raw-cpuid", "serde", "serde_json", @@ -2749,6 +2908,37 @@ dependencies = [ "url", "uuid", "ya-runtime-sdk", + "ya-vm-file-server", +] + +[[package]] +name = "ya-vm-file-server" +version = "0.2.3" +source = "git+https://github.com/golemfactory/ya-vm-file-server#d5f0c9b9c74ff92fad7514e8a0410618ef7a42ec" +dependencies = [ + "anyhow", + "async-trait", + "bitflags", + "byteorder", + "bytes", + "enum_primitive", + "filetime", + "futures", + "log", + "log4rs", + "num-traits 0.2.15", + "tokio", + "tokio-stream", + "tokio-util 0.7.4", +] + +[[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]] diff --git a/README.md b/README.md index 8c4c4edb..925a3f69 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ya-runtime-vm -`ya-runtime-vm` is an implementation of a Docker-like runtime environment for Linux systems. +`ya-runtime-vm` is an implementation of a Docker-like runtime environment for Linux systems. This repository consists of 2 crates: @@ -18,7 +18,7 @@ This repository consists of 2 crates: Prerequisites: - `rustc` - + Recommendation: use the Rust toolchain installer from [https://rustup.rs/](https://rustup.rs/) - `musl-gcc` @@ -36,6 +36,20 @@ cd runtime cargo build ``` +### Building under Windows +Using visual studio build tools install: +* MVSC C++ x64/x86 build tools +* Win 10 SDK + +Examples has `pnet` dependency which annoying to use under Windows. +To do so, follow steps in [Here](https://github.com/libpnet/libpnet#windows) +* Install WinPcap +* Download [WiniCap developers pack](https://www.winpcap.org/devel.htm) +* Extract Lib.rs from it (note x64/x86 version!) +* Copy to your toolchain directory `C:\Users\USERNAME\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\x86_64-pc-windows-msvc\lib` + + + ## Installing Prerequisites: @@ -75,8 +89,8 @@ FLAGS: -V, --version Prints version information OPTIONS: - -w, --workdir - -t, --task-package + -w, --workdir + -t, --task-package --cpu-cores [default: 1] --mem-gib [default: 0.25] --storage-gib [default: 0.25] @@ -95,6 +109,22 @@ SUBCOMMANDS: Directories specified in the `VOLUME` command are a mountpoint for directories on the host filesystem. Contents of those directories will appear as empty during execution. - + If you need to place static assets inside the image, try not to use the `VOLUME` command for that directory. +## Running examples +* Some of the examples require ya-runtime-vm installed, so follow [Installing](#installing) paragraph first. +* Create a .gvmi image used by examples with following steps: + * Build docker image + ``` + cd runtime/examples + docker build -t ya-runtime-vm-examples . + ``` + * Convert to gvmi + ``` + gvmkit ya-runtime-vm-examples --output /path/to/ya-runtime-vm/squashfs_drive + ``` +* Then run: + ``` + cargo run --example EXAMPLE_NAME + ``` diff --git a/qemu-win10/.gitattributes b/qemu-win10/.gitattributes new file mode 100644 index 00000000..5135af0e --- /dev/null +++ b/qemu-win10/.gitattributes @@ -0,0 +1 @@ +*.exe filter=lfs diff=lfs merge=lfs -text diff --git a/qemu-win10/qemu-system-x86_64.exe b/qemu-win10/qemu-system-x86_64.exe new file mode 100644 index 00000000..b5ad91d1 --- /dev/null +++ b/qemu-win10/qemu-system-x86_64.exe @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1dc918997fde64a2a57963dca04e616c1855cdd7b49563ab382677bbd2cc3a8a +size 13918463 diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 1f86419e..76cb1bd5 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -26,9 +26,11 @@ version = "0.4" features = ["macros", "logger"] [dependencies] +ya-vm-file-server = {git = "https://github.com/golemfactory/ya-vm-file-server", version = "0.2.3"} anyhow = "1.0" bollard-stubs = "1.40.2" -crc = "1.8" +crc = "3.0" +dunce = "1.0.3" futures = "0.3" log = "0.4.8" rand = "0.8" diff --git a/runtime/build.rs b/runtime/build.rs index 57a145eb..bf03d7bd 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -7,6 +7,11 @@ use std::process::Command; static RERUN_IF_CHANGED: &str = "cargo:rerun-if-changed"; fn main() -> anyhow::Result<()> { + // skip build under Windows + if cfg!(windows) { + return Ok(()); + } + // skip build for CI if env::var("CI").is_ok() { return Ok(()); @@ -30,28 +35,25 @@ fn main() -> anyhow::Result<()> { } println!( - "{}", - format!( - r#" - {rerun}={root}/Makefile - {rerun}={include}/communication.h - {rerun}={include}/cyclic_buffer.h - {rerun}={include}/forward.h - {rerun}={include}/network.h - {rerun}={include}/process_bookkeeping.h - {rerun}={include}/proto.h - {rerun}={src}/communication.c - {rerun}={src}/cyclic_buffer.c - {rerun}={src}/forward.c - {rerun}={src}/network.c - {rerun}={src}/process_bookkeeping.c - {rerun}={src}/init.c - "#, - rerun = RERUN_IF_CHANGED, - root = init_dir.display(), - include = include_dir.display(), - src = src_dir.display(), - ) + r#" + {rerun}={root}/Makefile + {rerun}={include}/communication.h + {rerun}={include}/cyclic_buffer.h + {rerun}={include}/forward.h + {rerun}={include}/network.h + {rerun}={include}/process_bookkeeping.h + {rerun}={include}/proto.h + {rerun}={src}/communication.c + {rerun}={src}/cyclic_buffer.c + {rerun}={src}/forward.c + {rerun}={src}/network.c + {rerun}={src}/process_bookkeeping.c + {rerun}={src}/init.c + "#, + rerun = RERUN_IF_CHANGED, + root = init_dir.display(), + include = include_dir.display(), + src = src_dir.display(), ); Ok(()) } diff --git a/runtime/examples/Dockerfile b/runtime/examples/Dockerfile new file mode 100644 index 00000000..b35c22e3 --- /dev/null +++ b/runtime/examples/Dockerfile @@ -0,0 +1,4 @@ +FROM alpine:latest +RUN apk add --no-cache bash +VOLUME /golem/input +WORKDIR /golem/work diff --git a/runtime/examples/commands.rs b/runtime/examples/commands.rs index 9f985744..2b8c8d92 100644 --- a/runtime/examples/commands.rs +++ b/runtime/examples/commands.rs @@ -97,7 +97,13 @@ async fn main() -> anyhow::Result<()> { let temp_dir = tempdir::TempDir::new("ya-runtime-vm")?; let temp_dir_string = temp_dir.path().display().to_string(); - let drive_path_string = root_dir.join("squashfs_drive").display().to_string(); + let drive_path_string = root_dir + .join("runtime") + .join("poc") + .join("squashfs") + .join("ubuntu.gvmi") + .display() + .to_string(); let args = [ "--task-package", @@ -106,6 +112,7 @@ async fn main() -> anyhow::Result<()> { temp_dir_string.as_str(), ]; + //TODO - this seems to be hardcoded too much. How can it work on Win? let runtime_path = PathBuf::from("/usr/lib/yagna/plugins/ya-runtime-vm/ya-runtime-vm"); let mut cmd = Command::new(&runtime_path); diff --git a/runtime/examples/fs_benchmark.rs b/runtime/examples/fs_benchmark.rs new file mode 100644 index 00000000..e686f74c --- /dev/null +++ b/runtime/examples/fs_benchmark.rs @@ -0,0 +1,276 @@ +use futures::future; +use std::time::{Duration, Instant}; + +use structopt::StructOpt; + +use ya_runtime_sdk::runtime_api::deploy::ContainerVolume; + +use ya_runtime_vm::local_spawn_vm::{prepare_mount_directories, prepare_tmp_path, spawn_vm}; + +use std::sync::Arc; +use tokio::io; +use ya_runtime_vm::local_notification_handler::{ + start_local_agent_communication, LocalAgentCommunication, +}; + +/// Write for one byte to the file, create as many tasks as there are mount points +#[allow(dead_code)] +async fn test_parallel_write_small_chunks( + mount_args: Arc>, + comm: Arc, +) { + let mut tasks = vec![]; + + for ContainerVolume { name, path } in mount_args.as_ref() { + let cmd = format!("for i in {{1..100}}; do echo -ne a >> {path}/small_chunks; done; cat {path}/small_chunks"); + + let name = name.clone(); + let path = path.clone(); + let comm = comm.clone(); + let join = tokio::spawn(async move { + log::info!("Spawning task for {name}"); + test_write(comm.clone(), &cmd).await.unwrap(); + + { + comm.run_command("/bin/ls", &["ls", "-la", &path]) + .await + .unwrap(); + } + }); + + tasks.push(join); + } + + log::info!("Joining..."); + future::join_all(tasks).await; +} + +async fn test_parallel_write_big_chunk( + test_file_size: u64, + mount_args: &[ContainerVolume], + comm: Arc, +) { + // prepare chunk + + { + let start = Instant::now(); + // List files + let block_size = 1000000; + //comm.run_bash_command("touch /mnt/mnt1/tag0/big_file").await.unwrap(); + comm.run_command( + "/bin/dd", + &[ + "dd", + // TODO: /dev/random causes problems? + "if=/dev/urandom", + "of=/mnt/mnt1/tag0/big_file", + std::format!("bs={}", block_size).as_str(), + std::format!("count={}", test_file_size / block_size).as_str(), + ], + ) + .await + .unwrap(); + + //comm.run_bash_command(&format!("head -c {} /mnt/mnt1/tag0/big_file", test_file_size)).await.unwrap(); + /*comm.run_command( + "/bin/busybox", + &[ + "shred", + // TODO: /dev/random causes problems? + "-n", + "1", + "-s", + std::format!("{}", test_file_size).as_str(), + "/mnt/mnt1/tag0/big_file" + ], + ) + .await + .unwrap(); + */ + let test_file_size = test_file_size / block_size * block_size; + + let duration = start.elapsed(); + let time_in_secs = duration.as_secs_f64(); + let speed_mbs = test_file_size as f64 / time_in_secs / 1000.0 / 1000.0; + println!( + "File generated in {:.3}s. {:.3}MB/s", + time_in_secs, speed_mbs + ); + } + + let mut tasks = vec![]; + let start = Instant::now(); + + for ContainerVolume { name, path } in mount_args.as_ref() { + // let cmd = format!("A=\"A\"; for i in {{1..24}}; do A=\"${{A}}${{A}}\"; done; echo -ne $A >> {path}/big_chunk"); + let cmd = format!("cat /mnt/mnt1/tag0/big_file > {path}/big_chunk;"); + // let cmd = format!("cp /mnt/mnt1/tag0/big_file /{path}/big_chunk"); + + let name = name.clone(); + let path = path.clone(); + let comm = comm.clone(); + let join = tokio::spawn(async move { + log::info!("Spawning task for {name}"); + let start = Instant::now(); + test_write(comm.clone(), &cmd).await.unwrap(); + + log::info!( + "Copy big chunk for {name} took {:.2}s", + start.elapsed().as_secs_f64() + ); + { + // List files + comm.run_command("/bin/ls", &["ls", "-l", &path]) + .await + .unwrap(); + } + }); + + tasks.push(join); + } + + log::info!("Joining..."); + future::join_all(tasks).await; + let duration = start.elapsed(); + let time_in_secs = duration.as_secs_f64(); + let speed_mbs = + mount_args.len() as f64 * test_file_size as f64 / time_in_secs / 1000.0 / 1000.0; + println!( + "Files ({}) copied in {:.3}s. {:.3}MB/s", + mount_args.len(), + time_in_secs, + speed_mbs + ); +} + +#[allow(dead_code)] +async fn test_fio(mount_args: Arc>, comm: LocalAgentCommunication) { + for ContainerVolume { name: _, path } in mount_args.iter() { + // TODO: this is serialized + comm.run_command( + "/usr/bin/fio", + &[ + "fio", + "--randrepeat=1", + "--ioengine=libaio", + "--direct=1", + "--gtod_reduce=1", + "--name=test", + "--bs=4k", + "--iodepth=64", + "--readwrite=randrw", + "--rwmixread=75", + "--size=100M", + "--max-jobs=4", + "--numjobs=4", + &format!("--filename={path}/test_fio"), + ], + ) + .await + .unwrap(); + } +} + +#[derive(Debug, StructOpt)] +#[structopt(name = "options", about = "Options for performance benchmark")] +pub struct Opt { + /// Number of logical CPU cores + #[structopt(long, default_value = "1")] + cpu_cores: usize, + /// Amount of RAM [GiB] + #[structopt(long, default_value = "0.25")] + mem_gib: f64, + /// Amount of disk storage [GiB] + #[allow(unused)] + #[structopt(long, default_value = "0.25")] + storage_gib: f64, + /// Number of mounts + #[allow(unused)] + #[structopt(long, default_value = "3")] + mount_count: u32, + /// File size to test in bytes [bytes] + #[allow(unused)] + #[structopt(long, default_value = "10000000")] + file_test_size: u64, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let opt: Opt = Opt::from_args(); + env_logger::init(); + + log::info!("Running example fs_benchmark..."); + + let temp_path = prepare_tmp_path(); + let mount_args = prepare_mount_directories(&temp_path, 2); + + let mut vm_runner = spawn_vm(&temp_path, opt.cpu_cores, opt.mem_gib, false).await?; + + //let VM start before trying to connect p9 service + tokio::time::sleep(Duration::from_secs_f64(2.5)).await; + + vm_runner + .start_9p_service(&temp_path, &mount_args) + .await + .unwrap(); + + let comm = start_local_agent_communication(vm_runner.get_vm().get_manager_sock()).await?; + + comm.run_mount(&mount_args).await?; + + comm.run_bash_command("ls -la /mnt/mnt1").await?; + // test_parallel_write_small_chunks(mount_args.clone(), ga_mutex.clone(), notifications.clone()) + // .await; + + test_parallel_write_big_chunk(opt.file_test_size, &mount_args, comm.clone()).await; + + log::info!("test_parallel_write_big_chunk finished"); + + { + //run_process_with_output(&mut ga, ¬ifications, "/bin/ps", &["ps", "aux"]).await?; + //comm.run_command("/bin/ls", &["ls", "-la", "/dev"]).await?; + comm.run_bash_command("ls -la /dev;sleep 0.5").await?; + } + + { + //run_process_with_output(&mut ga, ¬ifications, "/bin/ps", &["ps", "aux"]).await?; + comm.run_bash_command("top -b -n 1").await?; + } + + vm_runner.stop_p9_service().await; + /* VM should quit now. */ + //let e = timeout(Duration::from_secs(5), vm_runner..wait()).await; + vm_runner.stop_vm(&Duration::from_secs(5), true).await?; + //log::info!("{:?}", e); + //child.kill().await.unwrap(); + + Ok(()) +} + +async fn test_write(comm: Arc, cmd: &str) -> io::Result<()> { + log::info!("***** test_big_write *****"); + + let argv = ["bash", "-c", &cmd]; + + comm.run_command("/bin/bash", &argv).await?; + + // log::info!("waiting on output for {id}"); + // notifications + // .get_output_available_notification(id) + // .await + // .notified() + // .await; + + // log::info!("waiting on query for {id}"); + // let out = { + // let mut ga = ga.lock().await; + // ga.query_output(id, 1, 0, u64::MAX) + // .await? + // .expect("Output query failed") + // }; + + // log::info!("Cmd output: {cmd}"); + // log::info!("Output len: {}", out.len()); + + Ok(()) +} diff --git a/runtime/examples/min_run.rs b/runtime/examples/min_run.rs new file mode 100644 index 00000000..5efb2a50 --- /dev/null +++ b/runtime/examples/min_run.rs @@ -0,0 +1,48 @@ +use std::time::Duration; +use structopt::StructOpt; +use ya_runtime_vm::local_notification_handler::start_local_agent_communication; +use ya_runtime_vm::local_spawn_vm::{prepare_mount_directories, prepare_tmp_path, spawn_vm}; + +#[derive(Debug, StructOpt)] +#[structopt(name = "options", about = "Options for VM")] +pub struct Opt { + /// Number of logical CPU cores + #[structopt(long, default_value = "1")] + cpu_cores: usize, + /// Amount of RAM [GiB] + #[structopt(long, default_value = "0.25")] + mem_gib: f64, + /// Amount of disk storage [GiB] + #[allow(unused)] + #[structopt(long, default_value = "0.25")] + storage_gib: f64, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let opt: Opt = Opt::from_args(); + env_logger::init(); + + log::info!("Running example fs_benchmark..."); + + let temp_path = prepare_tmp_path(); + let mount_args = prepare_mount_directories(&temp_path, 2); + + let mut vm_runner = spawn_vm(&temp_path, opt.cpu_cores, opt.mem_gib, false).await?; + + //let VM start before trying to connect p9 service + tokio::time::sleep(Duration::from_secs_f64(2.5)).await; + + vm_runner.start_9p_service(&temp_path, &mount_args).await?; + + let comm = start_local_agent_communication(vm_runner.get_vm().get_manager_sock()).await?; + + comm.run_mount(&mount_args).await?; + + vm_runner.stop_p9_service().await; + /* VM should quit now. */ + //let e = timeout(Duration::from_secs(5), vm_runner..wait()).await; + vm_runner.stop_vm(&Duration::from_secs(5), true).await?; + + Ok(()) +} diff --git a/runtime/init-container/Makefile b/runtime/init-container/Makefile index 9894a4f7..5417ae84 100644 --- a/runtime/init-container/Makefile +++ b/runtime/init-container/Makefile @@ -1,12 +1,18 @@ CC := musl-gcc CXX := /bin/false # -MMD to create dependency files (*.d) on first compilation +ifdef IGNORE_WARNINGS +CFLAGS := -MMD -std=c11 -O2 -fPIE -pie -Iinclude/ +else CFLAGS := -MMD -std=c11 -O2 -Wall -Wextra -Werror -fPIE -pie -Iinclude/ +endif ifneq ($(DEBUG), "") CFLAGS += -DNDEBUG endif +CFLAGS += -DWIN_P9_EXTRA_DEBUG_INFO=0 + ifneq ($(findstring $(MAKEFLAGS),s),s) ifndef V QUIET_CC = @echo ' ' CC $@; @@ -26,7 +32,7 @@ LIBURING_SUBMODULE ?= liburing SRC_DIR ?= src TEST_DIR ?= tests -OBJECTS = $(addprefix $(SRC_DIR)/,init.o communication.o process_bookkeeping.o cyclic_buffer.o) +OBJECTS = $(addprefix $(SRC_DIR)/,init.o communication.o communication_win_p9.o process_bookkeeping.o cyclic_buffer.o) OBJECTS_EXT = $(addprefix $(SRC_DIR)/,network.o forward.o) # Add headers to object dependencies for conditional recompilation on header change @@ -66,7 +72,7 @@ $(SRC_DIR)/forward.o: $(SRC_DIR)/forward.c uring $(QUIET_CC)$(CC) $(CFLAGS) -o $@ -c $< init: $(UNPACKED_HEADERS) uring $(OBJECTS) $(OBJECTS_EXT) - @echo init + @echo "Compile params for init:" $(CC) $(CFLAGS) $(QUIET_CC)$(CC) $(CFLAGS) -static -o $@ $(wordlist 3, $(words $^), $^) "$(CURDIR)/$(LIBURING_SUBMODULE)/src/liburing.a" @# default musl libs on some distros have debug symbols, lets strip them (and everything else) strip $@ @@ -109,6 +115,12 @@ initramfs.cpio.gz: init $(UNPACKED_KERNEL) cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/drivers/net/net_failover.ko initramfs cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/drivers/net/virtio_net.ko initramfs cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/fs/9p/9p.ko initramfs + cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/fs/ext4/ext4.ko initramfs + cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/fs/mbcache.ko initramfs + cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/fs/jbd2/jbd2.ko initramfs + cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/lib/crc16.ko initramfs + cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/crypto/crc32c_generic.ko initramfs + cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/lib/libcrc32c.ko initramfs cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/fs/squashfs/squashfs.ko initramfs cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/fs/overlayfs/overlay.ko initramfs cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/fs/fscache/fscache.ko initramfs @@ -117,7 +129,7 @@ initramfs.cpio.gz: init $(UNPACKED_KERNEL) cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/net/core/failover.ko initramfs cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/net/ipv6/ipv6.ko initramfs cp $(UNPACKED_KERNEL)/lib/modules/$(KERNEL_VER)/kernel/net/packet/af_packet.ko initramfs - cd initramfs && find . | cpio --quiet -o -H newc -R 0:0 | gzip -9 > ../$@ + cd initramfs && find . | cpio -v -o -H newc -R 0:0 | gzip -9 > ../$@ $(RM) -rf initramfs TESTS_NAMES := cyclic_buffer @@ -134,7 +146,7 @@ test: $(TESTS) .PHONY: clean clean: - $(RM) init $(SRC_DIR)/*.o $(TEST_DIR)/*.o *.o $(TESTS) + $(RM) init $(SRC_DIR)/*.o $(SRC_DIR)/*.d $(TEST_DIR)/*.o *.o $(TESTS) $(RM) vmlinuz-virt initramfs.cpio.gz $(MAKE) -s -C $(LIBURING_SUBMODULE) clean diff --git a/runtime/init-container/include/communication_win_p9.h b/runtime/init-container/include/communication_win_p9.h new file mode 100644 index 00000000..553292f0 --- /dev/null +++ b/runtime/init-container/include/communication_win_p9.h @@ -0,0 +1,23 @@ +#ifndef _COMMUNICATION_WIN_P9_H +#define _COMMUNICATION_WIN_P9_H + +#include +#include +#include + + +int initialize_p9_socket_descriptors(int max_p9_message_size); +uint32_t do_mount_win_p9(const char* tag, uint8_t channel, uint32_t max_p9_message_size, char* path); + +#define MAX_P9_VOLUMES (100) +//this variable has nothing to do with max p9 message size, it's just read buffer for demux communication +extern int g_p9_initialized; +extern int g_p9_fd; +extern int g_p9_current_channel; +extern int g_p9_socket_fds[MAX_P9_VOLUMES][2]; + +extern pthread_t g_p9_tunnel_thread_sender[MAX_P9_VOLUMES]; +extern pthread_mutex_t g_p9_tunnel_mutex_sender; +extern pthread_t g_p9_tunnel_thread_receiver; + +#endif // _COMMUNICATION_WIN_P9_H \ No newline at end of file diff --git a/runtime/init-container/include/proto.h b/runtime/init-container/include/proto.h index 1046bc43..636101da 100644 --- a/runtime/init-container/include/proto.h +++ b/runtime/init-container/include/proto.h @@ -111,6 +111,8 @@ enum SUB_MSG_MOUNT_VOLUME_TYPE { SUB_MSG_MOUNT_VOLUME_END = 0, /* Mount tag. (BYTES) */ SUB_MSG_MOUNT_VOLUME_TAG, + /* Mount message size. (BYTES) */ + SUB_MSG_MOUNT_VOLUME_MAX_P9_MESSAGE_SIZE, /* Path to mount at. (BYTES) */ SUB_MSG_MOUNT_VOLUME_PATH, }; diff --git a/runtime/init-container/run.py b/runtime/init-container/run.py new file mode 100644 index 00000000..863eaa56 --- /dev/null +++ b/runtime/init-container/run.py @@ -0,0 +1,28 @@ +"""Helper script for running VM manually under Windows""" +import os +command = "" +command += "qemu-system-x86_64.exe " +command += "-m 1G " +command += "-nographic -vga none " +command += "-kernel vmlinuz-virt " +command += "-initrd initramfs.cpio.gz " +command += "-net none -smp 2 " +command += '-append "console=ttyS0 panic=1" ' +command += "-device virtio-serial " +command += "-chardev socket,id=manager_cdev,host=127.0.0.1,port=9003,server,nowait " +command += "-chardev socket,id=net_cdev,host=127.0.0.1,port=9004,server,nowait " +command += "-chardev socket,id=p9_cdev,host=127.0.0.1,port=9005,server,nowait " +command += "-device virtserialport,chardev=manager_cdev,name=manager_port " +command += "-device virtserialport,chardev=net_cdev,name=net_port " +command += "-device virtserialport,chardev=p9_cdev,name=p9_port " +command += "-drive file=ubuntu.gvmi,cache=unsafe,readonly=on,format=raw,if=virtio " +command += "-drive file=empty_10GB.qcow2,format=qcow2,if=virtio " +command += "-no-reboot " +command += "-accel whpx " +command += "-nodefaults " +command += "--serial stdio " + +print(command) +os.system(command) + + diff --git a/runtime/init-container/src/communication_win_p9.c b/runtime/init-container/src/communication_win_p9.c new file mode 100644 index 00000000..62c795c1 --- /dev/null +++ b/runtime/init-container/src/communication_win_p9.c @@ -0,0 +1,363 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "communication_win_p9.h" + +int g_p9_initialized = 0; +int g_p9_fd = -1; +int g_p9_current_channel = 0; +int g_p9_socket_fds[MAX_P9_VOLUMES][2]; +pthread_t g_p9_tunnel_thread_sender[MAX_P9_VOLUMES]; +pthread_mutex_t g_p9_tunnel_mutex_sender; +pthread_t g_p9_tunnel_thread_receiver; + +//HACK - move it somewhere else +int create_dir_path(char* path); + +static int read_exact(int fd, void* buf, size_t size) { + int bytes_read = 0; + while (size) { + ssize_t ret = read(fd, buf, size); + if (ret == 0) { + return 0; + } + if (ret < 0) { + if (errno == EINTR) { + continue; + } + /* `errno` should be set. */ + return ret; + } + bytes_read += ret; + buf = (char*)buf + ret; + size -= ret; + } + return bytes_read; +} + +static int write_exact(int fd, const void* buf, size_t size) { + int bytes_written = 0; + while (size) { + ssize_t ret = write(fd, buf, size); + if (ret == 0) { + puts("written: WAITING FOR HOST (2) ..."); + sleep(1); + continue; + } + if (ret < 0) { + if (errno == EINTR) { + continue; + } + /* `errno` should be set. */ + return -1; + } + bytes_written += ret; + buf = (char*)buf + ret; + size -= ret; + } + return bytes_written; +} + +struct tunnel_from_p9_to_sock_params { + uint32_t max_packet_size; +}; + +static void* tunnel_from_p9_virtio_to_sock(void *data) { + struct tunnel_from_p9_to_sock_params* params = (struct tunnel_from_p9_to_sock_params*)data; + uint32_t max_packet_size = params->max_packet_size; + free(params); + params = NULL; + data = NULL; + + const int bufferSize = max_packet_size; + char* buffer = malloc(bufferSize + sizeof(uint32_t) + sizeof(uint8_t)); + + //experimental - set thread affinity to get better performance on Windows? + { + pthread_t thread; + thread = pthread_self(); + pthread_attr_t attr; + cpu_set_t cpus; + pthread_attr_init(&attr); + CPU_ZERO(&cpus); + CPU_SET(1, &cpus); + pthread_setaffinity_np(thread, sizeof(cpus), &cpus); + } + + + while (true) { + ssize_t bytes_read = 0; + + bytes_read = read_exact(g_p9_fd, buffer, sizeof(uint8_t) + sizeof(uint32_t)); + if (bytes_read == 0) { + goto success; + } + + if (bytes_read != sizeof(uint8_t) + sizeof(uint32_t)) { + fprintf(stderr, "Error during read from g_p9_fd: bytes_read != sizeof(channel)\n"); + goto error; + } + uint8_t channel = *(uint8_t*)buffer; + uint32_t packet_size = *(uint32_t*)(&buffer[1]); + + if (packet_size > max_packet_size) { + fprintf(stderr, "Error: Maximum packet size exceeded: packet_size > MAX_PACKET_SIZE\n"); + goto error; + } + + bytes_read = read_exact(g_p9_fd, buffer + sizeof(uint32_t) + sizeof(uint8_t), packet_size); + if (bytes_read != packet_size) { + fprintf(stderr, "Error during read from g_p9_fd: bytes_read != packet_size\n"); + goto error; + } + +#if WIN_P9_EXTRA_DEBUG_INFO + fprintf(stderr, "RECEIVE MESSAGE %ld\n", bytes_read); +#endif + if (bytes_read == -1) { + fprintf(stderr, "Error during read from g_p9_fd: bytes_read == -1\n"); + goto error; + } + if (write_exact(g_p9_socket_fds[channel][1], buffer + sizeof(uint32_t) + sizeof(uint8_t), bytes_read) == -1) { + fprintf(stderr, "Error writing to g_p9_socket_fds\n"); + goto error; + } + } +success: + free(buffer); + return (void*)0; +error: + free(buffer); + return (void*)-1; +} + +struct tunnel_from_p9_params { + uint8_t channel; + uint32_t max_packet_size; + uint32_t benchmark_loops; +}; + +static void* tunnel_from_p9_sock_to_virtio(void *data) { + struct tunnel_from_p9_params* params = (struct tunnel_from_p9_params*)data; + uint8_t channel = params->channel; + uint32_t max_packet_size = params->max_packet_size; + uint32_t benchmark_loops = params->benchmark_loops; + free(params); + params = NULL; + data = NULL; + + assert(channel_wide_int < MAX_P9_VOLUMES); + assert(channel == channel_wide_int); + +#if WIN_P9_EXTRA_DEBUG_INFO + fprintf(stderr, "P9 sender thread started channel: %d\n", channel); +#endif + + const int bufferSize = max_packet_size; + char* buffer = malloc(sizeof(uint8_t) + sizeof(uint32_t) + bufferSize); + + //experimental - set thread affinity to get better performance on Windows? + { + pthread_t thread; + thread = pthread_self(); + pthread_attr_t attr; + cpu_set_t cpus; + pthread_attr_init(&attr); + CPU_ZERO(&cpus); + CPU_SET(1, &cpus); + pthread_setaffinity_np(thread, sizeof(cpus), &cpus); + } + + //run benchmark if benchmark_loops >= 10, lower values don't make any sense. + if (benchmark_loops >= 10) { + fprintf(stderr, "Benchmark started: \n"); + //fill data + *((uint8_t*)buffer) = 249; + *((uint32_t*)&buffer[1]) = (uint32_t)max_packet_size; + for (uint32_t i = 0; i < max_packet_size; i++) { + buffer[i + sizeof(uint8_t) + sizeof(uint32_t)] = (uint8_t)(i % 256); + } + int loop_count = benchmark_loops; + for (int loop = 0; loop < loop_count; loop += 1) { + if (pthread_mutex_lock(&g_p9_tunnel_mutex_sender)) { + fprintf(stderr, "pthread_mutex_lock failed\n"); + return (void*)(int64_t)errno; + } + if (loop == loop_count - 1) { + buffer[sizeof(uint8_t) + sizeof(uint32_t)] = 255; //start benchmark + } else { + buffer[sizeof(uint8_t) + sizeof(uint32_t)] = 1; //continue benchmark + } + + bool write_succeeded = true; + + if (write_exact(g_p9_fd, buffer, max_packet_size + sizeof(uint8_t) + sizeof(uint32_t)) == -1) { + fprintf(stderr, "Failed write g_p9_fd 3\n"); + write_succeeded = false; + goto mutex_unlock_benchmark; + } +mutex_unlock_benchmark: + if (pthread_mutex_unlock(&g_p9_tunnel_mutex_sender)) { + fprintf(stderr, "pthread_mutex_unlock failed\n"); + return (void*)(int64_t)errno; + } + if (!write_succeeded) { + return (void*)(int64_t)errno; + } + } + fprintf(stderr, "Benchmark finished: \n"); + } + + while (true) { + ssize_t bytes_read = recv(g_p9_socket_fds[channel][1], buffer + sizeof(uint32_t) + sizeof(uint8_t), bufferSize, 0); + + if (bytes_read == 0) { + free(buffer); + return NULL; + } + + if (bytes_read == -1) { + free(buffer); + return (void*)(int64_t)errno; + } + + if (pthread_mutex_lock(&g_p9_tunnel_mutex_sender)) { + fprintf(stderr, "pthread_mutex_lock failed\n"); + return (void*)(int64_t)errno; + } + +#if WIN_P9_EXTRA_DEBUG_INFO + fprintf(stderr, "send message to channel %d, length: %ld\n", channel, bytes_read); +#endif + + bool write_succeeded = true; + + *((uint8_t*)buffer) = channel; + *((uint32_t*)&buffer[1]) = (uint32_t)bytes_read; + + if (write_exact(g_p9_fd, buffer, bytes_read + sizeof(uint8_t) + sizeof(uint32_t)) == -1) { + fprintf(stderr, "Failed write g_p9_fd 3\n"); + write_succeeded = false; + goto mutex_unlock; + } + +mutex_unlock: + if (pthread_mutex_unlock(&g_p9_tunnel_mutex_sender)) { + fprintf(stderr, "pthread_mutex_unlock failed\n"); + return (void*)(int64_t)errno; + } + if (!write_succeeded) { + return (void*)(int64_t)errno; + } + } +} + +int initialize_p9_socket_descriptors(int max_p9_message_size) { + if (!g_p9_initialized) { + for (int i = 0; i < MAX_P9_VOLUMES; i++) { + g_p9_socket_fds[i][0] = -1; + g_p9_socket_fds[i][1] = -1; + } + + if (pthread_mutex_init(&g_p9_tunnel_mutex_sender, NULL) == -1) { + fprintf(stderr, "Error: pthread_mutex_init error\n"); + return -1; + } + + struct tunnel_from_p9_to_sock_params* params = calloc(1, sizeof(struct tunnel_from_p9_to_sock_params)); + params->max_packet_size = max_p9_message_size; + if (pthread_create(&g_p9_tunnel_thread_receiver, NULL, &tunnel_from_p9_virtio_to_sock, params) == -1) { + fprintf(stderr, "Error: pthread_create failed pthread_create(&g_p9_tunnel_thread_receiver...\n"); + free(params); + return -1; + } + g_p9_initialized = 1; + } + return 0; +} + +uint32_t do_mount_win_p9(const char* tag, uint8_t channel, uint32_t max_p9_message_size, char* path) { + int ret = initialize_p9_socket_descriptors(max_p9_message_size); + if (ret != 0) { + return ret; + } + if (channel >= MAX_P9_VOLUMES) { + fprintf(stderr, "ERROR: channel >= MAX_P9_VOLUMES\n"); + return -1; + } + if (g_p9_socket_fds[channel][0] != -1 || g_p9_socket_fds[channel][1] != -1) { + fprintf(stderr, "Error: Looks like do mount called twice with the same channel\n"); + return -1; + } + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, g_p9_socket_fds[channel]) == -1) { + return errno; + } + + + /* + DEBUG CODE to check internal buffer size of socket stream + { + int n; + unsigned int m = sizeof(n); + getsockopt(g_p9_socket_fds[channel][0],SOL_SOCKET,SO_RCVBUF,(void *)&n, &m); + fprintf(stderr, "Socket size %d\n", n); + } + */ + + // TODO: there could be one thread with poll + //for every socket pair we need one reader + struct tunnel_from_p9_params* params = calloc(1, sizeof(struct tunnel_from_p9_params)); + params->channel = channel; + params->max_packet_size = max_p9_message_size; + params->benchmark_loops = 100000000 / max_p9_message_size; + if (pthread_create(&g_p9_tunnel_thread_sender[channel], NULL, &tunnel_from_p9_sock_to_virtio, (void*) params) == -1) { + free(params); + fprintf(stderr, "Error: pthread_create failed\n"); + return -1; + } + + tag = tag; + char* mount_cmd = NULL; + int mount_socked_fd = g_p9_socket_fds[channel][0]; + // TODO: snprintf + int buf_size = asprintf(&mount_cmd, "trans=fd,rfdno=%d,wfdno=%d,version=9p2000.L,msize=%d", mount_socked_fd, mount_socked_fd, max_p9_message_size); + if (buf_size < 0) { + free(mount_cmd); + return errno; + } + fprintf(stderr, "Starting mount: tag: %s, path: %s\n", tag, path); + fprintf(stderr, "Mount command: %s\n", mount_cmd); + if (mount(tag, path, "9p", 0, mount_cmd) < 0) { + fprintf(stderr, "Mount finished with error: %d\n", errno); + return errno; + } + + fprintf(stderr, "Mount finished.\n"); + free(mount_cmd); + return 0; +} diff --git a/runtime/init-container/src/init.c b/runtime/init-container/src/init.c index 695f40d4..282693c9 100644 --- a/runtime/init-container/src/init.c +++ b/runtime/init-container/src/init.c @@ -23,6 +23,8 @@ #include #include "communication.h" +#include "communication_win_p9.h" + #include "cyclic_buffer.h" #include "network.h" #include "process_bookkeeping.h" @@ -44,9 +46,9 @@ } #define VPORT_CMD "/dev/vport0p1" -#define VPORT_NET "/dev/vport0p2" -#define VPORT_INET "/dev/vport0p3" - +#define VPORT_P9 "/dev/vport0p2" +#define VPORT_NET "/dev/vport0p3" +#define VPORT_INET "/dev/vport0p4" #define DEV_VPN "eth0" #define DEV_INET "eth1" @@ -103,6 +105,7 @@ static noreturn void die(void) { sync(); (void)close(g_epoll_fd); (void)close(g_sig_fd); + (void)close(g_p9_fd); (void)close(g_inet_fd); (void)close(g_vpn_fd); (void)close(g_cmds_fd); @@ -123,6 +126,7 @@ static noreturn void die(void) { }) static void load_module(const char* path) { + fprintf(stderr, "Loading module %s\n", path); int fd = CHECK(open(path, O_RDONLY | O_CLOEXEC)); CHECK(syscall(SYS_finit_module, fd, "", 0)); CHECK(close(fd)); @@ -1059,20 +1063,11 @@ static void handle_kill_process(msg_id_t msg_id) { } } -static uint32_t do_mount(const char* tag, char* path) { - if (create_dir_path(path) < 0) { - return errno; - } - if (mount(tag, path, "9p", 0, "trans=virtio,version=9p2000.L") < 0) { - return errno; - } - return 0; -} - static void handle_mount(msg_id_t msg_id) { bool done = false; uint32_t ret = 0; char* tag = NULL; + uint32_t max_p9_message_size = 0; char* path = NULL; while (!done) { @@ -1087,6 +1082,9 @@ static void handle_mount(msg_id_t msg_id) { case SUB_MSG_MOUNT_VOLUME_TAG: CHECK(recv_bytes(g_cmds_fd, &tag, NULL, /*is_cstring=*/true)); break; + case SUB_MSG_MOUNT_VOLUME_MAX_P9_MESSAGE_SIZE: + CHECK(recv_u32(g_cmds_fd, &max_p9_message_size)); + break; case SUB_MSG_MOUNT_VOLUME_PATH: CHECK(recv_bytes(g_cmds_fd, &path, NULL, /*is_cstring=*/true)); break; @@ -1102,7 +1100,12 @@ static void handle_mount(msg_id_t msg_id) { goto out; } - ret = do_mount(tag, path); + if (create_dir_path(path) < 0) { + ret = errno; + goto out; + } + ret = do_mount_win_p9(tag, g_p9_current_channel, max_p9_message_size, path); + g_p9_current_channel += 1; out: free(path); @@ -1667,11 +1670,20 @@ int main(void) { load_module("/9pnet.ko"); load_module("/9pnet_virtio.ko"); load_module("/9p.ko"); + load_module("/crc16.ko"); + load_module("/crc32c_generic.ko"); + load_module("/libcrc32c.ko"); + load_module("/mbcache.ko"); + load_module("/jbd2.ko"); + load_module("/ext4.ko"); + g_cmds_fd = CHECK(open(VPORT_CMD, O_RDWR | O_CLOEXEC)); + g_p9_fd = CHECK(open(VPORT_P9, O_RDWR | O_CLOEXEC)); CHECK(mkdir("/mnt", S_IRWXU)); CHECK(mkdir("/mnt/image", S_IRWXU)); + CHECK(mkdir("/mnt/overlay", S_IRWXU)); CHECK(mkdir("/mnt/newroot", DEFAULT_DIR_PERMS)); @@ -1683,10 +1695,17 @@ int main(void) { CHECK(mkdir("/mnt/overlay/upper", S_IRWXU)); CHECK(mkdir("/mnt/overlay/work", S_IRWXU)); - CHECK(mount("/dev/vda", "/mnt/image", "squashfs", MS_RDONLY, "")); + + + + CHECK(mount("/dev/vda", "/mnt/image", "squashfs", MS_RDONLY, NULL)); + + CHECK(mount("overlay", "/mnt/newroot", "overlay", 0, "lowerdir=/mnt/image,upperdir=/mnt/overlay/upper,workdir=/mnt/overlay/work")); + + CHECK(umount2("/dev", MNT_DETACH)); CHECK(chdir("/mnt/newroot")); @@ -1695,6 +1714,7 @@ int main(void) { CHECK(chdir("/")); create_dir("/dev", DEFAULT_DIR_PERMS); + create_dir("/dev2", DEFAULT_DIR_PERMS); create_dir("/tmp", DEFAULT_DIR_PERMS); CHECK(mount("proc", "/proc", "proc", @@ -1731,12 +1751,19 @@ int main(void) { makedev(5, 2))); } + if (access("/dev/vdb", F_OK) == 0) { + CHECK(mkdir("/mnt/internal", S_IRWXU)); + CHECK(mount("/dev/vdb", "/mnt/internal", "ext4", 0, NULL)); + } + setup_network(); setup_agent_directories(); block_signals(); setup_sigfd(); + //make sure to create threads after blocking signals, not before. Otherwise signals are blocked. + main_loop(); stop_network(); } diff --git a/runtime/poc/runtime/vmrt.exe b/runtime/poc/runtime/vmrt.exe new file mode 100644 index 00000000..7c9a5aac --- /dev/null +++ b/runtime/poc/runtime/vmrt.exe @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:31f2a4abed8cafbf57b6c515b4e7b2a481f9647689227222ae74bfe28bf01205 +size 10152448 diff --git a/runtime/poc/squashfs/ubuntu.gvmi b/runtime/poc/squashfs/ubuntu.gvmi new file mode 100644 index 00000000..14549098 --- /dev/null +++ b/runtime/poc/squashfs/ubuntu.gvmi @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6fe81041f1801b8af3c59b2a88171ed1bcb49b1264004b2ec5be068c17ece096 +size 3846504 diff --git a/runtime/src/arg_builder.rs b/runtime/src/arg_builder.rs new file mode 100644 index 00000000..512f0f01 --- /dev/null +++ b/runtime/src/arg_builder.rs @@ -0,0 +1,32 @@ +//don't care about performance. It's only one time shot for starting command. +pub struct ArgsBuilder { + arg_vec: Vec, +} +impl ArgsBuilder { + pub fn new() -> ArgsBuilder { + ArgsBuilder { arg_vec: vec![] } + } + + pub fn get_args_vector(&self) -> Vec { + self.arg_vec.clone() + } + + pub fn get_args_string(&self) -> String { + self.arg_vec.join(" ") +} + + pub fn add_1(&mut self, arg: &str) { + self.arg_vec.push(arg.to_string()); + } + + pub fn add_2(&mut self, arg1: &str, arg2: &str) { + self.arg_vec.push(arg1.to_string()); + self.arg_vec.push(arg2.to_string()); + } + + //pub fn append_split(&mut self, split: std::str::Split<&str>) { + // for arg in split { + // self.arg_vec.push(arg.to_string()) + // } + //} +} diff --git a/runtime/src/demux_socket_comm.rs b/runtime/src/demux_socket_comm.rs new file mode 100644 index 00000000..1fe524f3 --- /dev/null +++ b/runtime/src/demux_socket_comm.rs @@ -0,0 +1,220 @@ +use futures::future::{AbortHandle, Abortable}; +use std::borrow::BorrowMut; +use std::convert::TryInto; +use std::sync::Arc; +use tokio::io::{AsyncReadExt, AsyncWriteExt, DuplexStream}; +use tokio::sync::Mutex; +use tokio::task::JoinHandle; +use tokio::time::Instant; + +pub const MAX_P9_PACKET_SIZE: usize = 0x4000 - 5; //262144 +pub const MAX_DEMUX_PACKET_SIZE: usize = 0x4000; //262144 + +pub struct DemuxSocketHandle { + abort_handle_reader: AbortHandle, + abort_handle_writers: Vec, + join_handle_reader: JoinHandle<()>, + join_handle_writers: Vec>, +} + +pub async fn stop_demux_communication(dsh: DemuxSocketHandle) { + dsh.abort_handle_reader.abort(); + for abort_handle_writer in dsh.abort_handle_writers.iter() { + abort_handle_writer.abort(); + } + let _res = dsh.join_handle_reader.await; + for join_handle_writer in dsh.join_handle_writers { + let _res = join_handle_writer.await; + } +} + +pub fn start_demux_communication( + vm_stream: tokio::net::TcpStream, + p9_streams: Vec, +) -> anyhow::Result { + log::debug!("start_demux_communication - start"); + + let (mut vm_read_part, vm_write_part) = tokio::io::split(vm_stream); + + let mut p9_readers = vec![]; + let mut p9_writers = vec![]; + + let vm_write_part = Arc::new(Mutex::new(vm_write_part)); + + for p9_stream in p9_streams { + let (vm_read_part, vm_write_part) = tokio::io::split(p9_stream); + + p9_readers.push(vm_read_part); + p9_writers.push(vm_write_part); + } + + let (abort_handle, abort_registration) = AbortHandle::new_pair(); + + let mut benchmark_start: Option = None; + let mut benchmark_bytes: u64 = 0; + log::debug!("spawning vm_to_p9_splitter..."); + let vm_to_p9_splitter = tokio::spawn(async move { + let reader_future = Abortable::new( + async move { + loop { + let mut header_buffer = [0; 5]; + let mut message_buffer: Vec = vec![0; MAX_DEMUX_PACKET_SIZE]; + + if let Err(err) = vm_read_part.read_exact(&mut header_buffer).await { + log::error!("unable to read dmux data: {}", err); + break; + } + + let (channel_bytes, packet_size_bytes) = header_buffer.split_at(1); + let channel = u8::from_le_bytes(channel_bytes.try_into().unwrap()) as usize; + + let is_benchmark_packet = channel == 249; + + if !is_benchmark_packet && channel >= p9_writers.len() { + log::error!("channel exceeded number of connected p9 servers channel: {}, p9_stream.len: {}", channel, p9_writers.len()); + break; + } + + let packet_size = + u32::from_le_bytes(packet_size_bytes.try_into().unwrap()) as usize; + + if packet_size > message_buffer.len() { + log::error!("packet_size > message_buffer.len(), packet_size: {}, message_buffer.len: {}", packet_size, message_buffer.len()); + break; + } + + if let Err(err) = vm_read_part + .read_exact(&mut message_buffer[0..packet_size]) + .await + { + log::error!("read exact 2 {}", err); + break; + } + + if !is_benchmark_packet { + //check above guarantees that index will succeeded + if let Err(err) = p9_writers[channel] + .write_all(&message_buffer[0..packet_size]) + .await + { + log::error!( + "Write to p9_writer failed on channel {}: {}", + channel, + err + ); + break; + } + } else { + if benchmark_start == None { + benchmark_start = Some(Instant::now()); + } + benchmark_bytes += packet_size as u64; + if message_buffer[0] == 255 { + let time_sec = benchmark_start.unwrap().elapsed().as_secs_f64(); + let benchmark_mbytes = 1.0E-6 * benchmark_bytes as f64; + let speed = benchmark_mbytes / time_sec; + println!( + "Benchmark finished in {}, size {:.2}MB speed: {:.2}MB/s", + time_sec, benchmark_mbytes, speed + ); + benchmark_start = None; + benchmark_bytes = 0; + } + } + } + }, + abort_registration, + ); + match reader_future.await { + Ok(()) => { + log::error!("Reader part of p9 communication ended too soon"); + } + Err(e) => { + log::info!("Future aborted, reason {e}"); + } + } + }); + + let mut join_handle_writers: Vec> = vec![]; + let mut abort_handles: Vec = vec![]; + + for (channel, mut p9_reader) in p9_readers.into_iter().enumerate() { + let vm_write_part = vm_write_part.clone(); + let (abort_handle, abort_registration) = AbortHandle::new_pair(); + + log::debug!("spawning p9_to_vm_merger channel {}...", channel); + let p9_to_vm_merger = tokio::spawn(async move { + let writer_future = Abortable::new( + async move { + let mut message_buffer: Vec = vec![0; MAX_DEMUX_PACKET_SIZE]; + + loop { + let bytes_read = match p9_reader.read(&mut message_buffer).await { + Ok(bytes_read) => bytes_read, + Err(err) => { + log::error!("read p9 streams {}", err); + break; + } + }; + + { + let mut vm_write_guard = vm_write_part.lock().await; + + let channel_u8 = channel as u8; + let channel_bytes = channel_u8.to_le_bytes(); + if let Err(err) = + vm_write_guard.borrow_mut().write_all(&channel_bytes).await + { + log::error!("Write to vm_write_part failed: {}", err); + break; + } + + let bytes_read_u32 = bytes_read as u32; + let packet_size_bytes = bytes_read_u32.to_le_bytes(); + + if let Err(err) = vm_write_guard + .borrow_mut() + .write_all(&packet_size_bytes) + .await + { + log::error!("Write to vm_write_part failed: {}", err); + break; + } + + if let Err(err) = vm_write_guard + .borrow_mut() + .write_all(&message_buffer[0..bytes_read]) + .await + { + log::error!("Write to vm_write_part failed: {}", err); + break; + } + } + } + }, + abort_registration, + ); + + match writer_future.await { + Ok(()) => { + log::error!( + "Writer part of p9 communication ended too soon. channel: {}", + channel + ); + } + Err(_e) => { + log::info!("Writer aborted for channel: {}", channel); + } + } + }); + join_handle_writers.push(p9_to_vm_merger); + abort_handles.push(abort_handle); + } + + Ok(DemuxSocketHandle { + join_handle_reader: vm_to_p9_splitter, + abort_handle_reader: abort_handle, + join_handle_writers, + abort_handle_writers: abort_handles, + }) +} diff --git a/runtime/src/deploy.rs b/runtime/src/deploy.rs index c830c428..4ff6fe06 100644 --- a/runtime/src/deploy.rs +++ b/runtime/src/deploy.rs @@ -3,10 +3,10 @@ use std::io::SeekFrom; use std::path::PathBuf; use bollard_stubs::models::ContainerConfig; -use crc::crc32; +use crc::{Crc, CRC_32_ISO_HDLC}; use serde::{Deserialize, Serialize}; use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt}; -use tokio_byteorder::LittleEndian; +use tokio_byteorder::{AsyncReadBytesExt, LittleEndian}; use uuid::Uuid; use ya_runtime_sdk::runtime_api::deploy::ContainerVolume; @@ -32,7 +32,7 @@ impl Deployment { task_package: PathBuf, ) -> Result where - Input: AsyncRead + AsyncSeek + Unpin, + Input: AsyncRead + AsyncReadBytesExt + AsyncSeek + Unpin, { let json_len: u32 = { let mut buf = [0; 8]; @@ -47,13 +47,15 @@ impl Deployment { }; let json = { let mut buf = String::new(); - let pos = -1 * (json_len + 8) as i64; + let pos = -(json_len as i64 + 8); input.seek(SeekFrom::End(pos)).await?; input.take(json_len as u64).read_to_string(&mut buf).await?; buf }; - if crc32::checksum_ieee(json.as_bytes()) != crc { + let crc32 = Crc::::new(&CRC_32_ISO_HDLC); + + if crc32.checksum(json.as_bytes()) != crc { return Err(anyhow::anyhow!("Invalid ContainerConfig crc32 sum")); } @@ -81,7 +83,7 @@ fn parse_user(user: Option<&String>) -> anyhow::Result<(u32, u32)> { let user = user .map(|s| s.trim()) .ok_or_else(|| anyhow::anyhow!("User field missing"))?; - let mut split = user.splitn(2, ":"); + let mut split = user.splitn(2, ':'); let uid: u32 = split .next() .ok_or_else(|| anyhow::anyhow!("Missing UID"))? diff --git a/runtime/src/guest_agent_comm.rs b/runtime/src/guest_agent_comm.rs index 6a4e38d0..38b8a121 100644 --- a/runtime/src/guest_agent_comm.rs +++ b/runtime/src/guest_agent_comm.rs @@ -1,19 +1,21 @@ +pub use crate::response_parser::Notification; +use crate::response_parser::{parse_one_response, GuestAgentMessage, Response, ResponseWithId}; use futures::channel::mpsc; use futures::future::{BoxFuture, FutureExt}; use futures::lock::Mutex; use futures::{SinkExt, StreamExt}; -use std::path::Path; use std::sync::Arc; use std::{io, marker::PhantomData}; +#[cfg(windows)] +use tokio::net::TcpStream; +#[cfg(unix)] +use tokio::net::UnixStream; use tokio::{ io::{split, AsyncWriteExt, ReadHalf, WriteHalf}, - net::UnixStream, spawn, time, }; -pub use crate::response_parser::Notification; -use crate::response_parser::{parse_one_response, GuestAgentMessage, Response, ResponseWithId}; - +#[allow(clippy::enum_variant_names)] #[repr(u8)] enum MsgType { MsgQuit = 1, @@ -35,6 +37,7 @@ enum SubMsgQuitType { SubMsgEnd, } +#[allow(clippy::enum_variant_names)] enum SubMsgRunProcessType<'a> { SubMsgEnd, SubMsgRunProcessBin(&'a [u8]), @@ -55,9 +58,11 @@ enum SubMsgKillProcessType { enum SubMsgMountVolumeType<'a> { SubMsgEnd, SubMsgMountVolumeTag(&'a [u8]), + SubMsgMountVolumeMaxP9MessageSize(u32), SubMsgMountVolumePath(&'a [u8]), } +#[allow(clippy::enum_variant_names)] enum SubMsgQueryOutputType { SubMsgEnd, SubMsgQueryOutputId(u64), @@ -66,6 +71,7 @@ enum SubMsgQueryOutputType { SubMsgQueryOutputLen(u64), } +#[allow(clippy::enum_variant_names)] enum SubMsgNetCtlType<'a> { SubMsgEnd, SubMsgNetCtlFlags(u16), @@ -99,8 +105,23 @@ struct Message { phantom: PhantomData, } +#[cfg(unix)] +type PlatformStream = UnixStream; +#[cfg(windows)] +type PlatformStream = TcpStream; + +/* +#[cfg(unix)] +type PlatformAddr = Path; +#[cfg(windows)] +type PlatformAddr = SocketAddr; +*/ + +type OutputStream = WriteHalf; +type InputStream = ReadHalf; + pub struct GuestAgent { - stream: WriteHalf, + stream: OutputStream, last_msg_id: u64, responses: mpsc::Receiver, responses_reader_handle: Option>, @@ -273,8 +294,12 @@ impl EncodeInto for SubMsgMountVolumeType<'_> { 1u8.encode_into(buf); tag.encode_into(buf); } - SubMsgMountVolumeType::SubMsgMountVolumePath(path) => { + SubMsgMountVolumeType::SubMsgMountVolumeMaxP9MessageSize(tag) => { 2u8.encode_into(buf); + tag.encode_into(buf); + } + SubMsgMountVolumeType::SubMsgMountVolumePath(path) => { + 3u8.encode_into(buf); path.encode_into(buf); } } @@ -389,7 +414,7 @@ pub type RemoteCommandResult = Result; fn reader<'f, F>( agent: Arc>, - mut stream: ReadHalf, + mut stream: InputStream, mut notification_handler: F, mut responses: mpsc::Sender, ) -> BoxFuture<'f, io::Error> @@ -398,8 +423,7 @@ where { let (mut tx, rx) = mpsc::channel(8); spawn(async move { - let _ = rx - .for_each(|n| notification_handler(n, agent.clone())) + rx.for_each(|n| notification_handler(n, agent.clone())) .await; }); async move { @@ -421,18 +445,17 @@ where } impl GuestAgent { - pub async fn connected( - path: P, + pub async fn connected( + path: &str, timeout: u32, notification_handler: F, ) -> io::Result>> where F: FnMut(Notification, Arc>) -> BoxFuture<'static, ()> + Send + 'static, - P: AsRef, { let mut timeout_remaining = timeout; loop { - match UnixStream::connect(&path).await { + match PlatformStream::connect(path).await { Ok(s) => { let (stream_read, stream_write) = split(s); let (response_send, response_receive) = mpsc::channel(10); @@ -552,6 +575,7 @@ impl GuestAgent { self.get_ok_response(msg_id).await } + #[allow(clippy::too_many_arguments)] async fn spawn_new_process( &mut self, bin: &str, @@ -606,6 +630,7 @@ impl GuestAgent { self.get_u64_response(msg_id).await } + #[allow(clippy::too_many_arguments)] pub async fn run_process( &mut self, bin: &str, @@ -622,6 +647,7 @@ impl GuestAgent { .await } + #[allow(clippy::too_many_arguments)] pub async fn run_entrypoint( &mut self, bin: &str, @@ -653,7 +679,12 @@ impl GuestAgent { self.get_ok_response(msg_id).await } - pub async fn mount(&mut self, tag: &str, path: &str) -> io::Result> { + pub async fn mount( + &mut self, + tag: &str, + p9_max_msg_size: u32, + path: &str, + ) -> io::Result> { let mut msg = Message::default(); let msg_id = self.get_new_msg_id(); @@ -661,6 +692,10 @@ impl GuestAgent { msg.append_submsg(&SubMsgMountVolumeType::SubMsgMountVolumeTag(tag.as_bytes())); + msg.append_submsg(&SubMsgMountVolumeType::SubMsgMountVolumeMaxP9MessageSize( + p9_max_msg_size, + )); + msg.append_submsg(&SubMsgMountVolumeType::SubMsgMountVolumePath( path.as_bytes(), )); diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 0369ea8a..e29b880e 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1,5 +1,10 @@ +mod arg_builder; pub mod cpu; +pub mod demux_socket_comm; pub mod deploy; pub mod guest_agent_comm; +pub mod local_notification_handler; +pub mod local_spawn_vm; mod response_parser; -pub mod vmrt; +pub mod vm; +pub mod vm_runner; diff --git a/runtime/src/local_notification_handler.rs b/runtime/src/local_notification_handler.rs new file mode 100644 index 00000000..314a5fc1 --- /dev/null +++ b/runtime/src/local_notification_handler.rs @@ -0,0 +1,208 @@ +use futures::lock::Mutex; +use std::{collections::HashMap, sync::Arc}; +use tokio::io; +use tokio::sync; + +use crate::demux_socket_comm::MAX_P9_PACKET_SIZE; +use crate::guest_agent_comm::{GuestAgent, Notification, RedirectFdType}; +use futures::future::FutureExt; +use std::convert::TryFrom; +use std::time::Duration; +use tokio::join; +use ya_runtime_sdk::runtime_api::deploy::ContainerVolume; + +#[derive(Default)] +pub struct LocalNotifications { + process_died: Mutex>>, + output_available: Mutex>>, +} + +impl LocalNotifications { + pub fn new() -> Self { + Self::default() + } + + pub async fn get_process_died_notification(&self, id: u64) -> Arc { + let notif = { + let mut lock = self.process_died.lock().await; + lock.entry(id) + .or_insert_with(|| Arc::new(sync::Notify::new())) + .clone() + }; + + notif + } + + pub async fn get_output_available_notification(&self, id: u64) -> Arc { + let notif = { + let mut lock = self.output_available.lock().await; + lock.entry(id) + .or_insert_with(|| Arc::new(sync::Notify::new())) + .clone() + }; + + notif + } + + pub async fn handle(&self, notification: Notification) { + match notification { + Notification::OutputAvailable { id, fd } => { + let _ = fd; + //log::debug!("Process {} has output available on fd {}", id, fd); + + self.get_output_available_notification(id) + .await + .notify_one(); + } + Notification::ProcessDied { id, reason } => { + let _ = reason; + //log::debug!("Process {} died with {:?}", id, reason); + self.get_process_died_notification(id).await.notify_one(); + } + } + } +} + +async fn read_streams( + is_finished: Arc>, + id: u64, + ga: Arc>, +) -> anyhow::Result<(Vec, Vec)> { + let mut stdout = Vec::::new(); + let mut stderr = Vec::::new(); + + let mut finishing = false; + loop { + tokio::time::sleep(Duration::from_millis(100)).await; + if !finishing { + let val = is_finished.lock().await; + if *val == 1 { + finishing = true; + } + } + let mut ga = ga.lock().await; + + let stdout_read = match ga.query_output(id, 1, 0, u64::MAX).await? { + Ok(out) => { + //let s = String::from_utf8_lossy(&out); + //log::info!("STDOUT {}:\n{}", id, s); + //io::stdout().write_all(&out).await?; + stdout.extend(out); + true + } + Err(_code) => { + false + // log::info!("STDOUT empty") }, + } + }; + + let stderr_read = match ga.query_output(id, 2, 0, u64::MAX).await? { + Ok(out) => { + //let s = String::from_utf8_lossy(&out); + //log::info!("STDERR {}:\n{}", id, s); + //io::stdout().write_all(&out).await?; + stderr.extend(out); + true + } + Err(_code) => { + //log::info!("STDERR empty") + false + } + }; + + if finishing && !stderr_read && !stdout_read { + break; + } + } + Ok((stdout, stderr)) +} + +pub struct LocalAgentCommunication { + ln: Arc, + ga: Arc>, +} + +impl LocalAgentCommunication { + pub fn get_ga(&self) -> Arc> { + self.ga.clone() + } + + pub async fn run_bash_command(&self, cmd: &str) -> io::Result<()> { + let argv = ["bash", "-c", cmd]; + self.run_command("/bin/bash", &argv).await + } + + pub async fn run_command(&self, bin: &str, argv: &[&str]) -> io::Result<()> { + let id = { + let mut ga = self.ga.lock().await; + ga.run_process( + bin, + argv, + None, + 0, + 0, + &[ + None, + Some(RedirectFdType::RedirectFdPipeBlocking(0x1000)), + Some(RedirectFdType::RedirectFdPipeBlocking(0x1000)), + ], + None, + ) + .await? + .expect("Run process failed") + }; + log::info!("Spawned process {} {:?} - id {}", bin, argv, id); + let died = self.ln.get_process_died_notification(id).await; + + let _output = self.ln.get_output_available_notification(id).await; + + let common = Arc::new(Mutex::new(0)); + let future1 = read_streams(common.clone(), id, self.ga.clone()); + let common = common.clone(); + let future2 = async move { + died.notified().await; + let mut val = common.lock().await; + *val = 1; + }; + let (res1, _res2) = join!(future1, future2); + match res1 { + Ok(r) => { + let stdout = String::from_utf8_lossy(&r.0); + let stderr = String::from_utf8_lossy(&r.1); + + log::info!("STDOUT {}:\n{}", id, stdout); + log::info!("STDERR {}:\n{}", id, stderr); + } + Err(err) => { + log::error!("COMMAND ENDED ERR: {} {:?}", id, err); + } + } + + Ok(()) + } + + pub async fn run_mount(&self, mount_args: &[ContainerVolume]) -> anyhow::Result<()> { + let mut guest_agent = self.ga.lock().await; + + for ContainerVolume { name, path } in mount_args.iter() { + let max_p9_packet_size = u32::try_from(MAX_P9_PACKET_SIZE).unwrap(); + + if let Err(e) = guest_agent.mount(name, max_p9_packet_size, path).await? { + log::error!("Mount failed at {name}, {path}, {e}") + } + } + Ok(()) + } +} +pub async fn start_local_agent_communication( + manager_sock: &str, +) -> anyhow::Result> { + let ln = Arc::new(LocalNotifications::new()); + let ln2 = ln.clone(); + let ga = GuestAgent::connected(manager_sock, 10, move |n, _g| { + let notifications = ln2.clone(); + async move { notifications.clone().handle(n).await }.boxed() + }) + .await?; + Ok(Arc::new(LocalAgentCommunication { ln, ga })) +} diff --git a/runtime/src/local_spawn_vm.rs b/runtime/src/local_spawn_vm.rs new file mode 100644 index 00000000..9987e815 --- /dev/null +++ b/runtime/src/local_spawn_vm.rs @@ -0,0 +1,117 @@ +use std::{ + env, fs, + path::{Path, PathBuf}, + sync::Arc, +}; + +use crate::vm::{RuntimeData, VMBuilder}; +use crate::vm_runner::VMRunner; +use futures::lock::Mutex; +use ya_runtime_sdk::{runtime_api::deploy::ContainerVolume, server::ContainerEndpoint}; + +fn get_project_dir() -> PathBuf { + PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + .canonicalize() + .expect("invalid manifest dir") +} + +fn join_as_string>(path: P, file: impl ToString) -> String { + let joined = path.as_ref().join(file.to_string()); + + // Under windows Paths has UNC prefix that is not parsed correctly by qemu + // Wrap Path with simplified method to remove that prefix + // It has no effect on Unix + dunce::simplified( + joined + // canonicalize checks existence of the file, it may failed, if does not exist + .canonicalize() + .unwrap_or_else(|_| panic!("{}", joined.display())) + .as_path(), + ) + .display() + .to_string() +} + +pub fn prepare_tmp_path() -> PathBuf { + let project_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + .canonicalize() + .expect("invalid manifest dir"); + let temp_path = project_dir.join(Path::new("tmp")); + if temp_path.exists() { + fs::remove_dir_all(&temp_path).unwrap(); + } + fs::create_dir_all(&temp_path).unwrap(); + temp_path +} + +pub fn prepare_mount_directories(base_path: &Path, number_of_drives: u64) -> Vec { + let mut mount_args = vec![]; + + for id in 0..number_of_drives { + let name = format!("inner{id}"); + + let inner_path = base_path.join(&name); + + std::fs::create_dir_all(&inner_path).unwrap_or_else(|_| { + panic!( + "Failed to create a dir {:?} inside temp dir", + inner_path.as_os_str() + ) + }); + + mount_args.push(ContainerVolume { + name, + path: format!("/mnt/mnt1/tag{id}"), + }); + } + mount_args +} + +pub async fn spawn_vm( + tmp_path: &Path, + cpu_cores: usize, + mem_gib: f64, + use_qcow2_volume: bool, +) -> anyhow::Result { + let project_dir = get_project_dir(); + let runtime_dir = project_dir.join("poc").join("runtime"); + let image_dir = project_dir.join("poc").join("squashfs"); + let init_dir = project_dir.join("init-container"); + if use_qcow2_volume { + let source_qcow2_file = project_dir + .join("poc") + .join("qcow2") + .join("empty_10GB.qcow2"); + let qcow2_file = tmp_path.join("rw_drive.qcow2"); + fs::copy(&source_qcow2_file, &qcow2_file)?; + let _qcow2_file = qcow2_file.canonicalize()?; + } + + let addr = |port| { + ContainerEndpoint::UdpDatagram(std::net::SocketAddr::V4(std::net::SocketAddrV4::new( + std::net::Ipv4Addr::new(127, 0, 0, 1), + port, + ))) + }; + + let runtime_data = Arc::new(Mutex::new(RuntimeData { + vpn: Some(addr(7070)), + inet: Some(addr(7071)), + ..Default::default() + })); + + let vm = VMBuilder::new( + cpu_cores, + (mem_gib * 1024.0) as usize, + &image_dir.join("ubuntu.gvmi"), + None, + ) + .with_kernel_path(join_as_string(&init_dir, "vmlinuz-virt")) + .with_ramfs_path(join_as_string(&init_dir, "initramfs.cpio.gz")) + .build(runtime_data) + .await?; + + let mut vm_runner = VMRunner::new(vm); + vm_runner.run_vm(runtime_dir).await?; + Ok(vm_runner) +} diff --git a/runtime/src/main.rs b/runtime/src/main.rs index e1d167e5..dfe962d6 100644 --- a/runtime/src/main.rs +++ b/runtime/src/main.rs @@ -1,19 +1,24 @@ use std::convert::TryFrom; use std::path::{Component, Path, PathBuf}; use std::sync::Arc; +use std::time::Duration; + use bollard_stubs::models::ContainerConfig; use futures::future::FutureExt; use futures::lock::Mutex; use futures::TryFutureExt; +use url::Url; +use ya_runtime_sdk::server::ContainerEndpoint; + use structopt::StructOpt; +use ya_runtime_vm::demux_socket_comm::MAX_P9_PACKET_SIZE; +use ya_runtime_vm::vm::VMBuilder; + use tokio::{ fs, io::{self, AsyncWriteExt}, }; -use url::Url; - -use ya_runtime_sdk::runtime_api::deploy::ContainerEndpoint; use ya_runtime_sdk::{ runtime_api::{ deploy::{DeployResult, StartMode}, @@ -21,30 +26,34 @@ use ya_runtime_sdk::{ }, serialize, server::Server, - Context, EmptyResponse, EndpointResponse, Error, ErrorExt, EventEmitter, OutputResponse, - ProcessId, ProcessIdResponse, RuntimeMode, + Context, EmptyResponse, EndpointResponse, Error, EventEmitter, OutputResponse, ProcessId, + ProcessIdResponse, RuntimeMode, }; + use ya_runtime_vm::{ cpu::CpuInfo, deploy::Deployment, - guest_agent_comm::{RedirectFdType, RemoteCommandResult}, - vmrt::{runtime_dir, start_vmrt, RuntimeData}, + guest_agent_comm::{GuestAgent, Notification, RedirectFdType, RemoteCommandResult}, }; -const FILE_TEST_IMAGE: &'static str = "self-test.gvmi"; -const FILE_DEPLOYMENT: &'static str = "deployment.json"; -const DEFAULT_CWD: &'static str = "/"; +use ya_runtime_vm::vm::RuntimeData; +use ya_runtime_vm::vm_runner::VMRunner; + +const DIR_RUNTIME: &str = "runtime"; +const FILE_TEST_IMAGE: &str = "self-test.gvmi"; +const FILE_DEPLOYMENT: &str = "deployment.json"; +const DEFAULT_CWD: &str = "/"; #[derive(StructOpt, Clone, Default)] #[structopt(rename_all = "kebab-case")] pub struct Cli { /// GVMI image path #[structopt(short, long, required_ifs( - &[ - ("command", "deploy"), - ("command", "start"), - ("command", "run") - ]) + &[ + ("command", "deploy"), + ("command", "start"), + ("command", "run") + ]) )] task_package: Option, /// Number of logical CPU cores @@ -80,20 +89,23 @@ impl ya_runtime_sdk::Runtime for Runtime { } fn start<'a>(&mut self, ctx: &mut Context) -> OutputResponse<'a> { + log::info!("ya_runtime_sdk::Runtime - start"); + let emitter = ctx .emitter .clone() - .expect("Service not running in Server mode"); - + .expect("Service is not running in Server mode"); let workdir = ctx.cli.workdir.clone().expect("Workdir not provided"); let deployment_file = std::fs::File::open(workdir.join(FILE_DEPLOYMENT)) - .expect("Unable to open the deployment file"); + .expect("Unable to open the deployment file"); let deployment: Deployment = serialize::json::from_reader(deployment_file) .expect("Failed to read the deployment file"); log::debug!("Deployment: {deployment:?}"); + let data = self.data.clone(); + let vpn_endpoint = ctx.cli.runtime.vpn_endpoint.clone(); let inet_endpoint = ctx.cli.runtime.inet_endpoint.clone(); @@ -114,7 +126,6 @@ impl ya_runtime_sdk::Runtime for Runtime { None }; - let data = self.data.clone(); async move { { let mut data = data.lock().await; @@ -194,7 +205,7 @@ impl ya_runtime_sdk::Runtime for Runtime { } async fn deploy(workdir: PathBuf, cli: Cli) -> anyhow::Result> { - let work_dir = normalize_path(&workdir).await?; + let workdir = normalize_path(&workdir).await?; let package_path = normalize_path(&cli.task_package.unwrap()).await?; let package_file = fs::File::open(&package_path).await?; @@ -205,17 +216,16 @@ async fn deploy(workdir: PathBuf, cli: Cli) -> anyhow::Result>, emitter: EventEmitter, ) -> anyhow::Result> { - start_vmrt(work_dir, runtime_data, emitter).await + let runtime_dir = runtime_dir().expect("Unable to resolve current directory"); + + let deployment = runtime_data + .lock() + .await + .deployment() + .expect("Missing deployment data") + .clone(); + + let vm = VMBuilder::new( + deployment.cpu_cores, + deployment.mem_mib, + &deployment.task_package, + None, + ) + .build(runtime_data.clone()) + .await?; + + let mut data = runtime_data.lock().await; + + let mut vm_runner = VMRunner::new(vm); + vm_runner.run_vm(runtime_dir).await?; + + vm_runner + .start_9p_service(&work_dir, &deployment.volumes) + .await?; + + let ga = GuestAgent::connected( + vm_runner.get_vm().get_manager_sock(), + 10, + move |notification, ga| { + let mut emitter = emitter.clone(); + async move { + let status = notification_into_status(notification, ga).await; + emitter.emit(status).await; + } + .boxed() + }, + ) + .await?; + + { + let mut ga = ga.lock().await; + for (idx, volume) in deployment.volumes.iter().enumerate() { + let max_p9_packet_size = u32::try_from(MAX_P9_PACKET_SIZE).unwrap(); + ga.mount( + format!("mnt{}", idx).as_str(), + max_p9_packet_size, + volume.path.as_str(), + ) + .await? + .expect("Mount failed"); + } + } + + data.vm_runner.replace(vm_runner); + data.ga.replace(ga); + + Ok(None) } async fn run_command( @@ -279,7 +347,7 @@ async fn run_command( ) .await; - Ok(convert_result(result, "Running process")?) + convert_result(result, "Running process") } async fn kill_command( @@ -298,7 +366,10 @@ async fn kill_command( async fn stop(runtime_data: Arc>) -> Result<(), server::ErrorResponse> { log::debug!("got shutdown"); let mut data = runtime_data.lock().await; - let mut runtime = data.runtime().unwrap(); + + let mut vm_runner = data.vm_runner.take().unwrap(); + + vm_runner.stop_p9_service().await; { let mutex = data.ga().unwrap(); @@ -306,8 +377,8 @@ async fn stop(runtime_data: Arc>) -> Result<(), server::Error convert_result(ga.quit().await, "Sending quit")?; } - runtime - .wait() + vm_runner + .stop_vm(&Duration::from_secs(5), false) .await .expect("Waiting for runtime stop failed"); Ok(()) @@ -326,7 +397,7 @@ fn offer() -> anyhow::Result> { "golem.inf.cpu.brand": cpu.model.brand, "golem.inf.cpu.model": model, "golem.inf.cpu.capabilities": cpu.capabilities, - "golem.runtime.capabilities": ["inet", "vpn", "manifest-support"] + "golem.runtime.capabilities": ["vpn"] }, "constraints": "" }))) @@ -340,9 +411,8 @@ async fn test() -> anyhow::Result<()> { .join(FILE_TEST_IMAGE) .canonicalize() .expect("Test image not found"); - println!("Task package: {}", task_package.display()); - let work_dir = std::env::temp_dir(); + log::debug!("Task package: {}", task_package.display()); let runtime_data = RuntimeData { deployment: Some(Deployment { cpu_cores: 1, @@ -357,9 +427,13 @@ async fn test() -> anyhow::Result<()> { }; println!("Starting runtime"); - start(work_dir, runtime.data.clone(), EventEmitter::spawn(e)) - .await - .expect("Failed to start runtime"); + start( + std::env::temp_dir(), + runtime.data.clone(), + EventEmitter::spawn(e), + ) + .await + .expect("Failed to start runtime"); println!("Stopping runtime"); stop(runtime.data.clone()) @@ -429,14 +503,11 @@ async fn join_network( } async fn normalize_path>(path: P) -> anyhow::Result { - Ok(fs::canonicalize(path) + Ok(fs::canonicalize(path.as_ref()) .await? .components() .into_iter() - .filter(|c| match c { - Component::Prefix(_) => false, - _ => true, - }) + .filter(|c| !matches!(c, Component::Prefix(_))) .collect::()) } @@ -505,7 +576,70 @@ async fn run_entrypoint( }) } +async fn notification_into_status( + notification: Notification, + ga: Arc>, +) -> server::ProcessStatus { + match notification { + Notification::OutputAvailable { id, fd } => { + log::debug!("Process {} has output available on fd {}", id, fd); + + let output = { + let result = { + let mut guard = ga.lock().await; + guard.query_output(id, fd as u8, 0, u64::MAX).await + }; + match result { + Ok(Ok(vec)) => vec, + Ok(Err(e)) => { + log::error!("Remote error while querying output: {:?}", e); + Vec::new() + } + Err(e) => { + log::error!("Error querying output: {:?}", e); + Vec::new() + } + } + }; + let (stdout, stderr) = match fd { + 1 => (output, Vec::new()), + _ => (Vec::new(), output), + }; + + server::ProcessStatus { + pid: id, + running: true, + return_code: 0, + stdout, + stderr, + } + } + Notification::ProcessDied { id, reason } => { + log::debug!("Process {} died with {:?}", id, reason); + + // TODO: reason._type ? + server::ProcessStatus { + pid: id, + running: false, + return_code: reason.status as i32, + stdout: Vec::new(), + stderr: Vec::new(), + } + } + } +} + +fn runtime_dir() -> io::Result { + Ok(std::env::current_exe()? + .parent() + .ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))? + .to_path_buf() + .join(DIR_RUNTIME)) +} + #[tokio::main] async fn main() -> anyhow::Result<()> { + log::debug!("Runtime VM starting - log level debug message ..."); + log::info!("Runtime VM starting - log level info message ..."); ya_runtime_sdk::run::().await } diff --git a/runtime/src/raw_socket_comm.rs b/runtime/src/raw_socket_comm.rs new file mode 100644 index 00000000..0dd84b87 --- /dev/null +++ b/runtime/src/raw_socket_comm.rs @@ -0,0 +1,209 @@ +use std::cell::UnsafeCell; +use std::convert::TryInto; +use std::io::prelude::*; +use std::net::Shutdown; +use std::sync::Mutex; +use std::time::Duration; + +#[derive(Default, Debug)] +pub struct RawSocketCommunication { + vm_stream_thread: Option>, + p9_stream_threads: Vec>, + shared_unsafe_data: Option>>, +} + +pub struct SharedUnsafeData { + //tri: i32, + vm_stream: std::net::TcpStream, + p9_streams: Vec, + vm_stream_write_mutex: Mutex, +} + +impl Drop for SharedUnsafeData { + fn drop(&mut self) { + log::error!("Dropping SharedUnsafeData"); + } +} + +#[derive(Copy, Clone)] +pub struct SharedUnsafeDataPointer { + pub obj_ptr: *mut SharedUnsafeData, +} +unsafe impl Send for SharedUnsafeDataPointer {} + +const MAX_PACKET_SIZE: usize = 16384; + +const TEST_CONCURRENT_ACCESS: bool = false; + +impl RawSocketCommunication { + pub fn new() -> RawSocketCommunication { + RawSocketCommunication { + vm_stream_thread: None, + p9_stream_threads: vec![], + shared_unsafe_data: None, + } + } + + pub fn start_raw_comm( + &mut self, + vm_stream: std::net::TcpStream, + p9_streams: Vec, + ) { + let number_of_p9_threads = p9_streams.len(); + + self.shared_unsafe_data = Some(Box::new(UnsafeCell::new(SharedUnsafeData { + vm_stream, + p9_streams, + vm_stream_write_mutex: Mutex::new(0), + }))); + + let unsafe_data = SharedUnsafeDataPointer { + obj_ptr: self.shared_unsafe_data.as_ref().unwrap().get(), + }; + + self.vm_stream_thread = Some(std::thread::spawn(move || { + let mut header_buffer = [0; 3]; + let mut message_buffer = [0; MAX_PACKET_SIZE]; + loop { + unsafe { + //std::thread::sleep(Duration::from_millis(10)); + + match (*unsafe_data.obj_ptr) + .vm_stream + .read_exact(&mut header_buffer) + { + Ok(()) => {} + Err(err) => { + log::error!("read exact 1: {}", err); + break; + } + } + /*if (read_size_header != 3) { + log::error!("read_size_header != 3"); + break; + }*/ + + let (channel_bytes, packet_size_bytes) = header_buffer.split_at(1); + let channel = u8::from_le_bytes(channel_bytes.try_into().unwrap()) as usize; + + if channel >= (*unsafe_data.obj_ptr).p9_streams.len() { + log::error!("channel exceeded number of connected p9 servers channel: {}, p9_stream.len: {}", channel, (*unsafe_data.obj_ptr).p9_streams.len()); + break; + } + + let packet_size = + u16::from_le_bytes(packet_size_bytes.try_into().unwrap()) as usize; + + if packet_size > message_buffer.len() { + log::error!("packet_size > message_buffer.len()"); + break; + } + + match (*unsafe_data.obj_ptr) + .vm_stream + .read_exact(&mut message_buffer[0..packet_size]) + { + Ok(()) => {} + Err(err) => { + log::error!("read exact 2 {}", err); + break; + } + } + /*if read_size_packet != packet_size { + log::error!("read_size_packet != packet_size"); + break; + }*/ + + //log::debug!("Received packet channel: {}, packet_size: {}", channel, packet_size); + + let _r6 = (*unsafe_data.obj_ptr).p9_streams[channel] + .write_all(&mut message_buffer[0..packet_size]); + } + } + })); + + for channel in 0..number_of_p9_threads { + self.p9_stream_threads.push(std::thread::spawn(move || { + let mut message_buffer = [0; MAX_PACKET_SIZE]; + + loop { + //log::debug!("Thread channel {}", channel); + unsafe { + let bytes_read = match (*unsafe_data.obj_ptr).p9_streams[channel] + .read(&mut message_buffer) + { + Ok(bytes_read) => bytes_read, + Err(err) => { + log::error!("read p9 streams {}", err); + break; + } + }; + { + //critical section for writing to common socket + let _write_guard = + (*unsafe_data.obj_ptr).vm_stream_write_mutex.lock().unwrap(); + + //log::debug!("Sending message back: channel:{}, packet_size:{}", channel, bytes_read); + + let channel_u8 = channel as u8; + let mut channel_bytes = channel_u8.to_le_bytes(); + let _r5 = (*unsafe_data.obj_ptr) + .vm_stream + .write_all(&mut channel_bytes); + + if TEST_CONCURRENT_ACCESS { + std::thread::sleep(Duration::from_millis(50)); + } + + let bytes_read_u16 = bytes_read as u16; + let mut packet_size_bytes = bytes_read_u16.to_le_bytes(); + + let _r = (*unsafe_data.obj_ptr) + .vm_stream + .write_all(&mut packet_size_bytes); + + if TEST_CONCURRENT_ACCESS { + std::thread::sleep(Duration::from_millis(50)); + let split_send = bytes_read / 2; + let _r1 = (*unsafe_data.obj_ptr) + .vm_stream + .write_all(&mut message_buffer[0..split_send]); + std::thread::sleep(Duration::from_millis(50)); + let _r2 = (*unsafe_data.obj_ptr) + .vm_stream + .write_all(&mut message_buffer[split_send..bytes_read]); + } else { + let _r3 = (*unsafe_data.obj_ptr) + .vm_stream + .write_all(&mut message_buffer[0..bytes_read]); + } + + drop(_write_guard); + } + } + } + })); + } + } + + pub fn finish_raw_comm(self) { + if let Some(shared_data) = self.shared_unsafe_data { + unsafe { + let _res1 = (*shared_data.get()).vm_stream.shutdown(Shutdown::Both); + let _res = (*shared_data.get()) + .p9_streams + .iter() + .map(|p9stream| p9stream.shutdown(Shutdown::Both)); + } + } + + if let Some(thread) = self.vm_stream_thread { + log::debug!("Joining thread: thread_join_handle"); + let _join_res = thread.join(); + log::debug!("Thread thread_join_handle joined"); + } + for thread in self.p9_stream_threads { + let _join_res = thread.join(); + } + } +} diff --git a/runtime/src/response_parser.rs b/runtime/src/response_parser.rs index ebc9e7df..11598f65 100644 --- a/runtime/src/response_parser.rs +++ b/runtime/src/response_parser.rs @@ -90,27 +90,27 @@ pub async fn parse_one_response( let typ = recv_u8(stream).await?; match typ { 0 => Ok(GuestAgentMessage::Response(ResponseWithId { - id: id, + id, resp: Response::Ok, })), 1 => { let val = recv_u64(stream).await?; Ok(GuestAgentMessage::Response(ResponseWithId { - id: id, + id, resp: Response::OkU64(val), })) } 2 => { let buf = recv_bytes(stream).await?; Ok(GuestAgentMessage::Response(ResponseWithId { - id: id, + id, resp: Response::OkBytes(buf), })) } 3 => { let code = recv_u32(stream).await?; Ok(GuestAgentMessage::Response(ResponseWithId { - id: id, + id, resp: Response::Err(code), })) } @@ -119,10 +119,7 @@ pub async fn parse_one_response( let proc_id = recv_u64(stream).await?; let fd = recv_u32(stream).await?; Ok(GuestAgentMessage::Notification( - Notification::OutputAvailable { - id: proc_id, - fd: fd, - }, + Notification::OutputAvailable { id: proc_id, fd }, )) } else { Err(io::Error::new( @@ -138,10 +135,7 @@ pub async fn parse_one_response( let type_ = ExitType::try_from(recv_u8(stream).await?)?; Ok(GuestAgentMessage::Notification(Notification::ProcessDied { id: proc_id, - reason: ExitReason { - status: status, - type_: type_, - }, + reason: ExitReason { status, type_ }, })) } else { Err(io::Error::new( diff --git a/runtime/src/vm.rs b/runtime/src/vm.rs new file mode 100644 index 00000000..b66ca162 --- /dev/null +++ b/runtime/src/vm.rs @@ -0,0 +1,382 @@ +use futures::lock::Mutex; +use std::path::Path; +use std::sync::atomic::Ordering::Relaxed; +use std::sync::Arc; +use std::{ + ffi::OsStr, + net::{Ipv4Addr, SocketAddr, SocketAddrV4}, + path::PathBuf, + sync::atomic::AtomicU32, +}; +use tokio::process::Command; +use ya_runtime_sdk::server::ContainerEndpoint; + +use crate::arg_builder::ArgsBuilder; +use crate::deploy::Deployment; +use crate::guest_agent_comm::GuestAgent; +use crate::vm_runner::VMRunner; + +const FILE_VMLINUZ: &str = "vmlinuz-virt"; +const FILE_INITRAMFS: &str = "initramfs.cpio.gz"; + +#[derive(Default)] +pub struct RuntimeData { + pub vm_runner: Option, + pub vpn: Option, + pub inet: Option, + pub deployment: Option, + pub ga: Option>>, +} + +impl RuntimeData { + //fn vm_runner(&mut self) -> anyhow::Result { + // self.vm_runner + // .take() + // .ok_or_else(|| anyhow::anyhow!("VM runner process not available")) + //} + + pub fn deployment(&self) -> anyhow::Result<&Deployment> { + self.deployment + .as_ref() + .ok_or_else(|| anyhow::anyhow!("Runtime not deployed")) + } + + pub fn ga(&self) -> anyhow::Result>> { + self.ga + .clone() + .ok_or_else(|| anyhow::anyhow!("Runtime not started")) + } +} + +#[derive(Default)] +pub struct VMBuilder { + rw_drive: Option, + task_package: String, + cpu_cores: usize, + mem_mib: usize, + kernel_path: Option, + ramfs_path: Option, +} + +impl VMBuilder { + pub fn new( + cpu_cores: usize, + mem_mib: usize, + task_package: &Path, + rw_drive: Option<&PathBuf>, + ) -> Self { + Self { + rw_drive: rw_drive.map(|rw_drive| rw_drive.as_os_str().to_str().unwrap().into()), + task_package: task_package.as_os_str().to_str().unwrap().into(), + cpu_cores, + mem_mib, + kernel_path: None, + ramfs_path: None, + } + } + + pub fn with_kernel_path(mut self, path: String) -> Self { + self.kernel_path = Some(path); + self + } + + pub fn with_ramfs_path(mut self, path: String) -> Self { + self.ramfs_path = Some(path); + self + } + + pub async fn build(self, runtime_data: Arc>) -> anyhow::Result { + let mut data = runtime_data.lock().await; + let manager_sock; + // TODO: that doesn't need to be a tcp connection under unix + let p9_sock = "127.0.0.1:9005"; + + #[cfg(unix)] + { + let uid = uuid::Uuid::new_v4().simple().to_string(); + manager_sock = std::env::temp_dir().join(format!("{}.sock", uid)); + } + + #[cfg(windows)] + { + manager_sock = "127.0.0.1:9003"; + } + + let chardev_9p = |n, p: &str| { + let addr: SocketAddr = p.parse().unwrap(); + format!( + "socket,host={},port={},server,id={}", + addr.ip(), + addr.port(), + n + ) + }; + + let acceleration = if cfg!(windows) { "whpx" } else { "kvm" }; + + let kernel_path = self.kernel_path.unwrap_or_else(|| FILE_VMLINUZ.to_string()); + let ramfs_path = self + .ramfs_path + .unwrap_or_else(|| FILE_INITRAMFS.to_string()); + + let temp_dir = std::env::temp_dir(); + let uid = uuid::Uuid::new_v4().simple().to_string(); + let vpn_remote = data.vpn.clone(); + let inet_remote = data.inet.clone(); + + #[rustfmt::skip] + let (ab, (vpn, inet)) = { + let mut ab = ArgsBuilder::new(); + ab.add_2("-m", &format!("{}M", self.mem_mib)); + ab.add_1("-nographic"); + ab.add_2("-vga", "none"); + ab.add_2("-kernel", &kernel_path); + ab.add_2("-initrd", &ramfs_path); + ab.add_2("-smp", &format!("{}", self.cpu_cores)); + ab.add_2("-append", r#""console=ttyS0 panic=1""#); + ab.add_2("-device", "virtio-serial"); + ab.add_2("-chardev", &chardev("manager_cdev", &manager_sock)); + ab.add_2("-chardev", &chardev_9p("p9_cdev", p9_sock)); + ab.add_2("-device", "virtserialport,chardev=manager_cdev,name=manager_port" ); + ab.add_2("-device", "virtserialport,chardev=p9_cdev,name=p9_port"); + ab.add_2("-drive", &format!("file={},cache=unsafe,readonly=on,format=raw,if=virtio", self.task_package)); + if let Some(rw_drive) = self.rw_drive { ab.add_2("-drive", &format!("file={},format=qcow2,if=virtio", rw_drive)) } + ab.add_1("-no-reboot"); + ab.add_2("-accel", acceleration); + ab.add_1("-nodefaults"); + ab.add_2("--serial", "stdio"); + + let (vpn, inet) = + // backward-compatibility mode + if vpn_remote.is_none() && inet_remote.is_none() { + ab.add_2("-net", "none"); + + let vpn = configure_chardev_endpoint(&mut ab, ("vpn", 9004), &temp_dir, &uid)?; + let inet = configure_chardev_endpoint(&mut ab, ("inet", 9006), &temp_dir, &uid)?; + (vpn, inet) + // virtio-net (preferred) + } else { + let mut pair = SocketPairConf::default(); + pair.probe().await?; + + let vpn = configure_netdev_endpoint(&mut ab, "vpn", &vpn_remote, pair.first)?; + let inet = configure_netdev_endpoint(&mut ab, "inet", &inet_remote, pair.second)?; + (vpn, inet) + }; + + (ab, (vpn, inet)) + }; + + data.vpn.replace(vpn); + data.inet.replace(inet); + + let args = ab.get_args_vector(); + log::debug!("Arguments for VM array: {:?}", args); + log::info!("VM runtime command line: {}", ab.get_args_string()); + + Ok(VM { + #[cfg(windows)] + manager_sock: manager_sock.to_string(), + #[cfg(unix)] + manager_sock: manager_sock.display().to_string(), + p9_sock: p9_sock.to_string(), + args, + }) + } +} + +/// Hold VM parameters, can be later used to create Command object and spawn the VM +#[derive(Debug)] +pub struct VM { + manager_sock: String, + p9_sock: String, + + args: Vec, +} + +impl VM { + pub fn get_manager_sock(&self) -> &str { + &self.manager_sock + } + + pub fn get_9p_sock(&self) -> &str { + &self.p9_sock + } + + pub fn get_args(&self) -> &Vec { + &self.args + } + + /// Creates Command object with args from the VM instance + pub fn get_cmd>(&self, exe_path: S) -> tokio::process::Command { + let mut cmd = Command::new(exe_path); + cmd.args(&self.args); + + cmd + } +} + +#[derive(Copy, Clone, Debug)] +struct SocketConf { + ip: Ipv4Addr, + udp: u16, + tcp: u16, +} + +#[derive(Debug)] +struct SocketPairConf { + first: SocketConf, + second: SocketConf, +} + +impl Default for SocketPairConf { + fn default() -> Self { + let ip = Ipv4Addr::new(127, 0, 0, 1); + Self { + first: SocketConf { ip, udp: 0, tcp: 0 }, + second: SocketConf { ip, udp: 0, tcp: 0 }, + } + } +} + +impl SocketPairConf { + // FIXME: TOC/TOU + async fn probe(&mut self) -> anyhow::Result<()> { + let first = std::net::UdpSocket::bind(&(self.first.ip, self.first.udp))?; + let second = std::net::UdpSocket::bind(&(self.second.ip, self.second.udp))?; + + self.first.udp = first.local_addr()?.port(); + self.second.udp = second.local_addr()?.port(); + + let first = std::net::TcpListener::bind(&(self.first.ip, self.first.tcp))?; + let second = std::net::TcpListener::bind(&(self.second.ip, self.second.tcp))?; + + self.first.tcp = first.local_addr()?.port(); + self.second.tcp = second.local_addr()?.port(); + + Ok(()) + } +} + +struct HexWriter<'a>(&'a [u8]); + +impl<'a> std::fmt::LowerHex for HexWriter<'a> { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + for (i, byte) in self.0.iter().enumerate() { + let sep = if i < self.0.len().saturating_sub(1) { + ":" + } else { + "" + }; + fmt.write_fmt(format_args!("{:02x}{}", byte, sep))?; + } + Ok(()) + } +} + +#[cfg(unix)] +fn chardev(n: &str, p: &Path) -> String { + format!("socket,path={},server=on,wait=off,id={}", p.display(), n) +} + +#[cfg(windows)] +fn chardev(n: &str, p: &str) -> String { + let addr: SocketAddr = p.parse().unwrap(); + format!( + "socket,host={},port={},server=on,wait=off,id={}", + addr.ip(), + addr.port(), + n + ) +} + +fn configure_netdev_endpoint( + ab: &mut ArgsBuilder, + id: &str, + endpoint: &Option, + conf: SocketConf, +) -> anyhow::Result { + static COUNTER: AtomicU32 = AtomicU32::new(1); + + let ipv4 = conf.ip; + let endpoint = if let Some(endpoint) = endpoint { + match endpoint { + ContainerEndpoint::UdpDatagram(remote_addr) => { + let port = conf.udp; + + ab.add_2( + "-netdev", + &format!("socket,id={id},udp={remote_addr},localaddr={ipv4}:{port}"), + ); + + ContainerEndpoint::UdpDatagram(SocketAddrV4::new(ipv4, port).into()) + } + ContainerEndpoint::TcpStream(remote_addr) => { + ab.add_2("-netdev", &format!("socket,id={id},connect={remote_addr}")); + + ContainerEndpoint::TcpStream(*remote_addr) + } + ContainerEndpoint::TcpListener(_) => { + let port = conf.tcp; + + ab.add_2("-netdev", &format!("socket,id={id},listen={ipv4}:{port}")); + + ContainerEndpoint::TcpStream(SocketAddrV4::new(ipv4, port).into()) + } + _ => return Err(anyhow::anyhow!("Unsupported remote VPN VM endpoint")), + } + } else { + let port = conf.tcp; + + ab.add_2("-netdev", &format!("socket,id={id},listen={ipv4}:{port}")); + + ContainerEndpoint::TcpListener(SocketAddrV4::new(ipv4, port).into()) + }; + + let counter = COUNTER.fetch_add(1, Relaxed); + let bytes = counter.to_be_bytes(); + + ab.add_2( + "-device", + &format!( + "virtio-net-pci,netdev={id},mac=90:13:{:0x}", + HexWriter(&bytes), + ), + ); + + Ok(endpoint) +} + +fn configure_chardev_endpoint( + ab: &mut ArgsBuilder, + id: (&str, u16), + temp_dir: impl AsRef, + uid: &str, +) -> anyhow::Result { + let sock; + + #[cfg(unix)] + { + sock = temp_dir.as_ref().join(format!("{}_{}.sock", uid, id.0)); + } + + #[cfg(windows)] + { + sock = format!("127.0.0.1:{}", id.1); + } + + ab.add_2("-chardev", &chardev(&format!("{}_cdev", id.0), &sock)); + + ab.add_2( + "-device", + &format!("virtserialport,chardev={0}_cdev,name={0}_port", id.0), + ); + + #[cfg(unix)] + return Ok(ContainerEndpoint::UnixStream(sock)); + + #[cfg(windows)] + return Ok(ContainerEndpoint::TcpStream( + SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), id.1).into(), + )); +} diff --git a/runtime/src/vm_runner.rs b/runtime/src/vm_runner.rs new file mode 100644 index 00000000..487b37ed --- /dev/null +++ b/runtime/src/vm_runner.rs @@ -0,0 +1,231 @@ +use crate::demux_socket_comm::{ + start_demux_communication, stop_demux_communication, DemuxSocketHandle, MAX_P9_PACKET_SIZE, +}; +use crate::vm::VM; +use anyhow::anyhow; +use std::path::Path; +use std::path::PathBuf; +use std::process::Stdio; +use std::time::Duration; +use tokio::process::Child; +use tokio::{ + io::{self, AsyncBufReadExt}, + spawn, +}; +use tokio::{net::TcpStream, time::sleep}; +use ya_runtime_sdk::runtime_api::deploy::ContainerVolume; +use ya_vm_file_server::InprocServer; + +pub struct VMRunner { + instance: Option, + vm: VM, + file_servers: Option>, + demux_handle: Option, +} + +pub enum ReaderOutputType { + StdOutput, + StdError, +} + +impl VMRunner { + pub fn new(vm: VM) -> Self { + VMRunner { + instance: None, + vm, + demux_handle: None, + file_servers: None, + } + } + + pub fn get_vm(&self) -> &VM { + &self.vm + } + + pub async fn run_vm(&mut self, runtime_dir: PathBuf) -> anyhow::Result<()> { + #[cfg(windows)] + let vm_executable = "vmrt.exe"; + #[cfg(unix)] + let vm_executable = "vmrt"; + + let mut cmd = self.vm.get_cmd(&runtime_dir.join(vm_executable)); + cmd.current_dir(runtime_dir); + let mut instance = cmd + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .kill_on_drop(true) + .spawn()?; + + let stdout = instance + .stdout + .take() + .ok_or_else(|| anyhow!("stdout take error"))?; + let stderr = instance + .stderr + .take() + .ok_or_else(|| anyhow!("stdout take error"))?; + spawn(VMRunner::reader_to_log(stdout, ReaderOutputType::StdOutput)); + spawn(VMRunner::reader_to_log(stderr, ReaderOutputType::StdError)); + + self.instance = Some(instance); + + Ok(()) + } + + /* + pub async fn start_guest_agent_communication(&mut self, event_emitter: EventEmitter) -> anyhow::Result<()> { + let ga = GuestAgent::connected( + self.vm.get_manager_sock(), + 10, + move |notification, ga| { + let mut emitter = event_emitter.clone(); + async move { + let status = VMRunner::notification_into_status(notification, ga).await; + emitter.emit(status).await; + }.boxed() + }, + ).await?; + self.ga = Some(ga); + Ok(()) + } + */ + + async fn connect_to_9p_endpoint(&self, tries: usize) -> anyhow::Result { + log::debug!("Connect to the 9P VM endpoint..."); + + for _ in 0..tries { + match TcpStream::connect(self.vm.get_9p_sock()).await { + Ok(stream) => { + log::debug!("Connected to the 9P VM endpoint"); + return Ok(stream); + } + Err(e) => { + log::debug!("Failed to connect to 9P VM endpoint: {e}"); + // The VM is not ready yet, try again + sleep(Duration::from_secs(1)).await; + } + }; + } + + Err(anyhow!( + "Failed to connect to the 9P VM endpoint after #{tries} tries" + )) + } + + /// Spawns tasks handling 9p communication for given mount points + pub async fn start_9p_service( + &mut self, + work_dir: &Path, + volumes: &[ContainerVolume], + ) -> anyhow::Result<()> { + log::debug!("Connecting to the 9P VM endpoint..."); + + let vmp9stream = self.connect_to_9p_endpoint(10).await?; + + log::debug!("Spawn 9P inproc servers..."); + + let mut runtime_9ps = vec![]; + + for volume in volumes.iter() { + let mount_point_host = work_dir + .join(&volume.name) + .to_str() + .ok_or_else(|| anyhow!("cannot resolve 9P mount point"))? + .to_string(); + + log::debug!("Creating inproc 9p server with mount point {mount_point_host}"); + let runtime_9p = InprocServer::new(&mount_point_host); + + runtime_9ps.push(runtime_9p); + } + + log::debug!("Connect to 9P inproc servers..."); + + let mut p9streams = vec![]; + + for server in &runtime_9ps { + let client_stream = server.attach_client(MAX_P9_PACKET_SIZE); + p9streams.push(client_stream); + } + + let demux_socket_handle = start_demux_communication(vmp9stream, p9streams)?; + + self.file_servers = Some(runtime_9ps); + self.demux_handle = Some(demux_socket_handle); + // start_demux_communication(vm_stream, p9_streams); + Ok(()) + } + + pub async fn stop_p9_service(&mut self) { + //todo - stop servers as well + if let Some(demux_handle) = self.demux_handle.take() { + stop_demux_communication(demux_handle).await; + } + } + + pub async fn stop_vm( + &mut self, + timeout: &Duration, + kill_on_timeout: bool, + ) -> anyhow::Result<()> { + if let Some(instance) = self.instance.as_mut() { + let stopped = tokio::select! { + _ = tokio::time::sleep(*timeout) => { + log::warn!("Waiting for VM timed out"); + if !kill_on_timeout { + return Err(anyhow!("Waiting for VM timed out")) + } + false + } + _ = instance.wait() => { + log::info!("VM closed successfully"); + true + } + }; + if !stopped && kill_on_timeout { + instance.start_kill()?; + tokio::select! { + _ = tokio::time::sleep(*timeout) => { + log::error!("Cannot kill VM"); + return Err(anyhow!("Cannot kill VM due to unknown reason")) + } + _ = instance.wait() => { + log::info!("VM killed successfully"); + true + } + }; + } + }; + Ok(()) + } + + async fn reader_to_log(reader: T, stream_type: ReaderOutputType) { + let mut reader = io::BufReader::new(reader); + let mut buf = Vec::new(); + loop { + match reader.read_until(b'\n', &mut buf).await { + Ok(0) => { + log::warn!("VM: reader.read_until returned 0"); + tokio::time::sleep(Duration::from_millis(100)).await; + } + Ok(_) => { + let bytes = strip_ansi_escapes::strip(&buf).unwrap(); + match stream_type { + ReaderOutputType::StdOutput => { + log::debug!("VM: {}", String::from_utf8_lossy(&bytes).trim_end()); + } + ReaderOutputType::StdError => { + log::debug!( + "VM Error Stream: {}", + String::from_utf8_lossy(&bytes).trim_end() + ); + } + } + buf.clear(); + } + Err(e) => log::error!("VM output error: {}", e), + } + } + } +} diff --git a/runtime/src/vmrt.rs b/runtime/src/vmrt.rs deleted file mode 100644 index da387deb..00000000 --- a/runtime/src/vmrt.rs +++ /dev/null @@ -1,388 +0,0 @@ -use std::net::{Ipv4Addr, SocketAddrV4}; -use std::path::{Path, PathBuf}; -use std::process::Stdio; -use std::sync::atomic::AtomicU32; -use std::sync::atomic::Ordering::Relaxed; -use std::sync::Arc; - -use futures::lock::Mutex; -use futures::FutureExt; -use tokio::io::AsyncBufReadExt; -use tokio::{io, process, spawn}; - -use ya_runtime_sdk::runtime_api::server; -use ya_runtime_sdk::server::ContainerEndpoint; -use ya_runtime_sdk::{serialize, ErrorExt, EventEmitter}; - -use crate::deploy::Deployment; -use crate::guest_agent_comm::{GuestAgent, Notification}; - -const DIR_RUNTIME: &'static str = "runtime"; -const FILE_RUNTIME: &'static str = "vmrt"; -const FILE_VMLINUZ: &'static str = "vmlinuz-virt"; -const FILE_INITRAMFS: &'static str = "initramfs.cpio.gz"; - -#[derive(Default)] -pub struct RuntimeData { - pub runtime: Option, - pub vpn: Option, - pub inet: Option, - pub deployment: Option, - pub ga: Option>>, -} - -impl RuntimeData { - pub fn runtime(&mut self) -> anyhow::Result { - self.runtime - .take() - .ok_or_else(|| anyhow::anyhow!("Runtime process not available")) - } - - pub fn deployment(&self) -> anyhow::Result<&Deployment> { - self.deployment - .as_ref() - .ok_or_else(|| anyhow::anyhow!("Runtime not deployed")) - } - - pub fn ga(&self) -> anyhow::Result>> { - self.ga - .clone() - .ok_or_else(|| anyhow::anyhow!("Runtime not started")) - } -} - -pub async fn start_vmrt( - work_dir: PathBuf, - runtime_data: Arc>, - emitter: EventEmitter, -) -> anyhow::Result> { - let runtime_dir = runtime_dir().or_err("Unable to resolve current directory")?; - let temp_dir = std::env::temp_dir(); - let uid = uuid::Uuid::new_v4().simple().to_string(); - - let mut data = runtime_data.lock().await; - let deployment = data.deployment.clone().or_err("Missing deployment data")?; - let volumes = deployment.volumes.clone(); - - let manager_sock = temp_dir.join(format!("{}.sock", uid)); - let vpn_remote = data.vpn.clone(); - let inet_remote = data.inet.clone(); - - let mut cmd = process::Command::new(runtime_dir.join(FILE_RUNTIME)); - cmd.current_dir(&runtime_dir); - cmd.args(&[ - "-m", - format!("{}M", deployment.mem_mib).as_str(), - "-nographic", - "-vga", - "none", - "-kernel", - FILE_VMLINUZ, - "-initrd", - FILE_INITRAMFS, - "-enable-kvm", - "-cpu", - "host", - "-smp", - deployment.cpu_cores.to_string().as_str(), - "-append", - "console=ttyS0 panic=1", - "-device", - "virtio-serial", - "-device", - "virtio-rng-pci", - "-chardev", - format!( - "socket,path={},server=on,wait=off,id=manager_cdev", - manager_sock.display() - ) - .as_str(), - "-device", - "virtserialport,chardev=manager_cdev,name=manager_port", - "-drive", - format!( - "file={},cache=unsafe,readonly=on,format=raw,if=virtio", - deployment.task_package.display() - ) - .as_str(), - "-no-reboot", - ]); - - let (vpn, inet) = - // backward-compatibility mode - if vpn_remote.is_none() && inet_remote.is_none() { - cmd.args(["-net", "none"]); - - let vpn = configure_chardev_endpoint(&mut cmd, "vpn", &temp_dir, &uid)?; - let inet = configure_chardev_endpoint(&mut cmd, "inet", &temp_dir, &uid)?; - (vpn, inet) - // virtio-net (preferred) - } else { - let mut pair = SocketPairConf::default(); - pair.probe().await?; - - let vpn = configure_netdev_endpoint(&mut cmd, "vpn", &vpn_remote, pair.first)?; - let inet = configure_netdev_endpoint(&mut cmd, "inet", &inet_remote, pair.second)?; - (vpn, inet) - }; - - data.vpn.replace(vpn); - data.inet.replace(inet); - - for (idx, volume) in volumes.iter().enumerate() { - cmd.arg("-virtfs"); - cmd.arg(format!( - "local,id={tag},path={path},security_model=none,mount_tag={tag}", - tag = format!("mnt{}", idx), - path = work_dir.join(&volume.name).to_string_lossy(), - )); - } - - log::info!("Executing command: {cmd:?}"); - - let mut runtime = cmd - .stdin(Stdio::null()) - .stdout(Stdio::piped()) - .kill_on_drop(true) - .spawn()?; - - let stdout = runtime.stdout.take().unwrap(); - spawn(reader_to_log(stdout)); - - let ga = GuestAgent::connected(manager_sock, 10, move |notification, ga| { - let mut emitter = emitter.clone(); - async move { - let status = notification_into_status(notification, ga).await; - emitter.emit(status).await; - } - .boxed() - }) - .await?; - - { - let mut ga = ga.lock().await; - for (idx, volume) in deployment.volumes.iter().enumerate() { - ga.mount(format!("mnt{}", idx).as_str(), volume.path.as_str()) - .await? - .expect("Mount failed"); - } - } - - data.runtime.replace(runtime); - data.ga.replace(ga); - - Ok(None) -} - -#[derive(Copy, Clone, Debug)] -struct SocketConf { - ip: Ipv4Addr, - udp: u16, - tcp: u16, -} - -#[derive(Debug)] -struct SocketPairConf { - first: SocketConf, - second: SocketConf, -} - -impl Default for SocketPairConf { - fn default() -> Self { - let ip = Ipv4Addr::new(127, 0, 0, 1); - Self { - first: SocketConf { ip, udp: 0, tcp: 0 }, - second: SocketConf { ip, udp: 0, tcp: 0 }, - } - } -} - -impl SocketPairConf { - // FIXME: TOC/TOU - async fn probe(&mut self) -> anyhow::Result<()> { - let first = std::net::UdpSocket::bind(&(self.first.ip, self.first.udp))?; - let second = std::net::UdpSocket::bind(&(self.second.ip, self.second.udp))?; - - self.first.udp = first.local_addr()?.port(); - self.second.udp = second.local_addr()?.port(); - - let first = std::net::TcpListener::bind(&(self.first.ip, self.first.tcp))?; - let second = std::net::TcpListener::bind(&(self.second.ip, self.second.tcp))?; - - self.first.tcp = first.local_addr()?.port(); - self.second.tcp = second.local_addr()?.port(); - - Ok(()) - } -} - -fn configure_chardev_endpoint( - cmd: &mut process::Command, - id: &str, - temp_dir: impl AsRef, - uid: &str, -) -> anyhow::Result { - let sock = temp_dir.as_ref().join(format!("{}_{}.sock", uid, id)); - - cmd.arg("-chardev"); - cmd.arg(format!( - "socket,path={},server,wait=off,id={id}_cdev", - sock.display() - )); - - cmd.arg("-device"); - cmd.arg(format!("virtserialport,chardev={id}_cdev,name={id}_port")); - - Ok(ContainerEndpoint::UnixStream(sock)) -} - -fn configure_netdev_endpoint( - cmd: &mut process::Command, - id: &str, - endpoint: &Option, - conf: SocketConf, -) -> anyhow::Result { - static COUNTER: AtomicU32 = AtomicU32::new(1); - - let ipv4 = conf.ip; - let endpoint = if let Some(endpoint) = endpoint { - match endpoint { - ContainerEndpoint::UdpDatagram(remote_addr) => { - let port = conf.udp; - - cmd.arg("-netdev"); - cmd.arg(format!( - "socket,id={id},udp={remote_addr},localaddr={ipv4}:{port}" - )); - - ContainerEndpoint::UdpDatagram(SocketAddrV4::new(ipv4, port).into()) - } - ContainerEndpoint::TcpStream(remote_addr) => { - cmd.arg("-netdev"); - cmd.arg(format!("socket,id={id},connect={remote_addr}")); - - ContainerEndpoint::TcpStream(*remote_addr) - } - ContainerEndpoint::TcpListener(_) => { - let port = conf.tcp; - - cmd.arg("-netdev"); - cmd.arg(format!("socket,id={id},listen={ipv4}:{port}")); - - ContainerEndpoint::TcpStream(SocketAddrV4::new(ipv4, port).into()) - } - _ => return Err(anyhow::anyhow!("Unsupported remote VPN VM endpoint")), - } - } else { - let port = conf.tcp; - - cmd.arg("-netdev"); - cmd.arg(format!("socket,id={id},listen={ipv4}:{port}")); - - ContainerEndpoint::TcpListener(SocketAddrV4::new(ipv4, port).into()) - }; - - let counter = COUNTER.fetch_add(1, Relaxed); - let bytes = counter.to_be_bytes(); - - cmd.arg("-device"); - cmd.arg(format!( - "virtio-net-pci,netdev={id},mac=90:13:{:0x}", - HexWriter(&bytes), - )); - - Ok(endpoint) -} - -struct HexWriter<'a>(&'a [u8]); - -impl<'a> std::fmt::LowerHex for HexWriter<'a> { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - for (i, byte) in self.0.iter().enumerate() { - let sep = if i < self.0.len().saturating_sub(1) { - ":" - } else { - "" - }; - fmt.write_fmt(format_args!("{:02x}{}", byte, sep))?; - } - Ok(()) - } -} - -pub fn runtime_dir() -> io::Result { - Ok(std::env::current_exe()? - .parent() - .ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))? - .to_path_buf() - .join(DIR_RUNTIME)) -} - -async fn reader_to_log(reader: T) { - let mut reader = io::BufReader::new(reader); - let mut buf = Vec::new(); - loop { - match reader.read_until(b'\n', &mut buf).await { - Ok(0) => break, - Ok(_) => { - let bytes = strip_ansi_escapes::strip(&buf).unwrap(); - log::debug!("VM: {}", String::from_utf8_lossy(&bytes).trim_end()); - buf.clear(); - } - Err(e) => log::error!("VM output error: {}", e), - } - } -} - -async fn notification_into_status( - notification: Notification, - ga: Arc>, -) -> server::ProcessStatus { - match notification { - Notification::OutputAvailable { id, fd } => { - log::debug!("Process {} has output available on fd {}", id, fd); - - let output = { - let result = { - let mut guard = ga.lock().await; - guard.query_output(id, fd as u8, 0, u64::MAX).await - }; - match result { - Ok(Ok(vec)) => vec, - Ok(Err(e)) => { - log::error!("Remote error while querying output: {:?}", e); - Vec::new() - } - Err(e) => { - log::error!("Error querying output: {:?}", e); - Vec::new() - } - } - }; - let (stdout, stderr) = match fd { - 1 => (output, Vec::new()), - _ => (Vec::new(), output), - }; - - server::ProcessStatus { - pid: id, - running: true, - return_code: 0, - stdout, - stderr, - } - } - Notification::ProcessDied { id, reason } => { - log::debug!("Process {} died with {:?}", id, reason); - - // TODO: reason._type ? - server::ProcessStatus { - pid: id, - running: false, - return_code: reason.status as i32, - stdout: Vec::new(), - stderr: Vec::new(), - } - } - } -} diff --git a/scripts/gen_image.py b/scripts/gen_image.py new file mode 100644 index 00000000..72970787 --- /dev/null +++ b/scripts/gen_image.py @@ -0,0 +1,3 @@ +import os + +os.command("qemu-img create -f qcow2 foobar.qcow2 100M")