diff --git a/Cargo.lock b/Cargo.lock index 999616f..ea371ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,15 +25,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi 0.3.8", -] - [[package]] name = "anyhow" version = "1.0.31" @@ -64,7 +55,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae84766bab9f774e32979583ba56d6af8c701288c6dc99144819d5d2ee0b170f" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.6", "flate2", "futures-core", "memchr", @@ -93,6 +84,12 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + [[package]] name = "autocfg" version = "1.0.0" @@ -143,12 +140,49 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "buf_redux" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" +dependencies = [ + "memchr", + "safemem", +] + [[package]] name = "bumpalo" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5356f1d23ee24a1f785a56d1d1a5f0fd5b0f6a0c0fb2412ce11da71649ab78f6" +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "byteorder" version = "1.3.4" @@ -167,9 +201,9 @@ dependencies = [ [[package]] name = "bytes" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "cc" @@ -185,30 +219,25 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "chrono" -version = "0.4.11" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ + "libc", "num-integer", "num-traits", "serde", "time 0.1.43", + "winapi 0.3.8", ] [[package]] -name = "clap" -version = "2.33.1" +name = "cloudabi" +version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "ansi_term", - "atty", "bitflags", - "strsim", - "term_size", - "textwrap", - "unicode-width", - "vec_map", ] [[package]] @@ -248,7 +277,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg", + "autocfg 1.0.0", "cfg-if", "lazy_static", ] @@ -259,23 +288,30 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7313c0d620d0cb4dbd9d019e461a4beb501071ff46ec0ab933efb4daa76d73e3" +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array", +] + [[package]] name = "dirs" -version = "2.0.2" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff" dependencies = [ - "cfg-if", "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" dependencies = [ - "cfg-if", "libc", "redox_users", "winapi 0.3.8", @@ -295,9 +331,9 @@ checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" [[package]] name = "either" -version = "1.5.3" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "encoding_rs" @@ -316,11 +352,17 @@ checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" dependencies = [ "atty", "humantime", - "log", + "log 0.4.11", "regex", "termcolor", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "flate2" version = "1.0.14" @@ -354,6 +396,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -465,6 +513,15 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + [[package]] name = "getrandom" version = "0.1.14" @@ -482,26 +539,42 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.6", "fnv", "futures-core", "futures-sink", "futures-util", "http 0.2.1", "indexmap", - "log", + "log 0.4.11", "slab", "tokio", "tokio-util", ] [[package]] -name = "heck" -version = "0.3.1" +name = "headers" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +checksum = "ed18eb2459bf1a09ad2d6b1547840c3e5e62882fa09b9a6a20b1de8e3228848f" dependencies = [ - "unicode-segmentation", + "base64 0.12.1", + "bitflags", + "bytes 0.5.6", + "headers-core", + "http 0.2.1", + "mime 0.3.16", + "sha-1", + "time 0.1.43", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http 0.2.1", ] [[package]] @@ -541,7 +614,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.6", "fnv", "itoa", ] @@ -552,7 +625,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.6", "http 0.2.1", ] @@ -577,7 +650,7 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96816e1d921eca64d208a85aab4f7798455a8e34229ee5a88c935bdee1b78b14" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.6", "futures-channel", "futures-core", "futures-util", @@ -586,7 +659,7 @@ dependencies = [ "http-body", "httparse", "itoa", - "log", + "log 0.4.11", "net2", "pin-project", "time 0.1.43", @@ -601,7 +674,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.6", "hyper", "native-tls", "tokio", @@ -619,8 +692,8 @@ dependencies = [ "http 0.1.21", "httparse", "language-tags", - "log", - "mime", + "log 0.4.11", + "mime 0.3.16", "percent-encoding 1.0.1", "time 0.1.43", "unicase 2.6.0", @@ -654,7 +727,16 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" dependencies = [ - "autocfg", + "autocfg 1.0.0", +] + +[[package]] +name = "input_buffer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" +dependencies = [ + "bytes 0.5.6", ] [[package]] @@ -666,6 +748,12 @@ dependencies = [ "libc", ] +[[package]] +name = "ipnet" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" + [[package]] name = "itoa" version = "0.4.5" @@ -683,19 +771,16 @@ dependencies = [ [[package]] name = "k8s-openapi" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96affb18356fc998baa692c2185d198095ffdf6e6fff3bc9efb4927952f86851" +checksum = "57f95fd36c08ce592e67400a0f1a66f432196997d5a7e9a97e8743c33d8a9312" dependencies = [ - "base64 0.11.0", - "bytes 0.5.4", + "base64 0.12.1", + "bytes 0.5.6", "chrono", - "http 0.2.1", - "percent-encoding 2.1.0", "serde", "serde-value", "serde_json", - "url 2.1.1", ] [[package]] @@ -720,8 +805,11 @@ dependencies = [ "k8s-openapi", "kube", "kubelet", - "log", + "log 0.4.11", "oci-distribution", + "serde", + "serde_derive", + "serde_json", "tempfile", "tokio", "wasm3", @@ -730,13 +818,13 @@ dependencies = [ [[package]] name = "kube" -version = "0.33.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e753b759a9d9a36bd435c22ec9d31b6c8b89c452e5fd7932abf374ede02cc028" +checksum = "be34ed86dca3021a649b574a1628917d694bb75650a3b50c492e1786589a5ac6" dependencies = [ "Inflector", "base64 0.12.1", - "bytes 0.5.4", + "bytes 0.5.6", "chrono", "dirs", "either", @@ -744,12 +832,14 @@ dependencies = [ "futures-util", "http 0.2.1", "k8s-openapi", - "log", + "log 0.4.11", "openssl", + "pem", "reqwest", "serde", "serde_json", "serde_yaml", + "static_assertions", "thiserror", "time 0.2.16", "tokio", @@ -758,32 +848,36 @@ dependencies = [ [[package]] name = "kubelet" -version = "0.2.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92a7b9056806668b939b65479b7a95b25cffaf836027245e6b358b2f2e9d625e" +checksum = "28b6650bd7fd90c809ddec1f9128445c566bad0a3368461ac483be1aa8c3b718" dependencies = [ "anyhow", "async-trait", + "base64 0.12.1", "chrono", "dirs", "futures", "hostname", + "http 0.2.1", "hyper", "k8s-openapi", "kube", "lazy_static", - "log", + "log 0.4.11", "native-tls", "oci-distribution", + "rcgen", "reqwest", - "rpassword", "serde", "serde_json", - "structopt", + "serde_yaml", "thiserror", "tokio", "tokio-tls", "url 2.1.1", + "uuid", + "warp", ] [[package]] @@ -818,9 +912,18 @@ checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "log" -version = "0.4.8" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +dependencies = [ + "log 0.4.11", +] + +[[package]] +name = "log" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ "cfg-if", ] @@ -843,19 +946,40 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +[[package]] +name = "mime" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" +dependencies = [ + "log 0.3.9", +] + [[package]] name = "mime" version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "mime_guess" +version = "1.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216929a5ee4dd316b1702eedf5e74548c123d370f47841ceaac38ca154690ca3" +dependencies = [ + "mime 0.2.6", + "phf", + "phf_codegen", + "unicase 1.4.2", +] + [[package]] name = "mime_guess" version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" dependencies = [ - "mime", + "mime 0.3.16", "unicase 2.6.0", ] @@ -880,7 +1004,7 @@ dependencies = [ "iovec", "kernel32-sys", "libc", - "log", + "log 0.4.11", "miow", "net2", "slab", @@ -910,6 +1034,24 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "multipart" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136eed74cadb9edd2651ffba732b19a450316b680e4f48d6c79e905799e19d01" +dependencies = [ + "buf_redux", + "httparse", + "log 0.4.11", + "mime 0.2.6", + "mime_guess 1.8.8", + "quick-error", + "rand 0.6.5", + "safemem", + "tempfile", + "twoway", +] + [[package]] name = "native-tls" version = "0.2.4" @@ -918,7 +1060,7 @@ checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" dependencies = [ "lazy_static", "libc", - "log", + "log 0.4.11", "openssl", "openssl-probe", "openssl-sys", @@ -945,7 +1087,7 @@ version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ - "autocfg", + "autocfg 1.0.0", "num-traits", ] @@ -955,19 +1097,19 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ - "autocfg", + "autocfg 1.0.0", ] [[package]] name = "oci-distribution" -version = "0.1.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "845125104f2412b29592f6b8e8545245bd5a83f8776849c396b893e60ce4b890" +checksum = "93339a7d7ca64c348bd8bca841dfcfcdb6f2b3f5a1cc77518b75b9ad6884d96d" dependencies = [ "anyhow", "futures-util", "hyperx", - "log", + "log 0.4.11", "reqwest", "serde", "serde_json", @@ -981,11 +1123,17 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "openssl" -version = "0.10.29" +version = "0.10.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" +checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" dependencies = [ "bitflags", "cfg-if", @@ -1003,11 +1151,11 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.56" +version = "0.9.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02309a7f127000ed50594f0b50ecc69e7c654e16d41b4e8156d1b3df8e0b52e" +checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" dependencies = [ - "autocfg", + "autocfg 1.0.0", "cc", "libc", "pkg-config", @@ -1016,13 +1164,24 @@ dependencies = [ [[package]] name = "ordered-float" -version = "1.0.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" +checksum = "9fe9037165d7023b1228bc4ae9a2fa1a2b0095eca6c2998c624723dfd01314a5" dependencies = [ "num-traits", ] +[[package]] +name = "pem" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59698ea79df9bf77104aefd39cc3ec990cb9693fb59c3b0a70ddf2646fdffb4b" +dependencies = [ + "base64 0.12.1", + "once_cell", + "regex", +] + [[package]] name = "percent-encoding" version = "1.0.1" @@ -1035,6 +1194,45 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "phf" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" +dependencies = [ + "phf_shared", + "rand 0.6.5", +] + +[[package]] +name = "phf_shared" +version = "0.7.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" +dependencies = [ + "siphasher", + "unicase 1.4.2", +] + [[package]] name = "pin-project" version = "0.4.17" @@ -1079,32 +1277,6 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" -[[package]] -name = "proc-macro-error" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check 0.9.1", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "syn-mid", - "version_check 0.9.1", -] - [[package]] name = "proc-macro-hack" version = "0.5.15" @@ -1119,9 +1291,9 @@ checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" [[package]] name = "proc-macro2" -version = "1.0.13" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53f5ffe53a6b28e37c9c1ce74893477864d64f74778a93a4beb43c8fa167f639" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] @@ -1141,6 +1313,25 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.7", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc 0.1.0", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift", + "winapi 0.3.8", +] + [[package]] name = "rand" version = "0.7.3" @@ -1149,9 +1340,19 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ "getrandom", "libc", - "rand_chacha", - "rand_core", - "rand_hc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.7", + "rand_core 0.3.1", ] [[package]] @@ -1161,9 +1362,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", ] +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.5.1" @@ -1173,13 +1389,96 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi 0.3.8", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi 0.3.8", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.7", + "rand_core 0.4.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rcgen" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4974f7e96ee51fa3c90c3022e02c3a7117e71cb2a84518a55e44360135200c25" +dependencies = [ + "chrono", + "pem", + "ring", + "yasna", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", ] [[package]] @@ -1228,13 +1527,13 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.10.4" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b81e49ddec5109a9dcfc5f2a317ff53377c915e9ae9d4f2fb50914b85614e2" +checksum = "e9eaa17ac5d7b838b7503d118fa16ad88f440498bf9ffe5424e621f93190d61e" dependencies = [ "async-compression", - "base64 0.11.0", - "bytes 0.5.4", + "base64 0.12.1", + "bytes 0.5.6", "encoding_rs", "futures-core", "futures-util", @@ -1242,18 +1541,18 @@ dependencies = [ "http-body", "hyper", "hyper-tls", + "ipnet", "js-sys", "lazy_static", - "log", - "mime", - "mime_guess", + "log 0.4.11", + "mime 0.3.16", + "mime_guess 2.0.3", "native-tls", "percent-encoding 2.1.0", "pin-project-lite", "serde", "serde_json", "serde_urlencoded", - "time 0.1.43", "tokio", "tokio-tls", "url 2.1.1", @@ -1264,12 +1563,17 @@ dependencies = [ ] [[package]] -name = "rpassword" -version = "4.0.5" +name = "ring" +version = "0.16.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99371657d3c8e4d816fb6221db98fa408242b0b53bac08f8676a41f8554fe99f" +checksum = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4" dependencies = [ + "cc", "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", "winapi 0.3.8", ] @@ -1294,12 +1598,31 @@ dependencies = [ "semver", ] +[[package]] +name = "rustls" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1" +dependencies = [ + "base64 0.11.0", + "log 0.4.11", + "ring", + "sct", + "webpki", +] + [[package]] name = "ryu" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + [[package]] name = "schannel" version = "0.1.19" @@ -1310,6 +1633,22 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "sct" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "security-framework" version = "0.4.4" @@ -1350,18 +1689,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.110" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" +checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" dependencies = [ "serde_derive", ] [[package]] name = "serde-value" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a65a7291a8a568adcae4c10a677ebcedbc6c9cec91c054dee2ce40b0e3290eb" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" dependencies = [ "ordered-float", "serde", @@ -1369,9 +1708,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.110" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" +checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8" dependencies = [ "proc-macro2", "quote", @@ -1403,9 +1742,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c7a592a1ec97c9c1c68d75b6e537dcbf60c7618e038e7841e00af1d9ccf0c4" +checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5" dependencies = [ "dtoa", "linked-hash-map", @@ -1413,6 +1752,18 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer", + "digest", + "fake-simd", + "opaque-debug", +] + [[package]] name = "sha1" version = "0.6.0" @@ -1429,6 +1780,12 @@ dependencies = [ "libc", ] +[[package]] +name = "siphasher" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" + [[package]] name = "slab" version = "0.4.2" @@ -1441,12 +1798,24 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "standback" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e4b8c631c998468961a9ea159f064c5c8499b95b5e4a34b77849d45949d540" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "stdweb" version = "0.4.20" @@ -1496,58 +1865,17 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "structopt" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef" -dependencies = [ - "clap", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "syn" -version = "1.0.22" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1425de3c33b0941002740a420b1a906a350b88d08b82b2c8a01035a3f9447bac" +checksum = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] -[[package]] -name = "syn-mid" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tempfile" version = "3.1.0" @@ -1556,22 +1884,12 @@ checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ "cfg-if", "libc", - "rand", + "rand 0.7.3", "redox_syscall", "remove_dir_all", "winapi 0.3.8", ] -[[package]] -name = "term_size" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" -dependencies = [ - "libc", - "winapi 0.3.8", -] - [[package]] name = "termcolor" version = "1.1.0" @@ -1581,30 +1899,20 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "term_size", - "unicode-width", -] - [[package]] name = "thiserror" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5976891d6950b4f68477850b5b9e5aa64d955961466f9e174363f573e54e8ca7" +checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab81dbd1cd69cd2ce22ecfbdd3bdb73334ba25350649408cc6c085f46d89573d" +checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" dependencies = [ "proc-macro2", "quote", @@ -1670,11 +1978,11 @@ dependencies = [ [[package]] name = "tokio" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58" +checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.6", "fnv", "futures-core", "iovec", @@ -1701,6 +2009,18 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4" +dependencies = [ + "futures-core", + "rustls", + "tokio", + "webpki", +] + [[package]] name = "tokio-tls" version = "0.3.1" @@ -1711,16 +2031,29 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b8fe88007ebc363512449868d7da4389c9400072a3f666f212c7280082882a" +dependencies = [ + "futures", + "log 0.4.11", + "pin-project", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" dependencies = [ - "bytes 0.5.4", + "bytes 0.5.6", "futures-core", "futures-sink", - "log", + "log 0.4.11", "pin-project-lite", "tokio", ] @@ -1731,12 +2064,77 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" +[[package]] +name = "tracing" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27" +dependencies = [ + "cfg-if", + "log 0.4.11", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" +dependencies = [ + "pin-project", + "tracing", +] + [[package]] name = "try-lock" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" +[[package]] +name = "tungstenite" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfea31758bf674f990918962e8e5f07071a3161bd7c4138ed23e416e1ac4264e" +dependencies = [ + "base64 0.11.0", + "byteorder", + "bytes 0.5.6", + "http 0.2.1", + "httparse", + "input_buffer", + "log 0.4.11", + "rand 0.7.3", + "sha-1", + "url 2.1.1", + "utf-8", +] + +[[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" +dependencies = [ + "memchr", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + [[package]] name = "unicase" version = "1.4.2" @@ -1773,24 +2171,18 @@ dependencies = [ "smallvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" - -[[package]] -name = "unicode-width" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" - [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" version = "1.7.2" @@ -1814,16 +2206,31 @@ dependencies = [ ] [[package]] -name = "vcpkg" -version = "0.2.8" +name = "urlencoding" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" +checksum = "c9232eb53352b4442e40d7900465dfc534e8cb2dc8f18656fcb2ac16112b5593" [[package]] -name = "vec_map" -version = "0.8.2" +name = "utf-8" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" + +[[package]] +name = "uuid" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" +dependencies = [ + "rand 0.7.3", +] + +[[package]] +name = "vcpkg" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" [[package]] name = "version_check" @@ -1843,10 +2250,39 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" dependencies = [ - "log", + "log 0.4.11", "try-lock", ] +[[package]] +name = "warp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df341dee97c9ae29dfa5e0b0fbbbf620e0d6a36686389bedf83b3daeb8b0d0ac" +dependencies = [ + "bytes 0.5.6", + "futures", + "headers", + "http 0.2.1", + "hyper", + "log 0.4.11", + "mime 0.3.16", + "mime_guess 2.0.3", + "multipart", + "pin-project", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls", + "tokio-tungstenite", + "tower-service", + "tracing", + "tracing-futures", + "urlencoding", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -1873,7 +2309,7 @@ checksum = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94" dependencies = [ "bumpalo", "lazy_static", - "log", + "log 0.4.11", "proc-macro2", "quote", "syn", @@ -1967,6 +2403,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "winapi" version = "0.2.8" @@ -2012,9 +2458,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winreg" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" dependencies = [ "winapi 0.3.8", ] @@ -2048,3 +2494,12 @@ checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "yasna" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de7bff972b4f2a06c85f6d8454b09df153af7e3a4ec2aac81db1b105b684ddb" +dependencies = [ + "chrono", +] diff --git a/Cargo.toml b/Cargo.toml index 0900a7c..ebe1732 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,11 +12,14 @@ async-trait = "0.1" chrono = { version = "0.4", features = ["serde"] } env_logger = "0.7" futures = "0.3" -k8s-openapi = { version = "0.7", features = ["v1_17"] } -kube = "0.33" -kubelet = {version = "0.2", features = ["cli"]} +k8s-openapi = { version = "0.9", default-features = false, features = ["v1_17"] } +kube = { version= "0.40", default-features = false, features = ["native-tls"] } +kubelet = "0.5" log = "0.4" -oci-distribution = "0.1" +oci-distribution = "0.4" +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" tempfile = "3.1" tokio = { version = "0.2", features = ["fs", "stream", "macros", "io-util", "sync"] } wasm3 = { git = "https://github.com/Veykril/wasm3-rs.git", features = ["wasi"] } diff --git a/README.md b/README.md index a407d4e..c0dc9f1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ # krustlet-wasm3 [Krustlet](https://github.com/deislabs/krustlet) provider for the [wasm3](https://github.com/wasm3/wasm3) runtime. + +## Prerequisites + +[Install Clang 3.9](https://rust-lang.github.io/rust-bindgen/requirements.html#installing-clang-39) and [rust-bindgen](https://rust-lang.github.io/rust-bindgen) prior to running `cargo build`: + +```console +$ apt install llvm-dev libclang-dev clang +$ cargo install bindgen +``` diff --git a/src/provider/runtime.rs b/runtime.rs similarity index 71% rename from src/provider/runtime.rs rename to runtime.rs index fcebc69..90adcbb 100644 --- a/src/provider/runtime.rs +++ b/runtime.rs @@ -5,15 +5,16 @@ use tempfile::NamedTempFile; use tokio::sync::watch::{self, Sender}; use tokio::task::JoinHandle; use wasm3::{Environment, Module}; -use kubelet::handle::{RuntimeHandle, Stop}; -use kubelet::status::ContainerStatus; +use kubelet::container::Handle as ContainerHandle; +use kubelet::container::Status as ContainerStatus; +use kubelet::handle::StopHandler; -pub struct HandleStopper { +pub struct Runtime { handle: JoinHandle>, } #[async_trait::async_trait] -impl Stop for HandleStopper { +impl StopHandler for Runtime { async fn stop(&mut self) -> anyhow::Result<()> { // no nothing Ok(()) @@ -26,13 +27,13 @@ impl Stop for HandleStopper { } /// A runtime context for running a wasm module with wasm3 -pub struct Runtime { +pub struct Wasm3Runtime { module_bytes: Vec, stack_size: u32, output: Arc, } -impl Runtime { +impl Wasm3Runtime { pub async fn new + Send + Sync + 'static>(module_bytes: Vec, stack_size: u32, log_dir: L) -> anyhow::Result { let temp = tokio::task::spawn_blocking(move || -> anyhow::Result { Ok(NamedTempFile::new_in(log_dir)?) @@ -46,7 +47,7 @@ impl Runtime { }) } - pub async fn start(&mut self) -> anyhow::Result> { + pub async fn start(&mut self) -> anyhow::Result> { let temp = self.output.clone(); let output_write = tokio::task::spawn_blocking(move || -> anyhow::Result { Ok(temp.reopen()?) @@ -64,10 +65,9 @@ impl Runtime { temp: self.output.clone(), }; - Ok(RuntimeHandle::new( - HandleStopper{handle}, + Ok(ContainerHandle::new( + Runtime{handle}, log_handle_factory, - status_recv, )) } } @@ -77,7 +77,7 @@ pub struct LogHandleFactory { temp: Arc, } -impl kubelet::handle::LogHandleFactory for LogHandleFactory { +impl kubelet::log::HandleFactory for LogHandleFactory { /// Creates `tokio::fs::File` on demand for log reading. fn new_handle(&self) -> tokio::fs::File { tokio::fs::File::from_std(self.temp.reopen().unwrap()) @@ -94,20 +94,46 @@ async fn spawn_wasm3( _output_write: std::fs::File, //TODO: hook this up such that log output will be written to the file ) -> anyhow::Result>> { let handle = tokio::task::spawn_blocking(move || -> anyhow::Result<_> { - let env = Environment::new().expect("cannot create environment"); + + let env = match Environment::new() { + // We can't map errors here or it moves the send channel, so we + // do it in a match + Ok(m) => m, + Err(e) => { + let message = "cannot create environment"; + error!("{}: {:?}", message, e); + send( + status_sender.clone(), + name, + Status::Terminated { + failed: true, + message: message.into(), + timestamp: chrono::Utc::now(), + }, + &mut cx, + ); + return Err(e); + } + } + let rt = env.create_runtime(stack_size).expect("cannot create runtime"); + let module = Module::parse(&env, &module_bytes).expect("cannot parse module"); + let mut module = rt.load_module(module).expect("cannot load module"); + module.link_wasi().expect("cannot link WASI"); + let func = module.find_function::<(), ()>("_start").expect("cannot find function '_start' in module"); + func.call().expect("cannot call '_start' in module"); - status_sender - .broadcast(ContainerStatus::Terminated { + + status_sender.broadcast(ContainerStatus::Terminated { failed: false, message: "Module run completed".into(), timestamp: chrono::Utc::now(), - }) - .expect("status should be able to send"); + }).expect("status should be able to send"); + Ok(()) }); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..5442486 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,170 @@ +//! A custom kubelet backend that can run [WASI](https://wasi.dev/) based workloads +//! +//! The crate provides the [`WasiProvider`] type which can be used +//! as a provider with [`kubelet`]. +//! +//! # Example +//! ```rust,no_run +//! use kubelet::{Kubelet, config::Config}; +//! use kubelet::store::oci::FileStore; +//! use std::sync::Arc; +//! use wasi_provider::WasiProvider; +//! +//! async { +//! // Get a configuration for the Kubelet +//! let kubelet_config = Config::default(); +//! let client = oci_distribution::Client::default(); +//! let store = Arc::new(FileStore::new(client, &std::path::PathBuf::from(""))); +//! +//! // Load a kubernetes configuration +//! let kubeconfig = kube::Config::infer().await.unwrap(); +//! +//! // Instantiate the provider type +//! let provider = WasiProvider::new(store, &kubelet_config, kubeconfig.clone()).await.unwrap(); +//! +//! // Instantiate the Kubelet +//! let kubelet = Kubelet::new(provider, kubeconfig, kubelet_config).await.unwrap(); +//! // Start the Kubelet and block on it +//! kubelet.start().await.unwrap(); +//! }; +//! ``` + +#![deny(missing_docs)] + +mod wasi_runtime; + +use std::collections::HashMap; +use std::path::PathBuf; +use std::sync::Arc; + +use async_trait::async_trait; +use kubelet::node::Builder; +use kubelet::pod::{key_from_pod, pod_key, Handle, Pod}; +use kubelet::provider::{Provider, ProviderError}; +use kubelet::store::Store; +use kubelet::volume::Ref; +use tokio::sync::mpsc::{self, Receiver, Sender}; +use tokio::sync::RwLock; +use wasi_runtime::Runtime; + +mod states; + +use states::registered::Registered; +use states::terminated::Terminated; + +const TARGET_WASM32_WASI: &str = "wasm32-wasi"; +const LOG_DIR_NAME: &str = "wasi-logs"; +const VOLUME_DIR: &str = "volumes"; + +/// WasiProvider provides a Kubelet runtime implementation that executes WASM +/// binaries conforming to the WASI spec. +#[derive(Clone)] +pub struct WasiProvider { + shared: SharedPodState, +} + +#[derive(Clone)] +struct SharedPodState { + handles: Arc>>>, + store: Arc, + log_path: PathBuf, + kubeconfig: kube::Config, + volume_path: PathBuf, +} + +impl WasiProvider { + /// Create a new wasi provider from a module store and a kubelet config + pub async fn new( + store: Arc, + config: &kubelet::config::Config, + kubeconfig: kube::Config, + ) -> anyhow::Result { + let log_path = config.data_dir.join(LOG_DIR_NAME); + let volume_path = config.data_dir.join(VOLUME_DIR); + tokio::fs::create_dir_all(&log_path).await?; + tokio::fs::create_dir_all(&volume_path).await?; + Ok(Self { + shared: SharedPodState { + handles: Default::default(), + store, + log_path, + volume_path, + kubeconfig, + }, + }) + } +} + +struct ModuleRunContext { + modules: HashMap>, + volumes: HashMap, + status_sender: Sender<(String, kubelet::container::Status)>, + status_recv: Receiver<(String, kubelet::container::Status)>, +} + +/// State that is shared between pod state handlers. +pub struct PodState { + key: String, + run_context: ModuleRunContext, + errors: usize, + shared: SharedPodState, +} + +// No cleanup state needed, we clean up when dropping PodState. +#[async_trait] +impl kubelet::state::AsyncDrop for PodState { + async fn async_drop(self) { + { + let mut handles = self.shared.handles.write().await; + handles.remove(&self.key); + } + } +} + +#[async_trait::async_trait] +impl Provider for WasiProvider { + type InitialState = Registered; + type TerminatedState = Terminated; + type PodState = PodState; + + const ARCH: &'static str = TARGET_WASM32_WASI; + + async fn node(&self, builder: &mut Builder) -> anyhow::Result<()> { + builder.set_architecture("wasm-wasi"); + builder.add_taint("NoExecute", "kubernetes.io/arch", Self::ARCH); + Ok(()) + } + + async fn initialize_pod_state(&self, pod: &Pod) -> anyhow::Result { + let (tx, rx) = mpsc::channel(pod.all_containers().len()); + let run_context = ModuleRunContext { + modules: Default::default(), + volumes: Default::default(), + status_sender: tx, + status_recv: rx, + }; + let key = key_from_pod(pod); + Ok(PodState { + key, + run_context, + errors: 0, + shared: self.shared.clone(), + }) + } + + async fn logs( + &self, + namespace: String, + pod_name: String, + container_name: String, + sender: kubelet::log::Sender, + ) -> anyhow::Result<()> { + let mut handles = self.shared.handles.write().await; + let handle = handles + .get_mut(&pod_key(&namespace, &pod_name)) + .ok_or_else(|| ProviderError::PodNotFound { + pod_name: pod_name.clone(), + })?; + handle.output(&container_name, sender).await + } +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 15b7f97..0000000 --- a/src/main.rs +++ /dev/null @@ -1,23 +0,0 @@ -use kubelet::config::Config; -use kubelet::module_store::FileModuleStore; -use kubelet::Kubelet; - -mod provider; -use provider::Provider; - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let config = Config::new_from_flags(env!("CARGO_PKG_VERSION")); - let kubeconfig = kube::Config::infer().await?; - - env_logger::init(); - - let client = oci_distribution::Client::default(); - let mut module_store_path = config.data_dir.join(".oci"); - module_store_path.push("modules"); - let store = FileModuleStore::new(client, &module_store_path); - - let provider = Provider::new(store, &config, kubeconfig.clone()).await?; - let kubelet = Kubelet::new(provider, kubeconfig, config); - kubelet.start().await -} diff --git a/src/provider/mod.rs b/src/provider/mod.rs deleted file mode 100644 index 3a011e4..0000000 --- a/src/provider/mod.rs +++ /dev/null @@ -1,206 +0,0 @@ -//! A custom kubelet backend that can run [WASI](https://wasi.dev/) based workloads using wasm3 -//! -//! The crate provides the [`Provider`] type which can be used -//! as a provider with [`kubelet`]. -//! -//! # Example -//! ```rust,no_run -//! use kubelet::{Kubelet, config::Config}; -//! use kubelet::module_store::FileModuleStore; -//! use wasm3_provider::Provider; -//! -//! async { -//! // Get a configuration for the Kubelet -//! let kubelet_config = Config::default(); -//! let client = oci_distribution::Client::default(); -//! let store = FileModuleStore::new(client, &std::path::PathBuf::from("")); -//! -//! // Load a kubernetes configuration -//! let kubeconfig = kube::Config::infer().await.unwrap(); -//! -//! // Instantiate the provider type -//! let provider = Provider::new(store, &kubelet_config, kubeconfig.clone()).await.unwrap(); -//! -//! // Instantiate the Kubelet -//! let kubelet = Kubelet::new(provider, kubeconfig, kubelet_config); -//! // Start the Kubelet and block on it -//! kubelet.start().await.unwrap(); -//! }; -//! ``` -use std::path::PathBuf; - -use k8s_openapi::api::core::v1::Pod as KubePod; -use kube::api::DeleteParams; -use kube::Api; -use kube::Config as KubeConfig; -use kubelet::config::Config as KubeletConfig; -use kubelet::handle::{key_from_pod, pod_key, PodHandle}; -use kubelet::module_store::ModuleStore; -use kubelet::provider::ProviderError; -use kubelet::Pod; -use log::{debug, error, info, trace}; -use std::collections::HashMap; -use std::sync::Arc; -use tokio::sync::RwLock; - -mod runtime; -use runtime::{HandleStopper, LogHandleFactory, Runtime}; - -const TARGET_WASM32_WASI: &str = "wasm32-wasi"; -const LOG_DIR_NAME: &str = "wasm3-logs"; - -/// Provider provides a Kubelet runtime implementation that executes WASM -/// binaries conforming to the WASI spec -pub struct Provider { - handles: Arc>>>, - store: S, - log_path: PathBuf, - kubeconfig: KubeConfig, -} - -impl Provider { - /// Create a new wasi provider from a module store and a kubelet config - pub async fn new(store: S, config: &KubeletConfig, kubeconfig: KubeConfig) -> anyhow::Result { - let log_path = config.data_dir.join(LOG_DIR_NAME); - tokio::fs::create_dir_all(&log_path).await?; - Ok(Self { - handles: Default::default(), - store, - log_path, - kubeconfig, - }) - } -} - -#[async_trait::async_trait] -impl kubelet::Provider for Provider { - const ARCH: &'static str = TARGET_WASM32_WASI; - - async fn add(&self, pod: Pod) -> anyhow::Result<()> { - // To run an Add event, we load the WASM, update the pod status to Running, - // and then execute the WASM, passing in the relevant data. - // When the pod finishes, we update the status to Succeeded unless it - // produces an error, in which case we mark it Failed. - - let pod_name = pod.name(); - let mut containers = HashMap::new(); - let client = kube::Client::new(self.kubeconfig.clone()); - - let mut modules = self.store.fetch_pod_modules(&pod).await?; - info!("Starting containers for pod {:?}", pod_name); - for container in pod.containers() { - let module_data = modules - .remove(&container.name) - .expect("FATAL ERROR: module map not properly populated"); - - // TODO: expose this as a feature flag (--stack-size) - let mut runtime = Runtime::new(module_data, (1024 * 60) as u32, self.log_path.clone()).await?; - - debug!("Starting container {} on thread", container.name); - let handle = runtime.start().await?; - containers.insert(container.name.clone(), handle); - } - info!( - "All containers started for pod {:?}. Updating status", - pod_name - ); - - // Wrap this in a block so the write lock goes out of scope when we are done - { - // Grab the entry while we are creating things - let mut handles = self.handles.write().await; - handles.insert( - key_from_pod(&pod), - PodHandle::new(containers, pod, client, None)?, - ); - } - - Ok(()) - } - - async fn modify(&self, pod: Pod) -> anyhow::Result<()> { - // The only things we care about are: - // 1. metadata.deletionTimestamp => signal all containers to stop and then mark them - // as terminated - // 2. spec.containers[*].image, spec.initContainers[*].image => stop the currently - // running containers and start new ones? - // 3. spec.activeDeadlineSeconds => Leaving unimplemented for now - // TODO: Determine what the proper behavior should be if labels change - debug!( - "Got pod modified event for {} in namespace {}", - pod.name(), - pod.namespace() - ); - trace!("Modified pod spec: {:#?}", pod.as_kube_pod()); - if let Some(_timestamp) = pod.deletion_timestamp() { - let mut handles = self.handles.write().await; - match handles.get_mut(&key_from_pod(&pod)) { - Some(h) => { - h.stop().await?; - // Follow up with a delete when everything is stopped - let dp = DeleteParams { - grace_period_seconds: Some(0), - ..Default::default() - }; - let pod_client: Api = Api::namespaced( - kube::client::Client::new(self.kubeconfig.clone()), - pod.namespace(), - ); - match pod_client.delete(pod.name(), &dp).await { - Ok(_) => Ok(()), - Err(e) => Err(e.into()), - } - } - None => { - // This isn't an error with the pod, so don't return an error (otherwise it will - // get updated in its status). This is an unlikely case to get into and means - // that something is likely out of sync, so just log the error - error!( - "Unable to find pod {} in namespace {} when trying to stop all containers", - pod.name(), - pod.namespace() - ); - Ok(()) - } - } - } else { - Ok(()) - } - // TODO: Implement behavior for stopping old containers and restarting when the container - // image changes - } - - async fn delete(&self, pod: Pod) -> anyhow::Result<()> { - let mut handles = self.handles.write().await; - match handles.remove(&key_from_pod(&pod)) { - Some(_) => debug!( - "Pod {} in namespace {} removed", - pod.name(), - pod.namespace() - ), - None => info!( - "unable to find pod {} in namespace {}, it was likely already deleted", - pod.name(), - pod.namespace() - ), - } - Ok(()) - } - - async fn logs( - &self, - namespace: String, - pod_name: String, - _container_name: String, - _sender: kubelet::LogSender, - ) -> anyhow::Result<()> { - let mut handles = self.handles.write().await; - let _containers = handles - .get_mut(&pod_key(&namespace, &pod_name)) - .ok_or_else(|| ProviderError::PodNotFound { - pod_name: pod_name.clone(), - })?; - // pod.output(&container_name, sender).await - unimplemented!() - } -} diff --git a/src/states.rs b/src/states.rs new file mode 100644 index 0000000..192ddaa --- /dev/null +++ b/src/states.rs @@ -0,0 +1,11 @@ +pub(crate) mod completed; +pub(crate) mod crash_loop_backoff; +pub(crate) mod error; +pub(crate) mod image_pull; +pub(crate) mod image_pull_backoff; +pub(crate) mod initializing; +pub(crate) mod registered; +pub(crate) mod running; +pub(crate) mod starting; +pub(crate) mod terminated; +pub(crate) mod volume_mount; diff --git a/src/states/completed.rs b/src/states/completed.rs new file mode 100644 index 0000000..e0ed125 --- /dev/null +++ b/src/states/completed.rs @@ -0,0 +1,25 @@ +use crate::PodState; +use kubelet::state::prelude::*; + +/// Pod was deleted. +#[derive(Default, Debug)] +pub struct Completed; + +#[async_trait::async_trait] +impl State for Completed { + async fn next( + self: Box, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result> { + Ok(Transition::Complete(Ok(()))) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Succeeded, "Completed") + } +} diff --git a/src/states/crash_loop_backoff.rs b/src/states/crash_loop_backoff.rs new file mode 100644 index 0000000..4626558 --- /dev/null +++ b/src/states/crash_loop_backoff.rs @@ -0,0 +1,29 @@ +use crate::PodState; +use kubelet::state::prelude::*; + +use super::registered::Registered; + +#[derive(Debug)] +pub struct CrashLoopBackoff; + +#[async_trait::async_trait] +impl State for CrashLoopBackoff { + async fn next( + self: Box, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result> { + tokio::time::delay_for(std::time::Duration::from_secs(60)).await; + Ok(Transition::next(self, Registered)) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, "CrashLoopBackoff") + } +} + +impl TransitionTo for CrashLoopBackoff {} diff --git a/src/states/error.rs b/src/states/error.rs new file mode 100644 index 0000000..7568c8a --- /dev/null +++ b/src/states/error.rs @@ -0,0 +1,41 @@ +use kubelet::state::prelude::*; + +use super::crash_loop_backoff::CrashLoopBackoff; +use super::registered::Registered; +use crate::PodState; + +#[derive(Default, Debug)] +/// The Pod failed to run. +// If we manually implement, we can allow for arguments. +pub struct Error { + pub message: String, +} + +#[async_trait::async_trait] +impl State for Error { + async fn next( + self: Box, + pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result> { + pod_state.errors += 1; + if pod_state.errors > 3 { + pod_state.errors = 0; + Ok(Transition::next(self, CrashLoopBackoff)) + } else { + tokio::time::delay_for(std::time::Duration::from_secs(5)).await; + Ok(Transition::next(self, Registered)) + } + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, &self.message) + } +} + +impl TransitionTo for Error {} +impl TransitionTo for Error {} diff --git a/src/states/finished.rs b/src/states/finished.rs new file mode 100644 index 0000000..858cf6c --- /dev/null +++ b/src/states/finished.rs @@ -0,0 +1,25 @@ +use crate::PodState; +use kubelet::state::prelude::*; + +/// Pod execution completed with no errors. +#[derive(Default, Debug)] +pub struct Finished; + +#[async_trait::async_trait] +impl State for Finished { + async fn next( + self: Box, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result> { + Ok(Transition::Complete(Ok(()))) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Succeeded, "Finished") + } +} diff --git a/src/states/image_pull.rs b/src/states/image_pull.rs new file mode 100644 index 0000000..6f0cde0 --- /dev/null +++ b/src/states/image_pull.rs @@ -0,0 +1,47 @@ +use kubelet::state::prelude::*; +use log::error; + +use crate::PodState; + +use super::image_pull_backoff::ImagePullBackoff; +use super::volume_mount::VolumeMount; + +/// Kubelet is pulling container images. +#[derive(Default, Debug)] +pub struct ImagePull; + +#[async_trait::async_trait] +impl State for ImagePull { + async fn next( + self: Box, + pod_state: &mut PodState, + pod: &Pod, + ) -> anyhow::Result> { + let client = kube::Client::new(pod_state.shared.kubeconfig.clone()); + let auth_resolver = kubelet::secret::RegistryAuthResolver::new(client, &pod); + pod_state.run_context.modules = match pod_state + .shared + .store + .fetch_pod_modules(&pod, &auth_resolver) + .await + { + Ok(modules) => modules, + Err(e) => { + error!("{:?}", e); + return Ok(Transition::next(self, ImagePullBackoff)); + } + }; + Ok(Transition::next(self, VolumeMount)) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, "ImagePull") + } +} + +impl TransitionTo for ImagePull {} +impl TransitionTo for ImagePull {} diff --git a/src/states/image_pull_backoff.rs b/src/states/image_pull_backoff.rs new file mode 100644 index 0000000..7f71b3b --- /dev/null +++ b/src/states/image_pull_backoff.rs @@ -0,0 +1,29 @@ +use super::image_pull::ImagePull; +use crate::PodState; +use kubelet::state::prelude::*; + +/// Kubelet encountered an error when pulling container image. +#[derive(Default, Debug)] +pub struct ImagePullBackoff; + +#[async_trait::async_trait] +impl State for ImagePullBackoff { + async fn next( + self: Box, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result> { + tokio::time::delay_for(std::time::Duration::from_secs(60)).await; + Ok(Transition::next(self, ImagePull)) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, "ImagePullBackoff") + } +} + +impl TransitionTo for ImagePullBackoff {} diff --git a/src/states/initializing.rs b/src/states/initializing.rs new file mode 100644 index 0000000..8de743e --- /dev/null +++ b/src/states/initializing.rs @@ -0,0 +1,151 @@ +use std::collections::HashMap; + +use log::{error, info}; + +use crate::PodState; +use k8s_openapi::api::core::v1::Pod as KubePod; +use kube::api::{Api, PatchParams}; +use kubelet::container::{ContainerKey, Status as ContainerStatus}; +use kubelet::pod::{key_from_pod, Handle}; +use kubelet::state::prelude::*; + +use super::error::Error; +use super::starting::{start_container, ContainerHandleMap, Starting}; + +async fn patch_init_status( + client: &Api, + pod_name: &str, + name: String, + status: &ContainerStatus, +) -> anyhow::Result<()> { + // We need to fetch the current status because there is no way to merge with a strategic merge patch here + let mut init_container_statuses = match client.get(pod_name).await { + Ok(p) => match p.status { + Some(s) => s.init_container_statuses.unwrap_or_default(), + None => { + return Err(anyhow::anyhow!( + "Pod is missing status information. This should not occur" + )); + } + }, + Err(e) => { + error!("Unable to fetch current status of pod {}, aborting status patch (will be retried on next status update): {:?}", pod_name, e); + // FIXME: This is kinda...ugly. But we can't just + // randomly abort the whole process due to an error + // fetching the current status. We should probably have + // some sort of retry mechanism, but that is another + // task for another day + Vec::default() + } + }; + match init_container_statuses.iter().position(|s| s.name == name) { + Some(i) => { + init_container_statuses[i] = status.to_kubernetes(name); + } + None => { + init_container_statuses.push(status.to_kubernetes(name)); + } + }; + let s = serde_json::json!({ + "metadata": { + "resourceVersion": "", + }, + "status": { + "initContainerStatuses": init_container_statuses, + } + }); + client + .patch_status(pod_name, &PatchParams::default(), serde_json::to_vec(&s)?) + .await?; + Ok(()) +} + +#[derive(Debug)] +pub struct Initializing; + +#[async_trait::async_trait] +impl State for Initializing { + async fn next( + self: Box, + pod_state: &mut PodState, + pod: &Pod, + ) -> anyhow::Result> { + let client: Api = Api::namespaced( + kube::Client::new(pod_state.shared.kubeconfig.clone()), + pod.namespace(), + ); + let mut container_handles: ContainerHandleMap = HashMap::new(); + + for init_container in pod.init_containers() { + info!( + "Starting init container {:?} for pod {:?}", + init_container.name(), + pod.name() + ); + + let handle = start_container(pod_state, pod, &init_container).await?; + + container_handles.insert( + ContainerKey::Init(init_container.name().to_string()), + handle, + ); + + while let Some((name, status)) = pod_state.run_context.status_recv.recv().await { + if let Err(e) = patch_init_status(&client, &pod.name(), name.clone(), &status).await + { + error!("Unable to patch status, will retry on next update: {:?}", e); + } + if let ContainerStatus::Terminated { + timestamp: _, + message, + failed, + } = status + { + if failed { + // HACK: update the status message informing which init container failed + let s = serde_json::json!({ + "metadata": { + "resourceVersion": "", + }, + "status": { + "message": format!("Init container {} failed", name), + } + }); + + // If we are in a failed state, insert in the init containers we already ran + // into a pod handle so they are available for future log fetching + let pod_handle = Handle::new(container_handles, pod.clone(), None).await?; + let pod_key = key_from_pod(&pod); + { + let mut handles = pod_state.shared.handles.write().await; + handles.insert(pod_key, pod_handle); + } + client + .patch_status( + pod.name(), + &PatchParams::default(), + serde_json::to_vec(&s)?, + ) + .await?; + return Ok(Transition::next(self, Error { message })); + } else { + break; + } + } + } + } + info!("Finished init containers for pod {:?}", pod.name()); + Ok(Transition::next(self, Starting::new(container_handles))) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pmeod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Running, "Initializing") + } +} + +impl TransitionTo for Initializing {} +impl TransitionTo for Initializing {} diff --git a/src/states/registered.rs b/src/states/registered.rs new file mode 100644 index 0000000..4453529 --- /dev/null +++ b/src/states/registered.rs @@ -0,0 +1,58 @@ +use log::{error, info}; + +use super::error::Error; +use super::image_pull::ImagePull; +use crate::PodState; +use kubelet::container::Container; +use kubelet::state::prelude::*; + +fn validate_pod_runnable(pod: &Pod) -> anyhow::Result<()> { + for container in pod.containers() { + validate_not_kube_proxy(&container)?; + } + Ok(()) +} + +fn validate_not_kube_proxy(container: &Container) -> anyhow::Result<()> { + if let Some(image) = container.image()? { + if image.whole().starts_with("k8s.gcr.io/kube-proxy") { + return Err(anyhow::anyhow!("Cannot run kube-proxy")); + } + } + Ok(()) +} + +/// The Kubelet is aware of the Pod. +#[derive(Default, Debug)] +pub struct Registered; + +#[async_trait::async_trait] +impl State for Registered { + async fn next( + self: Box, + _pod_state: &mut PodState, + pod: &Pod, + ) -> anyhow::Result> { + match validate_pod_runnable(&pod) { + Ok(_) => (), + Err(e) => { + let message = format!("{:?}", e); + error!("{}", message); + return Ok(Transition::next(self, Error { message })); + } + } + info!("Pod added: {}.", pod.name()); + Ok(Transition::next(self, ImagePull)) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, "Registered") + } +} + +impl TransitionTo for Registered {} +impl TransitionTo for Registered {} diff --git a/src/states/running.rs b/src/states/running.rs new file mode 100644 index 0000000..ec159d8 --- /dev/null +++ b/src/states/running.rs @@ -0,0 +1,111 @@ +use k8s_openapi::api::core::v1::Pod as KubePod; +use kube::api::{Api, PatchParams}; +use kubelet::container::Status; +use kubelet::state::prelude::*; +use log::error; + +use super::completed::Completed; +use super::error::Error; +use crate::PodState; + +async fn patch_container_status( + client: &Api, + pod_name: &str, + name: String, + status: &Status, +) -> anyhow::Result<()> { + // We need to fetch the current status because there is no way to merge with a strategic merge patch ere + let mut container_statuses = match client.get(pod_name).await { + Ok(p) => match p.status { + Some(s) => s.container_statuses.unwrap_or_default(), + None => { + return Err(anyhow::anyhow!( + "Pod is missing status information. This should not occur" + )); + } + }, + Err(e) => { + error!("Unable to fetch current status of pod {}, aborting status patch (will be retried on next status update): {:?}", pod_name, e); + // FIXME: This is kinda...ugly. But we can't just + // randomly abort the whole process due to an error + // fetching the current status. We should probably have + // some sort of retry mechanism, but that is another + // task for another day + Vec::default() + } + }; + match container_statuses.iter().position(|s| s.name == name) { + Some(i) => { + container_statuses[i] = status.to_kubernetes(name); + } + None => { + container_statuses.push(status.to_kubernetes(name)); + } + }; + let s = serde_json::json!({ + "metadata": { + "resourceVersion": "", + }, + "status": { + "containerStatuses": container_statuses, + } + }); + client + .patch_status(pod_name, &PatchParams::default(), serde_json::to_vec(&s)?) + .await?; + Ok(()) +} + +/// The Kubelet is running the Pod. +#[derive(Default, Debug)] +pub struct Running; + +#[async_trait::async_trait] +impl State for Running { + async fn next( + self: Box, + pod_state: &mut PodState, + pod: &Pod, + ) -> anyhow::Result> { + let client: Api = Api::namespaced( + kube::Client::new(pod_state.shared.kubeconfig.clone()), + pod.namespace(), + ); + let mut completed = 0; + let total_containers = pod.containers().len(); + + while let Some((name, status)) = pod_state.run_context.status_recv.recv().await { + // TODO: implement a container state machine such that it will self-update the Kubernetes API as it transitions through these stages. + if let Err(e) = patch_container_status(&client, &pod.name(), name, &status).await { + error!("Unable to patch status, will retry on next update: {:?}", e); + } + if let Status::Terminated { + timestamp: _, + message, + failed, + } = status + { + if failed { + return Ok(Transition::next(self, Error { message })); + } else { + completed += 1; + if completed == total_containers { + return Ok(Transition::next(self, Completed)); + } + } + } + } + Ok(Transition::next(self, Completed)) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Running, "Running") + } +} + +impl TransitionTo for Running {} +impl TransitionTo for Running {} diff --git a/src/states/starting.rs b/src/states/starting.rs new file mode 100644 index 0000000..086f6a9 --- /dev/null +++ b/src/states/starting.rs @@ -0,0 +1,141 @@ +use std::collections::HashMap; +use std::ops::Deref; +use std::path::PathBuf; +use std::sync::Arc; + +use log::{debug, info}; +use tokio::sync::Mutex; + +use kubelet::container::{Container, ContainerKey}; +use kubelet::pod::{key_from_pod, Handle}; +use kubelet::provider; +use kubelet::state::prelude::*; +use kubelet::volume::Ref; + +use crate::wasi_runtime::{self, HandleFactory, Runtime, WasiRuntime}; +use crate::PodState; + +use super::running::Running; + +fn volume_path_map( + container: &Container, + volumes: &HashMap, +) -> anyhow::Result>> { + if let Some(volume_mounts) = container.volume_mounts().as_ref() { + volume_mounts + .iter() + .map(|vm| -> anyhow::Result<(PathBuf, Option)> { + // Check the volume exists first + let vol = volumes.get(&vm.name).ok_or_else(|| { + anyhow::anyhow!( + "no volume with the name of {} found for container {}", + vm.name, + container.name() + ) + })?; + let mut guest_path = PathBuf::from(&vm.mount_path); + if let Some(sub_path) = &vm.sub_path { + guest_path.push(sub_path); + } + // We can safely assume that this should be valid UTF-8 because it would have + // been validated by the k8s API + Ok((vol.deref().clone(), Some(guest_path))) + }) + .collect::>>>() + } else { + Ok(HashMap::default()) + } +} + +pub(crate) async fn start_container( + pod_state: &mut PodState, + pod: &Pod, + container: &Container, +) -> anyhow::Result> +{ + let module_data = pod_state + .run_context + .modules + .remove(container.name()) + .expect("FATAL ERROR: module map not properly populated"); + let client = kube::Client::new(pod_state.shared.kubeconfig.clone()); + let env = provider::env_vars(&container, pod, &client).await; + let args = container.args().clone().unwrap_or_default(); + let container_volumes = volume_path_map(container, &pod_state.run_context.volumes)?; + + let runtime = WasiRuntime::new( + container.name().to_owned(), + module_data, + env, + args, + container_volumes, + pod_state.shared.log_path.clone(), + pod_state.run_context.status_sender.clone(), + ) + .await?; + + debug!("Starting container {} on thread", container.name()); + runtime.start().await +} + +pub(crate) type ContainerHandleMap = + HashMap>; + +#[derive(Default, Debug)] +/// The Kubelet is starting the Pod containers +pub(crate) struct Starting { + init_handles: Arc>, +} + +impl Starting { + pub(crate) fn new(init_handles: ContainerHandleMap) -> Self { + Starting { + init_handles: Arc::new(Mutex::new(init_handles)), + } + } +} + +#[async_trait::async_trait] +impl State for Starting { + async fn next( + self: Box, + pod_state: &mut PodState, + pod: &Pod, + ) -> anyhow::Result> { + let mut container_handles: ContainerHandleMap = HashMap::new(); + + { + let mut lock = self.init_handles.lock().await; + container_handles.extend((*lock).drain()) + } + + info!("Starting containers for pod {:?}", pod.name()); + for container in pod.containers() { + let container_handle = start_container(pod_state, &pod, &container).await?; + container_handles.insert( + ContainerKey::App(container.name().to_string()), + container_handle, + ); + } + + let pod_handle = Handle::new(container_handles, pod.clone(), None).await?; + let pod_key = key_from_pod(&pod); + { + let mut handles = pod_state.shared.handles.write().await; + handles.insert(pod_key, pod_handle); + } + info!("All containers started for pod {:?}.", pod.name()); + + Ok(Transition::next(self, Running)) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, "Starting") + } +} + +impl TransitionTo for Starting {} diff --git a/src/states/terminated.rs b/src/states/terminated.rs new file mode 100644 index 0000000..ca33676 --- /dev/null +++ b/src/states/terminated.rs @@ -0,0 +1,29 @@ +use crate::PodState; +use kubelet::state::prelude::*; + +/// Pod was deleted. +#[derive(Default, Debug)] +pub struct Terminated; + +#[async_trait::async_trait] +impl State for Terminated { + async fn next( + self: Box, + pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result> { + let mut lock = pod_state.shared.handles.write().await; + if let Some(handle) = lock.get_mut(&pod_state.key) { + handle.stop().await?; + } + Ok(Transition::Complete(Ok(()))) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Succeeded, "Terminated") + } +} diff --git a/src/states/volume_mount.rs b/src/states/volume_mount.rs new file mode 100644 index 0000000..1ee976d --- /dev/null +++ b/src/states/volume_mount.rs @@ -0,0 +1,45 @@ +use crate::PodState; +use kubelet::state::prelude::*; +use kubelet::volume::Ref; +use log::error; + +use super::error::Error; +use super::initializing::Initializing; + +/// Kubelet is pulling container images. +#[derive(Default, Debug)] +pub struct VolumeMount; + +#[async_trait::async_trait] +impl State for VolumeMount { + async fn next( + self: Box, + pod_state: &mut PodState, + pod: &Pod, + ) -> anyhow::Result> { + let client = kube::Client::new(pod_state.shared.kubeconfig.clone()); + pod_state.run_context.volumes = + match Ref::volumes_from_pod(&pod_state.shared.volume_path, &pod, &client).await { + Ok(volumes) => volumes, + Err(e) => { + error!("{:?}", e); + let error_state = Error { + message: e.to_string(), + }; + return Ok(Transition::next(self, error_state)); + } + }; + Ok(Transition::next(self, Initializing)) + } + + async fn json_status( + &self, + _pod_state: &mut PodState, + _pod: &Pod, + ) -> anyhow::Result { + make_status(Phase::Pending, "VolumeMount") + } +} + +impl TransitionTo for VolumeMount {} +impl TransitionTo for VolumeMount {} diff --git a/src/wasi_runtime.rs b/src/wasi_runtime.rs new file mode 100644 index 0000000..ef7b458 --- /dev/null +++ b/src/wasi_runtime.rs @@ -0,0 +1,341 @@ +use futures::task; +use log::{error, info, trace}; +use std::collections::HashMap; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use tempfile::NamedTempFile; +use tokio::sync::mpsc::Sender; +use tokio::task::JoinHandle; +use wasm3::{Environment, Module}; + +use kubelet::container::Handle as ContainerHandle; +use kubelet::container::Status; +use kubelet::handle::StopHandler; + +pub struct Runtime { + handle: JoinHandle>, +} + +#[async_trait::async_trait] +impl StopHandler for Runtime { + async fn stop(&mut self) -> anyhow::Result<()> { + Ok(()) + } + + async fn wait(&mut self) -> anyhow::Result<()> { + (&mut self.handle).await??; + Ok(()) + } +} + +/// WasiRuntime provides a WASI compatible runtime. A runtime should be used for +/// each "instance" of a process and can be passed to a thread pool for running +pub struct WasiRuntime { + // name of the process + name: String, + /// Data needed for the runtime + data: Arc, + /// The tempfile that output from the wasmtime process writes to + output: Arc, + /// A channel to send status updates on the runtime + status_sender: Sender<(String, Status)>, + /// The stack size to be used with the wasm3 runtime. + stack_size: u32, +} + +struct Data { + /// binary module data to be run as a wasm module + module_data: Vec, + /// key/value environment variables made available to the wasm process + env: HashMap, + /// the arguments passed as the command-line arguments list + args: Vec, + /// a hash map of local file system paths to optional path names in the runtime + /// (e.g. /tmp/foo/myfile -> /app/config). If the optional value is not given, + /// the same path will be allowed in the runtime + dirs: HashMap>, +} + +/// Holds our tempfile handle. +pub struct HandleFactory { + temp: Arc, +} + +impl kubelet::log::HandleFactory for HandleFactory { + /// Creates `tokio::fs::File` on demand for log reading. + fn new_handle(&self) -> tokio::fs::File { + tokio::fs::File::from_std(self.temp.reopen().unwrap()) + } +} + +impl WasiRuntime { + /// Creates a new WasiRuntime + /// + /// # Arguments + /// + /// * `module_path` - the path to the WebAssembly binary + /// * `env` - a collection of key/value pairs containing the environment variables + /// * `args` - the arguments passed as the command-line arguments list + /// * `dirs` - a map of local file system paths to optional path names in the runtime + /// (e.g. /tmp/foo/myfile -> /app/config). If the optional value is not given, + /// the same path will be allowed in the runtime + /// * `log_dir` - location for storing logs + pub async fn new + Send + Sync + 'static>( + name: String, + module_data: Vec, + env: HashMap, + args: Vec, + dirs: HashMap>, + log_dir: L, + status_sender: Sender<(String, Status)>, + ) -> anyhow::Result { + let temp = tokio::task::spawn_blocking(move || -> anyhow::Result { + Ok(NamedTempFile::new_in(log_dir)?) + }) + .await??; + + // We need to use named temp file because we need multiple file handles + // and if we are running in the temp dir, we run the possibility of the + // temp file getting cleaned out from underneath us while running. If we + // think it necessary, we can make these permanent files with a cleanup + // loop that runs elsewhere. These will get deleted when the reference + // is dropped + Ok(WasiRuntime { + name, + data: Arc::new(Data { + module_data, + env, + args, + dirs, + }), + output: Arc::new(temp), + status_sender, + stack_size: 1, + }) + } + + pub async fn start(&self) -> anyhow::Result> { + let temp = self.output.clone(); + // Because a reopen is blocking, run in a blocking task to get new + // handles to the tempfile + let output_write = tokio::task::spawn_blocking(move || -> anyhow::Result { + Ok(temp.reopen()?) + }) + .await??; + + let handle = self.spawn_wasm3(output_write).await?; + + let log_handle_factory = HandleFactory { + temp: self.output.clone(), + }; + + Ok(ContainerHandle::new( + Runtime { + handle, + }, + log_handle_factory, + )) + } + + // Spawns a running wasmtime instance with the given context and status + // channel. Due to the Instance type not being Send safe, all of the logic + // needs to be done within the spawned task + async fn spawn_wasm3( + &self, + _output_write: std::fs::File, + ) -> anyhow::Result>> { + // Clone the module data Arc so it can be moved + let data = self.data.clone(); + let name = self.name.clone(); + let stack_size = self.stack_size.clone(); + let status_sender = self.status_sender.clone(); + + let handle = tokio::task::spawn_blocking(move || -> anyhow::Result<_> { + let waker = task::noop_waker(); + let mut cx = Context::from_waker(&waker); + + let env = match Environment::new() { + // We can't map errors here or it moves the send channel, so we + // do it in a match + Ok(e) => e, + Err(e) => { + let message = "cannot create environment"; + error!("{}: {:?}", message, e); + send( + status_sender.clone(), + name, + Status::Terminated { + failed: true, + message: message.into(), + timestamp: chrono::Utc::now(), + }, + &mut cx, + ); + return Err(anyhow::anyhow!("{}: {}", message, e)); + } + }; + + let rt = match env.create_runtime(stack_size) { + // We can't map errors here or it moves the send channel, so we + // do it in a match + Ok(rt) => rt, + Err(e) => { + let message = "cannot create runtime"; + error!("{}: {:?}", message, e); + send( + status_sender.clone(), + name.clone(), + Status::Terminated { + failed: true, + message: message.into(), + timestamp: chrono::Utc::now(), + }, + &mut cx, + ); + return Err(anyhow::anyhow!("{}: {}", message, e)); + } + }; + + let module = match Module::parse(&env, &data.module_data) { + // We can't map errors here or it moves the send channel, so we + // do it in a match + Ok(m) => m, + Err(e) => { + let message = "cannot parse module"; + error!("{}: {:?}", message, e); + send( + status_sender.clone(), + name.clone(), + Status::Terminated { + failed: true, + message: message.into(), + timestamp: chrono::Utc::now(), + }, + &mut cx, + ); + return Err(anyhow::anyhow!("{}: {}", message, e)); + } + }; + + let mut module = match rt.load_module(module) { + // We can't map errors here or it moves the send channel, so we + // do it in a match + Ok(m) => m, + Err(e) => { + let message = "cannot load module"; + error!("{}: {:?}", message, e); + send( + status_sender.clone(), + name.clone(), + Status::Terminated { + failed: true, + message: message.into(), + timestamp: chrono::Utc::now(), + }, + &mut cx, + ); + return Err(anyhow::anyhow!("{}: {}", message, e)); + } + }; + + match module.link_wasi() { + // We can't map errors here or it moves the send channel, so we + // do it in a match + Ok(_) => {} + Err(e) => { + let message = "cannot link WASI"; + error!("{}: {:?}", message, e); + send( + status_sender.clone(), + name.clone(), + Status::Terminated { + failed: true, + message: message.into(), + timestamp: chrono::Utc::now(), + }, + &mut cx, + ); + return Err(anyhow::anyhow!("{}: {}", message, e)); + } + }; + + let func = match module.find_function::<(), ()>("_start") { + // We can't map errors here or it moves the send channel, so we + // do it in a match + Ok(f) => f, + Err(e) => { + let message = "cannot find function '_start' in module"; + error!("{}: {:?}", message, e); + send( + status_sender.clone(), + name.clone(), + Status::Terminated { + failed: true, + message: message.into(), + timestamp: chrono::Utc::now(), + }, + &mut cx, + ); + return Err(anyhow::anyhow!("{}: {}", message, e)); + } + }; + + match func.call() { + // We can't map errors here or it moves the send channel, so we + // do it in a match + Ok(_) => {} + Err(e) => { + let message = "unable to run module"; + error!("{}: {:?}", message, e); + send( + status_sender.clone(), + name.clone(), + Status::Terminated { + failed: true, + message: message.into(), + timestamp: chrono::Utc::now(), + }, + &mut cx, + ); + return Err(anyhow::anyhow!("{}: {}", message, e)); + } + }; + + info!("module run complete"); + send( + status_sender.clone(), + name, + Status::Terminated { + failed: false, + message: "Module run complete".into(), + timestamp: chrono::Utc::now(), + }, + &mut cx, + ); + + Ok(()) + }); + + Ok(handle) + } +} + +fn send(mut sender: Sender<(String, Status)>, name: String, status: Status, cx: &mut Context<'_>) { + loop { + if let Poll::Ready(r) = sender.poll_ready(cx) { + if r.is_ok() { + sender + .try_send((name, status)) + .expect("Possible deadlock, exiting"); + return; + } + trace!("Receiver for status showing as closed: {:?}", r); + } + trace!( + "Channel for container {} not ready for send. Attempting again", + name + ); + } +}